写了个汇编程序,照着别人的写的,其中有个系统调用,然后就去搜了一下linux系统的系统调用表。这一搜不要紧,搜出问题来了。
我找到了两个版本,一个x86-64版本的,一个i386版本的。比如32位的sys_exit系统调用里调用号%eax为1,64位里sys_exit调用号%rax为60,这个不重要。然后我看了一下我的代码,我用了32位的调用号,功能也是32位的。第一反应就是编译的elf是32位的,然后我看了一下readelf -h 64,为64位。后来发现还有个简单命令 直接file 64就能看。然后我就觉得很不可思议。
接着我查了两个版本的头文件在哪里,看怎么用的。学到很多东西。比如查看调用头文件
printf SYS_exit | gcc -include sys/syscall.h -E -
echo '#include ' | gcc -E -x c -l
echo '#include ' | gcc -E -x c - | egrep '# [0-9]+ ' | awk '{print $3;}' | sort -u
发现是找的是64位的,然后我更糊涂了。主要就这几个文件
# 1 "/usr/include/x86_64-linux-gnu/asm/unistd_64.h" 1 3 4
# 14 "/usr/include/x86_64-linux-gnu/asm/unistd.h" 2 3 4
# 25 "/usr/include/x86_64-linux-gnu/sys/syscall.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/syscall.h" 1 3 4
# 1 "/usr/include/x86_64-linux-gnu/asm/unistd_32.h"
看头文件确实是看是否定义i386,但是我其实是没用头文件的,直接写的中断号,用as编译的代码,没有使用gcc。
然后我查unistd_32.h和unistd_64.h的区别,然后找到原因了。
首先我64位系统,默认都是编译的64位二进制这个没啥问题。汇编和链接的时候可以制定生成32位:
as e.s --32 -o 32.o
ld 32.o -o 32
我生成32位汇编也是一样能执行的。
其次,32位汇编和64位汇编所使用的系统中断是不一样的。intel和amd都提供了硬件中断,而且还不兼容。为了兼容尽量使用的一下两个:
32位: sysenter sysexit.
64位: syscall sysret.
与这个相对应的是软件中断,那个速度比较慢,也是比较老的遗留下来的。int $0x80。
然后我代码用的是这个老的软中断。软中断用的都是32位的调用号,而且在64位的系统里,同样是可以使用的,因为他只使用低32位的寄存器。所以问题就解决了,我是用的软中断导致的,应该看软中断的调用号,而不是看编译的二进制是64位还是32位。
Read more...