荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: jjk (UNIX+C+XML+?? 傻了?), 信区: Linux
标  题: gcc常用的编译选项对代码的影响[转载]
发信站: 荔园晨风BBS站 (Mon Apr 22 19:44:57 2002), 转信

【 以下文字转载自 jjk 的信箱 】
【 原文由 jjk.bbs@apue.dhs.org 所发表 】
发信人: xzc@smth.org (sanren), 信区: UKP
标  题: gcc常用的编译选项对代码的影响
发信站: BBS 水木清华站 (Tue Apr 16 17:14:11 2002)
转信站: APUE!news.tiaozhan.com!thunews.dhs.org!SMTH

by alert7
2001-12-21
测试环境 redhat 6.2
★ 前言
本文讨论gcc的一些常用编译选项对代码的影响。当然代码变了,
它的内存布局也就会变了,随之exploit也就要做相应的变动。
gcc的编译选项实在太多,本文检了几个最常用的选项。
★ 演示程序
[alert7@redhat62 alert7]$ cat > test.c
#include
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 :       push   %ebp
0x80483e5 :     mov    %esp,%ebp
0x80483e7 :     call   0x80483d0
0x80483ec :     xor    %eax,%eax
0x80483ee :    jmp    0x80483f0
0x80483f0 :    leave
0x80483f1 :    ret
....
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483d0 :        push   %ebp
0x80483d1 :       mov    %esp,%ebp
0x80483d3 :       push   $0x8048450
0x80483d8 :       call   0x8048308
0x80483dd :      add    $0x4,%esp
0x80483e0 :      leave
0x80483e1 :      ret
0x80483e2 :      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 :       push   %ebp
0x80483d9 :     mov    %esp,%ebp
0x80483db :     call   0x80483c8
0x80483e0 :     xor    %eax,%eax
0x80483e2 :    leave
0x80483e3 :    ret
0x80483e4 :    nop
...
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483c8 :        push   %ebp
0x80483c9 :       mov    %esp,%ebp
0x80483cb :       push   $0x8048440
0x80483d0 :       call   0x8048308
0x80483d5 :      leave
0x80483d6 :      ret
0x80483d7 :      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 :       push   %ebp
0x80483d9 :     mov    %esp,%ebp
0x80483db :     call   0x80483c8
0x80483e0 :     xor    %eax,%eax
0x80483e2 :    leave
0x80483e3 :    ret
...
0x80483ef :    nop
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483c8 :        push   %ebp
0x80483c9 :       mov    %esp,%ebp
0x80483cb :       push   $0x8048440
0x80483d0 :       call   0x8048308
0x80483d5 :      leave
0x80483d6 :      ret
0x80483d7 :      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 :       call   0x80483d0
0x80483e5 :     xor    %eax,%eax
0x80483e7 :     jmp    0x80483f0
0x80483e9 :     lea    0x0(%esi,1),%esi
0x80483f0 :    ret
....
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483d0 :        push   $0x8048450
0x80483d5 :       call   0x8048308
0x80483da :      add    $0x4,%esp
0x80483dd :      ret
0x80483de :      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 :       call   0x80483c8
0x80483dd :     xor    %eax,%eax
0x80483df :     ret
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483c8 :        push   $0x8048430
0x80483cd :       call   0x8048308
0x80483d2 :      add    $0x4,%esp
0x80483d5 :      ret
0x80483d6 :      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 :       push   %ebp
0x80483f9 :     mov    %esp,%ebp
0x80483fb :     push   %ebx
0x80483fc :     call   0x8048401
0x8048401 :     pop    %ebx//取得该指令的地址
0x8048402 :    add    $0x1093,%ebx//此时ebx里面存放着是GOT表的地址
0x8048408 :    call   0x80483d0
0x804840d :    xor    %eax,%eax
0x804840f :    jmp    0x8048411
0x8048411 :    mov    0xfffffffc(%ebp),%ebx
0x8048414 :    leave
0x8048415 :    ret
...
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483d0 :        push   %ebp
0x80483d1 :       mov    %esp,%ebp
0x80483d3 :       push   %ebx
0x80483d4 :       call   0x80483d9
0x80483d9 :       pop    %ebx
0x80483da :      add    $0x10bb,%ebx
0x80483e0 :      lea    0xffffefdc(%ebx),%edx
0x80483e6 :      mov    %edx,%eax
0x80483e8 :      push   %eax
0x80483e9 :      call   0x8048308
0x80483ee :      add    $0x4,%esp
0x80483f1 :      mov    0xfffffffc(%ebp),%ebx
0x80483f4 :      leave
0x80483f5 :      ret
0x80483f6 :      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 :       push   %ebp
0x80481b5 :     mov    %esp,%ebp
0x80481b7 :     call   0x80481a0
0x80481bc :     xor    %eax,%eax
0x80481be :    jmp    0x80481c0
0x80481c0 :    leave
0x80481c1 :    ret
...
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80481a0 :        push   %ebp
0x80481a1 :       mov    %esp,%ebp
0x80481a3 :       push   $0x8071528
0x80481a8 :       call   0x804865c
0x80481ad :      add    $0x4,%esp
0x80481b0 :      leave
0x80481b1 :      ret
0x80481b2 :      mov    %esi,%esi
End of assembler dump.
[alert7@redhat62 alert7]$ ldd test
        not a dynamic executable
-static出来的代码已经没有PLT了,GOT虽然有,已经全部为0了。


--

※ 来源:·BBS 水木清华站 smth.org·[FROM: 166.111.65.35]
--
※ 转寄:·UNIX编程 apue.dhs.org·[FROM: 210.39.3.50]
--
※ 转载:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.0.146]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店