歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 在64位主機上編譯產生32位的目標代碼

在64位主機上編譯產生32位的目標代碼

日期:2017/3/1 10:05:38   编辑:Linux編程

今天有看CS630的Chapter 15,發現裡頭的一個例程manydots.s無法正常編譯。

$ gcc manydots.s -o manydots
/tmp/ccIvmRVT.o: In function `_start':
(.text+0x0): multiple definition of `_start'
/usr/lib/gcc/x86_64-linux-gnu/4.3.1/../../../../lib/crt1.o:(.text+0x0): first defined here
/usr/lib/gcc/x86_64-linux-gnu/4.3.1/../../../../lib/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: ld returned 1 exit status
$ sed -i -e "s/_start/main/g" manydots.s
$ gcc manydots.s -o manydots
$ ./manydots
Segmentation fault
$ file manydots
manydots:
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked
(uses shared libs), for GNU/Linux 2.6.8, not stripped

通過上面的實驗,首先根據提示發現_start有multiple
definition,所以根據自己的經驗,把裡頭的_start符號替換成main。因為用gcc編譯時默認的程序入口是main,而不是
_start。資料[2]告訴我們_start是真正的程序入口,但是這個真正的入口是gcc默認鏈接到我們的可執行文件中的,如果我們這裡又設置一個
_start符號,那就是multiple
definition了(你可以通過gcc的-S選項編譯一個C語言程序產生匯編代碼,看看匯編代碼的程序入口,剛好是main,關於誰是真正的程序入
口,你可以看看資料[2])。
那修改了_start為main後,能夠正常編譯,但為什麼還出現segmentation fault呢?原因是源代碼mangdots.s是為32為平台寫的,而我用的處理器是64位的,並且安裝了64位的Ubuntu/Linux。
[color="black"]$ cat /proc/cpuinfo | grep "model name"
model name : AMD Athlon(tm) 64 X2 Dual Core Processor 4200+
model name : AMD Athlon(tm) 64 X2 Dual Core Processor 4200+
$ uname -a
Linux falcon 2.6.26-1-amd64 #1 SMP Thu Aug 28 11:13:42 UTC 2008 x86_64 GNU/Linux


我們發現,64位平台跟32位平台有很大的不同,包括參數傳遞方式,指令集都有很大的變化,那怎麼能夠讓它正常運行呢?利用
gcc的-m32參數編譯產生32位的目標代碼,而不是64位的目標代碼,因為32位的目標代碼可以運行在64位的主機上。
$ gcc -m32 manydots.s -o manydots
$ ./manydots
How many dots do you want to see? 10
..........
$ file manydots
manydots:
ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically
linked (uses shared libs), for GNU/Linux 2.6.8, not stripped
可以看到,這樣就okay了。
實際上,我們還可以分步來做:先匯編,後鏈接。這樣可以減少目標代碼的大小,先看看原來的大小。
[color="black"]$ wc -c manydots
6495 manydots


我們分步匯編、鏈接:
[color="black"]// 這個時候是需要一個默認的_start入口的,如果不指定,會默認設置一個程序入口地址,因為這個時候沒有人給我們設置一個真正的入口_start了。
$ sed -i -e "s/main/_start/g" manydots.s
$ as --32 manydots.s -o manydots.o
$ ld -m elf_i386 manydots.o -o manydots
$ wc -c manydots
1026 manydots
$ echo "6495-1026" | bc
5469
$ ./manydots
How many dots do you want to see? 10
..........

可以發現,這樣也可以正常工作,不過目標減少了5469個字節。為什麼會有這樣的效果呢?資料[2]給出了詳細的解釋,如果感興趣,可以研究一下。
對了,“as --32 manydots.s -o manydots.o”可以直接用“$ gcc -m32 -c manydots.s -o manydots.o” 來做,他們兩個實際上做了同一個事情,你可以通過gcc的--verbose查看:
$ gcc --verbose -m32 -c manydots.s -o manydots.o
Using built-in specs.
Target: x86_64-linux-gnu
Configured
with: ../src/configure -v --with-pkgversion='Debian 4.3.1-9'
--with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs
--enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr
--enable-shared --with-system-zlib --libexecdir=/usr/lib
--without-included-gettext --enable-threads=posix --enable-nls
--with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3
--enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc
--enable-mpfr --enable-cld --enable-checking=release
--build=x86_64-linux-gnu --host=x86_64-linux-gnu
--target=x86_64-linux-gnu
Thread model: posix
gcc version 4.3.1 (Debian 4.3.1-9)
COLLECT_GCC_OPTIONS='-v' '-m32' '-c' '-o' 'manydots.o' '-mtune=generic'
as -V -Qy --32 -o manydots.o manydots.s
GNU assembler version 2.18.0 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.18.0.20080103
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.3.1/32/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/32/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-m32' '-c' '-o' 'manydots.o' '-mtune=generic'
最後總結一下,在64位主機上編譯產生32位目標代碼的辦法:
一、辦法一:直接通過gcc匯編、鏈接
1、確保不要有重復的_start入口,把_start替換成main
2、用gcc加上-m32參數進行匯編和鏈接
二、辦法二:分步匯編、鏈接
1、匯編的時候,用gcc加上-m32參數或者用as加上--32參數。
2、在鏈接的時候,用ld加上-m elf_i386參數。

Copyright © Linux教程網 All Rights Reserved