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
* 另外,.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) */
pushq $buffer
pushq $output
call printf
/* 清除传给printf的参数 */
addq $16, %rsp
/* 调用void _exit(int status) */
pushq $0
call exit
输出:
$ ./ccpuid
AuthenticAMD$
在gdb中, 调用printf之前和之后, 查看$output的内存:
(gdb) x/30c &output
0x600420 <output>: 84 'T'104 'h' 101 'e' 32 ' '67 'C'80 'P'85 'U'32 ' '
0x600428 <output+8>: 86 'V'101 'e' 110 'n' 100 'd' 111 'o' 114 'r' 32 ' '105 'i'
0x600430 <output+16>: 115 's' 32 ' '37 '%'115 's' 10 '\n' 0 '\0'0 '\0'0 '\0'
0x600438 <buffer>: 65 'A'117 'u' 116 't' 104 'h' 101 'e' 110 'n'
没问题呀, 为什么好象没传给printf呢? 原帖由 公用帐号 于 2008-4-30 17:30 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
/* filename: ccpuid.s使用C库的printf和exit的cpupid.s
*
* $ as-o ccpuid.o ccpuid.s
* $ ld -o ccpuid ccpuid.o -lc -dynamic-linker /lib64/ld-linux.so.2
* $ ./ccpuid
*/
/* 一个不需 ...
这由于 x64 的寻址模式所致,x64 的 imme 操作数除了 GPRs 外一律都不能是 64 位,
说明白点,就是:
除了 mov rax, 0x1122334455667788、 mov rdx, 0x1122334455667788 之外,所有的 imme 操作数最大只能是 32 位。
像: movqword ptr , 0x1122334455667788 这条指令结果只能是:截断 imme 且符号扩展为64位,
等于:mov qword ptr , 0x0000000055667788
PUSH 在 64 位下,所有的 PUSH imme 都是 64 位的,结果只像 mov ,imme 一像截断 imme 且符号扩展为 64 位。
所以: pushq $buffer 这条指令的结果可能并不是你想要的。
要这么做:
movq $buffer, %rax
movq $output, %r8
pushq %rax
pushq $r8
.... ...
:mrgreen:
[ 本帖最后由 mik 于 2008-4-30 18:15 编辑 ]
回复 #1 公用帐号 的帖子
思考了一下,貌似即使 64 位符号扩展应该没问题的,大多数代码不会用到0x80000000以上的区域的吧我上面说的方法你试试看行不行。不行的话把代码 objdump 贴出来再分析分析。看看是不是调用 printf 时的编码问题
[ 本帖最后由 mik 于 2008-4-30 18:27 编辑 ] LZ:
不是imme被截断的原因,原因是:64位下是用寄存器传递参数。
偶也是习惯32位的编程模式,一时忘了64位编程的变化。渐愧:mrgreen:
你了解下 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: 原帖由 mik 于 2008-5-1 08:55 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
LZ:
不是imme被截断的原因,原因是:64位下是用寄存器传递参数。
偶也是习惯32位的编程模式,一时忘了64位编程的变化。渐愧:mrgreen:
你了解下 64 位的ABI规定:
1、7个通用寄存器(rdi,rsi,rdx,rc ...
老大,这个帖子是我发的。
请问你是从哪里找到的这份abi文档?我在AMD的Mnauals/Developer Guides/Revision Guides这三部分的文档中都没有找到它。
谢谢:em02: 版主,能不能把修改后的所有源代码贴出来? 原帖由 bradpitt88 于 2008-5-29 15:53 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
版主,能不能把修改后的所有源代码贴出来?
当然可以:)
/* 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 编辑 ] 原帖由 bradpitt88 于 2008-5-30 22:43 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
版主,我还有一个问题,我分别用as和ld编译、链接生成的程序运行起来没有问题,但是,我使用gcc -o cpuid cpuid.s来生成的可执行文件运行起来却会出现“段错误”的提示。我想了很久也没有弄明白。麻烦你给解释一 ...
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 编辑 ]
页:
[1]
2