1. Build的四个阶段
1) 预处理:gcc -E (cpp)
选项:-D -U -I -M -g3 (output macro info for debugger)
2) 编译: gcc -S (cc1)
优化选项:-O0, -O1, -O2, -O3, -Os
一个关于优化级别选项的对比例子,
#include <stdio.h> int main() { char *c = malloc(8); memset(c, 's', 8); return 0; }
不优化编译
>gcc -g2 memset.c
查看产生的汇编代码,
>objdump -dS a.out
#include <stdio.h> int main() { 4004e8: 55 push %rbp 4004e9: 48 89 e5 mov %rsp,%rbp 4004ec: 48 83 ec 10 sub $0x10,%rsp char *c = malloc(8); 4004f0: bf 08 00 00 00 mov $0x8,%edi 4004f5: e8 0e ff ff ff callq 400408 <malloc@plt> 4004fa: 48 89 45 f8 mov %rax,-0x8(%rbp) memset(c, 's', 8); 4004fe: 48 8b 7d f8 mov -0x8(%rbp),%rdi 400502: ba 08 00 00 00 mov $0x8,%edx 400507: be 73 00 00 00 mov $0x73,%esi 40050c: e8 e7 fe ff ff callq 4003f8 <memset@plt> return 0; 400511: b8 00 00 00 00 mov $0x0,%eax } 400516: c9 leaveq 400517: c3 retq
优化编译
>gcc -g2 -O3 memset.c
#include <stdio.h> int main() { 4004a0: 48 83 ec 08 sub $0x8,%rsp char *c = malloc(8); 4004a4: bf 08 00 00 00 mov $0x8,%edi 4004a9: e8 12 ff ff ff callq 4003c0 <malloc@plt> memset(c, 's', 8); 4004ae: 48 ba 73 73 73 73 73 mov $0x7373737373737373,%rdx 4004b5: 73 73 73 4004b8: 48 89 10 mov %rdx,(%rax) return 0; } 4004bb: 31 c0 xor %eax,%eax 4004bd: 48 83 c4 08 add $0x8,%rsp
建议不要在测试阶段打开优化。
gdb信息选项:
-g == -g2
-g3 include symbols and extra.
下面显示了不同的级别产生的代码的大小。
/home/a/j/nomad2:cat hello.c #include <stdio.h> int main() { printf("hello world!/n"); return 0; } /home/a/j/nomad2:cc hello.c -o hg0 /home/a/j/nomad2:cc -g1 hello.c -o hg1 /home/a/j/nomad2:cc -g2 hello.c -o hg2 /home/a/j/nomad2:cc -g3 hello.c -o hg3 /home/a/j/nomad2:ls -lrt hg* -rwxr-xr-x 1 nomad2 member 8828 Dec 11 20:02 hg0 -rwxr-xr-x 1 nomad2 member 9444 Dec 11 20:02 hg1 -rwxr-xr-x 1 nomad2 member 9636 Dec 11 20:02 hg2 -rwxr-xr-x 1 nomad2 member 22468 Dec 11 20:03 hg3
在产生的ELF(Executable & Library Format, magic is /x7FELF, man 5 elf)文件中,debug信息占到若干个section,
/home/a/j/nomad2:objdump -h hg1|grep debug 26 .debug_aranges 000000c0 0000000000000000 0000000000000000 000009b0 2**4 27 .debug_pubnames 00000040 0000000000000000 0000000000000000 00000a70 2**0 28 .debug_info 00000221 0000000000000000 0000000000000000 00000ab0 2**0 29 .debug_abbrev 00000094 0000000000000000 0000000000000000 00000cd1 2**0 30 .debug_line 00000167 0000000000000000 0000000000000000 00000d65 2**0 31 .debug_frame 00000040 0000000000000000 0000000000000000 00000ed0 2**3 32 .debug_str 000000b1 0000000000000000 0000000000000000 00000f10 2**0 33 .debug_loc 0000004c 0000000000000000 0000000000000000 00000fc1 2**0 34 .debug_ranges 00000090 0000000000000000 0000000000000000 00001010 2**4 /home/a/j/nomad2:objdump -h hg3|grep debug 26 .debug_aranges 000000c0 0000000000000000 0000000000000000 000009b0 2**4 27 .debug_pubnames 00000040 0000000000000000 0000000000000000 00000a70 2**0 28 .debug_info 000002b3 0000000000000000 0000000000000000 00000ab0 2**0 29 .debug_abbrev 000000ae 0000000000000000 0000000000000000 00000d63 2**0 30 .debug_line 000002aa 0000000000000000 0000000000000000 00000e11 2**0 31 .debug_frame 00000040 0000000000000000 0000000000000000 000010c0 2**3 32 .debug_str 000000b1 0000000000000000 0000000000000000 00001100 2**0 33 .debug_loc 0000004c 0000000000000000 0000000000000000 000011b1 2**0 34 .debug_macinfo 00003085 0000000000000000 0000000000000000 000011fd 2**0 35 .debug_ranges 00000090 0000000000000000 0000000000000000 00004290 2**4
使用objdump -W可以查看具体的debug段信息。
3) 汇编: gcc -c (as)
可以使用-m(achine)选项指定产生目标平台的代码。
4) 链接: ld
insertion of libraries(static) or reference(dynamic).
选项:
-l -L -shared -static
在release阶段,可以strip掉debug信息, “-d”选项只删除掉调试符号信息。
/home/a/j/nomad2:strip -d hg3 /home/a/j/nomad2:strip -d hg2 /home/a/j/nomad2:strip -d hg1 /home/a/j/nomad2:strip -d hg0 /home/a/j/nomad2:ls -lrt hg* -rwxr-xr-x 1 nomad2 member 6575 Dec 11 20:09 hg3 -rwxr-xr-x 1 nomad2 member 6575 Dec 11 20:10 hg2 -rwxr-xr-x 1 nomad2 member 6575 Dec 11 20:10 hg1 -rwxr-xr-x 1 nomad2 member 6575 Dec 11 20:10 hg0 /home/a/j/nomad2:strip hg3 /home/a/j/nomad2:strip hg2 /home/a/j/nomad2:strip hg1 /home/a/j/nomad2:strip hg0 /home/a/j/nomad2:ls -lrt hg* -rwxr-xr-x 1 nomad2 member 4496 Dec 11 20:10 hg3 -rwxr-xr-x 1 nomad2 member 4496 Dec 11 20:11 hg2 -rwxr-xr-x 1 nomad2 member 4496 Dec 11 20:11 hg1 -rwxr-xr-x 1 nomad2 member 4496 Dec 11 20:11 hg0
2. Parse ELF tools
1) objdump
常用选项: -d(反汇编代码段),-g -W (显示调试信息), -S(同-d,显示源文件),-s(以十六进制格式显示所有段) ,-t(显示符号表),-x(显示所有头部信息)
2) readelf
3) nm: 显示符号表,要求不能被strip过
/home/a/j/nomad2:nm hg3 nm: hg3: no symbols
4) ldd: resolve shared library dependencies
举例说明,
/home/a/j/nomad2:file /lib/librt-2.7.so /lib/librt-2.7.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), for GNU/Linux 2.6.8, stripped /home/a/j/nomad2:file a.out a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), for GNU/Linux 2.6.8, dynamically linked (uses shared libs), not stripped
动态库的load可以分为装载时和运行时(dl_open),下面是运行时装载动态库的例子。
nomad2@ubuntu:~/c$ cat d.c #include <dlfcn.h> #include <stdio.h> int main(int argc, char**argv) { void *so = dlopen("/lib/libc.so.6", RTLD_NOW); void *f = dlsym(so, argv[1]); if (f) { printf("find %s in libc/n", argv[1]); } else { printf("can't find %s in libc/n", argv[1]); } } nomad2@ubuntu:~/c$ cc -ldl -g2 d.c nomad2@ubuntu:~/c$ ./a.out printf find printf in libc nomad2@ubuntu:~/c$ ./a.out printfa can't find printfa in libc
3. gdb
3种使用模式:
1) start a binary in the debugger, gdb a.out
2) running process attachment, gdb a.out PID
3) post-mortem, core file analysis, gdb a.out core
常用命令总结:
break, bt(backtrack of stack call of current thread), disass, i r, stepi, info threads, r, c, p, x(dump memory from address)
关于core文件
1) ulimit -c -s -t
注意编译器在编译程序时,本身也是需要栈空间的。可以使用函数getrlimit, setrlimit来get/set resource limits。
/home/a/j/nomad2:ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) 100000 scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 69632 max locked memory (kbytes, -l) 32 max memory size (kbytes, -m) 100000 open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) 1000 max user processes (-u) 64 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
2) core文件的格式
/home/a/j/nomad2:cat /proc/sys/kernel/core_pattern core
man 5 core to reference the naming rule of core dump files.
注意:如果发生了core,但是gdb backtrace找不到在哪个函数core了,可以先ldd这个程序,看依赖于哪些动态链接库,然后nm动态链接库并且搜索core的地址的前几位,基本就可确定在哪个函数core了。一般的text段以0x8***开始。
对于正在运行的程序,可以通过检查maps文件来确定so的地址范围, 例如
nomad2@ubuntu:/proc/26847$ pwd /proc/26847 nomad2@ubuntu:/proc/26847$ cat maps 00400000-004be000 r-xp 00000000 08:01 17694722 /bin/bash 006bd000-006c7000 rw-p 000bd000 08:01 17694722 /bin/bash 006c7000-0097a000 rw-p 006c7000 00:00 0 [heap] 7fbad9edb000-7fbadbedb000 r--s 00000000 08:01 8323249 /var/cache/nscd/passwd 7fbadbedb000-7fbadc033000 r-xp 00000000 08:01 17825953 /lib/libc-2.7.so 7fbadc033000-7fbadc233000 ---p 00158000 08:01 17825953 /lib/libc-2.7.so 7fbadc233000-7fbadc236000 r--p 00158000 08:01 17825953 /lib/libc-2.7.so 7fbadc236000-7fbadc238000 rw-p 0015b000 08:01 17825953 /lib/libc-2.7.so 7fbadc238000-7fbadc23d000 rw-p 7fbadc238000 00:00 0 7fbadc23d000-7fbadc23f000 r-xp 00000000 08:01 17825970 /lib/libdl-2.7.so 7fbadc23f000-7fbadc43f000 ---p 00002000 08:01 17825970 /lib/libdl-2.7.so 7fbadc43f000-7fbadc441000 rw-p 00002000 08:01 17825970 /lib/libdl-2.7.so 7fbadc441000-7fbadc478000 r-xp 00000000 08:01 17825802 /lib/libncurses.so.5.6 7fbadc478000-7fbadc677000 ---p 00037000 08:01 17825802 /lib/libncurses.so.5.6 7fbadc677000-7fbadc67c000 rw-p 00036000 08:01 17825802 /lib/libncurses.so.5.6 7fbadc67c000-7fbadc699000 r-xp 00000000 08:01 17825944 /lib/ld-2.7.so 7fbadc889000-7fbadc88b000 rw-p 7fbadc889000 00:00 0 7fbadc896000-7fbadc899000 rw-p 7fbadc896000 00:00 0 7fbadc899000-7fbadc89b000 rw-p 0001d000 08:01 17825944 /lib/ld-2.7.so 7fffe4885000-7fffe489a000 rw-p 7ffffffea000 00:00 0 [stack] 7fffe49fe000-7fffe4a00000 r-xp 7fffe49fe000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]