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

发布于 2022-09-18 21:56:06 字数 5398 浏览 17 评论 0

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

究竟谁懂我的在乎 2022-09-25 21:56:06

原帖由 公用帐号 于 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 编辑 ]

浅暮の光 2022-09-25 21:56:06

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

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

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

我乃一代侩神 2022-09-25 21:56:06

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:

老街孤人 2022-09-25 21:56:06

原帖由 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这三部分的文档中都没有找到它。

谢谢

王权女流氓 2022-09-25 21:56:06

版主,能不能把修改后的所有源代码贴出来?

明媚如初 2022-09-25 21:56:06

原帖由 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

塔塔猫 2022-09-25 21:56:06

版主,我还有一个问题,我分别用as和ld编译、链接生成的程序运行起来没有问题,但是,我使用gcc -o cpuid cpuid.s来生成的可执行文件运行起来却会出现“段错误”的提示。我想了很久也没有弄明白。麻烦你给解释一下!谢谢!顺便说一下,我的系统是64位的ubuntu8.04。

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

痴者 2022-09-25 21:56:06

原帖由 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连接了。

幸福还没到 2022-09-25 21:56:06

版主,我用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 编辑 ]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文