x86-64的calling convention,和ia32有什么大区别? (汇编程序问题)
/* filename: ccpuid.s 使用C库的printf和exit的cpupid.s * * $ as <-gstabs+> -o ccpuid.o ccpuid.s * $ ld -o ccpuid ccpuid.o -lc -dynamic-linker /lib64/ld-linux.so.2 * $ ./ccpuid */ /* 一个不需要链接其他 *.o 的*.s文件,必须定义.data、.text这两个section .section .bss /* vendor ID字符串包含在ebx-edx-ecx中 */ /* 调用printf("The CPU Vendor is %s\n", buffer) */ /* 调用void _exit(int status) */ |
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
这由于 x64 的寻址模式所致,x64 的 imme 操作数除了 GPRs 外一律都不能是 64 位,
说明白点,就是:
除了 mov rax, 0x1122334455667788、 mov rdx, 0x1122334455667788 之外,所有的 imme 操作数最大只能是 32 位。
像: mov qword ptr [rax], 0x1122334455667788 这条指令结果只能是:截断 imme 且符号扩展为64位,
等于:mov qword ptr [rax], 0x0000000055667788
PUSH 在 64 位下,所有的 PUSH imme 都是 64 位的,结果只像 mov [mem],imme 一像截断 imme 且符号扩展为 64 位。
所以: pushq $buffer 这条指令的结果可能并不是你想要的。
要这么做:
movq $buffer, %rax
movq $output, %r8
pushq %rax
pushq $r8
.... ...
[ 本帖最后由 mik 于 2008-4-30 18:15 编辑 ]
思考了一下,貌似即使 64 位符号扩展应该没问题的,大多数代码不会用到0x80000000以上的区域的吧
我上面说的方法你试试看行不行。不行的话把代码 objdump 贴出来再分析分析。看看是不是调用 printf 时的编码问题
[ 本帖最后由 mik 于 2008-4-30 18:27 编辑 ]
LZ:
不是imme被截断的原因,原因是:64位下是用寄存器传递参数。
偶也是习惯32位的编程模式,一时忘了64位编程的变化。渐愧
你了解下 64 位的ABI规定:
1、7个通用寄存器(rdi,rsi,rdx,rcx,r8,r9 和 rax)依次用作函数传递参数。
2、rsp 及 rbp 用于管理堆栈
3、r10 及 r11 用于临时寄存器
4、5个通用寄存器(r12,r13,r14,r15 及 rbx)由被调用方保存
这里有一份 AMD 的 ABI:
老大,这个帖子是我发的。
请问你是从哪里找到的这份abi文档? 我在AMD的Mnauals/Developer Guides/Revision Guides这三部分的文档中都没有找到它。
谢谢
版主,能不能把修改后的所有源代码贴出来?
当然可以:)
/* filename: ccpuid.s 使用C库的printf和exit的cpupid.s
*
* for x86-64
*
* $ as <-gstabs+> -o ccpuid.o ccpuid.s
* $ ld -o ccpuid ccpuid.o -lc -dynamic-linker /lib64/ld-linux.so.2
* $ ./ccpuid
* The CPU Vendor is AuthenticAMD
* $ echo $? //打印上一个子进程的退出值
* 0
*/
/* 一个不需要链接其他 *.o 的*.s文件,必须定义.data、.text这两个section
* 另外,.bss这个section可选。 必须定义_start,这是程序的第一条指令开始
* 的地方 -- 除非ld时用-e <symbol>指定别的符号为入口地址。 又,_start
* 必须用.globl这个directive指定为全局符号
*/
.section .data
output:
/* .asciz 定义C风格的ASCII字符串,也就是末尾有个'\0' */
.asciz "The CPU Vendor is %s\n"
.section .bss
/* 在BSS段中定义一个buffer,其大小是12个字节 */
.lcomm buffer, 12
.section .text
.globl _start /* 定义_start符号为全局符号 */
_start:
nop
/* %eax清零,来执行cpuid指令 */
xorl %eax, %eax
cpuid
/* vendor ID字符串包含在ebx-edx-ecx中 */
movq $buffer, %rdi
movl %ebx, (%rdi)
movl %edx, 4(%rdi)
movl %ecx, 8(%rdi)
/* 调用printf("The CPU Vendor is %s\n", buffer)
* 注意,x86-64的calling cenvention是:参数从左到右
* 依次为:rdi, rsi, rdx, rcx, r8, r9和rax这7个通用
* 寄存器
*/
movq $output, %rdi
movq $buffer, %rsi
call printf
/* 调用void _exit(int status) */
movq $0, %rdi
call exit
版主,我还有一个问题,我分别用as和ld编译、链接生成的程序运行起来没有问题,但是,我使用gcc -o cpuid cpuid.s来生成的可执行文件运行起来却会出现“段错误”的提示。我想了很久也没有弄明白。麻烦你给解释一下!谢谢!顺便说一下,我的系统是64位的ubuntu8.04。
[ 本帖最后由 bradpitt88 于 2008-5-30 22:45 编辑 ]
gcc编译有两个问题。
第一, ld寻找的程序入口是_start符号,这也是我们的程序中定义了的。 而gcc寻找的入口是main符号,这个符号,我们的程序中没有定义;
第二, gcc在编译程序, 到连接那一步时, 会默认去连接一些*.o文件, 这些*.o文件里是常用的历程和符号。 用gcc -v |grep collect2可以看到即使编译一个最小的程序, gcc也会默认连接上很多*.o文件。
段错误问题可能是和这些*.o文件有关, 因为这个程序不是C程序,不需要那些*.o文件, 而gcc连接了。
版主,我用gcc编译的汇编程序的入口已经使用了main,在32位的机子上,用as、ld编译链接和使用gcc编译的程序运行起来都没有任何的问题(当然,32位机子上的代码是略有不同的),只有64位的机子上才会出现我说的以上问题。不知道有没有什么办法解决呢。
版主,本人用一个简单的程序试过,在64位的机子上用as、ld生成的程序运行没有问题,但换成了gcc来生成程序运行就会出现“instruction fault“的提示。下面是程序代码(请留意一下注释的部分):
.section .data
msg:
.asciz "Hello, ubuntu! \n"
.section .text
.globl main #用as、ld生成程序时要把main改成_start
main:
movq $msg, %rdi
pushq %rdi
call printf #如果把这句话注释起来,在64位机子上用gcc生成的程序运行
#起来一切正常,但是加上这句话后在64位机子上用gcc生成的
#程序运行起来就会出错,问题出在call printf这个语句上
movq $0, %rdi
pushq %rdi
call exit
[ 本帖最后由 bradpitt88 于 2008-6-2 13:39 编辑 ]