• GCC的几个常用选项


    gcc常用的编译选项对代码的影响

    创建时间:2001-12-21
    文章属性:原创
    文章来源:http://xfocus.org/
    文章提交:alert7 (sztcww_at_sina.com)

    测试环境 redhat 6.2

    ★ 前言

    本文讨论gcc的一些常用编译选项对代码的影响。当然代码变了,
    它的内存布局也就会变了,随之exploit也就要做相应的变动。
    gcc的编译选项实在太多,本文检了几个最常用的选项。


    ★ 演示程序

    [alert7@redhat62 alert7]$ cat > test.c
    #include <stdio.h>
    void hi(void)
    {
    printf("hi");
    }

    int main(int argc, char *argv[])
    {
            hi();
            return 0;
    }


    ★ 一般情况

    [alert7@redhat62 alert7]$ gcc -o test test.c
    [alert7@redhat62 alert7]$ wc -c test
      11773 test
    [alert7@redhat62 alert7]$ gdb -q test
    (gdb) disass main
    Dump of assembler code for function main:
    0x80483e4 <main>:       push   %ebp
    0x80483e5 <main+1>:     mov    %esp,%ebp
    0x80483e7 <main+3>:     call   0x80483d0 <hi>
    0x80483ec <main+8>:     xor    %eax,%eax
    0x80483ee <main+10>:    jmp    0x80483f0 <main+12>
    0x80483f0 <main+12>:    leave
    0x80483f1 <main+13>:    ret
    ....
    End of assembler dump.
    (gdb) disass hi
    Dump of assembler code for function hi:
    0x80483d0 <hi>:        push   %ebp
    0x80483d1 <hi+1>:       mov    %esp,%ebp
    0x80483d3 <hi+3>:       push   $0x8048450
    0x80483d8 <hi+8>:       call   0x8048308 <printf>
    0x80483dd <hi+13>:      add    $0x4,%esp
    0x80483e0 <hi+16>:      leave
    0x80483e1 <hi+17>:      ret
    0x80483e2 <hi+18>:      mov    %esi,%esi
    End of assembler dump.

    来看看部分的内存映象
                       (内存高址)
                                  +--------+
                                  |bffffbc4| argv的地址(即argv[0]的地址)
                       0xbffffb84 +--------+
                                  |00000001| argc的值
                       0xbffffb80 +--------+
                                  |400309cb|main的返回地址
                       0xbffffb7c +--------+ <-- 调用main函数前的esp
                                  |bffffb98| 调用main函数前的ebp
                       0xbffffb78 +--------+ <-- main函数的ebp
                                  |080483ec| hi()的返回地址
                       0xbffffb74 +--------+
                                  |bffffb78| 调用hi()前的esp
                       0xbffffb70 +--------+
                                  |08048450| "hi"的地址
                       0xbffffb6c +--------+
                                  | ...... |
                       (内存低址)

    leave    指令所做的操作相当于MOV ESP,EBP 然后 POP EBP
    ret    指令所做的操作相当于POP EIP


    ★ -O 编译选项

    With `-O', the compiler tries to reduce code size and execution time.
    When you specify `-O', the two options `-fthread-jumps' and
    `-fdefer-pop' are turned  on
    优化,减少代码大小和执行的时间

    [alert7@redhat62 alert7]$ gcc -O -o test test.c
    [alert7@redhat62 alert7]$ wc -c test
      11757 test
    [alert7@redhat62 alert7]$ gdb -q test
    (gdb) disass main
    Dump of assembler code for function main:
    0x80483d8 <main>:       push   %ebp
    0x80483d9 <main+1>:     mov    %esp,%ebp
    0x80483db <main+3>:     call   0x80483c8 <hi>
    0x80483e0 <main+8>:     xor    %eax,%eax
    0x80483e2 <main+10>:    leave
    0x80483e3 <main+11>:    ret
    0x80483e4 <main+12>:    nop
    ...
    End of assembler dump.
    (gdb) disass hi
    Dump of assembler code for function hi:
    0x80483c8 <hi>:        push   %ebp
    0x80483c9 <hi+1>:       mov    %esp,%ebp
    0x80483cb <hi+3>:       push   $0x8048440
    0x80483d0 <hi+8>:       call   0x8048308 <printf>
    0x80483d5 <hi+13>:      leave
    0x80483d6 <hi+14>:      ret
    0x80483d7 <hi+15>:      nop
    End of assembler dump.

    在main()中,把一条jmp指令优化掉了,很显然,这条指令是可以不需要的。
    在hi()中,把add    $0x4,%esp优化掉了,这会不会使stack不平衡呢?
    来看看部分的内存映象
                       (内存高址)
                                  +--------+
                                  |bffffbc4| argv的地址(即argv[0]的地址)
                       0xbffffb84 +--------+
                                  |00000001| argc的值
                       0xbffffb80 +--------+
                                  |400309cb|main的返回地址
                       0xbffffb7c +--------+ <-- 调用main函数前的esp
                                  |bffffb98| 调用main函数前的ebp
                       0xbffffb78 +--------+ <-- main函数的ebp
                                  |080483e0| hi()的返回地址
                       0xbffffb74 +--------+
                                  |bffffb78| 调用hi()前的esp
                       0xbffffb70 +--------+
                                  |08048440| "hi"的地址
                       0xbffffb6c +--------+
                                  | ...... |
                       (内存低址)

    leave    指令所做的操作相当于把MOV ESP,EBP 然后 POP EBP 
    看到leave指令操作了没有,先把ebp-->esp,再pop ebp,这样即使
    在过程内堆栈的esp,ebp是不平衡的,但只要返回时候碰到leave指令
    就会平衡了,所以把add    $0x4,%esp优化掉也是没有问题的。


    ★ -O2 编译选项

    -O2    Optimize  even more.  Nearly all supported optimizations that do
        not involve a space-speed tradeoff are performed.  Loop unrolling
        and function inlining are not done, for example.  As compared to -O,
            this option increases both compilation time and the performance of
        the generated code.

    [alert7@redhat62 alert7]$ gcc -O2 -o test test.c
    [alert7@redhat62 alert7]$ wc -c test
      11757 test
    [alert7@redhat62 alert7]$ gdb -q test
    (gdb) disass main
    Dump of assembler code for function main:
    0x80483d8 <main>:       push   %ebp
    0x80483d9 <main+1>:     mov    %esp,%ebp
    0x80483db <main+3>:     call   0x80483c8 <hi>
    0x80483e0 <main+8>:     xor    %eax,%eax
    0x80483e2 <main+10>:    leave
    0x80483e3 <main+11>:    ret
    ...
    0x80483ef <main+23>:    nop
    End of assembler dump.
    (gdb) disass hi
    Dump of assembler code for function hi:
    0x80483c8 <hi>:        push   %ebp
    0x80483c9 <hi+1>:       mov    %esp,%ebp
    0x80483cb <hi+3>:       push   $0x8048440
    0x80483d0 <hi+8>:       call   0x8048308 <printf>
    0x80483d5 <hi+13>:      leave
    0x80483d6 <hi+14>:      ret
    0x80483d7 <hi+15>:      nop
    End of assembler dump.

    由于程序比较简单,再优化也没有好优化的了,所以跟-O出来的一样。


    ★ -fomit-frame-pointer 编译选项

    -fomit-frame-pointer
                  Don't keep the frame pointer in a register for functions
              that don't need one.  This avoids the  instructions to save,
              set up and restore frame pointers; it also makes an extra
              register available in many functions.  It also makes
              debugging impossible on most machines.

    忽略帧指针。这样在程序就不需要保存,安装,和恢复ebp了。这样ebp也就是一个
    free的register了,在函数中就可以随便使用了。

    [alert7@redhat62 alert7]$ gcc -fomit-frame-pointer -o test test.c
    [alert7@redhat62 alert7]$ wc -c test
      11773 test
    [alert7@redhat62 alert7]$ gdb -q test
    (gdb) disass main
    Dump of assembler code for function main:
    0x80483e0 <main>:       call   0x80483d0 <hi>
    0x80483e5 <main+5>:     xor    %eax,%eax
    0x80483e7 <main+7>:     jmp    0x80483f0 <main+16>
    0x80483e9 <main+9>:     lea    0x0(%esi,1),%esi
    0x80483f0 <main+16>:    ret
    ....
    End of assembler dump.
    (gdb) disass hi
    Dump of assembler code for function hi:
    0x80483d0 <hi>:        push   $0x8048450
    0x80483d5 <hi+5>:       call   0x8048308 <printf>
    0x80483da <hi+10>:      add    $0x4,%esp
    0x80483dd <hi+13>:      ret
    0x80483de <hi+14>:      mov    %esi,%esi
    End of assembler dump.

    在main()和hi()中都去掉了以下指令
    push   %ebp
    mov    %esp,%ebp//这两条指令安装
    leave//这条指令恢复
    来看看部分的内存映象
                       (内存高址)
                                  +--------+
                                  |bffffbc4| argv的地址(即argv[0]的地址)
                       0xbffffb84 +--------+
                                  |00000001| argc的值
                       0xbffffb80 +--------+
                                  |400309cb|main的返回地址
                       0xbffffb7c +--------+
                                  |080483e5| hi()的返回地址
                       0xbffffb78 +--------+
                                  |08048450|  "hi"字符串的地址
                       0xbffffb74 +--------+
                                  | ...... |
                       (内存低址)
    没有保存上层执行环境的ebp.


    ★ -fomit-frame-pointer && -O2

    -fomit-frame-pointer编译选项去掉了
    push   %ebp
    mov    %esp,%ebp//这两条指令安装
    leave//这条指令恢复
    -O2编译选项去掉了
    add    $0x4,%esp

    两个加起来会不会这四条指令一起去掉,从而使stack不平衡呢?

    [alert7@redhat62 alert7]$ gcc -fomit-frame-pointer -O2 -o test test.c
    [alert7@redhat62 alert7]$ wc -c test
      11741 test
    [alert7@redhat62 alert7]$ gdb -q test
    (gdb) disass main
    Dump of assembler code for function main:
    0x80483d8 <main>:       call   0x80483c8 <hi>
    0x80483dd <main+5>:     xor    %eax,%eax
    0x80483df <main+7>:     ret
    End of assembler dump.
    (gdb) disass hi
    Dump of assembler code for function hi:
    0x80483c8 <hi>:        push   $0x8048430
    0x80483cd <hi+5>:       call   0x8048308 <printf>
    0x80483d2 <hi+10>:      add    $0x4,%esp
    0x80483d5 <hi+13>:      ret
    0x80483d6 <hi+14>:      mov    %esi,%esi
    End of assembler dump.
    来看看部分的内存映象
                       (内存高址)
                                  +--------+
                                  |bffffbc4| argv的地址(即argv[0]的地址)
                       0xbffffb84 +--------+
                                  |00000001| argc的值
                       0xbffffb80 +--------+
                                  |400309cb|main的返回地址
                       0xbffffb7c +--------+
                                  |080483dd| hi()的返回地址
                       0xbffffb78 +--------+
                                  |08048430|  "hi"字符串的地址
                       0xbffffb74 +--------+
                                  | ...... |
                       (内存低址)

    此时就没有把add    $0x4,%esp优化掉,如果优化掉的话,整个stack就
    会变的不平衡,从而会导致程序出错。


    ★ -fPIC 编译选项

    -fPIC    If  supported for the target machine, emit position-independent
        code, suitable for dynamic linking,even if branches need large
        displacements.
    产生位置无关代码(PIC),一般创建共享库时用到。
    在x86上,PIC的代码的符号引用都是通过ebx进行操作的。

    [alert7@redhat62 alert7]$ gcc -fPIC -o test test.c
    [alert7@redhat62 alert7]$ wc -c test
      11805 test
    [alert7@redhat62 alert7]$ gdb -q test
    (gdb) disass main
    Dump of assembler code for function main:
    0x80483f8 <main>:       push   %ebp
    0x80483f9 <main+1>:     mov    %esp,%ebp
    0x80483fb <main+3>:     push   %ebx
    0x80483fc <main+4>:     call   0x8048401 <main+9>
    0x8048401 <main+9>:     pop    %ebx//取得该指令的地址
    0x8048402 <main+10>:    add    $0x1093,%ebx//此时ebx里面存放着是GOT表的地址
    0x8048408 <main+16>:    call   0x80483d0 <hi>
    0x804840d <main+21>:    xor    %eax,%eax
    0x804840f <main+23>:    jmp    0x8048411 <main+25>
    0x8048411 <main+25>:    mov    0xfffffffc(%ebp),%ebx
    0x8048414 <main+28>:    leave
    0x8048415 <main+29>:    ret
    ...
    End of assembler dump.
    (gdb) disass hi
    Dump of assembler code for function hi:
    0x80483d0 <hi>:        push   %ebp
    0x80483d1 <hi+1>:       mov    %esp,%ebp
    0x80483d3 <hi+3>:       push   %ebx
    0x80483d4 <hi+4>:       call   0x80483d9 <hi+9>
    0x80483d9 <hi+9>:       pop    %ebx
    0x80483da <hi+10>:      add    $0x10bb,%ebx
    0x80483e0 <hi+16>:      lea    0xffffefdc(%ebx),%edx
    0x80483e6 <hi+22>:      mov    %edx,%eax
    0x80483e8 <hi+24>:      push   %eax
    0x80483e9 <hi+25>:      call   0x8048308 <printf>
    0x80483ee <hi+30>:      add    $0x4,%esp
    0x80483f1 <hi+33>:      mov    0xfffffffc(%ebp),%ebx
    0x80483f4 <hi+36>:      leave
    0x80483f5 <hi+37>:      ret
    0x80483f6 <hi+38>:      mov    %esi,%esi
    End of assembler dump.
    来看看部分的内存映象
     
        (内存高址)
                  +--------+
                  |bffffbc4| argv的地址(即argv[0]的地址)
       0xbffffb84 +--------+
                  |00000001| argc的值
       0xbffffb80 +--------+
                  |400309cb|main的返回地址
       0xbffffb7c +--------+ <-- 调用main函数前的esp
                  |bffffb98| 调用main函数前的ebp
       0xbffffb78 +--------+ <-- main函数的ebp
                  |401081ec| 保存的ebx
       0xbffffb74 +--------+
                  |0804840d| (存放过call 0x8048401的下一条指令地址)
       0xbffffb70 +--------+
                  |bffffb78| 调用hi()前的esp
       0xbffffb6c +--------+
                  |08049494| GOT表地址
       0xbffffb68 +--------+
                  |08048470|(存放过call 0x80483d9的下一条指令地址)
       0xbffffb64 +--------+
                  | ...... |
         (内存低址)


    ★ -static 编译选项

    -static
        On systems that support dynamic linking, this prevents
        linking with the shared libraries.  On other  systems,
        this option has no effect.
    把一些函数都静态的编译到程序中,而无需动态链接了。

    [alert7@redhat62 alert7]$ gcc -o test -static test.c
    [alert7@redhat62 alert7]$ wc -c test
    962808 test
    [alert7@redhat62 alert7]$ gdb -q test
    (gdb) disass main
    Dump of assembler code for function main:
    0x80481b4 <main>:       push   %ebp
    0x80481b5 <main+1>:     mov    %esp,%ebp
    0x80481b7 <main+3>:     call   0x80481a0 <hi>
    0x80481bc <main+8>:     xor    %eax,%eax
    0x80481be <main+10>:    jmp    0x80481c0 <main+12>
    0x80481c0 <main+12>:    leave
    0x80481c1 <main+13>:    ret
    ...
    End of assembler dump.
    (gdb) disass hi
    Dump of assembler code for function hi:
    0x80481a0 <hi>:        push   %ebp
    0x80481a1 <hi+1>:       mov    %esp,%ebp
    0x80481a3 <hi+3>:       push   $0x8071528
    0x80481a8 <hi+8>:       call   0x804865c <printf>
    0x80481ad <hi+13>:      add    $0x4,%esp
    0x80481b0 <hi+16>:      leave
    0x80481b1 <hi+17>:      ret
    0x80481b2 <hi+18>:      mov    %esi,%esi
    End of assembler dump.
    [alert7@redhat62 alert7]$ ldd test
            not a dynamic executable
    -static出来的代码已经没有PLT了,GOT虽然有,已经全部为0了。

  • 相关阅读:
    UWP关于图片缓存的那些破事儿
    UWP中的文件相关操作
    数据结构-快速排序(C#实现)
    C#与Swift异步操作的差异
    Windows环境下使用Clover四叶草引导双硬盘安装OSX 10.11.5原版镜像
    Winform以任意角度旋转PictureBox中的图片的方法
    Xcode调用旧版本库出现Undefined symbols for architecture x86_64: ld: symbol(s) not found for architecture x86_64
    做WP程序时遇到的一些问题及解决方法
    WInform关闭程序的几种方法以及区别。
    显示在标题上的进度条
  • 原文地址:https://www.cnblogs.com/aquester/p/9891999.html
Copyright © 2020-2023  润新知