$ gcc manydots.s -o manydots |
通过上面的实验,首先根据提示发现_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。
$ cat /proc/cpuinfo | grep "model name" |
根据资料[3,4,5],我们发现,64位平台跟32位平台有很大的不同,包括参数传递方式,指令集都有很大的变化,那怎么能够让它正常运行呢?利用 gcc的-m32参数编译产生32位的目标代码,而不是64位的目标代码,因为32位的目标代码可以运行在64位的主机上。
$ gcc -m32 manydots.s -o manydots |
可以看到,这样就okay了。
实际上,我们还可以分步来做:先汇编,后链接。这样可以减少目标代码的大小,先看看原来的大小。
$ wc -c manydots |
我们分步汇编、链接:
// 这个时候是需要一个默认的_start入口的,如果不指定,会默认设置一个程序入口地址,因为这个时候没有人给我们设置一个真正的入口_start了。 |
可以发现,这样也可以正常工作,不过目标减少了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 |
最后总结一下,在64位主机上编译产生32位目标代码的办法:
一、办法一:直接通过gcc汇编、链接
1、确保不要有重复的_start入口,把_start替换成main
2、用gcc加上-m32参数进行汇编和链接
二、办法二:分步汇编、链接
1、汇编的时候,用gcc加上-m32参数或者用as加上--32参数。
2、在链接的时候,用ld加上-m elf_i386参数。
[1] CS630 on ubuntu with qemu
http://oss.lzu.edu.cn/blog/blog.php?/do_showone/tid_1808.html
[2] 为你的可执行文件“减肥”
http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_1547.html
[3] GCC在AMD64平台下的参数传递
http://hi.baidu.com/bluebanboom/blog/item/381959af65ff36fbfaed5068.html
[4] Intel的64位扩展技术简介
[5] AMD64 Architecture Tech Docs
,,30_2252_739_7044,00.html