【问题标题】:Understanding assembly of a simple C program了解简单 C 程序的汇编
【发布时间】:2015-08-24 03:14:21
【问题描述】:

我正在尝试理解这个简单的 C 程序的汇编。

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
void foobar(char *a){
    char c = a[0];
}
int main(){
    int fd = open("file.txt", O_RDONLY);
        char buf1[100]="\0";
    char buf[100];
    int aa=0,b=1,c=2,d=3,f=2,g=3;
    read(fd,buf1,104);
    if(strlen(buf1) > 100){

    }else{
        strcpy(buf,buf1);
    }
    //strcpy(buf,buf1);
    foobar(buf1);
}

我得到的使用 gdb 对可执行文件的反汇编是 foobar 反汇编。

   0x000000000040067d <+0>: push   rbp
   0x000000000040067e <+1>: mov    rbp,rsp
   0x0000000000400681 <+4>: mov    QWORD PTR [rbp-0x18],rdi
   0x0000000000400685 <+8>: mov    rax,QWORD PTR [rbp-0x18]
   0x0000000000400689 <+12>:    movzx  eax,BYTE PTR [rax]
   0x000000000040068c <+15>:    mov    BYTE PTR [rbp-0x1],al
   0x000000000040068f <+18>:    pop    rbp

在 foobar 之前的主要反汇编

   0x0000000000400784 <+243>:   lea    rax,[rbp-0xf0]
   0x000000000040078b <+250>:   mov    rdi,rax
   0x000000000040078e <+253>:   call   0x40067d <foobar>
   0x0000000000400793 <+258>:   mov    rbx,QWORD PTR [rbp-0x18]
   0x0000000000400797 <+262>:   xor    rbx,QWORD PTR fs:0x28
   0x00000000004007a0 <+271>:   je     0x4007a7 <main+278>
   0x0000000000400690 <+19>:    ret   

现在,我有一个关于 foobar 反汇编的问题

0x0000000000400681 <+4>:    mov    QWORD PTR [rbp-0x18],rdi
0x0000000000400685 <+8>:    mov    rax,QWORD PTR [rbp-0x18]

指令不行

mov rax, rdi

将完成上述两条指令所需的工作。为什么要为 rdi 使用额外的内存位置 rbp - 0x18 ? 和引用传递有关吗?

编辑: 我想问的另一个问题是,为什么 foobar 函数正在访问不在 foobar 框架中的 something(rbp - 0x18)。?

我的 gcc 版本是 gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2

编辑: 在编译时使用 -O1 -O2 和 -O3 优化标志后,foobar 程序集变为

   0x0000000000400670 <+0>: repz ret 

在使用 -O3 标志时,main 的一些反汇编是

   0x0000000000400551 <+81>:    rep stos QWORD PTR es:[rdi],rax
   0x0000000000400554 <+84>:    mov    DWORD PTR [rdi],0x0
   0x000000000040055a <+90>:    mov    cl,0x64
   0x000000000040055c <+92>:    mov    edi,r8d
   0x000000000040055f <+95>:    call   0x4004b0 <__read_chk@plt>
   0x0000000000400564 <+100>:   mov    rdx,QWORD PTR [rsp+0x68]
   0x0000000000400569 <+105>:   xor    rdx,QWORD PTR fs:0x28
   0x0000000000400572 <+114>:   jne    0x400579 <main+121>
   0x0000000000400574 <+116>:   add    rsp,0x78
   0x0000000000400578 <+120>:   ret    
   0x0000000000400579 <+121>:   call   0x4004c0 <__stack_chk_fail@plt>

我在 main 中找不到任何对 foobar 的调用。

【问题讨论】:

  • 你的编译器标志是什么?
  • 我正在使用以下命令进行编译。 gcc a.c -o a -g
  • 使用一些优化设置,否则代码不会优化:) 如-O3.
  • 我想问的另一个问题是为什么 foobar 函数正在访问不在 foobar 框架中的东西(rbp - 0x18)。

标签: c gcc assembly gdb reverse-engineering


【解决方案1】:

这是个好问题。可以这么说,我赞扬你“在引擎盖下偷看”。

对编译代码进行了大量研究。有时您希望代码快速运行,有时您希望它很小,有时您希望它快速编译。由于对编译器的研究,编译器可以生成以上述任何方式运行的代码。为了允许用户选择他们想要的选项之一,gcc 有command line options that control the level of optimization

默认情况下,gcc 使用 -O0,它不会对代码进行太多优化,而是专注于最快的编译时间。因此,您有时会发现指令序列效率低下。


当您打开 -O3 标志时,编译器内联 foobar 的代码。如您所知,函数调用需要时间,因此,如果函数 foobar 足够短,编译器将只复制 foobar 的整个代码而不是调用它,从而消除了对 call 和 ret 指令的需要。这使得代码更快一点,但也让它变得更大。

考虑一个被调用 100 次的 100 条指令函数。如果这个函数是内联的,代码大小将急剧增加,而不会增加多少额外的速度。如果您设置了高优化级别并且所讨论的函数非常小,编译器只会内联代码。

您可能已经注意到没有任何东西可以代替 foobar 函数。它已被“优化”,这意味着编译器将其完全删除。这是因为编译器可以判断 foobar 没有做任何有用的事情。也就是说,它没有side effects。在 -O0 处,没有任何优化。在更高的优化级别,gcc 开始优化没有副作用的函数以节省空间。

我已经有几年没有写过 x86 汇编了(现在只是 arm),但如果我没记错的话,repz ret 由于分支预测,实际上是一种更有效的 ret 形式。更多信息可以在here找到。

我现在要睡觉了。如果您还有问题,我会稍后回复:)。

【讨论】:

  • 当我使用优化标志运行时,我在 foobar 反汇编中只得到一条指令。为什么会这样? 0x0000000000400670 : repz ret
  • 您可以在问题的末尾添加新的反汇编吗?确保包括您使用的优化标志。
【解决方案2】:

正如一些人评论的那样,您应该进行一些优化来编译,例如至少有gcc -O1(最好是gcc -O2)。

如果专门使用GCC 编译,我建议也传递-fverbose-asm,因为这会在生成的汇编文件中生成有用的cmets。

这里是相关列表,在 Debian/Sid/amd64 上使用 GCC 5.1 编译,使用 gcc-5 -O2 -fverbose-asm -S go.c,然后使用寻呼机查看生成的 go.s 汇编程序文件:

        .section        .text.unlikely,"ax",@progbits
.LCOLDB0:
        .text
.LHOTB0:
        .p2align 4,,15
        .globl  foobar
        .type   foobar, @function
foobar:
.LFB25:
        .cfi_startproc
        rep ret
        .cfi_endproc
.LFE25:
        .size   foobar, .-foobar
        .section        .text.unlikely
.LCOLDE0:
        .text
.LHOTE0:
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC1:
        .string "file.txt"
        .section        .rodata
.LC2:
        .string ""
        .string ""
        .zero   98
        .section        .text.unlikely
.LCOLDB3:
        .section        .text.startup,"ax",@progbits
.LHOTB3:
        .p2align 4,,15
        .globl  main
        .type   main, @function
main:
.LFB26:
        .cfi_startproc
        subq    $120, %rsp      #,
        .cfi_def_cfa_offset 128
        xorl    %esi, %esi      #
        movl    $.LC1, %edi     #,
        xorl    %eax, %eax      #
        call    open    #
        movl    %eax, %r8d      #, fd
        movzwl  .LC2(%rip), %eax        #, tmp92
        leaq    8(%rsp), %rdi   #, tmp93
        movl    $11, %ecx       #, tmp95
        movl    $104, %edx      #,
        movq    %rsp, %rsi      #,
        movl    $0, 4(%rsp)     #, buf1
        movw    %ax, (%rsp)     # tmp92, buf1
        xorl    %eax, %eax      #
        movw    %ax, 2(%rsp)    #, buf1
        xorl    %eax, %eax      # tmp94
        rep stosq
        movl    $0, (%rdi)      #, buf1
        movl    %r8d, %edi      # fd,
        call    read    #
        movq    %rsp, %rax      #, D.3346
.L3:
        movl    (%rax), %edx    #* D.3346, tmp100
        addq    $4, %rax        #, D.3346
        leal    -16843009(%rdx), %ecx   #, tmp99
        notl    %edx    # tmp100
        andl    %edx, %ecx      # tmp100, tmp99
        andl    $-2139062144, %ecx      #, tmp99
        je      .L3     #,
        xorl    %eax, %eax      #
        addq    $120, %rsp      #,
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
.LFE26:
        .size   main, .-main

编译器内联调用foobar 并将其主体优化为空(因为您的源代码对foobar 没有可观察副作用)。然后它删除了对foobar 的任何调用,因为它没用。

您可以尝试使用-fdump-tree-all 进行编译。您将获得数百个转储文件,对应于生成它们的许多 GCC 优化过程。

您还可以使用 MELT(一种类似 Lisp 的特定领域语言来扩展 GCC)自定义您的 gcc,您甚至可以使用 MELT 的 @ 搜索一些 Gimple 或 Tree 模式987654323@ 模式(在 GCC 内部的 Gimple 表示上的一种 grep)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-11
    • 2018-01-04
    • 1970-01-01
    相关资源
    最近更新 更多