【问题标题】:x86 vs x86_64 calling conventionx86 与 x86_64 调用约定
【发布时间】:2017-06-11 17:34:26
【问题描述】:

我们有两种架构和两个不同的年份:2007 年的 x86 和 2017 年的 x86_64。在 2007 年出版的一本基于 x86 的书中,列出了一个编译文件的示例:

#include <stdio.h>
#include <stdlib.h>

void usage(char *program_name) {

    printf("Usage: %s <message> <# of times to repeat>\n", program_name);
    exit(1);
}

int main(int argc, char *argv[]) {

    int i, count;

    if(argc < 3)            // If fewer than 3 arguments are used, 
        usage(argv[0]);     // display usage message and exit.

    count = atoi(argv[2]);  // Convert the 2nd arg into an integer.
    printf("Repeating %d times..\n", count);

    for(i = 0; i < count; i++)
        printf("%3d - %s\n", i, argv[1]);   // Printf the 1st arg.

    return 0;
}

当通过 gcc -g -o file.o file.c 编译并在 gdb 中反汇编文件或简单地将文件 objdump 到 x86_64 时:它看起来像这样:

0000000000000000 <usage>:
 0: 55                      push   rbp
 1: 48 89 e5                mov    rbp,rsp
 4: 48 83 ec 10             sub    rsp,0x10
 8: 48 89 7d f8             mov    QWORD PTR [rbp-0x8],rdi
 c: 48 8b 45 f8             mov    rax,QWORD PTR [rbp-0x8]
10: 48 89 c6                mov    rsi,rax
13: 48 8d 3d 00 00 00 00    lea    rdi,[rip+0x0]  # 1a <usage+0x1a>
1a: b8 00 00 00 00          mov    eax,0x0
1f: e8 00 00 00 00          call   24 <usage+0x24>
24: bf 01 00 00 00          mov    edi,0x1
29: e8 00 00 00 00          call   2e <main>

000000000000002e <main>:
2e: 55                      push   rbp
2f: 48 89 e5                mov    rbp,rsp
32: 48 83 ec 20             sub    rsp,0x20
36: 89 7d ec                mov    DWORD PTR [rbp-0x14],edi
39: 48 89 75 e0             mov    QWORD PTR [rbp-0x20],rsi
3d: 83 7d ec 02             cmp    DWORD PTR [rbp-0x14],0x2
41: 7f 0f                   jg     52 <main+0x24>
43: 48 8b 45 e0             mov    rax,QWORD PTR [rbp-0x20]
47: 48 8b 00                mov    rax,QWORD PTR [rax]
4a: 48 89 c7                mov    rdi,rax
4d: e8 00 00 00 00          call   52 <main+0x24>
52: 48 8b 45 e0             mov    rax,QWORD PTR [rbp-0x20]
56: 48 83 c0 10             add    rax,0x10
5a: 48 8b 00                mov    rax,QWORD PTR [rax]
5d: 48 89 c7                mov    rdi,rax
60: e8 00 00 00 00          call   65 <main+0x37>
65: 89 45 f8                mov    DWORD PTR [rbp-0x8],eax
68: 8b 45 f8                mov    eax,DWORD PTR [rbp-0x8]
6b: 89 c6                   mov    esi,eax
6d: 48 8d 3d 00 00 00 00    lea    rdi,[rip+0x0]  # 74 <main+0x46>
74: b8 00 00 00 00          mov    eax,0x0
79: e8 00 00 00 00          call   7e <main+0x50>
7e: c7 45 fc 00 00 00 00    mov    DWORD PTR [rbp-0x4],0x0
85: eb 25                   jmp    ac <main+0x7e>
87: 48 8b 45 e0             mov    rax,QWORD PTR [rbp-0x20]
8b: 48 83 c0 08             add    rax,0x8
8f: 48 8b 10                mov    rdx,QWORD PTR [rax]
92: 8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
95: 89 c6                   mov    esi,eax
97: 48 8d 3d 00 00 00 00    lea    rdi,[rip+0x0]  # 9e <main+0x70>
9e: b8 00 00 00 00          mov    eax,0x0
a3: e8 00 00 00 00          call   a8 <main+0x7a>
a8: 83 45 fc 01             add    DWORD PTR [rbp-0x4],0x1
ac: 8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
af: 3b 45 f8                cmp    eax,DWORD PTR [rbp-0x8]
b2: 7c d3                   jl     87 <main+0x59>
b4: b8 00 00 00 00          mov    eax,0x0
b9: c9                      leave  
ba: c3                      ret    

但是,从我在 x86 上的汇编程序中学到的知识是,在调用函数之前,通常的调用约定(推送 rbp,将 rsp 移动到 rbp,基本上是构建一个新的堆栈框架加上所有arguments) 设置。但是在上面 main 的 objdump 输出中,没有类似的行

call    <addr_of_function_usage>    # <usage+0x0>

通常喜欢 x86 或书中的内容。所以我的问题是,在这个 objdump 中,对使用函数的调用发生在哪里?

提前致谢。

【问题讨论】:

  • 你为什么认为必须打电话?请提供对您平台的 ABI 或 C 标准的参考。
  • 您正在查看目标文件的反汇编,这对您(或我而言)毫无意义。编译时考虑添加-masm=intel -fno-dwarf2-cfi-asm并将-c更改为-S并查看file.s。您会发现代码更容易理解。
  • @ChronoKitsune 你是对的,老实说不知道这个。但是,您对“目标文件的反汇编,这没有任何意义”到底是什么意思? objdump 和它的程序集输出不应该一样吗?
  • Chrono 意味着那些e8 00 00 00 00 将被链接器替换为正确的偏移量。站在他们的立场上,他们具有误导性。 call 52 &lt;main+0x24&gt; 是您的电话。另外请注意,这与 ABI 无关,并且 x86_64 ABI 与 x86 ABI 非常不同。
  • @MargaretBloom 感谢您的澄清。现在我的案子结束了。

标签: c assembly calling-convention


【解决方案1】:

在目标文件中,函数的地址还没有填写。出于这个原因,反汇编程序无法查看您尝试调用的函数。如果您查看反汇编,您会看到一堆call 指令,其中一个可能调用usage。好像是call 地址0x42

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-27
    • 1970-01-01
    • 2012-01-06
    • 2011-01-05
    • 2021-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多