├── 01 基本概念 ├── 01 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── tool.c │ ├── tool.h │ └── tool_base.S └── README.md ├── 02 格式 └── README.md ├── 03 简要示例 ├── 01 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S └── README.md ├── 04 简要链接指令 ├── 01 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S └── README.md ├── 05 符号值分配 ├── 01 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S ├── 02 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S ├── 03 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S └── README.md ├── 06 SECTIONS指令 ├── 01 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S ├── 02 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S ├── 03 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S ├── 04 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S ├── 05 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S ├── 06 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S ├── 07 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S └── README.md ├── 07 MEMORY指令 ├── 01 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── linker_script.lds │ ├── tool.c │ ├── tool.h │ └── tool_base.S └── README.md ├── 08 PHDRS指令 └── README.md ├── 09 VERSION指令 ├── 01 │ ├── .gitignore │ ├── Makefile │ ├── app.c │ ├── libtool.so │ ├── tool.c │ ├── tool.h │ ├── tool_base.S │ └── version_script.lds └── README.md ├── 10 Linker Scripts中的表达式 └── README.md ├── 11 构建可运行程序 ├── 01 │ ├── .gitignore │ ├── Makefile │ ├── linker_script.lds │ ├── main.c │ └── test └── README.md ├── 12 抛弃glibc依赖 ├── 01 │ ├── .gitignore │ ├── Makefile │ ├── linker_script.lds │ ├── main.c │ ├── syscall.S │ └── syscall.h ├── 02 │ ├── .gitignore │ ├── Makefile │ ├── linker_script.lds │ ├── main.c │ ├── syscall.S │ └── syscall.h └── README.md ├── README.html └── README.md /01 基本概念/01/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /01 基本概念/01/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | ./app 5 | 6 | app:app.o tool.o tool_base.o 7 | gcc -g -o $@ $^ 8 | 9 | 10 | app.o:app.c 11 | gcc -g -c -o $@ $< 12 | 13 | 14 | tool.o:tool.c tool.h 15 | gcc -g -c -o $@ $< 16 | 17 | tool_base.o:tool_base.S 18 | as -g -o $@ $< 19 | -------------------------------------------------------------------------------- /01 基本概念/01/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | int rev; 11 | 12 | rev = math_pow(2, 10); 13 | printf("2^10=%d\n", rev); 14 | 15 | rev = math_add(2, 10); 16 | printf("2+10=%d\n", rev); 17 | 18 | printf("math_add func len:0x%x\n", 19 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 20 | 21 | printf("math_add=0x%x\n",(unsigned long long)math_add); 22 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 23 | 24 | bss_var = 0x55aa; 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /01 基本概念/01/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /01 基本概念/01/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /01 基本概念/01/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /01 基本概念/README.md: -------------------------------------------------------------------------------- 1 | ##### 1、概述 2 |         Linux编译产出文件种类包括目标文件、可执行文件、动态链接文件、目标归档文件。这些文件格式常为`ELF`格式。 3 | 4 | 文件类型|英文名|扩展名|生成指令 5 | --|--|--|-- 6 | 目标文件|object file|.o|gcc -C ... 7 | 可执行文件|executable file|————|ld ... 8 | 动态链接文件|shared object file|.so|ld -shared ... 9 | 目标归档文件|archived file|.a|ar c ... 10 | 11 | 12 |         针对一个目标文件,其内容被划分成多个成为`section`的区段,比如记录指令的`.text`段,记录数据的`.data`段。每个区段组成表项,记录在ELF的头部区域。每个表项都记录该段在文件内部的偏移,段被加载的内存地址。 13 | 14 | 15 |         当一个程序被连接时,多个文件的`section`会按照一定规则合并成节(`segment`)。程序执行时,系统以节的视角加载该可执行程序。一种特殊情况是,当程序的存储载体是ROM介质,那么程序执行时,需要预先把存储数据的segment拷贝到RAM介质中。还要注意的是,这个RAM介质和ROM介质处于同一个地址空间的下不同地址段中。也就是说,节在存储时会记录两个地址,运行地址称为VMA,存储地址称为LMA。 16 | 17 |         C函数、变量和汇编的标号在编译时都会转化为符号(`symbol`)。符号的值为该变量的地址。当一个源文件引用外部的符号时,编译产出得到一个未定义的符号,未定义的符号在链接决议时转化为定义的符号,并在指令区域填入正确的符号地址。 18 | 19 |         除了函数和变量,符号还可以来自系统环境信息,比如源程序文件名,也可以来自`linker script`,甚至可以来自汇编源程序。 20 | 21 | ##### 2、测试用例 22 | 23 |         下面动手操练下上述概念。创建三个源文件app.c、tool.c和tool.h。编译过程由Makefile管理。请简要浏览Makefile以了解编译过程。这些代码位于01文件夹中。 24 | 25 |         使用 `readelf`查看文件的符号表如下。 26 | ``` 27 | $ readelf -s tool.o 28 | 29 | Symbol table '.symtab' contains 9 entries: 30 | Num: Value Size Type Bind Vis Ndx Name 31 | 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 32 | 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS tool.c 33 | 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 34 | 3: 0000000000000000 0 SECTION LOCAL DEFAULT 2 35 | 4: 0000000000000000 0 SECTION LOCAL DEFAULT 3 36 | 5: 0000000000000000 0 SECTION LOCAL DEFAULT 5 37 | 6: 0000000000000000 0 SECTION LOCAL DEFAULT 6 38 | 7: 0000000000000000 0 SECTION LOCAL DEFAULT 4 39 | 8: 0000000000000000 53 FUNC GLOBAL DEFAULT 1 math_pow 40 | 41 | ``` 42 | ``` 43 | $ readelf -s app.o 44 | 45 | Symbol table '.symtab' contains 13 entries: 46 | Num: Value Size Type Bind Vis Ndx Name 47 | 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 48 | 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS app.c 49 | 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 50 | 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 51 | 4: 0000000000000000 0 SECTION LOCAL DEFAULT 4 52 | 5: 0000000000000000 0 SECTION LOCAL DEFAULT 5 53 | 6: 0000000000000000 0 SECTION LOCAL DEFAULT 7 54 | 7: 0000000000000000 0 SECTION LOCAL DEFAULT 8 55 | 8: 0000000000000000 0 SECTION LOCAL DEFAULT 6 56 | 9: 0000000000000000 67 FUNC GLOBAL DEFAULT 1 main 57 | 10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_ 58 | 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND math_pow 59 | 12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf 60 | 61 | ``` 62 | ``` 63 | $ readelf -s tool_base.o 64 | 65 | Symbol table '.symtab' contains 10 entries: 66 | Num: Value Size Type Bind Vis Ndx Name 67 | 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 68 | 1: 0000000000000000 0 SECTION LOCAL DEFAULT 1 69 | 2: 0000000000000000 0 SECTION LOCAL DEFAULT 2 70 | 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 71 | 4: 0000000000000000 0 SECTION LOCAL DEFAULT 6 72 | 5: 0000000000000000 0 SECTION LOCAL DEFAULT 8 73 | 6: 0000000000000000 0 SECTION LOCAL DEFAULT 4 74 | 7: 0000000000000000 0 SECTION LOCAL DEFAULT 9 75 | 8: 0000000000000000 0 NOTYPE GLOBAL DEFAULT 1 math_add 76 | 9: 0000000000000007 0 NOTYPE GLOBAL DEFAULT 1 math_add_end 77 | 78 | ``` 79 |         源程序文件名`tool.c`和`app.c`也成为目标文件的符号,并且类型为FILE。`app.o`记录的外部符号`path_pow`为NOTYPE类型,并且索引类型为UND未决议类型。 80 | 81 |         我们在汇编程序中定义了`math_add_end`符号。我们该如何认识这个符号?这个符号有值吗?这个符号有地址吗?这个符号的类型是什么? 82 | 83 |         `math_add_end`没有长度,即不占用地址空间。所以其没有值概念,但是可以对其取地址,地址即为连接后VMA地址。由于`math_add_end`仅仅记录一个内存位置,所以我们把它声明为void *类型,并在其后的内存位置填入magic数据。app.c中打印了该符号的地址和值。毫无疑问符号地址位于`math_add`后7个字节处,其值为magic值,因为该符号本身没有值,对齐取值会导致内存越界。 84 | 85 |         日志打印如下。 86 | 87 | ``` 88 | ./app 89 | 2^10=1024 90 | 2+10=12 91 | math_add func len:0x7 92 | math_add=0xd26b47a6 93 | math_add_end=0xf0f0f0f0 94 | ``` 95 | 96 | 97 |         `linker script`也可以设置符号。我们会在后续章节中介绍。 -------------------------------------------------------------------------------- /02 格式/README.md: -------------------------------------------------------------------------------- 1 | ##### 1、注释写法 2 |         `linker script`使用`/* */`方式表达注释。 3 | 4 | ##### 2、文件名写法 5 | 6 |         当包含文件时,你可以直接键入文件名。如果文件名包含特殊字符,需要使用引号包裹文件名。 -------------------------------------------------------------------------------- /03 简要示例/01/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /03 简要示例/01/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool_base.o linker_script.lds 7 | ld -e main -T linker_script.lds -o $@ app.o tool.o tool_base.o 8 | 9 | app.o:app.c 10 | gcc -g -c -o $@ $< 11 | 12 | 13 | tool.o:tool.c tool.h 14 | gcc -g -c -o $@ $< 15 | 16 | tool_base.o:tool_base.S 17 | as -g -o $@ $< 18 | -------------------------------------------------------------------------------- /03 简要示例/01/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | #define printf 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | int rev; 13 | 14 | rev = math_pow(2, 10); 15 | printf("2^10=%d\n", rev); 16 | 17 | rev = math_add(2, 10); 18 | printf("2+10=%d\n", rev); 19 | 20 | printf("math_add func len:0x%x\n", 21 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 22 | 23 | printf("math_add=0x%x\n",(unsigned long long)math_add); 24 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 25 | 26 | bss_var = 0x55aa; 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /03 简要示例/01/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | SECTIONS 3 | { 4 | . = 0x400000; 5 | .text : { *(.text) } 6 | . = 0x8000000; 7 | .data : { *(.data) } 8 | . = 0xc000000; 9 | .bss : { *(.bss) } 10 | } 11 | -------------------------------------------------------------------------------- /03 简要示例/01/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /03 简要示例/01/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /03 简要示例/01/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /03 简要示例/README.md: -------------------------------------------------------------------------------- 1 | ##### 1、测试示例 2 |         这一小节我们演示`linker script`的简单用法。 3 | 4 |         第一节中我们创建了一个测试用例,并编译连接出一个能够运行的可执行程序`app`。使用`readelf`命令,获取可执行程序的`segment`分配如下图。 5 | 6 | ``` 7 | $ readelf -l app 8 | 9 | Elf file type is DYN (Shared object file) 10 | Entry point 0x580 11 | There are 9 program headers, starting at offset 64 12 | 13 | Program Headers: 14 | Type Offset VirtAddr PhysAddr 15 | FileSiz MemSiz Flags Align 16 | PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040 17 | 0x00000000000001f8 0x00000000000001f8 R E 0x8 18 | INTERP 0x0000000000000238 0x0000000000000238 0x0000000000000238 19 | 0x000000000000001c 0x000000000000001c R 0x1 20 | [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] 21 | LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 22 | 0x0000000000000a04 0x0000000000000a04 R E 0x200000 23 | LOAD 0x0000000000000dd8 0x0000000000200dd8 0x0000000000200dd8 24 | 0x0000000000000258 0x0000000000000260 RW 0x200000 25 | DYNAMIC 0x0000000000000df0 0x0000000000200df0 0x0000000000200df0 26 | 0x00000000000001e0 0x00000000000001e0 RW 0x8 27 | NOTE 0x0000000000000254 0x0000000000000254 0x0000000000000254 28 | 0x0000000000000044 0x0000000000000044 R 0x4 29 | GNU_EH_FRAME 0x0000000000000890 0x0000000000000890 0x0000000000000890 30 | 0x0000000000000044 0x0000000000000044 R 0x4 31 | GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 32 | 0x0000000000000000 0x0000000000000000 RWE 0x10 33 | GNU_RELRO 0x0000000000000dd8 0x0000000000200dd8 0x0000000000200dd8 34 | 0x0000000000000228 0x0000000000000228 R 0x1 35 | 36 | Section to Segment mapping: 37 | Segment Sections... 38 | 00 39 | 01 .interp 40 | 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame 41 | 03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 42 | 04 .dynamic 43 | 05 .note.ABI-tag .note.gnu.build-id 44 | 06 .eh_frame_hdr 45 | 07 46 | 08 .init_array .fini_array .jcr .dynamic .got 47 | 48 | ``` 49 | 50 |         指令节的虚拟地址为`0x0000000000000000`,`.data`和`.bss`保存到同一个节中,虚拟地址为`0x0000000000200dd8`。 51 | 52 | 53 |         使用如下linker script重新编译程序。 54 | ``` 55 | SECTIONS 56 | { 57 | . = 0x400000; 58 | .text : { *(.text) } 59 | . = 0x8000000; 60 | .data : { *(.data) } 61 | . = 0xc000000; 62 | .bss : { *(.bss) } 63 | } 64 | ``` 65 | `segments`分配情况如下图。 66 | ``` 67 | ld -e main -T linker_script.lds -o app app.o tool.o tool_base.o 68 | readelf -l app 69 | 70 | Elf file type is EXEC (Executable file) 71 | Entry point 0x400000 72 | There are 4 program headers, starting at offset 64 73 | 74 | Program Headers: 75 | Type Offset VirtAddr PhysAddr 76 | FileSiz MemSiz Flags Align 77 | LOAD 0x0000000000200000 0x0000000000400000 0x0000000000400000 78 | 0x00000000000000e0 0x00000000000000e0 R E 0x200000 79 | LOAD 0x0000000000400000 0x0000000008000000 0x0000000008000000 80 | 0x0000000000000004 0x0000000000000004 RW 0x200000 81 | LOAD 0x0000000000600000 0x000000000c000000 0x000000000c000000 82 | 0x0000000000000000 0x0000000000000004 RW 0x200000 83 | GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 84 | 0x0000000000000000 0x0000000000000000 RWE 0x10 85 | 86 | Section to Segment mapping: 87 | Segment Sections... 88 | 00 .text .eh_frame 89 | 01 .data 90 | 02 .bss 91 | 03 92 | ``` 93 | 94 | ##### 2、示例分析 95 |         `.text`节虚拟地址为`0x400000`, `.data`节虚拟地址为`0x8000000`, `.bss`节的虚拟地址为`0xc000000`。 -------------------------------------------------------------------------------- /04 简要链接指令/01/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /04 简要链接指令/01/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool_base.o linker_script.lds 7 | ld -T linker_script.lds 8 | 9 | app.o:app.c 10 | gcc -g -c -o $@ $< 11 | 12 | 13 | tool.o:tool.c tool.h 14 | gcc -g -c -o $@ $< 15 | 16 | tool_base.o:tool_base.S 17 | as -g -o $@ $< 18 | -------------------------------------------------------------------------------- /04 简要链接指令/01/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | #define printf 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | int rev; 13 | 14 | rev = math_pow(2, 10); 15 | printf("2^10=%d\n", rev); 16 | 17 | rev = math_add(2, 10); 18 | printf("2+10=%d\n", rev); 19 | 20 | printf("math_add func len:0x%x\n", 21 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 22 | 23 | printf("math_add=0x%x\n",(unsigned long long)math_add); 24 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 25 | 26 | bss_var = 0x55aa; 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /04 简要链接指令/01/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(tool.o tool_base.o) 4 | STARTUP(app.o) 5 | OUTPUT(app) 6 | 7 | SECTIONS 8 | { 9 | . = 0x400000; 10 | .text : { *(.text) } 11 | . = 0x8000000; 12 | .data : { *(.data) } 13 | . = 0xc000000; 14 | .bss : { *(.bss) } 15 | } 16 | -------------------------------------------------------------------------------- /04 简要链接指令/01/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /04 简要链接指令/01/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /04 简要链接指令/01/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /04 简要链接指令/README.md: -------------------------------------------------------------------------------- 1 | ##### 1、指定程序入口 2 | 3 |         使用ENTRY指令指定程序入口。格式为如下。 4 | 5 | > ENTRY(*symbol*) 6 | 7 |         其它指定入口方式如下。 8 | 9 | - 使用命令行-e 参数 10 | - 使用`ENTRY(symbol)`指令 11 | - 平台相关定义,众多平台为start 12 | - .text段的第一个字节 13 | - 地址零 14 | 15 | ##### 二、包含其它链接脚本 16 | 17 | > INCLUDE *filename* 18 | 19 | ##### 三、指定输入文件 20 | 21 | > INPUT (*filename*, *filename*, ...) 22 | 23 | > INPUT (*filename* *filename* ...) 24 | 25 |         如果指定`INPUT (-lfile)`形式时,`ld`会转化为`lib*file*.a`形式。 26 | 27 | 28 | ##### 四、输出文件名 29 | 30 | > OUTPUT(*filename*) 31 | 32 |         支持命令行-o 参数设置输出文件名。 33 | 34 | ##### 五、设置二进制文件搜索路径 35 | 36 | > SEARCH_DIR(*path*) 37 | 38 | ##### 六、设置首个输入文件 39 | 40 | > STARTUP(*filename*) 41 | 42 |         设置文件为第一个输入文件,相当于在命令行指定文件为第一个参数。 43 | 44 | 45 | ##### 七、 测试示例 46 | 47 |         测试示例为该级目录下的`01`文件夹。`linker script`脚本如下。 48 | 49 | ``` 50 | ENTRY(main) 51 | INPUT(tool.o tool_base.o) 52 | STARTUP(app.o) 53 | OUTPUT(app) 54 | 55 | SECTIONS 56 | { 57 | . = 0x400000; 58 | .text : { *(.text) } 59 | . = 0x8000000; 60 | .data : { *(.data) } 61 | . = 0xc000000; 62 | .bss : { *(.bss) } 63 | } 64 | ``` 65 | 66 | 67 | ##### 八、指定输出文件格式 68 | 69 | > OUTPUT_FORMAT(*bfdname*) 70 | > OUTPUT_FORMAT(default, big, little) 71 | 72 |         `OUTPUT_FORMAT`命令指定输出文件的格式。其用法和命令行的中`--oformat bfdname`参数一致。 73 | 74 |         可以通过`objdump -i`命令查看所有的bfd格式。本机的格式输出如下图。 75 | 76 | ``` 77 | BFD header file version (GNU Binutils for Debian) 2.28 78 | elf64-x86-64 79 | (header little endian, data little endian) 80 | i386 81 | elf32-i386 82 | (header little endian, data little endian) 83 | i386 84 | elf32-iamcu 85 | (header little endian, data little endian) 86 | iamcu 87 | elf32-x86-64 88 | (header little endian, data little endian) 89 | i386 90 | a.out-i386-linux 91 | (header little endian, data little endian) 92 | i386 93 | pei-i386 94 | (header little endian, data little endian) 95 | i386 96 | pei-x86-64 97 | (header little endian, data little endian) 98 | i386 99 | elf64-l1om 100 | (header little endian, data little endian) 101 | l1om 102 | elf64-k1om 103 | (header little endian, data little endian) 104 | k1om 105 | elf64-little 106 | (header little endian, data little endian) 107 | i386 108 | l1om 109 | k1om 110 | iamcu 111 | plugin 112 | elf64-big 113 | (header big endian, data big endian) 114 | i386 115 | l1om 116 | k1om 117 | iamcu 118 | plugin 119 | elf32-little 120 | (header little endian, data little endian) 121 | i386 122 | l1om 123 | k1om 124 | iamcu 125 | plugin 126 | elf32-big 127 | (header big endian, data big endian) 128 | i386 129 | l1om 130 | k1om 131 | iamcu 132 | plugin 133 | pe-x86-64 134 | (header little endian, data little endian) 135 | i386 136 | pe-bigobj-x86-64 137 | (header little endian, data little endian) 138 | i386 139 | pe-i386 140 | (header little endian, data little endian) 141 | i386 142 | plugin 143 | (header little endian, data little endian) 144 | srec 145 | (header endianness unknown, data endianness unknown) 146 | i386 147 | l1om 148 | k1om 149 | iamcu 150 | plugin 151 | symbolsrec 152 | (header endianness unknown, data endianness unknown) 153 | i386 154 | l1om 155 | k1om 156 | iamcu 157 | plugin 158 | verilog 159 | (header endianness unknown, data endianness unknown) 160 | i386 161 | l1om 162 | k1om 163 | iamcu 164 | plugin 165 | tekhex 166 | (header endianness unknown, data endianness unknown) 167 | i386 168 | l1om 169 | k1om 170 | iamcu 171 | plugin 172 | binary 173 | (header endianness unknown, data endianness unknown) 174 | i386 175 | l1om 176 | k1om 177 | iamcu 178 | plugin 179 | ihex 180 | (header endianness unknown, data endianness unknown) 181 | i386 182 | l1om 183 | k1om 184 | iamcu 185 | plugin 186 | 187 | elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 a.out-i386-linux 188 | i386 elf64-x86-64 elf32-i386 ----------- elf32-x86-64 a.out-i386-linux 189 | l1om ------------ ---------- ----------- ------------ ---------------- 190 | k1om ------------ ---------- ----------- ------------ ---------------- 191 | iamcu ------------ ---------- elf32-iamcu ------------ ---------------- 192 | plugin ------------ ---------- ----------- ------------ ---------------- 193 | 194 | pei-i386 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big 195 | i386 pei-i386 pei-x86-64 ---------- ---------- elf64-little elf64-big 196 | l1om -------- ---------- elf64-l1om ---------- elf64-little elf64-big 197 | k1om -------- ---------- ---------- elf64-k1om elf64-little elf64-big 198 | iamcu -------- ---------- ---------- ---------- elf64-little elf64-big 199 | plugin -------- ---------- ---------- ---------- elf64-little elf64-big 200 | 201 | elf32-little elf32-big pe-x86-64 pe-bigobj-x86-64 pe-i386 plugin srec 202 | i386 elf32-little elf32-big pe-x86-64 pe-bigobj-x86-64 pe-i386 ------ srec 203 | l1om elf32-little elf32-big --------- ---------------- ------- ------ srec 204 | k1om elf32-little elf32-big --------- ---------------- ------- ------ srec 205 | iamcu elf32-little elf32-big --------- ---------------- ------- ------ srec 206 | plugin elf32-little elf32-big --------- ---------------- ------- ------ srec 207 | 208 | symbolsrec verilog tekhex binary ihex 209 | i386 symbolsrec verilog tekhex binary ihex 210 | l1om symbolsrec verilog tekhex binary ihex 211 | k1om symbolsrec verilog tekhex binary ihex 212 | iamcu symbolsrec verilog tekhex binary ihex 213 | plugin symbolsrec verilog tekhex binary ihex 214 | 215 | ``` 216 | 217 | ##### 九、指定输入文件格式 218 | 219 | > TARGET(*bfdname*) 220 | 221 |         该指令影响`INPUT`和`GROUP`指令。等同于命令行`-b bfdname`参数。如果TARGET参数被使用,而`OUTPUT_FORMAT`参数未使用,则输出文件时指定为`TARGET`的最后一个参数。 222 | 223 | ##### 十、存储别名 224 | 225 | > REGION_ALIAS(*alias*, *region*) 226 | 227 |         给存储单元`*region*`指定别名`*alias*`。 228 | 229 | ``` 230 | MEMORY 231 | { 232 | RAM : ORIGIN = 0, LENGTH = 4M 233 | } 234 | 235 | REGION_ALIAS("REGION_TEXT", RAM); 236 | REGION_ALIAS("REGION_RODATA", RAM); 237 | REGION_ALIAS("REGION_DATA", RAM); 238 | REGION_ALIAS("REGION_BSS", RAM); 239 | ``` 240 | 241 |         上述例子中,RAM被指定REGION_TEXT、REGION_RODATA、REGION_DATA和REGION_BSS四个别名。 242 | 243 | 244 | ##### 十一、断言 245 | 246 | > ASSERT(exp, message) 247 | 248 | ##### 十二、声明外部符号 249 | 250 | > EXTERN(symbol symbol ...) -------------------------------------------------------------------------------- /05 符号值分配/01/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /05 符号值分配/01/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool_base.o linker_script.lds 7 | ld -T linker_script.lds 8 | 9 | app.o:app.c 10 | gcc -g -c -o $@ $< 11 | 12 | 13 | tool.o:tool.c tool.h 14 | gcc -g -c -o $@ $< 15 | 16 | tool_base.o:tool_base.S 17 | as -g -o $@ $< 18 | -------------------------------------------------------------------------------- /05 符号值分配/01/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | #define printf 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | int rev; 13 | 14 | rev = math_pow(2, 10); 15 | printf("2^10=%d\n", rev); 16 | 17 | rev = math_add(2, 10); 18 | printf("2+10=%d\n", rev); 19 | 20 | printf("math_add func len:0x%x\n", 21 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 22 | 23 | printf("math_add=0x%x\n",(unsigned long long)math_add); 24 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 25 | 26 | bss_var = 0x55aa; 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /05 符号值分配/01/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(tool.o tool_base.o) 4 | STARTUP(app.o) 5 | OUTPUT(app) 6 | 7 | SECTIONS 8 | { 9 | . = 0x400000; 10 | .text : { *(.text) } 11 | . = 0x8000000; 12 | .data : { *(.data) } 13 | . = 0xc000000; 14 | .bss : { *(.bss) } 15 | 16 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 17 | } 18 | -------------------------------------------------------------------------------- /05 符号值分配/01/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /05 符号值分配/01/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /05 符号值分配/01/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /05 符号值分配/02/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /05 符号值分配/02/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool_base.o linker_script.lds 7 | ld -T linker_script.lds 8 | 9 | app.o:app.c 10 | gcc -g -c -o $@ $< 11 | 12 | 13 | tool.o:tool.c tool.h 14 | gcc -g -c -o $@ $< 15 | 16 | tool_base.o:tool_base.S 17 | as -g -o $@ $< 18 | -------------------------------------------------------------------------------- /05 符号值分配/02/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | #define printf 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | int rev; 13 | 14 | rev = math_pow(2, 10); 15 | printf("2^10=%d\n", rev); 16 | 17 | rev = math_add(2, 10); 18 | printf("2+10=%d\n", rev); 19 | 20 | printf("math_add func len:0x%x\n", 21 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 22 | 23 | printf("math_add=0x%x\n",(unsigned long long)math_add); 24 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 25 | 26 | bss_var = 0x55aa; 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /05 符号值分配/02/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(tool.o tool_base.o) 4 | STARTUP(app.o) 5 | OUTPUT(app) 6 | 7 | SECTIONS 8 | { 9 | . = 0x400000; 10 | .text : { *(.text) } 11 | . = 0x8000000; 12 | .data : { *(.data) } 13 | . = 0xc000000; 14 | .bss : { *(.bss) } 15 | 16 | HIDDEN(_bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000); 17 | } 18 | -------------------------------------------------------------------------------- /05 符号值分配/02/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /05 符号值分配/02/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /05 符号值分配/02/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /05 符号值分配/03/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /05 符号值分配/03/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool_base.o linker_script.lds 7 | ld -T linker_script.lds 8 | 9 | app.o:app.c 10 | gcc -g -c -o $@ $< 11 | 12 | 13 | tool.o:tool.c tool.h 14 | gcc -g -c -o $@ $< 15 | 16 | tool_base.o:tool_base.S 17 | as -g -o $@ $< 18 | -------------------------------------------------------------------------------- /05 符号值分配/03/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | extern void *_bss_end; 8 | 9 | #define printf 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | int rev; 14 | 15 | rev = math_pow(2, 10); 16 | printf("2^10=%d\n", rev); 17 | 18 | rev = math_add(2, 10); 19 | printf("2+10=%d\n", rev); 20 | 21 | printf("math_add func len:0x%x\n", 22 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 23 | 24 | printf("math_add=0x%x\n",(unsigned long long)math_add); 25 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 26 | 27 | bss_var = 0x55aa; 28 | rev = (typeof(rev))(unsigned long)&_bss_end; 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /05 符号值分配/03/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(tool.o tool_base.o) 4 | STARTUP(app.o) 5 | OUTPUT(app) 6 | 7 | SECTIONS 8 | { 9 | . = 0x400000; 10 | .text : { *(.text) } 11 | . = 0x8000000; 12 | .data : { *(.data) } 13 | . = 0xc000000; 14 | .bss : { *(.bss) } 15 | 16 | HIDDEN(_bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000); 17 | } 18 | -------------------------------------------------------------------------------- /05 符号值分配/03/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /05 符号值分配/03/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /05 符号值分配/03/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /05 符号值分配/README.md: -------------------------------------------------------------------------------- 1 | ##### 1、简单符号值 2 | 3 |         第一节已经对符号做了描述,并且提及链接脚本也是符号的产生原因之一。符号支持如下运算。 4 | 5 | 6 | > symbol = expression ; 7 | symbol += expression ; 8 | symbol -= expression ; 9 | symbol *= expression ; 10 | symbol /= expression ; 11 | symbol <<= expression ; 12 | symbol >>= expression ; 13 | symbol &= expression ; 14 | symbol |= expression ; 15 | 16 | 17 |         第一个用法定义符号,其它用法修改符号值。特殊符号`.`指示当前位置计数器。 18 | 19 |         本级目录`01`指示了符号的用法。连接脚本和符号表信息如下。 20 | 21 | ``` 22 | ENTRY(main) 23 | INPUT(tool.o tool_base.o) 24 | STARTUP(app.o) 25 | OUTPUT(app) 26 | 27 | SECTIONS 28 | { 29 | . = 0x400000; 30 | .text : { *(.text) } 31 | . = 0x8000000; 32 | .data : { *(.data) } 33 | . = 0xc000000; 34 | .bss : { *(.bss) } 35 | 36 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 37 | } 38 | 39 | ``` 40 | 41 |         该链接脚本指定符号`_bss_end`指向`bss`节的尾部。并且设置`0x20000`字节对齐。 42 | 43 |         使用`readelf`命令查看该符号的记录。可以看到符号的值,并且为全局符号。 44 | ``` 45 | $ readelf -s app 46 | ... 47 | 15: 000000000c020000 0 NOTYPE GLOBAL DEFAULT 4 _bss_end 48 | ... 49 | ``` 50 | 51 | ##### 2、局部符号 52 | 53 |         程序为`02`文件夹。链接脚本修改如下。 54 | 55 | ``` 56 | ... 57 | HIDDEN(_bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000); 58 | ... 59 | ``` 60 | 61 | ``` 62 | $ readelf -s app 63 | ... 64 | 14: 000000000c020000 0 NOTYPE LOCAL DEFAULT 4 _bss_end 65 | ... 66 | ``` 67 | 68 | 69 | ##### 3、PROVIDE符号 70 | 71 | 上述定义        的符号不能喝程序中已有的符号重名,如果发生了重名会报告`multi definition`错误。`PROVIDE`可以在程序未定义符号时生成这个符号,如果程序已经定义该符号,则链接脚本不再生成该符号。 72 | 73 | ##### 4、PROVIDE_HIDDEN符号 74 | 75 |         等同于PROVIDE和HIDDEN的共同属性。 76 | 77 | 78 | ##### 5、C程序访问符号 79 | 80 |         C程序访问链接脚本定义的符号十分简单。本例为03目录。定义符号格式如下 81 | ``` 82 | HIDDEN(_bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000); 83 | ``` 84 | 85 |         访问符号方式如下。 86 | ``` 87 | extern void *_bss_end; 88 | rev = (typeof(rev))(unsigned long)&_bss_end; 89 | ``` 90 | 91 |         第一章我们描述了汇编指令定义的符号,当时指出符号只有地址意义没有内容意义。这些规则依然适用于链接脚本定义的符号。这里引用手册中的一句话描述其特性。 92 | 93 | 94 | > Linker scripts symbol declarations, by contrast, create an entry in the symbol table but do not assign any memory to them. Thus they are an address without a value. 95 | -------------------------------------------------------------------------------- /06 SECTIONS指令/01/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /06 SECTIONS指令/01/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool_base.o linker_script.lds 7 | ld -T linker_script.lds 8 | 9 | app.o:app.c 10 | gcc -g -c -o $@ $< 11 | 12 | 13 | tool.o:tool.c tool.h 14 | gcc -g -c -o $@ $< 15 | 16 | tool_base.o:tool_base.S 17 | as -g -o $@ $< 18 | -------------------------------------------------------------------------------- /06 SECTIONS指令/01/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | #define printf 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | int rev; 13 | 14 | rev = math_pow(2, 10); 15 | printf("2^10=%d\n", rev); 16 | 17 | rev = math_add(2, 10); 18 | printf("2+10=%d\n", rev); 19 | 20 | printf("math_add func len:0x%x\n", 21 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 22 | 23 | printf("math_add=0x%x\n",(unsigned long long)math_add); 24 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 25 | 26 | bss_var = 0x55aa; 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /06 SECTIONS指令/01/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(tool.o tool_base.o) 4 | STARTUP(app.o) 5 | OUTPUT(app) 6 | 7 | SECTIONS 8 | { 9 | . = 0x400000 + 0x100000; 10 | .text . + 0 : { *(.text) } 11 | 12 | . = 0x800000 + 0x10000; 13 | .data . : { *(.data) } 14 | 15 | . = 0xc00000; 16 | .bss ALIGN(0x10) * 2 : { *(.bss) } 17 | 18 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 19 | } 20 | -------------------------------------------------------------------------------- /06 SECTIONS指令/01/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /06 SECTIONS指令/01/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /06 SECTIONS指令/01/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /06 SECTIONS指令/02/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /06 SECTIONS指令/02/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool2.o tool_base.o linker_script.lds 7 | ld -T linker_script.lds 8 | 9 | tool2.o:tool.o 10 | cp $< $@ 11 | 12 | app.o:app.c 13 | gcc -g -c -o $@ $< 14 | 15 | 16 | tool.o:tool.c tool.h 17 | gcc -g -c -o $@ $< 18 | 19 | tool_base.o:tool_base.S 20 | as -g -o $@ $< 21 | -------------------------------------------------------------------------------- /06 SECTIONS指令/02/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | #define printf 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | int rev; 13 | 14 | rev = math_pow(2, 10); 15 | printf("2^10=%d\n", rev); 16 | 17 | rev = math_add(2, 10); 18 | printf("2+10=%d\n", rev); 19 | 20 | printf("math_add func len:0x%x\n", 21 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 22 | 23 | printf("math_add=0x%x\n",(unsigned long long)math_add); 24 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 25 | 26 | bss_var = 0x55aa; 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /06 SECTIONS指令/02/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(app.o tool.o tool_base.o) 4 | OUTPUT(app) 5 | 6 | SECTIONS 7 | { 8 | . = 0x400000 + 0x1000; 9 | .text . : {EXCLUDE_FILE(tool_base.o) *(.text)} 10 | . = . + 0x1000; 11 | .asm_text . : {tool_base.o(.text)} 12 | 13 | . = . + 0x100000; 14 | .data . : { *(.data) } 15 | 16 | . = . + 0x200000; 17 | .bss ALIGN(0x10) * 2 : { *(.bss) } 18 | 19 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /06 SECTIONS指令/02/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /06 SECTIONS指令/02/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /06 SECTIONS指令/02/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /06 SECTIONS指令/03/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /06 SECTIONS指令/03/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool2.o tool_base.o linker_script.lds 7 | ld --gc-sections -T linker_script.lds 8 | 9 | tool2.o:tool.o 10 | cp $< $@ 11 | 12 | app.o:app.c 13 | gcc -ffunction-sections -g -c -o $@ $< 14 | 15 | 16 | tool.o:tool.c tool.h 17 | gcc -g -c -o $@ $< 18 | 19 | tool_base.o:tool_base.S 20 | as -g -o $@ $< 21 | -------------------------------------------------------------------------------- /06 SECTIONS指令/03/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | #define printf 9 | 10 | int test_case1(void) 11 | { 12 | int rev; 13 | 14 | rev = math_pow(2, 10); 15 | printf("2^10=%d\n", rev); 16 | 17 | rev = math_add(2, 10); 18 | printf("2+10=%d\n", rev); 19 | 20 | printf("math_add func len:0x%x\n", 21 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 22 | 23 | printf("math_add=0x%x\n",(unsigned long long)math_add); 24 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 25 | return 0; 26 | } 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | //test_case1(); 31 | bss_var = 0x55aa; 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /06 SECTIONS指令/03/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(app.o tool.o tool_base.o) 4 | OUTPUT(app) 5 | 6 | SECTIONS 7 | { 8 | . = 0x400000 + 0x1000; 9 | .text . : {*(.text) KEEP(*(.text.*))} 10 | 11 | . = . + 0x100000; 12 | .data . : { *(.data) } 13 | 14 | . = . + 0x200000; 15 | .bss ALIGN(0x10) * 2 : { *(.bss) } 16 | 17 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /06 SECTIONS指令/03/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /06 SECTIONS指令/03/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /06 SECTIONS指令/03/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /06 SECTIONS指令/04/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /06 SECTIONS指令/04/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool2.o tool_base.o linker_script.lds 7 | ld --gc-sections -T linker_script.lds 8 | 9 | tool2.o:tool.o 10 | cp $< $@ 11 | 12 | app.o:app.c 13 | gcc -ffunction-sections -g -c -o $@ $< 14 | 15 | 16 | tool.o:tool.c tool.h 17 | gcc -g -c -o $@ $< 18 | 19 | tool_base.o:tool_base.S 20 | as -g -o $@ $< 21 | -------------------------------------------------------------------------------- /06 SECTIONS指令/04/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | #define printf 9 | 10 | int test_case1(void) 11 | { 12 | int rev; 13 | 14 | rev = math_pow(2, 10); 15 | printf("2^10=%d\n", rev); 16 | 17 | rev = math_add(2, 10); 18 | printf("2+10=%d\n", rev); 19 | 20 | printf("math_add func len:0x%x\n", 21 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 22 | 23 | printf("math_add=0x%x\n",(unsigned long long)math_add); 24 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 25 | return 0; 26 | } 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | test_case1(); 31 | bss_var = 0x55aa; 32 | glob_var = bss_var; 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /06 SECTIONS指令/04/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(app.o tool.o tool_base.o) 4 | OUTPUT(app) 5 | 6 | SECTIONS 7 | { 8 | . = 0x400000 + 0x1000; 9 | .text . : {*(.text) KEEP(*(.text.*)) FILL(0x90909090) 10 | . += 0x8; 11 | SHORT(0xff55)} 12 | 13 | 14 | . = . + 0x10; 15 | 16 | .linker_data : {BYTE(0xbb) SHORT(0xaabb) LONG(0xaabbaabb) QUAD(0x1122334455667788)} 17 | 18 | . = . + 0x20000; 19 | .data . : { *(.data) } 20 | 21 | 22 | . = . + 0x200000; 23 | .bss ALIGN(0x10) * 2 : { *(.bss) } 24 | 25 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /06 SECTIONS指令/04/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /06 SECTIONS指令/04/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /06 SECTIONS指令/04/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /06 SECTIONS指令/05/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | app.bin 4 | -------------------------------------------------------------------------------- /06 SECTIONS指令/05/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | objcopy -O binary app app.bin 5 | ls -lh 6 | 7 | app:app.o tool.o tool2.o tool_base.o linker_script.lds 8 | ld --gc-sections -T linker_script.lds 9 | 10 | tool2.o:tool.o 11 | cp $< $@ 12 | 13 | app.o:app.c 14 | gcc -ffunction-sections -g -c -o $@ $< 15 | 16 | 17 | tool.o:tool.c tool.h 18 | gcc -g -c -o $@ $< 19 | 20 | tool_base.o:tool_base.S 21 | as -g -o $@ $< 22 | -------------------------------------------------------------------------------- /06 SECTIONS指令/05/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | #define printf 9 | 10 | int test_case1(void) 11 | { 12 | int rev; 13 | 14 | rev = math_pow(2, 10); 15 | printf("2^10=%d\n", rev); 16 | 17 | rev = math_add(2, 10); 18 | printf("2+10=%d\n", rev); 19 | 20 | printf("math_add func len:0x%x\n", 21 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 22 | 23 | printf("math_add=0x%x\n",(unsigned long long)math_add); 24 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 25 | return 0; 26 | } 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | test_case1(); 31 | bss_var = 0x55aa; 32 | glob_var = bss_var; 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /06 SECTIONS指令/05/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(app.o tool.o tool_base.o) 4 | OUTPUT(app) 5 | 6 | SECTIONS 7 | { 8 | . = 0x400000; 9 | .text . : {*(.text) KEEP(*(.text.*)) FILL(0x90909090) 10 | . += 0x8; 11 | SHORT(0xff55)} 12 | 13 | _text_end = .; 14 | . = 0x8000000; 15 | .data ALIGN(0x10) : AT (_text_end) { *(.data) } 16 | .bss ALIGN(0x10) : AT (_text_end) { *(.bss) } 17 | 18 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /06 SECTIONS指令/05/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /06 SECTIONS指令/05/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /06 SECTIONS指令/05/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /06 SECTIONS指令/06/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /06 SECTIONS指令/06/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool2.o tool_base.o linker_script.lds 7 | ld --gc-sections -T linker_script.lds 8 | 9 | tool2.o:tool.o 10 | cp $< $@ 11 | 12 | app.o:app.c 13 | gcc -ffunction-sections -g -c -o $@ $< 14 | 15 | 16 | tool.o:tool.c tool.h 17 | gcc -g -c -o $@ $< 18 | 19 | tool_base.o:tool_base.S 20 | as -g -o $@ $< 21 | -------------------------------------------------------------------------------- /06 SECTIONS指令/06/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | #define printf 9 | 10 | int test_case1(void) 11 | { 12 | int rev; 13 | 14 | rev = math_pow(2, 10); 15 | printf("2^10=%d\n", rev); 16 | 17 | rev = math_add(2, 10); 18 | printf("2+10=%d\n", rev); 19 | 20 | printf("math_add func len:0x%x\n", 21 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 22 | 23 | printf("math_add=0x%x\n",(unsigned long long)math_add); 24 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 25 | return 0; 26 | } 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | test_case1(); 31 | bss_var = 0x55aa; 32 | glob_var = bss_var; 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /06 SECTIONS指令/06/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(app.o tool.o tool_base.o) 4 | OUTPUT(app) 5 | 6 | PHDRS 7 | { 8 | text PT_LOAD; 9 | gcsection PT_LOAD; 10 | data PT_LOAD; 11 | bss PT_LOAD; 12 | } 13 | 14 | . = 0x200000; 15 | SECTIONS 16 | { 17 | . = 0x4000; 18 | .text . : {*(.text)} : text 19 | .outer . : {*(.text.*)} : gcsection 20 | . = 0x8000000; 21 | .data : { *(.data) } : data 22 | .bss : { *(.bss) } : bss 23 | } 24 | 25 | -------------------------------------------------------------------------------- /06 SECTIONS指令/06/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /06 SECTIONS指令/06/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /06 SECTIONS指令/06/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /06 SECTIONS指令/07/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /06 SECTIONS指令/07/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool2.o tool_base.o linker_script.lds 7 | ld --gc-sections -T linker_script.lds 8 | 9 | tool2.o:tool.o 10 | cp $< $@ 11 | 12 | app.o:app.c 13 | gcc -ffunction-sections -g -c -o $@ $< 14 | 15 | 16 | tool.o:tool.c tool.h 17 | gcc -g -c -o $@ $< 18 | 19 | tool_base.o:tool_base.S 20 | as -g -o $@ $< 21 | -------------------------------------------------------------------------------- /06 SECTIONS指令/07/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | #define BSS_IN_OVERLAY 0 9 | 10 | extern void *__load_start_text_section; 11 | extern void *__load_stop_text_section; 12 | extern void *__load_start_data_section; 13 | extern void *__load_stop_data_section; 14 | 15 | #if BSS_IN_OVERLAY 16 | extern void *__load_start_bss_section; 17 | extern void *__load_stop_bss_section; 18 | #endif 19 | 20 | #define printf 21 | 22 | int test_case1(void) 23 | { 24 | int rev; 25 | 26 | rev = math_pow(2, 10); 27 | printf("2^10=%d\n", rev); 28 | 29 | rev = math_add(2, 10); 30 | printf("2+10=%d\n", rev); 31 | 32 | printf("math_add func len:0x%x\n", 33 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 34 | 35 | printf("math_add=0x%x\n",(unsigned long long)math_add); 36 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 37 | return 0; 38 | } 39 | 40 | int main(int argc, char *argv[]) 41 | { 42 | void *addr; 43 | test_case1(); 44 | bss_var = 0x55aa; 45 | glob_var = bss_var; 46 | 47 | addr = &__load_start_text_section; 48 | addr = &__load_stop_text_section; 49 | addr = &__load_start_data_section; 50 | addr = &__load_stop_data_section; 51 | 52 | #if BSS_IN_OVERLAY 53 | addr = &__load_start_bss_section; 54 | addr = &__load_stop_bss_section; 55 | #endif 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /06 SECTIONS指令/07/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(app.o tool.o tool_base.o) 4 | OUTPUT(app) 5 | 6 | PHDRS 7 | { 8 | text PT_LOAD; 9 | data PT_LOAD; 10 | bss PT_LOAD; 11 | } 12 | 13 | SECTIONS 14 | { 15 | OVERLAY 0x400000 : AT(0x1000) 16 | { 17 | .text_section{*(.text) *(.text.*)} : text 18 | .data_section{*(.data)} : data 19 | 20 | 21 | } 22 | .bss_section : {*(.bss)} : bss 23 | } 24 | -------------------------------------------------------------------------------- /06 SECTIONS指令/07/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /06 SECTIONS指令/07/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /06 SECTIONS指令/07/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /06 SECTIONS指令/README.md: -------------------------------------------------------------------------------- 1 | ##### 1、指令预览 2 | 3 | ``` 4 | *section* [address] [(type)] : 5 | [AT(lma)] 6 | [ALIGN(section_align) | ALIGN_WITH_INPUT] 7 | [SUBALIGN(subsection_align)] 8 | [constraint] 9 | { 10 | output-section-command 11 | output-section-command 12 | … 13 | } [>region] [AT>lma_region] [:phdr :phdr …] [=fillexp] [,] 14 | ``` 15 | 16 | ##### 2、输出节名 17 | 18 |         如果节名包含特殊字符,需要使用引号包裹。 19 | 20 |         特殊节名`/DISCARD/`会被丢弃。 21 | 22 | 23 | 24 | ##### 3、节地址 25 | 26 |         节地址是指该节的VMA地址。如果改地未明确指定,连接器会在考虑严格对齐情况下,首先按照region参数分配内存,其次根据当前位置计数器向下分地址。 27 | 28 | 29 | ``` 30 | .text ALIGN(0x10) : { *(.text) } 31 | ``` 32 |         .text定位在当前位置对齐16字节空间处。当指定一个section的地址后,当前位置计数器也会同步修改。测试代码位于01目录下。 33 | 34 | ``` 35 | 36 | ENTRY(main) 37 | INPUT(tool.o tool_base.o) 38 | STARTUP(app.o) 39 | OUTPUT(app) 40 | 41 | SECTIONS 42 | { 43 | . = 0x400000 + 0x100000; 44 | .text . + 0 : { *(.text) } 45 | 46 | . = 0x800000 + 0x10000; 47 | .data . : { *(.data) } 48 | 49 | . = 0xc00000; 50 | .bss ALIGN(0x10) * 2 : { *(.bss) } 51 | 52 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 53 | } 54 | 55 | ``` 56 |         生成节的描述信息如下。 57 | 58 | ``` 59 | $ make 60 | ld -T linker_script.lds 61 | readelf -l app 62 | 63 | Elf file type is EXEC (Executable file) 64 | Entry point 0x810000 65 | There are 4 program headers, starting at offset 64 66 | 67 | Program Headers: 68 | Type Offset VirtAddr PhysAddr 69 | FileSiz MemSiz Flags Align 70 | LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 71 | 0x0000000000100004 0x0000000000100004 RW 0x200000 72 | LOAD 0x0000000000210000 0x0000000000810000 0x0000000000810000 73 | 0x00000000000000e0 0x00000000000000e0 R E 0x200000 74 | LOAD 0x0000000000400000 0x0000000001800000 0x0000000001800000 75 | 0x0000000000000000 0x0000000000000004 RW 0x200000 76 | GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 77 | 0x0000000000000000 0x0000000000000000 RWE 0x10 78 | 79 | Section to Segment mapping: 80 | Segment Sections... 81 | 00 .data 82 | 01 .text .eh_frame 83 | 02 .bss 84 | 03 85 | 86 | ``` 87 | 88 | 89 | ##### 4、输入文件 90 | 91 |         通配所有文件的指定段。 92 | > *(.text) 93 | 94 |         过滤文件。 95 | > EXCLUDE_FILE (*crtend.o *otherfile.o) *(.ctors) 96 | *(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors) 97 | 98 |         增加多个段。这两种写的主要区别在于输入段的顺序不一致。 99 | >*(.text .rdata) 100 | *(.text) *(.rdata) 101 | 102 | 103 |         多个输入段时过滤文件。 104 | > *(EXCLUDE_FILE (*somefile.o) .text EXCLUDE_FILE (*somefile.o) .rdata) 105 | > EXCLUDE_FILE (*somefile.o) *(.text .rdata) 106 | 107 |         指定文件。 108 | > data.o(.data) 109 | 110 |         指定归档内的某个文件。 111 | > archive:file 112 | 113 |         指定归档的所有文件。 114 | > archive: 115 | 116 |         指定任何归档内的某一个文件。 117 | > :file 118 | 119 |         一个文件的所有段。 120 | > data.o 121 | 122 |         测试代码为02目录。连接脚本如下。该脚本从输入列表中排除汇编程序的编译产出,并把该产出放入单独的段.asm_text内。 123 | ``` 124 | ENTRY(main) 125 | INPUT(app.o tool.o tool_base.o) 126 | OUTPUT(app) 127 | 128 | SECTIONS 129 | { 130 | . = 0x400000 + 0x1000; 131 | .text . : EXCLUDE_FILE(tool_base.o) {*(.text)} 132 | . = . + 0x1000; 133 | .asm_text . : {tool_base.o(.text)} 134 | 135 | . = . + 0x100000; 136 | .data . : { *(.data) } 137 | 138 | . = . + 0x200000; 139 | .bss ALIGN(0x10) * 2 : { *(.bss) } 140 | 141 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 142 | } 143 | ``` 144 | 145 | ##### 5、gc-sections 146 |         通过参数,编译器可以把每个函数分配在单独的段中。在程序链接时,不被使用的段将会被丢弃。连接脚背可以通过KEEP指令防止不被引用的段不被丢弃。 147 | 148 | > KEEP(SORT_BY_NAME(*)(.ctors)). 149 | 150 |         测试代码位于`03`目录下。本测试脚本如下。`test_case1`函数是一个不被引用的段,这里将`.text.*`段使用KEEP标记,那么`test_case1`将会在可执行程序中被保留。即便`.text`没被`KEEP`标记,那么`tool.c`中的函数由于被`test_case1`引用而被保留。 151 | 152 | ``` 153 | 154 | ENTRY(main) 155 | INPUT(app.o tool.o tool_base.o) 156 | OUTPUT(app) 157 | 158 | SECTIONS 159 | { 160 | . = 0x400000 + 0x1000; 161 | .text . : {*(.text) KEEP(*(.text.*))} 162 | 163 | . = . + 0x100000; 164 | .data . : { *(.data) } 165 | 166 | . = . + 0x200000; 167 | .bss ALIGN(0x10) * 2 : { *(.bss) } 168 | 169 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 170 | } 171 | 172 | 173 | ``` 174 | 175 | ##### 6、输出数据 176 | 177 |         在节的内部可以使用部分指令插入数据。BYTE, SHORT, LONG和 QUAD 指令 保存 一个, 两个, 四个和八个字节. 178 | 179 |         `FILL(0x90909090)`填充未指明区域的内存。 180 | 181 |         测试代码如下图。`.text`使用`FILL`设置填充数据,`. += 0x8`扩大`.text`段8个字节,同时存入`0xff55`标记结尾。`.linker_data`使用数据填充指令填充15个字节的数据。可以使用`xxd`指令输出文件内容进行验证。 182 | ``` 183 | ENTRY(main) 184 | INPUT(app.o tool.o tool_base.o) 185 | OUTPUT(app) 186 | 187 | SECTIONS 188 | { 189 | . = 0x400000 + 0x1000; 190 | .text . : {*(.text) KEEP(*(.text.*)) FILL(0x90909090) 191 | . += 0x8; 192 | SHORT(0xff55)} 193 | 194 | 195 | . = . + 0x10; 196 | 197 | .linker_data : {BYTE(0xbb) SHORT(0xaabb) LONG(0xaabbaabb) QUAD(0x1122334455667788)} 198 | 199 | . = . + 0x20000; 200 | .data . : { *(.data) } 201 | 202 | 203 | . = . + 0x200000; 204 | .bss ALIGN(0x10) * 2 : { *(.bss) } 205 | 206 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 207 | } 208 | 209 | ``` 210 | 211 | ##### 7、输出段类型 212 | 213 |         默认情况下,输出段的类型由输入端决定。也可以明确指定段类型。 214 | NOLOAD 215 | 216 | - The section should be marked as not loadable, so that it will not be loaded into memory when the program is run. 217 | 218 | DSECT 219 | COPY 220 | INFO 221 | OVERLAY 222 | 223 | #### 8、LMA 224 |         默认LMA地址为VMA,可以使用AT指令指定精确地址,或则使用AT>指令指定内存区域。 225 | 226 |         测试代码位于05目录。该测试将可执行程序导出为binary文件。请查看LMA对binary文件的大小影响。 227 | 228 | ##### 9、对齐 229 | 230 |         输出文件对齐 231 | > ALIGN(value) 232 | 233 | 234 | ##### 10、指定输出段存储区域 235 | 236 | ``` 237 | MEMORY { rom : ORIGIN = 0x1000, LENGTH = 0x1000 } 238 | SECTIONS { ROM : { *(.text) } >rom } 239 | ``` 240 | 241 | ##### 11、自定义段 242 | ``` 243 | PHDRS { text PT_LOAD ; } 244 | SECTIONS { .text : { *(.text) } :text } 245 | ``` 246 | 测试示例为06文件夹。 247 | 248 | #### 12、填充空隙 249 | 250 | ``` 251 | SECTIONS { .text : { *(.text) } =0x90909090 } 252 | ``` 253 |         该测试可以参照本节的第七小节。 254 | 255 | 256 | ##### 十三、OVERLAY 257 | 258 |         overlay方式可以将多个段定义在同一个内存地址。这种用法主要考虑到部分内存存储速度较快,当执行指定代码时能够将对应的段加载到快速存储从而提高性能。 259 | 260 |         overlay的完整格式如下。 261 | 262 | ``` 263 | OVERLAY [start] : [NOCROSSREFS] [AT ( ldaddr )] 264 | { 265 | secname1 266 | { 267 | output-section-command 268 | output-section-command 269 | … 270 | } [:phdr…] [=fill] 271 | secname2 272 | { 273 | output-section-command 274 | output-section-command 275 | … 276 | } [:phdr…] [=fill] 277 | … 278 | } [>region] [:phdr…] [=fill] [,] 279 | ``` 280 | 281 |         这些段具备相同的起始VMA地址。overlay为每一个段生成`__load_start_secname`和`__load_stop_secname`两个符号。 282 | 283 |         本小节测试代码位于`07`目录。链接脚本和段格式分布如下。 284 | ``` 285 | ENTRY(main) 286 | INPUT(app.o tool.o tool_base.o) 287 | OUTPUT(app) 288 | 289 | PHDRS 290 | { 291 | text PT_LOAD; 292 | data PT_LOAD; 293 | bss PT_LOAD; 294 | } 295 | 296 | SECTIONS 297 | { 298 | OVERLAY 0x400000 : AT(0x1000) 299 | { 300 | .text_section{*(.text) *(.text.*)} : text 301 | .data_section{*(.data)} : data 302 | 303 | 304 | } 305 | .bss_section : {*(.bss)} : bss 306 | } 307 | 308 | ``` 309 | ``` 310 | $ readelf -l app 311 | 312 | Elf file type is EXEC (Executable file) 313 | Entry point 0x400077 314 | There are 3 program headers, starting at offset 64 315 | 316 | Program Headers: 317 | Type Offset VirtAddr PhysAddr 318 | FileSiz MemSiz Flags Align 319 | LOAD 0x0000000000001000 0x0000000000400000 0x0000000000001000 320 | 0x00000000000000d4 0x00000000000000d4 R E 0x1000 321 | LOAD 0x0000000000002000 0x0000000000400000 0x0000000000400000 322 | 0x0000000000000004 0x0000000000000004 RW 0x1000 323 | LOAD 0x00000000000020d4 0x00000000004000d4 0x00000000004000d4 324 | 0x0000000000000000 0x0000000000000004 RW 0x1000 325 | 326 | Section to Segment mapping: 327 | Segment Sections... 328 | 00 .text_section 329 | 01 .data_section 330 | 02 .bss 331 | ``` 332 | ``` 333 | $ readelf -s app 334 | 335 | Symbol table '.symtab' contains 24 entries: 336 | Num: Value Size Type Bind Vis Ndx Name 337 | 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 338 | 1: 0000000000400000 0 SECTION LOCAL DEFAULT 1 339 | 2: 0000000000400000 0 SECTION LOCAL DEFAULT 2 340 | 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 341 | 4: 0000000000000000 0 SECTION LOCAL DEFAULT 4 342 | 5: 0000000000000000 0 SECTION LOCAL DEFAULT 5 343 | 6: 0000000000000000 0 SECTION LOCAL DEFAULT 6 344 | 7: 0000000000000000 0 SECTION LOCAL DEFAULT 7 345 | 8: 0000000000000000 0 SECTION LOCAL DEFAULT 8 346 | 9: 0000000000000000 0 SECTION LOCAL DEFAULT 9 347 | 10: 00000000004000d4 0 SECTION LOCAL DEFAULT 10 348 | 11: 0000000000000000 0 FILE LOCAL DEFAULT ABS tool.c 349 | 12: 0000000000000000 0 FILE LOCAL DEFAULT ABS app.c 350 | 13: 0000000000400004 0 NOTYPE GLOBAL DEFAULT ABS __load_stop_data_section 351 | 14: 0000000000400000 0 NOTYPE GLOBAL DEFAULT ABS __load_start_data_section 352 | 15: 0000000000001000 0 NOTYPE GLOBAL DEFAULT ABS __load_start_text_section 353 | 16: 0000000000400000 4 OBJECT GLOBAL DEFAULT 2 glob_var 354 | 17: 000000000040003c 0 NOTYPE GLOBAL DEFAULT 1 math_add_end 355 | 18: 0000000000400000 53 FUNC GLOBAL DEFAULT 1 math_pow 356 | 19: 00000000004000d4 4 OBJECT GLOBAL DEFAULT 10 bss_var 357 | 20: 0000000000400035 0 NOTYPE GLOBAL DEFAULT 1 math_add 358 | 21: 0000000000400044 51 FUNC GLOBAL DEFAULT 1 test_case1 359 | 22: 0000000000400077 93 FUNC GLOBAL DEFAULT 1 main 360 | 23: 00000000000010d4 0 NOTYPE GLOBAL DEFAULT ABS __load_stop_text_section 361 | 362 | ``` -------------------------------------------------------------------------------- /07 MEMORY指令/01/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /07 MEMORY指令/01/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | readelf -l app 5 | 6 | app:app.o tool.o tool2.o tool_base.o linker_script.lds 7 | ld --gc-sections -T linker_script.lds 8 | 9 | tool2.o:tool.o 10 | cp $< $@ 11 | 12 | app.o:app.c 13 | gcc -ffunction-sections -g -c -o $@ $< 14 | 15 | 16 | tool.o:tool.c tool.h 17 | gcc -g -c -o $@ $< 18 | 19 | tool_base.o:tool_base.S 20 | as -g -o $@ $< 21 | -------------------------------------------------------------------------------- /07 MEMORY指令/01/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | 9 | #define printf 10 | 11 | int test_case1(void) 12 | { 13 | int rev; 14 | 15 | rev = math_pow(2, 10); 16 | printf("2^10=%d\n", rev); 17 | 18 | rev = math_add(2, 10); 19 | printf("2+10=%d\n", rev); 20 | 21 | printf("math_add func len:0x%x\n", 22 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 23 | 24 | printf("math_add=0x%x\n",(unsigned long long)math_add); 25 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 26 | return 0; 27 | } 28 | 29 | int main(int argc, char *argv[]) 30 | { 31 | void *addr; 32 | test_case1(); 33 | bss_var = 0x55aa; 34 | glob_var = bss_var; 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /07 MEMORY指令/01/linker_script.lds: -------------------------------------------------------------------------------- 1 | 2 | ENTRY(main) 3 | INPUT(app.o tool.o tool_base.o) 4 | OUTPUT(app) 5 | 6 | PHDRS 7 | { 8 | text PT_LOAD; 9 | data PT_LOAD; 10 | bss PT_LOAD; 11 | } 12 | 13 | MEMORY 14 | { 15 | rom (RX) : ORIGIN = 0x3fffff, LENGTH = 256K 16 | ram (WX) : ORIGIN = 0x820000, LENGTH = 32K 17 | } 18 | 19 | SECTIONS 20 | { 21 | .text_section : {*(.text) *(.text.*)} >rom :text 22 | .data_section : {*(.data)} >ram :data 23 | .bss_section : {*(.bss)} >ram :bss 24 | } 25 | -------------------------------------------------------------------------------- /07 MEMORY指令/01/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | int math_pow(int a, int b) 5 | { 6 | int result = 1; 7 | int i; 8 | for (i = 0; i < b; i ++) { 9 | result *= a; 10 | } 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /07 MEMORY指令/01/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /07 MEMORY指令/01/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /07 MEMORY指令/README.md: -------------------------------------------------------------------------------- 1 | ##### 1、完整格式 2 | 3 | ``` 4 | MEMORY 5 | { 6 | name [(attr)] : ORIGIN = origin, LENGTH = len 7 | … 8 | } 9 | ``` 10 | 11 | 12 | #### 2、attr 13 | 14 | attr|描述 15 | --|-- 16 | R | 只读 17 | W | 读写 18 | X | 可执行 19 | A | 可分配 20 | I | 初始 21 | L | 同I 22 | ! | 取反 23 | 24 |         示例用法。 25 | 26 | ``` 27 | MEMORY 28 | { 29 | rom (rx) : ORIGIN = 0, LENGTH = 256K 30 | ram (!rx) : org = 0x40000000, l = 4M 31 | } 32 | ``` 33 | 34 |         测试代码参考`01`目录。 -------------------------------------------------------------------------------- /08 PHDRS指令/README.md: -------------------------------------------------------------------------------- 1 | ##### 1、格式预览 2 | 3 | ``` 4 | PHDRS 5 | { 6 | name type [ FILEHDR ] [ PHDRS ] [ AT ( address ) ] 7 | [ FLAGS ( flags ) ] ; 8 | } 9 | ``` 10 | 11 |         `ld`默认一组`phdrs`。如果自定义了自己的`PHDRS`,那么默认的`phdrs`将会被屏蔽。 12 | 13 |         `FILEHDR`指明该节包含`ELF`头部。`PHDRS`指明该节包含`ELF`节表。 14 | 15 | ##### 2、类型 16 | 17 | 类型 | 描述 18 | --|-- 19 | PT_NULL | 未使用节 20 | PT_LOAD | 可加载节 21 | PT_DYNAMIC | 动态链接信息节 22 | PT_INTERP | 动态连接器节 23 | PT_NOTE | 记录信息节 24 | PT_SHLIB | 保留节 25 | PT_PHDR | 节表节 26 | PT_TLS | 线程信息存储节 27 | 28 |         测试代码参见`06`章相关信息。 -------------------------------------------------------------------------------- /09 VERSION指令/01/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | 4 | -------------------------------------------------------------------------------- /09 VERSION指令/01/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY:test 2 | 3 | test:app 4 | export LD_LIBRARY_PATH=. && ./app 5 | 6 | app:libtool.so app.o 7 | gcc -g -o $@ $^ 8 | 9 | app.o:app.c 10 | gcc -g -c -o $@ $< 11 | 12 | libtool.so:tool.o tool_base.o 13 | ld --version-script=version_script.lds -shared -o $@ $^ 14 | 15 | tool.o:tool.c tool.h 16 | gcc -fPIC -g -c -o $@ $< 17 | 18 | tool_base.o:tool_base.S 19 | as -g -o $@ $< 20 | 21 | clean: 22 | rm *.o app libtool.so 23 | -------------------------------------------------------------------------------- /09 VERSION指令/01/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tool.h" 3 | 4 | int bss_var; 5 | 6 | int glob_var = 0xaa55; 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | int rev; 11 | 12 | rev = math_pow(2, 10); 13 | printf("2^10=%d\n", rev); 14 | 15 | rev = math_add(2, 10); 16 | printf("2+10=%d\n", rev); 17 | 18 | printf("math_add func len:0x%x\n", 19 | (unsigned long long)&math_add_end - (unsigned long long)math_add); 20 | 21 | printf("math_add=0x%x\n",(unsigned long long)math_add); 22 | printf("math_add_end=0x%x\n",(unsigned long long)math_add_end); 23 | 24 | bss_var = 0x55aa; 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /09 VERSION指令/01/libtool.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iDalink/ld-linker-script/57f284cbaaf1957a3a2e44c1d171cffa96e57f7d/09 VERSION指令/01/libtool.so -------------------------------------------------------------------------------- /09 VERSION指令/01/tool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #if 1 4 | __asm__(".symver math_pow_old, math_pow@@VER_1.1"); 5 | __asm__(".symver math_pow_new, math_pow@VER_1.2"); 6 | #else 7 | int math_pow(int a, int b) 8 | { 9 | return 2020; 10 | } 11 | #endif 12 | 13 | int math_pow_old(int a, int b) 14 | { 15 | return 1010; 16 | } 17 | 18 | 19 | int math_pow_new(int a, int b) 20 | { 21 | int result = 1; 22 | int i; 23 | for (i = 0; i < b; i ++) { 24 | result *= a; 25 | } 26 | return result; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /09 VERSION指令/01/tool.h: -------------------------------------------------------------------------------- 1 | 2 | //指数运算 3 | int math_pow(int a, int b); 4 | 5 | long math_add(long a, long b); 6 | 7 | extern void *math_add_end; 8 | -------------------------------------------------------------------------------- /09 VERSION指令/01/tool_base.S: -------------------------------------------------------------------------------- 1 | .text 2 | .code64 3 | .global math_add 4 | .global math_add_end 5 | math_add: 6 | add %rsi,%rdi 7 | mov %rdi, %rax 8 | ret 9 | math_add_end = . 10 | .word 0xf0f0, 0xf0f0, 0xf0f0, 0xf0f0 11 | -------------------------------------------------------------------------------- /09 VERSION指令/01/version_script.lds: -------------------------------------------------------------------------------- 1 | 2 | VER_1.1 { 3 | math_pow; 4 | }; 5 | 6 | 7 | VER_1.2 { 8 | math_pow; 9 | } VER_1.1; 10 | -------------------------------------------------------------------------------- /09 VERSION指令/README.md: -------------------------------------------------------------------------------- 1 | ##### 1、符号导出版本 2 | 3 |         符号导出版本在version script中指明. 4 | 5 | 6 | ``` 7 | 8 | VER_1.1 { 9 | math_pow; 10 | }; 11 | 12 | 13 | VER_1.2 { 14 | math_pow; 15 | } VER_1.1; 16 | 17 | ``` 18 | 19 | 20 | ##### 2、函数别名绑定 21 | 22 | ``` 23 | __asm__(".symver math_pow_old, math_pow@@VER_1.1"); 24 | __asm__(".symver math_pow_new, math_pow@VER_1.2"); 25 | ``` 26 | 27 | ##### 三、默认函数 28 | 29 |         别名绑定时,两个`@@`符号的表示默认导出版本。一个符号必须导出一个版本。 -------------------------------------------------------------------------------- /10 Linker Scripts中的表达式/README.md: -------------------------------------------------------------------------------- 1 | ##### 1、常量 2 | 3 | ``` 4 | _fourk_1 = 4K; 5 | _fourk_2 = 4096; 6 | _fourk_3 = 0x1000; 7 | _fourk_4 = 10000o; 8 | ``` 9 | 10 | ##### 2、符号名 11 | 12 | ``` 13 | "SECTION" = 9; 14 | "with a space" = "also with a space" + 10; 15 | ``` 16 | 17 | ##### 3、运算符 18 | 19 | ``` 20 | precedence associativity Operators Notes 21 | (highest) 22 | 1 left ! - ~ (1) 23 | 2 left * / % 24 | 3 left + - 25 | 4 left >> << 26 | 5 left == != > < <= >= 27 | 6 left & 28 | 7 left | 29 | 8 left && 30 | 9 left || 31 | 10 right ? : 32 | 11 right &= += -= *= /= (2) 33 | ``` 34 | 35 | ##### 4、内建函数 36 | 37 | ###### 1) ABSOLUTE 38 |         PS: 节内的地址为相对该节起始的地址。 39 | ``` 40 | SECTIONS { 41 | . = 0x100; 42 | .text : { 43 | *(.text) 44 | . = 0x1000; 45 | *(.init) /*该部分的VMA为0x1100*/ 46 | } 47 | } 48 | ``` 49 | 50 | > ABSOLUTE 51 | 52 |         ABSOLUTE把节内相对地址转化为绝对地址。 53 | 54 | ``` 55 | SECTIONS { 56 | . = 0x100; 57 | .text : { 58 | *(.text) 59 | init_begin = ABSOLUTE(0x1000); 60 | *(.init) /*该部分的VMA为0x1100*/ 61 | } 62 | } 63 | ``` 64 | 65 | 66 | ###### 2) ADDR 67 | 68 | > ADDR(section) 69 | 70 |         返回指定段的起始地址。 71 | 72 | ###### 3. ALIGN 73 | 74 | > ALIGN(align) 75 | ALIGN(exp, align) 76 | 77 |         对齐align字节 78 | 79 | ###### 4. LENGTH 80 | 81 | > LENGTH(memory) 82 | 83 |         返回指定region的长度。 84 | 85 | ###### 5. LOADADDR 86 | 87 | > LOADADDR(section) 88 | 89 |         返回LMA地址。 90 | 91 | ###### 6. SIZEOF 92 | 93 | > SIZEOF(section) 94 | 95 |         返回指定段长度。 -------------------------------------------------------------------------------- /11 构建可运行程序/01/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app -------------------------------------------------------------------------------- /11 构建可运行程序/01/Makefile: -------------------------------------------------------------------------------- 1 | 2 | test:test2 3 | echo Test success! 4 | 5 | test2:main.o linker_script.lds 6 | ld -nostdlib -T linker_script.lds -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o app -L/usr/lib/gcc/x86_64-linux-gnu/6/ -L /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/Scrt1.o /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/6/crtbeginS.o main.o -lgcc -lgcc_s -lc /usr/lib/gcc/x86_64-linux-gnu/6/crtendS.o /usr/lib/x86_64-linux-gnu/crtn.o 7 | readelf -l app 8 | ./app 9 | 10 | test1:main.o 11 | ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o app -L/usr/lib/gcc/x86_64-linux-gnu/6/ /usr/lib/x86_64-linux-gnu/Scrt1.o /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/6/crtbeginS.o main.o -lgcc -lgcc_s -lc /usr/lib/gcc/x86_64-linux-gnu/6/crtendS.o /usr/lib/x86_64-linux-gnu/crtn.o 12 | readelf -l app 13 | ./app 14 | 15 | 16 | main.o:main.c 17 | gcc -g -c -o $@ $< 18 | 19 | 20 | -------------------------------------------------------------------------------- /11 构建可运行程序/01/linker_script.lds: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | SECTIONS 3 | { 4 | 5 | . = 0x2000; 6 | .rela.plt : 7 | { 8 | *(.rela.plt) 9 | } 10 | . = 0x1000; 11 | .text . : { *(.text) } 12 | 13 | .data . : { *(.data)} 14 | 15 | .bss : { *(.bss) } 16 | 17 | 18 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 19 | __init_array_start = .; 20 | __init_array_end = .; 21 | /DISCARD/ : {*(.note.*)} 22 | } 23 | 24 | -------------------------------------------------------------------------------- /11 构建可运行程序/01/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char bss_var[100]; 4 | char glob_var[100] = {0xf5}; 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | int a = 0x1234; 9 | printf("a:0x%x\n", a); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /11 构建可运行程序/01/test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iDalink/ld-linker-script/57f284cbaaf1957a3a2e44c1d171cffa96e57f7d/11 构建可运行程序/01/test -------------------------------------------------------------------------------- /11 构建可运行程序/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ##### 1、目标 4 |         你可能已经注意到,我们前面自定义`linker script`编译的可执行均无法正常运行。其原因是现代系统有无数的细节需要小心处理。一方面是,`printf`是系统`libc`提供的函数,为了保证编译通过我们只是简单得把`printf`函数屏蔽,这导致app运行过程中无法输出日志。而且我们为了保证编译简单性,直接将可执行入口指定为`main`函数,而不是常见的`_start`。另外一部分是,程序没有执行`exit`系统调用,这会导致程序无法正常退出,也就是你能在控制台看到程序执行异常。这个异常通常为`segmentation fault`. 5 | 6 |         本小节我们即解决运行问题。我们的目就是自定义链接脚本,并构建可执行应用程序。接下来的数个小节,即12和13小节,我们会继续探索更多实现方案,这将会更有趣。 7 | 8 | ##### 2、系统库依赖 9 | 10 |         我们先打开标准C库和部分必要的链接。默认`gcc`命令编译产出可执行程序时,会默认指定一组二进制文件。使用`gcc .... -v`命令可以看到完整的编译指令。笔者的计算机上,该指令输出如下。 11 | 12 | > /usr/lib/gcc/x86_64-linux-gnu/6/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/6/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper -plugin-opt=-fresolution=/tmp/ccFeIZgJ.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o app /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/6/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/6 -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/6/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/6/../../.. /tmp/ccy30oUZ.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/6/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o 13 | 14 | 15 | 16 |         我们先对该命令中的二进制文件做个整理。gcc指定部分参数时,会屏蔽其中部分文件。表格中的x标记表示该参数屏蔽该文件。如果你直接使用ld命令构建应用程序,那么任何默认的二进制文件均不会连接。 17 | 18 | 19 | 文件|包|功能|-nostartfiles|-nostdlib|-nodefaultlibs 20 | -|-|-|-|-|-|- 21 | /usr/lib/x86_64-linux-gnu/Scrt1.o|libc6-dev|_start符号|x|x| 22 | /usr/lib/x86_64-linux-gnu/crti.o|libc6-dev||x|x| 23 | /usr/lib/gcc/x86_64-linux-gnu/6/crtbeginS.o|libgcc-6-dev||x|x| 24 | *loads*| 25 | /usr/lib/gcc/x86_64-linux-gnu/6/libgcc.a|libgcc-6-dev|||x|x 26 | /usr/lib/gcc/x86_64-linux-gnu/6/libgcc_s.so|libgcc-6-dev|||x|x 27 | /usr/lib/x86_64-linux-gnu/libc.so|libc6-dev|||x|x 28 | /usr/lib/gcc/x86_64-linux-gnu/6/crtendS.o |libgcc-6-dev||x|x| 29 | /usr/lib/x86_64-linux-gnu/crtn.o|libc6-dev||x|x| 30 | 31 | ##### 3、构建可运行程序 32 | 33 |         前面已经分析,由于一个完整的程序依赖这些文件。那么我们只需要按照对应的方式在ld中指定这些文件即可。链接命令如下。 34 | 35 | > ld -nostdlib -T linker_script.lds -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o app -L/usr/lib/gcc/x86_64-linux-gnu/6/ -L /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/Scrt1.o /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/6/crtbeginS.o main.o -lgcc -lgcc_s -lc /usr/lib/gcc/x86_64-linux-gnu/6/crtendS.o /usr/lib/x86_64-linux-gnu/crtn.o 36 | 37 | 38 |         由于我们的可执行程序包含了动态库依赖,动态库的rela.plt是ld临时生成的段,需要明确保留该段。链接脚本如下。该部分测试代码位于01目录。 39 | 40 | ``` 41 | ENTRY(_start) 42 | SECTIONS 43 | { 44 | 45 | . = 0x2000; 46 | .rela.plt : 47 | { 48 | *(.rela.plt) 49 | } 50 | . = 0x1000; 51 | .text . : { *(.text) } 52 | 53 | .data . : { *(.data)} 54 | 55 | .bss : { *(.bss) } 56 | 57 | 58 | _bss_end = (. + 0x20000 - 1) / 0x20000 * 0x20000; 59 | __init_array_start = .; 60 | __init_array_end = .; 61 | /DISCARD/ : {*(.note.*)} 62 | } 63 | ``` -------------------------------------------------------------------------------- /12 抛弃glibc依赖/01/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app -------------------------------------------------------------------------------- /12 抛弃glibc依赖/01/Makefile: -------------------------------------------------------------------------------- 1 | 2 | test:app 3 | readelf -l app 4 | ./app 5 | 6 | app:main.o syscall.o 7 | ld -static -T linker_script.lds -e main -o app $^ 8 | 9 | main.o:main.c 10 | gcc -g -c -o $@ $< 11 | 12 | syscall.o:syscall.S 13 | as -o $@ $< 14 | 15 | -------------------------------------------------------------------------------- /12 抛弃glibc依赖/01/linker_script.lds: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | SECTIONS 3 | { 4 | . = 0; 5 | .text . : { *(.text) } 6 | .data . : { *(.data)} 7 | .bss : { *(.bss) } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /12 抛弃glibc依赖/01/main.c: -------------------------------------------------------------------------------- 1 | #include "syscall.h" 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | int a = 0x1234; 6 | //printf("a:0x%x\n", a); 7 | write(1, "Hello\n", 6); 8 | exit(0); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /12 抛弃glibc依赖/01/syscall.S: -------------------------------------------------------------------------------- 1 | 2 | 3 | SYSCALL_NO_WRITE = 1 4 | SYSCALL_NO_EXIT = 60 5 | 6 | .text 7 | .code64 8 | 9 | .global write 10 | write: 11 | mov $SYSCALL_NO_WRITE, %rax 12 | syscall 13 | ret 14 | 15 | .global exit 16 | exit: 17 | mov $SYSCALL_NO_EXIT, %rax 18 | syscall 19 | ret 20 | -------------------------------------------------------------------------------- /12 抛弃glibc依赖/01/syscall.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | void exit(int no); 4 | 5 | int write(int fd, void *buffer, unsigned long len); 6 | -------------------------------------------------------------------------------- /12 抛弃glibc依赖/02/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | app 3 | *.so 4 | -------------------------------------------------------------------------------- /12 抛弃glibc依赖/02/Makefile: -------------------------------------------------------------------------------- 1 | 2 | test:app 3 | readelf -l app 4 | export LD_LIBRARY_PATH=. 5 | ./app 6 | 7 | app:main.o libantlibc.so linker_script.lds Makefile 8 | ld -T linker_script.lds -dynamic-linker /lib64/ld-linux-x86-64.so.2 -L. -e main -o app main.o -lantlibc 9 | 10 | main.o:main.c 11 | gcc -g -c -o $@ $< 12 | 13 | libantlibc.so:syscall.o 14 | ld -shared -o $@ $^ 15 | readelf -l $@ 16 | 17 | syscall.o:syscall.S 18 | as -o $@ $< 19 | 20 | -------------------------------------------------------------------------------- /12 抛弃glibc依赖/02/linker_script.lds: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | SECTIONS 3 | { 4 | . = 0x1000; 5 | .text . : { *(.text) } 6 | .data . : { *(.data)} 7 | .bss : { *(.bss) } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /12 抛弃glibc依赖/02/main.c: -------------------------------------------------------------------------------- 1 | #include "syscall.h" 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | int a = 0x1234; 6 | //printf("a:0x%x\n", a); 7 | write(1, "Hello\n", 6); 8 | exit(0); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /12 抛弃glibc依赖/02/syscall.S: -------------------------------------------------------------------------------- 1 | 2 | 3 | SYSCALL_NO_WRITE = 1 4 | SYSCALL_NO_EXIT = 60 5 | 6 | .text 7 | .code64 8 | 9 | .global write 10 | write: 11 | mov $SYSCALL_NO_WRITE, %rax 12 | syscall 13 | ret 14 | 15 | .global exit 16 | exit: 17 | mov $SYSCALL_NO_EXIT, %rax 18 | syscall 19 | ret 20 | -------------------------------------------------------------------------------- /12 抛弃glibc依赖/02/syscall.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | void exit(int no); 4 | 5 | int write(int fd, void *buffer, unsigned long len); 6 | -------------------------------------------------------------------------------- /12 抛弃glibc依赖/README.md: -------------------------------------------------------------------------------- 1 | ##### 1、目标 2 | 上一节,我们已经体会到系统库的复杂。为了保持可控性,我们决定抛弃glibc,构建一个完全可控的应用程序。我们需要两个函数,`puts`和`exit`,分别实现日志输出和程序退出。 3 | 4 | ##### 2、系统调用 5 | 6 | 应用程序调用系统服务称之为系统调用(`system call`),应用程序之间的函数调用称之为库调用(`library call`)。x86的系统调用指令为int $0x80。x86-64架构的系统调用指令为syscall,系统调用返回指令为sysret指令。关于该指令的详细细节不再赘述。 7 | 8 | 下面给出`puts`和`exit`的汇编代码。 9 | 10 | ``` 11 | SYSCALL_NO_WRITE = 1 12 | SYSCALL_NO_EXIT = 60 13 | 14 | .text 15 | .code64 16 | 17 | .global write 18 | write: 19 | mov $SYSCALL_NO_WRITE, %rax 20 | syscall 21 | ret 22 | 23 | .global exit 24 | exit: 25 | mov $SYSCALL_NO_EXIT, %rax 26 | syscall 27 | ret 28 | 29 | ``` 30 | 31 | 32 | ##### 3、链接 33 | 34 |         为了简单起见,我们首先以静态方式链接系统调用函数。链接命令不再需要指定`glibc`的一串依赖,链接脚本也不再需要指出`plt`段。链接命令和链接脚本如下。测试代码位于01目录。 35 | 36 | > ld -static -T linker_script.lds -e main -o app $^ 37 | 38 | ``` 39 | ENTRY(_start) 40 | SECTIONS 41 | { 42 | . = 0; 43 | .text . : { *(.text) } 44 | .data . : { *(.data)} 45 | .bss : { *(.bss) } 46 | } 47 | ``` 48 |         可以看出应用程序的运行时结构十分精简。 49 | 50 | 51 | #### 4、构建标准库antlibc 52 | 53 |         我们决定将前一小节的两个系统调用函数构造成自己的libc,并把它取名为`antlibc`。理论上来说,只需要将其编译为动态链接库(`so`)即可。方便起见,我们的动态链接库`interpreter`采用glibc提供的`/lib64/ld-linux-x86-64.so.2`。 54 | 55 |         Makefile文件如下。完整代码位于02目录。该示例编译`syscall.o`为`libantlibc.so`。主程序`app`依赖`libantlibc.so`。 56 | 57 | ``` 58 | test:app 59 | readelf -l app 60 | export LD_LIBRARY_PATH=. 61 | ./app 62 | 63 | app:main.o libantlibc.so linker_script.lds Makefile 64 | ld -T linker_script.lds -dynamic-linker /lib64/ld-linux-x86-64.so.2 -L. -e main -o app main.o -lantlibc 65 | 66 | main.o:main.c 67 | gcc -g -c -o $@ $< 68 | 69 | libantlibc.so:syscall.o 70 | ld -shared -o $@ $^ 71 | readelf -l $@ 72 | 73 | syscall.o:syscall.S 74 | as -o $@ $< 75 | 76 | ``` 77 | 78 |         注意,我们的so库位于当前目录下,运行程序时需要将当前目录加入到链接库搜索路径中,命令为`export LD_LIBRARY_PATH=.`。 -------------------------------------------------------------------------------- /README.html: -------------------------------------------------------------------------------- 1 | 2 | README 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 263 | 264 | 265 |
266 |

一、什么是ld linker script

267 | 268 |

        程序链接时,目标文件根据一系列规则合并成可执行文件。这一步骤同时伴随着复杂的符号重定位、段合并和地址空间分配等步骤。

269 |

        ld linker scriptld提供的操作界面,用以对链接过程进行精确把控。使用ld linker script可以控制段的合并顺序、地址空间分配和链接符号导出。

270 |

二、为什么需要掌握ld linker script

271 | 272 |

        涉及u-bootkernel开发时,编译产出需要根据硬件环境进行特殊适配。例如,需要把指定指令放入固定存储空间内。甚至编译产出为binary类型文件,对指令和数据的分配都有硬性要求。这些对编译产出的精确控制,都离不开ld linker script的帮助。

273 |

三、受众群体

274 | 275 |

ld linker script主要面向bootloaderkernelsoc开发人员。这类环境需要对编译产出格式严格控制。另外应用开发仍然有必要了解这门技术,了解空间地址分配其对程序调试仍有裨益。

276 |

四、此博客有哪些特性

277 | 278 |

        毫无疑问地说,ld linker script仍然是十分冷门的技术,并且牵涉到大量底层的延伸技术细节。中文资料对这门技术的讲解仍然稀少,只有部分博客上有零零散散的介绍。

279 |

        此博客立足于现状,对整个技术做个全面介绍。所有章节都以官方文档(https://sourceware.org/binutils/docs/ld/)为中心,每个章节对应目录都配有演示示例。建议读者仔细阅读示例,多动手尝试实现细节,从而提高掌握水平。

280 |

五、我需要掌握哪些技术背景

281 | 282 |

        此博客需要读者具备一定的C应用开发经验,对程序的执行加载有所了解。同事要求读者尽可能具备深入的ELF技术储备。

283 |
284 |

文章地址:https://github.com/iDalink/ld-linker-script

285 |

章节目录

286 | 287 |
    288 |
  • 01 基本概念
  • 289 |
  • 02 格式
  • 290 |
  • 03 简要示例
  • 291 |
  • 04 简要链接指令
  • 292 |
  • 05 符号值分配
  • 293 |
  • 06 SECTIONS指令
  • 294 |
  • 07 MEMORY指令
  • 295 |
  • 08 PHDRS指令
  • 296 |
  • 09 VERSION指令
  • 297 |
  • 10 Linker Scripts中的表达式
  • 298 |
299 | 300 |
301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | #### 一、什么是ld linker script 3 | 4 |         程序链接时,目标文件根据一系列规则合并成可执行文件。这一步骤同时伴随着复杂的符号重定位、段合并和地址空间分配等步骤。 5 | 6 |         `ld linker script`是`ld`提供的操作界面,用以对链接过程进行精确把控。使用`ld linker script`可以控制段的合并顺序、地址空间分配和链接符号导出。 7 | 8 | #### 二、为什么需要掌握ld linker script 9 | 10 |         涉及`u-boot`和`kernel`开发时,编译产出需要根据硬件环境进行特殊适配。例如,需要把指定指令放入固定存储空间内。甚至编译产出为binary类型文件,对指令和数据的分配都有硬性要求。这些对编译产出的精确控制,都离不开`ld linker script`的帮助。 11 | 12 | #### 三、受众群体 13 | 14 | `ld linker script`主要面向`bootloader`、`kernel`、`soc`开发人员。这类环境需要对编译产出格式严格控制。另外应用开发仍然有必要了解这门技术,了解空间地址分配其对程序调试仍有裨益。 15 | 16 | #### 四、此博客有哪些特性 17 | 18 |         毫无疑问地说,`ld linker script`仍然是十分冷门的技术,并且牵涉到大量底层的延伸技术细节。中文资料对这门技术的讲解仍然稀少,只有部分博客上有零零散散的介绍。 19 | 20 |         此博客立足于现状,对整个技术做个全面介绍。所有章节都以[官方文档(https://sourceware.org/binutils/docs/ld/)](https://sourceware.org/binutils/docs/ld/ "https://sourceware.org/binutils/docs/ld/")为中心,每个章节对应目录都配有演示示例。建议读者仔细阅读示例,多动手尝试实现细节,从而提高掌握水平。 21 | 22 | #### 五、我需要掌握哪些技术背景 23 | 24 |         此博客需要读者具备一定的C应用开发经验,对程序的执行加载有所了解。同事要求读者尽可能具备深入的`ELF`技术储备。 25 | 26 | 27 | 28 | --- 29 | 文章地址:https://github.com/iDalink/ld-linker-script 30 | ###
章节目录
31 | - *01 基本概念* 32 | - *02 格式* 33 | - *03 简要示例* 34 | - *04 简要链接指令* 35 | - *05 符号值分配* 36 | - *06 SECTIONS指令* 37 | - *07 MEMORY指令* 38 | - *08 PHDRS指令* 39 | - *09 VERSION指令* 40 | - *10 Linker Scripts中的表达式* 41 | - *11 构建可运行程序* 42 | - *12 抛弃glibc依赖* 43 | 44 | --------------------------------------------------------------------------------