zoukankan      html  css  js  c++  java
  • gcc 编译 + 选项【转】

    转自:http://blog.csdn.net/princess9/article/details/6567678

    一般来说要现有项目中的编译选项,设置新的project的编译选项

    编译器 就是将“高级语言”翻译为“机器语言(低级语言)”的程序。一个现代编译器的主要工作流程:源代码 (source code) → 预处理器 (preprocessor) → 编译器 (compiler) → 汇编程序 (assembler) → 目标代码 (object code) → 链接器 (Linker) → 可执行程序 (executables)。

    分类

    GCC家族

      Cygwin   Mingw32   DJGPP   Dev-C++ (Mingw32)   还有正宗的GNU GCC 2.95.5~3.0.0.4版本   GNU C++   g++ 是一个开源的C++编译器,GCC3.3对标准化C++的支持达96.15%。   值得一提的是,gcc是gnu c的编译器, g++是gnu c++的编译器, 而egcs(Enhanced GNU Compiler Suite)可以认为是gcc的改进版。目前gcc已经改名,从原来代表GNU C Compiler改变为代表GNU Compiler Collection。而MinGW或Cgywin,是在windows平台上的gnu c/c++编译器,以及库文件,运行环境的集合。   在GCC家族中GNU GCC是根本,其它的编译器版本都是从它导出的。其中,Cygwin和Mingw32都是WIN32平台下的编译器,DJGPP是DOS下的32位编译 器。大家所熟知的DEV-C++充其量只是GCC的一个外壳,它所自带的编译器就是Mingw32的一个版本。这些GCC的版本中,Cygwin是最大 的,它与其说是一个编译器,倒不如说是一套编程工具。它不仅有编译器,还有其它很多的工具。其实,它就是一个UNIX系统在WIN32平台上的实现。实现 了大多常用的UNIX工具,最近的版本中连Apache这样的“工具”都集成进来的。不过,Cygwin虽然功能强大,但它却不是很易用(和UNIX相 似,熟悉UNIX的人用它可以很快上手),因为太多其它的工具分散了人们的注意力。相比之下Mingw32就要好用得多,它只有最基本的几个编程工具(只 可惜它不自带GDB)。GCC中并不只是C/C++编译器,其中还有很多其它的编译器如JAVA,Fortran,ADA等。它是一个编译器集合,不过有 些编译器只能在UNIX系统上用。

    MS家族

      MSC 5.0、6.0、7.0   MSQC 1.0、2.5   MSVC 1.0、4.2、6.0、7.0   Visual C++   VC++6.0对标准化C++的兼容仅达83.43%。   它是Visual Studio、Visual Studio.net 2002、Visual Studio.net 2003、Visual Studio.net 2005的后台 C++编译器。随着Stanley Lippman等编译器设计大师的加盟,它变得非常成熟可靠了。Visual C++ 7.1对标准C++的兼容性达到98.22%。

    Linux中gcc,g++常用编译选项
    -x language filename
      设定文件所使用的语言,使后缀名无效,对以后的多个有效.也就是根据约定,C语言的后缀名称是.c的,而C++的后缀名是.C或者.cpp,如果你很个 性,决定你的C代码文件的后缀名是.pig 哈哈,那你就要用这个参数,这个参数对他后面的文件名都起作用,除非到了下一个参数的使用。创意产品网 可以使用的参数有下面的这些:
      `c', `objective-c', `c-header', `c++', `cpp-output', `assembler', and `a
    ssembler-with-cpp'.
      看到英文,应该可以理解的。
     
      例子用法: cd..
      gcc -x c hello.pig

    -x none filename
      关掉上一个 选项 ,也就是让gcc根据文件名后缀,自动识别文件类型
      例子用法:
      gcc -x c hello.pig -x none hello2.c
      
    -c
      只激活预处理,编译,和汇编,也就是他只把程序做成obj文件
      例子用法:
      gcc -c hello.c
      他将生成.o的obj文件
    -S
      只激活预处理和编译,就是指把文件编译成为汇编代码。
      例子用法
      gcc -S hello.c
      他将生成.s的汇编代码,你可以用文本编辑器察看
    -E
      只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面.
      例子用法:
      gcc -E hello.c > pianoapan.txt
      gcc -E hello.c | more
      慢慢看吧,一个hello word 也要预处理成800行的代码
    -o
      制定目标名称,缺省的时候,gcc 编译出来的文件是a.out,很难听,如果你和我有同感,改掉它,哈哈
      例子用法
      gcc -o hello.exe hello.c (哦,windows用习惯了)
      gcc -o hello.asm -S hello.c
    -pipe
      使用管道代替编译中临时文件,在使用非gnu汇编工具的时候,可能有些问题
      gcc -pipe -o hello.exe hello.c


    -shared-libgcc 该选项指定使用共享版本的libgcc,在没有共享版本的libgcc的机器上该选项无效

    -static-libgcc

     

    -specs=<filename> gcc驱动程序读取该文件以确定哪些选项应该传递给那些子进程。
    该选项可以通过指定配置文件来覆盖默认配置,指定的文件将在默认配置文件读取后进行处理以修改默认配置。

     

    -ansi
      关闭gnu c中与ansi c不兼容的特性,激活ansi c的专有特性(包括禁止一些asm inline typeof关键字,以及UNIX,vax等预处理宏

    /* 注释中的不常用****************************************************

    -fno-asm
      此选项 实现ansi 选项 的功能的一部分,它禁止将asm,inline和typeof用作关键字。
        
    -fno-strict-prototype
      只对g++ 起作用,使用这个 选项 ,g++ 将对不带参数的函数,都认为是没有显式的对参数的个数和类型说明,而不是没有参数.
      而gcc无论是否使用这个参数,都将对没有带参数的函数,认为没有显式说明的类型

      
    -fthis-is-varialble
      就是向传统c++看齐,可以使用this当一般变量使用.
      
    -fcond-mismatch
      允许条件表达式的第二和第三参数类型不匹配,表达式的值将为void类型
      
    -funsigned-char
    -fno-signed-char
    -fsigned-char
    -fno-unsigned-char
      这四个参数是对char类型进行设置,决定将char类型设置成unsigned char(前两个参
    数)或者 signed char(后两个参数)

    *注释完成*********************************************/  

    -include file
      包含某个代码,简单来说,就是便于某个文件需要另一个文件的时候,就可以用它设
    定,功能就相当于在代码中使用#i nclude<filename>
      例子用法:
      gcc hello.c -include /root/pianopan.h
      
    -imacros file
      将file文件的宏,扩展到gcc/g++ 的输入文件,宏定义本身并不出现在输入文件中
      
    -Dmacro
      相当于C语言中的#define macro
      
    -Dmacro=defn
      相当于C语言中的#define macro=defn
      
    -Umacro
      相当于C语言中的#undef macro
    -undef
      取消对任何非标准宏的定义
      
    -Idir
      在你是用#i nclude"file"的时候,gcc/g++ 会先在当前目录查找你所制定的头文件,如
    果没有找到,他回到缺省的头文件目录找,如果使用-I制定了目录,他
      回先在你所制定的目录查找,然后再按常规的顺序去找.
      对于#i nclude<file>,gcc/g++ 会到-I制定的目录查找,查找不到,然后将到系统的缺
    省的头文件目录查找
      
    -I-
      就是取消前一个参数的功能,所以一般在-Idir之后使用
      
    -idirafter dir
      在-I的目录里面查找失败,讲到这个目录里面查找.
      
    -iprefix prefix
    -iwithprefix dir
      一般一起使用,当-I的目录查找失败,会到prefix+dir下查找
      
    -nostdinc
      使编译器不再系统缺省的头文件目录里面找头文件,一般和-I联合使用,明确限定头
    文件的位置
      
    -nostdin C++
      规定不在g++ 指定的标准路经中搜索,但仍在其他路径中搜索,.此选项 在创libg++库
    使用
      
    -C
      在预处理的时候,不删除注释信息,一般和-E使用,有时候分析程序,用这个很方便的


    -M
      生成文件关联的信息。包含目标文件所依赖的所有源代码你可以用gcc -M hello.c
    来测试一下,很简单。
      
    -MM
      和上面的那个一样,但是它将忽略由#i nclude<file>造成的依赖关系。
      
    -MD
      和-M相同,但是输出将导入到.d的文件里面
      
    -MMD
      和-MM相同,但是输出将导入到.d的文件里面
      
    -Wa,option
      此选项 传递option给汇编程序;如果option中间有逗号,就将option分成多个选项 ,然
    后传递给会汇编程序
      
    -Wl.option
      此选项 传递option给连接程序;如果option中间有逗号,就将option分成多个选项 ,然
    后传递给会连接程序.
      
    -llibrary
      制定编译的时候使用的库
      例子用法
      gcc -lcurses hello.c
      使用ncurses库编译程序
      
    -Ldir
      制定编译的时候,搜索库的路径。比如你自己的库,可以用它制定目录,不然
      编译器将只在标准库的目录找。这个dir就是目录的名称。
      
    -O0
    -O1
    -O2
    -O3
      编译器的优化选项 的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高 
        
    -g
      只是编译器,在编译的时候,产生调试信息。
      
    -gstabs
      此选项 以stabs格式声称调试信息,但是不包括gdb调试信息.
      
    -gstabs+
      此选项 以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息.
      
    -ggdb
      此选项 将尽可能的生成gdb的可以使用的调试信息.
    -static
      此选项 将禁止使用动态库,所以,编译出来的东西,一般都很大,也不需要什么
    动态连接库,就可以运行.
    -share
      此选项 将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
    -traditional
      试图让编译器支持传统的C语言特性

    -v

    打印详细的编译链接过程,

    本文讨论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了。


    ★ 小节

    抛砖引玉般简单的实例描述了下gcc常用的编译选项对代码的影响。
    不正之处,还请斧正。谢谢。

    很多弟兄可能都很关心如何优化编译自己的程序,虽然本人不赞成"骨灰"玩法,却也不得不承认这是掌握gcc的绝佳途径;
    因此献上此帖,以供各位玩家参考,绝对原创噢
    ============================
    大多数程序和库在编译时默认的优化级别是"2"(使用gcc选项:"-O2")并且在Intel/AMD平台上默认按照i386处理器来编译。
    如果你只想让编译出来的程序运行在特定的平台上,就需要执行更高级的编译器优化选项,以产生只能运行于特定平台的代码。

    一种方法是修改每个源码包中的Makefile文件,在其中寻找CFLAGS和CXXFLAGS变量(C和C++编译器的编译选项)并修改它的值。
    一些源码包比如binutils, gcc, glibc等等,在每个子文件夹中都有Makefile文件,这样修改起来就太累了!

    另一种简易做法是设置CFLAGS和CXXFLAGS环境变量。大多数configure脚本会使用这两个环境变量代替Makefile文件中的值。
    但是少数configure脚本并不这样做,他们必须需要手动编辑才行。

    为了设置CFLAGS和CXXFLAGS环境变量,你可以在bash中执行如下命令(也可以写进.bashrc以成为默认值):
    export CFLAGS="-O3 -march=<cpu类型>" && CXXFLAGS=$CFLAGS
    这是一个确保能够在几乎所有平台上都能正常工作的最小设置。

    "-march"选项表示为特定的cpu类型编译二进制代码(不能在更低级别的cpu上运行),
    Intel通常是:pentium2, pentium3, pentium3m, pentium4, pentium4m, pentium-m, prescott, nocona
    说明:pentium3m/pentium4m是笔记本用的移动P3/P4;pentium-m是迅驰I/II代笔记本的cpu;
    prescott是带SSE3的P4(以滚烫到可以煎鸡蛋而闻名);nocona则是最新的带有EMT64(64位)的P4(同样可以煎鸡蛋)
    AMD通常是:k6, k6-2, k6-3, athlon, athlon-tbird, athlon-xp, athlon-mp, opteron, athlon64, athlon-fx
    用AMD的一般都是DIYer,就不必解释了吧。

    如果编译时没有抱怨"segmentation fault, core dumped",那么你设定的"-O"优化参数一般就没什么问题。
    否则请降低优化级别("-O3" -> "-O2" -> "-O1" -> 取消)。
    个人意见:服务器使用"-O2"就可以了,它是最安全的优化参数(集合);桌面可以使用"-O3" ;
    不鼓励使用过多的自定义优化选项,其实他们之间没什么明显的速度差异(有时"-O3"反而更慢)。

    编译器对硬件非常敏感,特别是在使用较高的优化级别的时候,一丁点的内存错误都可能导致致命的失败。
    所以在编译时请千万不要超频你的电脑(我编译关键程序时总是先降频然的)。

    注意:选项的顺序很重要,如果有两个选项互相冲突,则以后一个为准。
    比如"-O3"将打开-finline-functions选项,但是可以用"-O3 -fno-inline-functions"既使用-O3的功能又关闭函数内嵌功能。

    更多的优化选项请参见:
    http://gcc.gnu.org/onlinedocs/gcc-3....e-Options.html
    http://gcc.gnu.org/onlinedocs/gcc-3....4-Options.html
    http://gcc.gnu.org/onlinedocs/gcc-4....e-Options.html
    http://gcc.gnu.org/onlinedocs/gcc-4....4-Options.html
    所有GCC选项完整列表参见:
    http://gcc.gnu.org/onlinedocs/gcc-3....n-Summary.html
    http://gcc.gnu.org/onlinedocs/gcc-4....n-Summary.html

    有两个页面值的参考:
    (对于gentoo-1.4)比较安全的优化选项
    http://www.freehackers.org/gentoo/gc...flag_gcc3.html
    (对于gentoo-1.4)进阶优化选项
    http://www.freehackers.org/gentoo/gc...g_gcc3opt.html

    *******************************************************************

    哦,忘了说一声,"-O2"已经启用绝大多数安全的优化选项了,所以其实你不必对那一堆选项发愁。
    先说说"-O3"在"-O2"基础上增加的几项,你可以按需添加(还算比较安全):
    [gcc-3.4.4]
    -finline-functions 允许编译器选择某些简单的函数在其被调用处展开
    -fweb 为每个web结构体分配一个伪寄存器
    -frename-registers 试图驱除代码中的假依赖关系,这个选项对具有大量寄存器的机器很有效。
    [gcc-4.0.2]
    -finline-functions 说明如上
    -funswitch-loops 将循环体中不改变值的变量移动到循环体之外
    -fgcse-after-reload **不太明白它的含义**[哪位大峡知道给小弟讲解一下,先行谢过 ]

    说完"-O3"再说说在嵌入式系统上常用的"-Os"选项,这个选项其实也很重要,它的含义是对生成的二进制代码进行尺寸上的优化,它打开了所 有"-O2"打开的选项,因此通常认为的"-Os"生成的二进制代码执行效率低的潜在意识是错误的!当然该选项与"-O2"的不同之处在于它在"-O2" 的基础上禁止了所有为了对齐而插入的空间,也就是将所有"-falign-*"系列的选项禁用了。这种禁用究竟是否一定降低了代码的执行效率,依据程序的 不同而不同,据说某些情况下"-Os"的效率比"-O3"还要高14%!请兄弟们在实践中自己摸索吧...

    ---------------------------------------------

    下面选择我认为比较重要的几项简单介绍一下[gcc-3.4.4],GCC选项完整列表太长了!精力有限。
    [注意]这里列出的都是非默认 的选项,你只需要添加你所需要的选项即可

    -w 禁止输出警告消息

    -Werror 将所有警告转换为错误

    -Wall 显示所有的警告消息

    -v 显示编译程序的当前版本号

    -V<version> 指定gcc将要运行的版本。只有在安装了多个版本gcc的机器上才有效。

    -ansi 按照ANSI标准编译程序,但并不限制与标准并不冲突的GNU扩展(一般不用该选项)

    -pedantic 如果要限制代码必须严格符合ISO标准,就在"-ansi"的基础上同时启用这个选项(很少使用)

    -std=<name> 指定C语言的标准(c89,c99,gnu89),该选项禁止了GNU C的扩展关键字asm,typeof,inline (一般不用该选项)

    -static 连接器将忽略动态连接库,同时通过将静态目标文件直接包含到结果目标文件完成对所有引用的解析。

    -shared 连接器将生成共享目标代码,该共享库可在运行时动态连接到程序形成完整的可执行体。
    如果使用gcc命令创建共享库作为其输出,该选项可以防止连接器将缺失main()方法视为错误。
    为了可以正确的工作,应该一致的使用选项"-fpic"以及目标平台选项编译构成同一个库的所有共享目标模块。

    -shared-libgcc 该选项指定使用共享版本的libgcc,在没有共享版本的libgcc的机器上该选项无效。

    -specs=<filename> gcc驱动程序读取该文件以确定哪些选项应该传递给那些子进程。
    该选项可以通过指定配置文件来覆盖默认配置,指定的文件将在默认配置文件读取后进行处理以修改默认配置。

    -pipe 使用管道而不是临时文件一个阶段到另一个阶段交换输出的方式,可以加快编译速度。建议使用。

    -o <filename> 指定输出文件,对各种输出皆有效。由于只能指定一个文件,所以在产生多个输出文件的情况下不要使用该选项。

    --help 显示gcc的命令行选项列表;与"-v"一起使用时还将显示gcc调用的各个进程所接受的选项。

    --target-help 显示目标机器相关的命令行选项列表

    -b<machine> 指示需要编译程序的目标机器;默认为编译程序所运行的目标机编译代码。
    目标机通过指定包含编译程序的目录来确定,通常为/usr/local/lib/gcc-lib/<machine>/<version>

    -B<lib-prefix> 指定库文件的位置,包括编译程序的文件、执行程序和数据文件,如果需要运行子程序(如cpp,as,ld)就会用该前缀来定位。
    这个前缀可以是用冒号分割的多个路径,环境变量GCC_EXEC_PREFIX和这个选项有相同的效果。

    -I<dir> 指定搜索系统头文件的目录,可以重复使用多个该选项指定多个目录。

    -dumpmachine 显示该程序的目标机名字,不做其他任何动作

    -dumpspecs 显示构件编译程序的规范信息,包括用来编译、汇编和连接gcc编译程序自身用到的所有选项,不做其他任何动作。

    -dumpversion 显示编译程序自身的版本号,不做其他任何动作

    -falign-functions=N 将所有函数的起始地址在N(N=1,2,4,8,16...)的边界上对齐,默认为机器自身的默认值,指定为1表示禁止对齐。

    -falign-jumps=N 将分支目标在N(N=1,2,4,8,16...)的边界上对齐,默认为机器自身的默认值,指定为1表示禁止对齐。
    -fno-align-labels 建议使用它,以保证不和-falign-jumps("-O2"默认启用的选项)冲突

    -fno-align-loops 建议使用它,以确保不会在分支目标前插入多余的空指令。

    -fbranch-probabilities 在使用"-fprofile-arcs"选项编译程序并执行它来创建包含每个代码块执行次数的文件之后,程序可以利用这一选项再次编译,
    文件中所产生的信息将被用来优化那些经常发生的分支代码。如果没有这些信息,gcc将猜测那一分支可能经常发生并进行优化。
    这类优化信息将会存放在一个以源文件为名字的并以".da"为后缀的文件中。

    -fno-guess-branch-probability 默认情况下gcc将使用随机模型进行猜测哪个分支更可能被经常执行,并以此来优化代码,该选项关闭它。

    -fprofile-arcs 在使用这一选项编译程序并运行它以创建包含每个代码块的执行次数的文件后,程序可以再次使用"-fbranch-probabilities"编译,
    文件中的信息可以用来优化那些经常选取的分支。如果没有这些信息,gcc将猜测哪个分支将被经常运行以进行优化。
    这类优化信息将会存放在一个以源文件为名字的并以".da"为后缀的文件中。

    -fforce-addr 必须将地址复制到寄存器中才能对他们进行运算。由于所需地址通常在前面已经加载到寄存器中了,所以这个选项可以改进代码。
    -fforce-mem 必须将数值复制到寄存器中才能对他们进行运算。由于所需数值通常在前面已经加载到寄存器中了,所以这个选项可以改进代码。

    -ffreestanding 所编译的程序能够在独立的环境中运行,该环境可以没有标准库,而且可以不从main()函数开始运行。
    该选项将设置"-fno-builtin",且等同于"-fno-hosted"。
    -fhosted 所编译的程序需要运行在宿主环境中,其中需要有完整的标准库,而且main()函数具有int型的返回值。
    -fno-builtin 除非利用"__builtin_"进行引用,否则不识别所有内建函数。

    -fmerge-all-constants 试图将跨编译单元的所有常量值和数组合并在一个副本中。但是标准C/C++要求每个变量都必须有不同的存储位置。

    -fmove-all-movables 将所有不变的表达式移动到循环体之外,这种做法的好坏取决于源代码中的循环结构。

    -fnon-call-exceptions 产生的代码可供陷阱指令(如非法浮点运算和非法内存寻址)抛出异常,需要相关平台的运行时支持,并不普遍有效。

    -fomit-frame-pointer 对于不需要栈指针的函数就不在寄存器中保存指针,因此可以忽略存储和检索地址的代码,并将寄存器用于普通用途。
    所有"-O"级别都打开着一选项,但仅在调试器可以不依靠栈指针运行时才有效。建议不需要调试的情况下显式的设置它。

    -fno-optional-diags 禁止输出诊断消息,C++标准并不需要这些消息。
    -fpermissive 将代码中与标准不符合的诊断消息作为警告而不是错误输出。

    -fpic 生成可用于共享库的位置独立代码(PIC),所有的内存寻址均通过全局偏移表(GOT)完成。该选项并非在所有的机器上都有效。
    要确定一个地址,需要将代码自身的内存位置作为表中的一项插入。该选项可以产生在共享库中存放并从中加载的目标模块。

    -fprefetch-loop-arrays 生成数组预读取指令,对于使用巨大数组的程序可以加快代码执行速度,适合数据库相关的大型软件等。

    -freg-struct-return 生成用寄存器返回短结构的代码,如果寄存器无法荣纳将使用内存。

    -fstack-check 为防止程序栈溢出而进行必要的检测,在多线程环境中运行时才可能需要它。

    -ftime-report 编译完成后显示编译耗时的统计信息

    -funroll-loops 如果在编译时可以确定迭代的次数非常少而且循环中的指令也非常少,可以使用该选项进行循环展开,以驱除循环和复制指令。

    -finline-limit=<size> 对伪指令数超过<size>的函数,编译程序将不进行展开,默认为600

    --param <name>=<value> gcc内部存在一些优化代码程度的限制,调整这些限制就是调整整个优化全局。下面列出了参数的名字和对应的解释:
    名字 解释
    max-delay-slot-insn-search 较大的数目可以生成更优化的代码,但是会降低编译速度,默认为100
    max-delay-slot-live-search 较大的数目可以生成更优化的代码,但是会降低编译速度,默认为333
    max-gcse-memory 执行GCSE优化使用的最大内存量,太小将使该优化无法进行,默认为50M
    max-gcse-passes 执行GCSE优化的最大迭代次数,默认为1

    *******************************************************************
    说完了命令行选项,下面来说说与硬件体系结构(主要是cpu)相关的设置[仅针对i386/x86_64]
    最大名鼎鼎的"-march"上面已经说过了,下面讲讲别的(仅挑些实用的)

    -mfpmath=sse P3和athlon-tbird以上级别的cpu支持

    -masm=<dialect> 使用指定的dialect输出汇编语言指令,可以使用"intel"或"att";默认为"att"

    -mieee-fp 指定编译器使用IEEE浮点比较,这样将会正确的处理比较结果为无序的情况。

    -malign-double 将double, long double, long long对齐于双字节边界上;有助于生成更高速的代码,但是程序的尺寸会变大。

    -m128bit-long-double 指定long double为128位,pentium以上的cpu更喜欢这种标准。

    -mregparm=N 指定用于传递整数参数的寄存器数目(默认不使用寄存器)。0<=N<=3 ;注意:当N>0时你必须使用同一参数重新构建所有的模块,包括所有的库。

    -mmmx
    -mno-mmx
    -msse
    -mno-sse
    -msse2
    -mno-sse2
    -msse3
    -mno-sse3
    -m3dnow
    -mno-3dnow
    上面的这些不用解释了,一看就明白,根据自己的CPU决定吧

    -maccumulate-outgoing-args 指定在函数引导段中计算输出参数所需最大空间,这在大部分现代cpu中是较快的方法;缺点是会增加代码尺寸。

    -mthreads 支持Mingw32的线程安全异常处理。对于依赖于线程安全异常处理的程序,必须启用这个选项。
    使用这个选项时会定义"-D_MT",它将包含使用选项"-lmingwthrd"连接的一个特殊的线程辅助库,用于为每个线程清理异常处理数据。

    -minline-all-stringops 嵌入所有的字符串操作。可以提高字符串操作的性能,但是会增加代码尺寸。

    -momit-leaf-frame-pointer 不为叶子函数在寄存器中保存栈指针,这样可以节省寄存器,但是将会是调试变的困难。参见"-fomit-frame-pointer"。

    下面这几个仅用于x86_64环境:

    -m64 生成专门运行于64位环境的代码,不能运行于32位环境

    -mcmodel=small [默认值]程序和它的符号必须位于2GB以下的地址空间。指针仍然是64位。程序可以静态连接也可以动态连接。
    -mcmodel=kernel 内核运行于2GB地址空间之外。在编译linux内核时必须使用该选项!
    -mcmodel=medium 程序必须位于2GB以下的地址空间,但是它的符号可以位于任何地址空间。程序可以静态连接也可以动态连接。
    注意:共享库不能使用这个选项编译!
    -mcmodel=large 对地址空间没有任何限制,这个选项的功能目前尚未实现。

    ==============================
    既然已经讲了这么多了索性再讲讲gcc使用的一些环境变量
    除了大名鼎鼎的CFLAGS和CXXFLAGS以外(其实是Autoconf的环境变量),再挑几个说说:
    所有的PATH类环境变量(除LD_RUN_PATH外)都是用冒号分割的目录列表。

    C_INCLUDE_PATH 编译C程序时使用的环境变量,用于查找头文件。

    CPLUS_INCLUDE_PATH 编译C++程序时使用的环境变量,用于查找头文件。

    OBJC_INCLUDE_PATH 编译Obj-C程序时使用的环境变量,用于查找头文件。

    CPATH 编译C/C++/Obj-C程序时使用的环境变量,用于查找头文件。

    COMPILER_PATH 如果没有用GCC_EXEC_PREFIX定位子程序,编译程序将会在此查找它的子程序。

    LIBRARY_PATH 连接程序将在这些目录中寻找特殊的连接程序文件。

    LD_LIBRARY_PATH 该环境变量不影响编译程序,但是程序运行的时候会有影响:程序会查找该目录列表以寻找共享库。
    当不能够在编译程序的目录中找到共享库的时候,执行程序必须设置该环境变量。

    LD_RUN_PATH 该环境变量不影响编译程序,但是程序运行的时候会有影响:它在运行时指出了文件的名字,运行的程序可以由此得到它的符号名字和地址。
    由于地址不会重新载入,因而可能符号应用其他文件中的绝对地址。这个和ld工具使用的"-R"选项完全一样。

    GCC_EXEC_PREFIX 编译程序执行所有子程序的名字的前缀,默认值是"<prefix>/lib/gcc-lib/",
    其中的<prefix>是安装时configure脚本指定的前缀。

    LANG 指定编译程序使用的字符集,可用于创建宽字符文件、串文字、注释;默认为英文。[目前只支持日文"C-JIS,C-SJIS,C-EUCJP",不支持中文]

    LC_ALL 指定多字节字符的字符分类,主要用于确定字符串的字符边界以及编译程序使用何种语言发出诊断消息;默认设置与LANG相同。
    中文相关的几项:"zh_CN.GB2312 , zh_CN.GB18030 , zh_CN.GBK , zh_CN.UTF-8 , zh_TW.BIG5"

    TMPDIR 编译程序存放临时工作文件的临时目录,这些临时文件通常在编译结束时被删除。

  • 相关阅读:
    揭秘!如何快速提高网站权重-关键词百度指数叠加
    dede编辑文章不更新时间的方法
    PHPCMS V9轻松完成WAP手机网站搭建全教程
    如何建立关键词词库
    3gcms-Flash幻灯片上传后图片模糊解决办法
    手机端wap站网页播放腾讯视频代码
    解决dede编辑器不能保存word文档样式问题
    vi查找替换命令详解 (转载)
    eclipse上安装 windowBuilder方法
    单播、多播(组播)和广播的区别
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/8565941.html
Copyright © 2011-2022 走看看