免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 7075 | 回复: 19
打印 上一主题 下一主题

x86-64的calling convention,和ia32有什么大区别? (汇编程序问题) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-04-30 17:30 |只看该作者 |倒序浏览
/* 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




输出:
[arc@localhost ~]$ ./ccpuid
AuthenticAMD[arc@localhost ~]$



在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呢?

论坛徽章:
0
2 [报告]
发表于 2008-04-30 18:10 |只看该作者
原帖由 公用帐号 于 2008-4-30 17:30 发表
/* 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 位。
   像: 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 编辑 ]

论坛徽章:
0
3 [报告]
发表于 2008-04-30 18:26 |只看该作者

回复 #1 公用帐号 的帖子

思考了一下,貌似即使 64 位符号扩展应该没问题的,大多数代码不会用到0x80000000以上的区域的吧

我上面说的方法你试试看行不行。不行的话把代码 objdump 贴出来再分析分析。看看是不是调用 printf 时的编码问题

[ 本帖最后由 mik 于 2008-4-30 18:27 编辑 ]

论坛徽章:
0
4 [报告]
发表于 2008-05-01 08:55 |只看该作者
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-0.98.pdf

380.59 KB, 下载次数: 188

论坛徽章:
0
5 [报告]
发表于 2008-05-19 12:25 |只看该作者
原帖由 mik 于 2008-5-1 08:55 发表
LZ:

不是imme被截断的原因,原因是:64位下是用寄存器传递参数。
偶也是习惯32位的编程模式,一时忘了64位编程的变化。渐愧

你了解下 64 位的ABI规定:

1、7个通用寄存器(rdi,rsi,rdx,rc ...


老大,这个帖子是我发的。

请问你是从哪里找到的这份abi文档?  我在AMD的Mnauals/Developer Guides/Revision Guides这三部分的文档中都没有找到它。

谢谢

论坛徽章:
0
6 [报告]
发表于 2008-05-29 15:53 |只看该作者
版主,能不能把修改后的所有源代码贴出来?

论坛徽章:
0
7 [报告]
发表于 2008-05-30 12:48 |只看该作者
原帖由 bradpitt88 于 2008-5-29 15:53 发表
版主,能不能把修改后的所有源代码贴出来?


当然可以:)


/* 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

论坛徽章:
0
8 [报告]
发表于 2008-05-30 22:43 |只看该作者
版主,我还有一个问题,我分别用as和ld编译、链接生成的程序运行起来没有问题,但是,我使用gcc -o cpuid cpuid.s来生成的可执行文件运行起来却会出现“段错误”的提示。我想了很久也没有弄明白。麻烦你给解释一下!谢谢!顺便说一下,我的系统是64位的ubuntu8.04。

[ 本帖最后由 bradpitt88 于 2008-5-30 22:45 编辑 ]

论坛徽章:
0
9 [报告]
发表于 2008-06-02 12:39 |只看该作者
原帖由 bradpitt88 于 2008-5-30 22:43 发表
版主,我还有一个问题,我分别用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连接了。

论坛徽章:
0
10 [报告]
发表于 2008-06-02 13:05 |只看该作者
版主,我用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 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP