这是目录
- 写在前面
- 一、内存管理
- 1、分段
- 2、分页
- 二、线程管理
- 三、静态库
- 1、编译
- 1.1、预处理
- 1.2、编译
- 1.3、汇编
- 1.4、链接
- 2、编译器
- 3、目标文件
- **.text**
- **.data**
- **.bss**
- **__attribute__**
- 3.1、符号
- 3.2、兼容C语言 — extern C
- 4、链接 — ld
写在前面
本文记录自己的学习生涯,学一点记一点,做好准备随时能够提桶。
一、内存管理
1、分段
程序所需要的内存空间大小的虚拟空间映射到某个物理地址空间。
问题:无法高效的使用整个内存,容易造成内存的浪费(为程序分配物理内存,程序并未完全使用物理内存)。
2、分页
分页:1、为了解决分段所带来的问题,即内存的高效使用;1、保护作用,可以单独设置每个页的属性、权限。
将内存分成一个个固定大小的页,如1K or 4K(由硬件决定),在此基础上,可以将程序的内存进一步细分。将程序使用的部分内存分配到物理内存,对于暂未使用的部分内存先不分配实际物理内存,后续使用到后再分配实际的物理地址。
进程当前正在使用的VP0、VP1分配到物理地址PP2、PP0,另一部分VP3、VP2分配到磁盘上,还有暂未使用的VP4、VP6、VP7不进行分配。
现在虚拟地址到物理地址的转换有专门的硬件完成(MMU):
二、线程管理
三、静态库
1、编译
a.c文件用于下列结果步骤演示
为了简化显示内容,文件尽可能的进行了精简。
#define PI (111)int main(){if(PI);return 0;}
1.1、预处理
gcc -E a.c -i a.i
pi@NanoPi-NEO2:~/project/test$ cat b.i# 1 "b.c"# 1 ""# 1 ""# 31 ""# 1 "/usr/include/stdc-predef.h" 1 3 4# 32 "" 2# 1 "b.c"int main(){if((111));return 0;}
展开宏,替换头文件,去掉注释,添加行号,保留编译命令–>xx.i
1.2、编译
gcc -S a.i -i a.s
pi@NanoPi-NEO2:~/project/test$ cat b.s .arch armv8-a.file "b.c".text.align2.global main.type main, %functionmain:.LFB0:.cfi_startprocmov w0, 0ret.cfi_endproc.LFE0:.size main, .-main.ident"GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0".section.note.GNU-stack,"",@progbits
生成汇编文件–>xx.s
1.3、汇编
gcc -E a.c -i a.i
将汇编文件生成机器代码–>xx.o
1.4、链接
gcc -E a.c -i a.i
将文件之前的引用(函数变量)链接在一起。
在不同的文件中会生成一张符号表,表中明确指出了文件中的所有符号(函数和变量),方便其他文件引用。
–>xx.out
2、编译器
3、目标文件
.text
代码段
.data
已经初始化的非0数据段(全局变量,局部静态变量,已经分配空间占实际内存)
.bss
未初始化或初始化为0的数据段(未初始化可能默认为0,没有必要在这个阶段分配空间,因此为空不分配空间,只保留符号表)
举个栗子:
#include"stdio.h"#include"stdint.h"uint16_t temp_1 = 222;uint16_t temp_2;int mian(){static uint16_t temp_3 = 111;static uint16_t temp_4;temp_1 = temp_3++;temp_2 = temp_1++;printf("this is test:%d\r\n",temp_1);return 0;}
编译上文.c:
gcc -c main.c
使用objdump查看上述代码编译生成的.o文件的信息。
objdump -x -s -d main.o
输出文件如下:
pi@NanoPi-NEO2:~/project/test$ objdump -x -s -d main.o main.o: file format elf64-littleaarch64main.oarchitecture: aarch64, flags 0x00000011:HAS_RELOC, HAS_SYMSstart address 0x0000000000000000private flags = 0:Sections:Idx NameSizeVMA LMA File offAlgn0 .text 0000008800000000000000000000000000000000000000402**2CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data 0000000400000000000000000000000000000000000000c82**1CONTENTS, ALLOC, LOAD, DATA2 .bss0000000200000000000000000000000000000000000000cc2**1ALLOC3 .rodata 0000001200000000000000000000000000000000000000d02**3CONTENTS, ALLOC, LOAD, READONLY, DATA4 .comment0000002b00000000000000000000000000000000000000e22**0CONTENTS, READONLY5 .note.GNU-stack 00000000000000000000000000000000000000000000010d2**0CONTENTS, READONLY6 .eh_frame 0000003800000000000000000000000000000000000001102**3CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATASYMBOL TABLE:0000000000000000 ldf *ABS*0000000000000000 main.c0000000000000000 ld.text0000000000000000 .text0000000000000000 ld.data0000000000000000 .data0000000000000000 ld.bss 0000000000000000 .bss0000000000000000 ld.rodata0000000000000000 .rodata0000000000000002 l O .data0000000000000002 temp_3.38380000000000000000 l O .bss 0000000000000002 temp_4.38390000000000000000 ld.note.GNU-stack0000000000000000 .note.GNU-stack0000000000000000 ld.eh_frame0000000000000000 .eh_frame0000000000000000 ld.comment 0000000000000000 .comment0000000000000000 g O .data0000000000000002 temp_10000000000000002 O *COM*0000000000000002 temp_20000000000000000 g F .text0000000000000088 mian0000000000000000 *UND*0000000000000000 printfContents of section .text: 0000 fd7bbfa9 fd030091 00000090 00000091.{.............. 0010 00004079 01040011 223c0012 01000090..@y...."<...... 0020 21000091 22000079 01000090 21000091!..."..y....!... 0030 20000079 00000090 00000091 00004079 ..y..........@y 0040 01040011 223c0012 01000090 21000091...."<......!... 0050 22000079 01000090 210040f9 20000079"..y....!.@. ..y 0060 00000090 00000091 00004079 e103002a..........@y...* 0070 00000090 00000091 00000094 00008052...............R 0080 fd7bc1a8 c0035fd6.{...._.Contents of section .data: 0000 de006f00 ..o.Contents of section .rodata: 0000 74686973 20697320 74657374 3a25640dthis is test:%d. 0010 0a00 ..Contents of section .comment: 0000 00474343 3a202855 62756e74 7520392e.GCC: (Ubuntu 9. 0010 332e302d 31377562 756e7475 317e32303.0-17ubuntu1~20 0020 2e303429 20392e33 2e3000 .04) 9.3.0. Contents of section .eh_frame: 0000 10000000 00000000 017a5200 04781e01.........zR..x.. 0010 1b0c1f00 20000000 18000000 00000000.... ........... 0020 88000000 00410e10 9d029e01 60dedd0e.....A......`... 0030 00000000 00000000........Disassembly of section .text:0000000000000000 <mian>: 0: a9bf7bfdstp x29, x30, [sp, #-16]! 4: 910003fdmov x29, sp 8: 90000000adrpx0, 0 <mian>8: R_AARCH64_ADR_PREL_PG_HI21 .data+0x2 c: 91000000add x0, x0, #0x0c: R_AARCH64_ADD_ABS_LO12_NC.data+0x210: 79400000ldrhw0, [x0]14: 11000401add w1, w0, #0x118: 12003c22and w2, w1, #0xffff1c: 90000001adrpx1, 0 <mian>1c: R_AARCH64_ADR_PREL_PG_HI21.data+0x220: 91000021add x1, x1, #0x020: R_AARCH64_ADD_ABS_LO12_NC .data+0x224: 79000022strhw2, [x1]28: 90000001adrpx1, 0 <mian>28: R_AARCH64_ADR_PREL_PG_HI21temp_12c: 91000021add x1, x1, #0x02c: R_AARCH64_ADD_ABS_LO12_NC temp_130: 79000020strhw0, [x1]34: 90000000adrpx0, 0 <mian>34: R_AARCH64_ADR_PREL_PG_HI21temp_138: 91000000add x0, x0, #0x038: R_AARCH64_ADD_ABS_LO12_NC temp_13c: 79400000ldrhw0, [x0]40: 11000401add w1, w0, #0x144: 12003c22and w2, w1, #0xffff48: 90000001adrpx1, 0 <mian>48: R_AARCH64_ADR_PREL_PG_HI21temp_14c: 91000021add x1, x1, #0x04c: R_AARCH64_ADD_ABS_LO12_NC temp_150: 79000022strhw2, [x1]54: 90000001adrpx1, 2 <mian+0x2>54: R_AARCH64_ADR_GOT_PAGEtemp_258: f9400021ldr x1, [x1]58: R_AARCH64_LD64_GOT_LO12_NCtemp_25c: 79000020strhw0, [x1]60: 90000000adrpx0, 0 <mian>60: R_AARCH64_ADR_PREL_PG_HI21temp_164: 91000000add x0, x0, #0x064: R_AARCH64_ADD_ABS_LO12_NC temp_168: 79400000ldrhw0, [x0]6c: 2a0003e1mov w1, w070: 90000000adrpx0, 0 <mian>70: R_AARCH64_ADR_PREL_PG_HI21.rodata74: 91000000add x0, x0, #0x074: R_AARCH64_ADD_ABS_LO12_NC .rodata78: 94000000bl0 <printf>78: R_AARCH64_CALL26printf7c: 52800000mov w0, #0x0// #080: a8c17bfdldp x29, x30, [sp], #1684: d65f03c0ret
.data段为初始化非0的数据,temp_1(de)和temp_3(6f)。而temp_2和temp_4并未分配空间,存放于bss。
attribute
attribute((section(“dame”))),在函数或者变量前加上这个,表示将函数或者变量放置在name段内。如下,新增一个dame段
#include"stdio.h"#include"stdint.h"__attribute__((section(".demo"))) uint8_t tttt;int mian(){printf("this is test\r\n");return 0;}
pi@NanoPi-NEO2:~/project/test$ objdump -x -s -d main_1.o main_1.o: file format elf64-littleaarch64main_1.oarchitecture: aarch64, flags 0x00000011:HAS_RELOC, HAS_SYMSstart address 0x0000000000000000private flags = 0:Sections:Idx NameSizeVMA LMA File offAlgn0 .text 0000002000000000000000000000000000000000000000402**2CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data 0000000000000000000000000000000000000000000000602**0CONTENTS, ALLOC, LOAD, DATA2 .bss0000000000000000000000000000000000000000000000602**0ALLOC3 .demo 0000000100000000000000000000000000000000000000602**0CONTENTS, ALLOC, LOAD, DATA4 .rodata 0000000e00000000000000000000000000000000000000682**3CONTENTS, ALLOC, LOAD, READONLY, DATA5 .comment0000002b00000000000000000000000000000000000000762**0CONTENTS, READONLY6 .note.GNU-stack 0000000000000000000000000000000000000000000000a12**0CONTENTS, READONLY7 .eh_frame 0000003800000000000000000000000000000000000000a82**3CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
3.1、符号
查看文件的符号表:
新建main_1.c文件
#include"stdio.h"#include"stdint.h"__attribute__((section(".demo"))) uint8_t tttt;uint8_t temp_1 = 0;uint8_t temp_2 = 0;void fun(uint8_t test){printf("this is test:%d\r\n",test);}int mian(){temp_1 = temp_2++;fun(temp_1);return 0;}
编译并查看文件内容:
pi@NanoPi-NEO2:~/project/test$ readelf -smain_1.oSymbol table '.symtab' contains 21 entries: Num:ValueSize TypeBind VisNdx Name 0: 0000000000000000 0 NOTYPELOCALDEFAULTUND1: 0000000000000000 0 FILELOCALDEFAULTABS main_1.c 2: 0000000000000000 0 SECTION LOCALDEFAULT13: 0000000000000000 0 SECTION LOCALDEFAULT34: 0000000000000000 0 SECTION LOCALDEFAULT45: 0000000000000000 0 SECTION LOCALDEFAULT56: 0000000000000000 0 NOTYPELOCALDEFAULT5 $d 7: 0000000000000000 0 NOTYPELOCALDEFAULT4 $d 8: 0000000000000000 0 SECTION LOCALDEFAULT69: 0000000000000000 0 NOTYPELOCALDEFAULT6 $d10: 0000000000000000 0 NOTYPELOCALDEFAULT1 $x11: 0000000000000000 0 SECTION LOCALDEFAULT8 12: 0000000000000014 0 NOTYPELOCALDEFAULT9 $d13: 0000000000000000 0 SECTION LOCALDEFAULT9 14: 0000000000000000 0 SECTION LOCALDEFAULT7 15: 0000000000000000 1 OBJECTGLOBAL DEFAULT5 tttt16: 0000000000000000 1 OBJECTGLOBAL DEFAULT4 temp_117: 0000000000000001 1 OBJECTGLOBAL DEFAULT4 temp_218: 000000000000000044 FUNCGLOBAL DEFAULT1 fun19: 0000000000000000 0 NOTYPEGLOBAL DEFAULTUND printf20: 000000000000002c80 FUNCGLOBAL DEFAULT1 mian
上述文件有fun和printf函数,fun函数能找到对应的定义,但是printf无法找到,前面ndx为UND(未定义)。
3.2、兼容C语言 – extern C
extern "C" {int func(int);int var;}
为什么这么做?
C++中编译后会将函数名或者变量名进行重新封装修饰,即:fun编译后会生成符号_ZN3fun3barE。
在C中会生成_fun 或者 fun,具体看编译器的支持。
使用extern C后C++会将括号中的文件安装C语言的编译格式生成符号表。
/*d.cpp*/#include#define PI (111)extern "C" {void funci(){;}}static void func(float){;}void ffunc(int){;}int main(){if(PI);return 0;}
编译调试查看:
pi@NanoPi-NEO2:~/project/test$ g++ -c d.cpppi@NanoPi-NEO2:~/project/test$ readelf -s d.oSymbol table '.symtab' contains 14 entries: Num:ValueSize TypeBind VisNdx Name 0: 0000000000000000 0 NOTYPELOCALDEFAULTUND1: 0000000000000000 0 FILELOCALDEFAULTABS d.cpp 2: 0000000000000000 0 SECTION LOCALDEFAULT13: 0000000000000000 0 SECTION LOCALDEFAULT24: 0000000000000000 0 SECTION LOCALDEFAULT35: 0000000000000000 0 NOTYPELOCALDEFAULT1 $x 6: 000000000000000820 FUNCLOCALDEFAULT1 _ZL4funcf 7: 0000000000000000 0 SECTION LOCALDEFAULT58: 0000000000000014 0 NOTYPELOCALDEFAULT6 $d 9: 0000000000000000 0 SECTION LOCALDEFAULT6 10: 0000000000000000 0 SECTION LOCALDEFAULT4 11: 0000000000000000 8 FUNCGLOBAL DEFAULT1 func12: 000000000000001c20 FUNCGLOBAL DEFAULT1 _Z5ffunci13: 0000000000000030 8 FUNCGLOBAL DEFAULT1 main
func没有使用C++的符号命名方式。fun使用了C++的符号命名。
_Z5ffunci:
_Z:固定字符
5:函数名有5个字符
i:int类型(f:float,v:void。。。)
4、链接 – ld
根据上文,每个文件都会生成一张符号表,如a.o和b.o两个文件,如何将a.o和b.o链接在一起生成可执行文件?
链接方法有两种:
1、按照文件的顺序依次合在一起,这样每个文件都会有重复的.text、.data。。。等等。
2、将所有的相同属性的段合在一起,即text在一起,data在一起。(主流方案)
举例:
#include"stdio.h"#include"stdint.h"void fun(uint8_t test){;}int test(){fun(1);return 0;}
#include"stdio.h"#include"stdint.h"int main(){test();return 0;}
gcc -c main.c main_1.cld main.o main_1.o -e main -o ab
-e main 表示将 main 函数作为程序入口,ld 链接器默认的程序入口为_start。
-o ab 表示链接输出文件名为 ab,默认为 a.out。
执行后生成的ab文件中所有的相同属性对会对应在一起。
pi@NanoPi-NEO2:~/project/test$ objdump -h main.o main.o: file format elf64-littleaarch64Sections:Idx NameSizeVMA LMA File offAlgn0 .text 0000001800000000000000000000000000000000000000402**2CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data 0000000000000000000000000000000000000000000000582**0CONTENTS, ALLOC, LOAD, DATA2 .bss0000000000000000000000000000000000000000000000582**0ALLOC
pi@NanoPi-NEO2:~/project/test$ objdump -h main_1.o main_1.o: file format elf64-littleaarch64Sections:Idx NameSizeVMA LMA File offAlgn0 .text 0000003000000000000000000000000000000000000000402**2CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data 0000000000000000000000000000000000000000000000702**0CONTENTS, ALLOC, LOAD, DATA2 .bss0000000000000000000000000000000000000000000000702**0ALLOC
pi@NanoPi-NEO2:~/project/test$ objdump -h abab: file format elf64-littleaarch64Sections:Idx NameSizeVMA LMA File offAlgn0 .text 000000c400000000004001200000000000400120000001202**2CONTENTS, ALLOC, LOAD, READONLY, CODE1 .eh_frame 0000006000000000004001e800000000004001e8000001e82**3CONTENTS, ALLOC, LOAD, READONLY, DATA2 .got000000100000000000410fd80000000000410fd800000fd82**3CONTENTS, ALLOC, LOAD, DATA3 .got.plt000000180000000000410fe80000000000410fe800000fe82**3CONTENTS, ALLOC, LOAD, DATA4 .data 0000000400000000004110000000000000411000000010002**1CONTENTS, ALLOC, LOAD, DATA5 .bss0000000c00000000004110040000000000411004000010042**1ALLOC6 .comment0000002a00000000000000000000000000000000000010042**0CONTENTS, READONLY
查看生成的文件符号表:
pi@NanoPi-NEO2:~/project/test$ readelf -s ab Symbol table '.symtab' contains 20 entries: Num:ValueSize TypeBind VisNdx Name 0: 0000000000000000 0 NOTYPELOCALDEFAULTUND1: 00000000004000b0 0 SECTION LOCALDEFAULT12: 00000000004000f8 0 SECTION LOCALDEFAULT23: 0000000000000000 0 SECTION LOCALDEFAULT34: 0000000000000000 0 FILELOCALDEFAULTABS main.c 5: 00000000004000b0 0 NOTYPELOCALDEFAULT1 $x 6: 000000000040010c 0 NOTYPELOCALDEFAULT2 $d 7: 0000000000000000 0 FILELOCALDEFAULTABS main_1.c 8: 00000000004000c8 0 NOTYPELOCALDEFAULT1 $x 9: 0000000000400130 0 NOTYPELOCALDEFAULT2 $d10: 0000000000410fe8 0 NOTYPEGLOBAL DEFAULT2 _bss_end__11: 0000000000410fe8 0 NOTYPEGLOBAL DEFAULT2 __bss_start__12: 00000000004000c820 FUNCGLOBAL DEFAULT1 fun13: 0000000000410fe8 0 NOTYPEGLOBAL DEFAULT2 __bss_end__14: 00000000004000dc28 FUNCGLOBAL DEFAULT1 test15: 0000000000410fe8 0 NOTYPEGLOBAL DEFAULT2 __bss_start16: 00000000004000b024 FUNCGLOBAL DEFAULT1 main17: 0000000000410fe8 0 NOTYPEGLOBAL DEFAULT2 __end__18: 0000000000410fe8 0 NOTYPEGLOBAL DEFAULT2 _edata19: 0000000000410fe8 0 NOTYPEGLOBAL DEFAULT2 _end
每一个函数都对应唯一的地址。对于单个文件的编译中找不到的符号会临时用一个假地址代替,直到链接的时候才会查找真实地址并替换。
比如:没有链接前,main.c找不到test函数,就会设置test函数地址为0,
链接后的文件会填充对应的地址。