您的位置:首页 > 运维架构 > Linux

Linux 编译的一些注意参数

2013-06-08 15:26 162 查看
-Wall 打开gcc的所有警告

-l参数就是用来指定程序要链接的库,-l参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢?就拿数学库来说,他的库名是m,他的库文件名是libm.so,很容易看出,把库文件名的头lib和尾.so去掉就是库名了。

好了现在我们知道怎么得到库名,当我们自已要用到一个第三方提供的库名字libtest.so,那么我们只要把libtest.so拷贝到/usr/lib里,编译时加上-ltest参数,我们就能用上libtest.so库了(当然要用libtest.so库里的函数,我们还需要与libtest.so配套的头文件)

放在/lib和/usr/lib和/usr/local/lib里的库直接用-l参数就能链接了,但如果库文件没放在这三个目录里,而是放在其他目录里,这时我们只用-l参数的话,链接还是会出错,出错信息大概是:“/usr/bin/ld: cannot find -lxxx”,也就是链接程序ld在那3个目录里找不到libxxx.so,这时另外一个参数-L就派上用场了,比如常用的X11的库,它在/usr/X11R6/lib目录下,我们编译时就要用-L/usr/X11R6/lib
-lX11参数,-L参数跟着的是库文件所在的目录名。再比如我们把libtest.so放在/aaa/bbb/ccc目录下,那链接参数就是-L/aaa/bbb/ccc -ltest

一:编译过程的4个阶段:预处理,编译,汇编,链接;

1:最常用的方式

gcc hello.c -o hello

2:预处理后停止编译

gcc -E hello.c -o hello.i(.i通常为已经预处理过的C原始程序)

3:将hello.i编译为目标代码,可以使用-c参数来完成

gcc -c hello.i -o hello.o

也可以使用-x参数让gcc从指定的步骤开始.

4:将生成的目标文件链接成可执行文件

gcc hello.o -o hello

5:多个源文件时

gcc f1.c f2.c -o file

二:gcc参数的警告提示功能

1:-pedantic 使用了ANSI/ISO C语言扩展语法的地方将产生相应的警告信息

2:-Wall 产生尽可能多的警告信息,建议始终带上

3:-Werror 将所有的警告当成错误进行处理

三:库依赖

函数库实际上就是一些头文件和库文件的集合.

-I选项向gcc的头文件搜索路径中添加新的目录

-L选项向gcc的库文件搜索路径中添加新的目录

-l指定链接的库文件名,如 -ldavid 表示指示gcc去连接库文件libdavid.so

-static强制使用静态链接库

动态库文件(.so)和静态库文件(.a)的区别在于程序执行时所需的代码是运行时动态加载的,还是编译时静态加载的.gcc优先使用动态库

四:其他

-On 控制优化代码的生成,n是一个代表优先级别的整数;n取0~3;-O相当于-O1;

-O1 减小代码的长度和执行时间,一般包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops);

-O2 在O1基础上,进行一些额外调整,如处理器指令跳转;

-O3 在O2基础上,循环展开一些其他和处理器特性相关的优化工作;

-pipe:管道,它可以用来同时连接两个程序,其中一个程序的输出将直接作为另一个程序的输入,这样就可以避免使用临时文件,提高编译速度,但编译时消耗更多的内存.

-Dmacro 定义指定的宏,使它能够通过源码中的#ifdef进行检验

-g3 获得有关调试程序的详细信息

-S 要求编译程序生成来自源代码的汇编程序输出

-v 启动所有报警

-w 禁止所有报警

五:常见错误类型

1:syntex error,语法错误

2:can't find include file,头文件错误

3:ld:lm:No such file or directory,找不到所需的函数库

4:Undefined symbol未定义符号:源代码文件未被包含或编译。

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

by alert7

2001-12-21

测试环境 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了。

★ 小节

抛砖引玉般简单的实例描述了下gcc常用的编译选项对代码的影响。

不正之处,还请斧正。谢谢。

文章来源:http://xfocus.org/

refer:: http://blog.163.com/shaohj_1999@126/blog/static/6340685120123123312253/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: