Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

# 编译链接和运行时库 ----- # 编译 C语言编译步骤: 1. 预处理 `gcc -E hello_world.c` 2. 编译 `gcc -S hello_world.c` 3. 汇编 `gcc -c hello_world.c` 4. 链接 `gcc hello_world.c` ----- # 编译步骤 1. 词法分析(flex) 2. 语法分析(bison) 3. 语义分析 4. 中间代码生成 5. 目标代码生成优化(llvm IR) ----- # 代码演示1: 1. scanner.l 2. parser.y 3. 遍历语法树 ----- ## 链接 2. 为什么需要链接 ? 2. ELF文件的结构是什么样子的 ? 3. section 和 segment 的区别是什么 ? 4. 静态链接的过程是什么样子的 ? 5. 强符号和弱符号 3. 为什么需要动态链接 ? 4. 如何降低动态链接的性能损失 ----- ## ELF文件的结构 1. ELF Header 2. Program Header 3. Section Header ![img](./images/a.png) ----- # Hack ELF Header typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf64_Half e_type; /* Object file type */ Elf64_Half e_machine; /* Architecture */ Elf64_Word e_version; /* Object file version */ Elf64_Addr e_entry; /* Entry point virtual address */ Elf64_Off e_phoff; /* Program header table file offset */ Elf64_Off e_shoff; /* Section header table file offset */ Elf64_Word e_flags; /* Processor-specific flags */ Elf64_Half e_ehsize; /* ELF header size in bytes */ Elf64_Half e_phentsize; /* Program header table entry size */ Elf64_Half e_phnum; /* Program header table entry count */ Elf64_Half e_shentsize; /* Section header table entry size */ Elf64_Half e_shnum; /* Section header table entry count */ Elf64_Half e_shstrndx; /* Section header string table index */ } Elf64_Ehdr; ----- # 演示代码2 ----- # Hack Program Header typedef struct { Elf64_Word p_type; /* Segment type */ Elf64_Word p_flags; /* Segment flags */ Elf64_Off p_offset; /* Segment file offset */ Elf64_Addr p_vaddr; /* Segment virtual address */ Elf64_Addr p_paddr; /* Segment physical address */ Elf64_Xword p_filesz; /* Segment size in file */ Elf64_Xword p_memsz; /* Segment size in memory */ Elf64_Xword p_align; /* Segment alignment */ } Elf64_Phdr; ----- # 演示代码3 : 首先看一段代码 #include extern char end; int main(int argc, char *argv[]) { printf("%p\n", &end); return 0; } ----- # Section Header typedef struct { Elf64_Word sh_name; /* Section name (string tbl index) */ Elf64_Word sh_type; /* Section type */ Elf64_Xword sh_flags; /* Section flags */ Elf64_Addr sh_addr; /* Section virtual addr at execution */ Elf64_Off sh_offset; /* Section file offset */ Elf64_Xword sh_size; /* Section size in bytes */ Elf64_Word sh_link; /* Link to another section */ Elf64_Word sh_info; /* Additional section information */ Elf64_Xword sh_addralign; /* Section alignment */ Elf64_Xword sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr; ----- # 说明几个关键的Section 1. .text 2. .data 3. .rodata 4. .bss 5. .got ----- # 静态链接: 链接前 Disassembly of section .text: 0000000000000000
: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 48 83 ec 10 sub $0x10,%rsp 8: 89 7d fc mov %edi,-0x4(%rbp) b: 48 89 75 f0 mov %rsi,-0x10(%rbp) f: b8 00 00 00 00 mov $0x0,%eax 14: e8 00 00 00 00 callq 19 (main+0x19) 19: b8 00 00 00 00 mov $0x0,%eax 1e: c9 leaveq 1f: c3 retq ----- # 静态链接: 链接后 0000000000001119
: 1119: 55 push %rbp 111a: 48 89 e5 mov %rsp,%rbp 111d: 48 83 ec 10 sub $0x10,%rsp 1121: 89 7d fc mov %edi,-0x4(%rbp) 1124: 48 89 75 f0 mov %rsi,-0x10(%rbp) 1128: b8 00 00 00 00 mov $0x0,%eax 112d: e8 07 00 00 00 callq 1139 1132: b8 00 00 00 00 mov $0x0,%eax 1137: c9 leaveq 1138: c3 retq 0000000000001139 : 1139: 55 push %rbp 113a: 48 89 e5 mov %rsp,%rbp 113d: 90 nop 113e: 5d pop %rbp 113f: c3 retq ----- # 和静态链接相关的Section 和结构体 1. .symtab 2. .rel.text .rel.data typedef struct { Elf64_Word st_name; /* Symbol name (string tbl index) */ unsigned char st_info; /* Symbol type and binding */ unsigned char st_other; /* Symbol visibility */ Elf64_Section st_shndx; /* Section index */ Elf64_Addr st_value; /* Symbol value */ Elf64_Xword st_size; /* Symbol size */ } Elf64_Sym; typedef struct { // 重定位入口 Elf64_Addr r_offset; /* Address */ Elf64_Xword r_info; /* Relocation type and symbol index */ /* 低8位表示类型,高24表示在符号表中间的下标 */ } Elf64_Rel; ----- # 静态链接的两种方法 1. PC相对寻址 2. 绝对寻址 符号的实际地址 + 保存在被修正位置的值 (- 被修正的位置) ----- # 动态链接 1. GOT(Global Offset Table) 2. PLT(Procedure Linkage Table) ----- # 延迟绑定的实现 #include int main(int argc, char *argv[]) { printf("%d\n", 1); return 0; } ----- # 延迟绑定的实现 0000000000001139
: 1139: 55 push %rbp 113a: 48 89 e5 mov %rsp,%rbp 113d: 48 83 ec 10 sub $0x10,%rsp 1141: 89 7d fc mov %edi,-0x4(%rbp) 1144: 48 89 75 f0 mov %rsi,-0x10(%rbp) 1148: be 01 00 00 00 mov $0x1,%esi 114d: 48 8d 3d b0 0e 00 00 lea 0xeb0(%rip),%rdi # 2004 <_IO_stdin_used+0x4> 1154: b8 00 00 00 00 mov $0x0,%eax 1159: e8 d2 fe ff ff callq 1030 (printf@plt) 115e: b8 00 00 00 00 mov $0x0,%eax 1163: c9 leaveq 1164: c3 retq 1165: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 116c: 00 00 00 116f: 90 nop ----- # 延迟绑定的实现 printf@plt: jmp * (printf@GOT) push n push ModuleID jump _dl_runtime_resolve ----- # 延迟绑定的实现 printf@plt: jmp * (printf@GOT) push n push ModuleID jump _dl_runtime_resolve Disassembly of section .plt: 0000000000001020 (.plt): 1020: ff 35 e2 2f 00 00 pushq 0x2fe2(%rip) # 4008 (_GLOBAL_OFFSET_TABLE_+0x8) 1026: ff 25 e4 2f 00 00 jmpq *0x2fe4(%rip) # 4010 (_GLOBAL_OFFSET_TABLE_+0x10) 102c: 0f 1f 40 00 nopl 0x0(%rax) 0000000000001030 (printf@plt): 1030: ff 25 e2 2f 00 00 jmpq *0x2fe2(%rip) # 4018 (printf@GLIBC_2.2.5) 1036: 68 00 00 00 00 pushq $0x0 103b: e9 e0 ff ff ff jmpq 1020 (.plt) ----- ## 链接 2. 为什么需要链接 ? 2. ELF文件的结构是什么样子的 ? 3. section 和 segment 的区别是什么 ? 4. 静态链接的过程是什么样子的 ? 5. 强符号和弱符号 3. 为什么需要动态链接 ? 4. 如何降低动态链接的性能损失 ----- # 运行时库 ----- ## 一些问题 1. main函数是程序执行的第一行吗? 2. malloc是如何实现的 ? 4. 如何不使用glibc实现io 4. 变长数组如何实现 ? ----- # 运行时库 1. 启动和退出函数 2. 辅助工具函数(string math) 3. IO 3. **HEAP** 4. 调试功能 ----- # 进程地址空间 ![](./images/b.png) ----- # 代码4: 自定义C语言运行时库 ----- # 说明几点 1. 汇编代码和64位机器的兼容 2. 变长参数 3. 修复Heap 实现的Bug 4. 调用惯例 5. 错误的链接脚本 ----- ## 一些问题 1. main函数是程序执行的第一行吗? 2. malloc是如何实现的 ? 4. 如何不使用glibc实现io 4. 变长数组如何实现 ? ----- # 疑惑 当使用write系统调用的时候,为什么不可以将用户栈地址作为参数 ?