前言
(一).gcc 简单理解就是编译器的组合装
man gcc 可以查出非常详细的用法,以下只简单总结几个常用的:
gcc -c 只编译,不链接 生成可重定位的目标文件(.o file)
gcc -o 生成可执行目标文件(a.out.file)
gcc -g 调试代码时使用
gcc -O 优化处理时使用
(二).readelf 用于查看ELF (Executable and Linkable Format)可执行可链接格式文件信息
man readelf 同理可查详细用法,以下仅列举用到的几个:
readelf -S [-S| --section-headers|–sections] 显示节头信息
readelf -s [-s|–syms|–symbols] 显示符号的项
readelf -h 读取文件头
*(三)*代码
/* main.c */
/* $begin main */
int sum(int *a, int n);
int array[2] = {1, 2};
int main()
{
int val = sum(array, 2);
return val;
}
/* $end main */
/* sum.c */
/* $begin sum */
int sum(int *a, int n)
{
int i, s = 0;
for (i = 0; i < n; i++) {
s += a[i];
}
return s;
}
/* $end sum */
一
ruby@ruby-VirtualBox:~/文档$ readelf -h sum.o
(一)结果
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: REL (可重定位文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x0
程序头起点: 0 (bytes into file)
Start of section headers: 592 (bytes into file)
标志: 0x0
本头的大小: 64 (字节)
程序头大小: 0 (字节)
Number of program headers: 0
节头大小: 64 (字节)
节头数量: 11
字符串表索引节头: 8
(二)分析
魔数:7f 45 4c 46
.strtab是在节头表中的索引。
程序入口地址是0,因为可重定位的目标文件,仅链接,不涉及执行。
程序头起点为0,大小也是0,说明没有程序头表。
节头表起始地址为592,大小为64个字节,11个表项,故节头表共有11*64B。
本头即ELF头,有64个字节,对应十六进制0x40。
字符串表是从11个中的第8个起。
二
ruby@ruby-VirtualBox:~/文档$ gcc -c sum.c
ruby@ruby-VirtualBox:~/文档$ readelf -S sum.o
(一)结果
共有 11 个节头,从偏移量 0x250 开始:
节头:
[号] 名称 类型 地址 偏移量 大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040 0000000000000045 0000000000000000 AX 0 0 1
[ 2] .data PROGBITS 0000000000000000 00000085 0000000000000000 0000000000000000 WA 0 0 1
[ 3] .bss NOBITS 0000000000000000 00000085 0000000000000000 0000000000000000 WA 0 0 1
[ 4] .comment PROGBITS 0000000000000000 00000085 0000000000000036 0000000000000001 MS 0 0 1
[ 5] .note.GNU-stack PROGBITS 0000000000000000 000000bb 0000000000000000 0000000000000000 0 0 1
[ 6] .eh_frame PROGBITS 0000000000000000 000000c0 0000000000000038 0000000000000000 A 0 0 8
[ 7] .rela.eh_frame RELA 0000000000000000 000001e0 0000000000000018 0000000000000018 I 9 6 8
[ 8] .shstrtab STRTAB 0000000000000000 000001f8 0000000000000054 0000000000000000 0 0 1
[ 9] .symtab SYMTAB 0000000000000000 000000f8 00000000000000d8 0000000000000018 10 8 8
[10] .strtab STRTAB 0000000000000000 000001d0 000000000000000b 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
(二)分析
节头表有11个节(【0】-【10】),起始地址 0x250
.text存放已编译的代码 ELF头的结尾是0x40故.text起始位置为0x40 ,大小为45。可装入,可执行,1字节方式对齐。
.data中存放已初始化的全局和静态变量,由.text可知.data从0x85 开始存放,由于sum.c 中并没有,所以大小为0,可写,可装入1字节方式对齐。
.bss中存放未初始化的全局和静态变量,由于sum.c 中并没有,所以大小为0。
.comment 中存放未初始化的全局和静态变量,但是不默认为0.
.symtab 一个符号表。
.strtab 一个字符串表。
地址都是0,因为仅链接,没有加载执行。
三
ruby@ruby-VirtualBox:~/文档$ readelf -s sum.o
(一)结果
Symbol table ‘.symtab’ contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS sum.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 4
8: 0000000000000000 69 FUNC GLOBAL DEFAULT 1 sum
(二)分析
唯一的全局类型就是第8行的函数sum。
其他均为局部变量。
四
ruby@ruby-VirtualBox:~/文档$ objdump -d sum.o
(一)结果
sum.o: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 :
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 89 7d e8 mov %rdi,-0x18(%rbp) //a[0]->%rdi 压栈
8: 89 75 e4 mov %esi,-0x1c(%rbp) //n->%esi
b: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
12: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)
19: eb 1d jmp 38 <sum+0x38>
1b: 8b 45 f8 mov -0x8(%rbp),%eax
1e: 48 98 cltq
20: 48 8d 14 85 00 00 00 lea 0x0(,%rax,4),%rdx //%rdx=%rax*4
27: 00
28: 48 8b 45 e8 mov -0x18(%rbp),%rax
2c: 48 01 d0 add %rdx,%rax
2f: 8b 00 mov (%rax),%eax
31: 01 45 fc add %eax,-0x4(%rbp)
34: 83 45 f8 01 addl $0x1,-0x8(%rbp)
38: 8b 45 f8 mov -0x8(%rbp),%eax
3b: 3b 45 e4 cmp -0x1c(%rbp),%eax
3e: 7c db jl 1b <sum+0x1b>
40: 8b 45 fc mov -0x4(%rbp),%eax
43: 5d pop %rbp
44: c3 retq
(二)分析
%rbp 被调用者保存
%rsp 栈指针
%rdi 第一个参数
%esi 32位第二个参数
%eax 32位返回值
%rdx 第三个参数
%rax 返回值
后序
探索问题:
只用gcc -o 单独编译main.c或sum.c不给文件名编译通不过
gcc -o联合编译main.c和sum.c 不给文件名会吞掉一个文件
gcc -o 联合编译main.c和sum.c 给出文件名才生成可执行目标文件