linux 程序编译链接过程、动态库、静态库、elf文件

目录

GCC编译链接过程:

ELF (Executable and Linkable Format)文件

静态库( .a)

动态链接库 ( .so)


GCC编译链接过程:

linux 程序编译链接过程、动态库、静态库、elf文件_第1张图片

gcc -E hello.c -o hello.i   
gcc -S hello.i –o hello.s  
gcc –c hello.s –o hello.o //-c只编译不链接
gcc hello.s –o hello   //链接,生成可执行文件

/*其他gcc编译选项*/
-static // 静态编译,禁止使用动态链接库
-shared // 生成动态链接库,尽可能的链接动态链接库
-L dir // 动态链接库的搜索路径
-lname // 小写l 向gcc传入一个依赖的库文件,可能是动态库 or 静态库。库的实际名称:lib[name].so or lib[name].a
-fpic  // 生成位置无关代码, position independent code
-I     // 大写的i,添加gcc编译头文件搜索路径

ELF (Executable and Linkable Format)文件

linux 程序编译链接过程、动态库、静态库、elf文件_第2张图片linux 程序编译链接过程、动态库、静态库、elf文件_第3张图片

 

/*elf header*/
#define EI_NIDENT	16
typedef struct elf32_hdr{
  unsigned char	e_ident[EI_NIDENT];
  Elf32_Half	e_type;
  Elf32_Half	e_machine;
  Elf32_Word	e_version;
  Elf32_Addr	e_entry;  /* Entry point */
  Elf32_Off	e_phoff;
  Elf32_Off	e_shoff;
  Elf32_Word	e_flags;
  Elf32_Half	e_ehsize;
  Elf32_Half	e_phentsize;
  Elf32_Half	e_phnum;
  Elf32_Half	e_shentsize;
  Elf32_Half	e_shnum;
  Elf32_Half	e_shstrndx;
} Elf32_Ehdr;

/*program header table*/
typedef struct elf32_phdr {
	Elf32_Word p_type;
	Elf32_Off p_offset;
	Elf32_Addr p_vaddr;
	Elf32_Addr p_paddr;
	Elf32_Word p_filesz;
	Elf32_Word p_memsz;
	Elf32_Word p_flags;
	Elf32_Word p_align;
} Elf32_Phdr;

/*section header table*/
typedef struct elf32_shdr {
  Elf32_Word	sh_name;
  Elf32_Word	sh_type;
  Elf32_Word	sh_flags;
  Elf32_Addr	sh_addr;
  Elf32_Off	sh_offset;
  Elf32_Word	sh_size;
  Elf32_Word	sh_link;
  Elf32_Word	sh_info;
  Elf32_Word	sh_addralign;
  Elf32_Word	sh_entsize;
} Elf32_Shdr;
elf header  (这个应该是x86 CPU的?, 针对ARM CPU 微小变动?)
e_ident

0-3:magic number :0x7f, E, L ,F

4:0)非法,1)32bit,2)64bit

5:0)非法,1)小端, 2)大端

6:verison, must be 1

7-15: 未使用字节, must be 0

e_type

文件类型:

ET_NONE(0):未知的文件格式;
ET_REL(1):可重定位文件,比如目标文件;
ET_EXEC(2):可执行文件;
ET_DYN(3):共享目标文件;
ET_CORE(4):Core转储文件,比如程序crash之后的转储文件;
ET_LOPROC(0xff00):特定处理器的文件标识;
ET_HIPROC(0xffff):特定处理器的文件标识;
[ET_LOPROC,ET_HIPROC]之间的值用来表示特定处理器的文件格式;

e_machine ET_NONE(0):未知的处理器架构;
EM_M32(1):AT&T WE 32100;
EM_SPARC(2):SPARC;
EM_386(3):Intel 80386;
EM_68K(4):Motorola 68000;
EM_88K(5):Motorola 88000;
EM_860(6):Intel 80860;
EM_MIPS(7):MIPS RS3000大端;
EM_MIPS_RS4_BE(10):MIPS RS4000大端;
其他,预留;
e_version

EV_NONE(0):非法的版本;

EV_CURRENT(`):当前版本;

e_entry 程序的虚拟入口地址,如果文件没有对应的入口可以为0;
e_phoff program header table offest
e_shoff section header table offest
e_flags 处理器相关标志位
e_ehsize ELF文件头大小; 52bytes
e_phentsize program header table  中单项的大小
e_phnum program header table  总项数
e_shentsize section header table 中单项的大小
e_shnum section header table 总项数
e_shstrndx section header table 中 节名称字符串表 所在节的索引;

linux 程序编译链接过程、动态库、静态库、elf文件_第4张图片

program header table
p_type

PT_NULL(0):当前项未使用,项中的成员是未定义的,需要忽略当前项;
PT_LOAD(1):当前Segment是一个可装载的Segment,即可以被装载映射到内存中,其大小由p_filesz和p_memsz描述。如果p_memsz>p_filesz则剩余的字节被置零,但是p_filesz>p_memsz是非法的。动态库一般包含两个该类型的段:代码段和数据段;
PT_DYNAMIC(2):动态段,使用了动态库特有的段,包含了动态链接必须的一些信息,比如需要链接的共享库列表、GOT等等;
PT_INTERP(3)当前段用于存储一段以NULL为结尾的字符串,该字符串表明了程序解释器的位置。且当前段仅仅对于可执行文件有实际意义,一个可执行文件中不能出现两个当前段,如果一个文件中包含当前段。比如/lib64/ld-linux-x86-64.so.2;【就是一个字符串,程序解释器的绝对路径,如下图占用19(0x13)个字节存储该路径】
PT_NOTE(4):用于保存与特定供应商或者系统相关的附加信息以便于兼容性、一致性检查,但是实际上只保存了操作系统的规范信息;
PT_SHLIB(5):保留段;
PT_PHDR(6):保存程序头表本身的位置和大小,当前段不能在文件中出现一次以上,且仅仅当程序表头为内存映像的一部分时起作用,它必须在所有加载项目之前;
[PT_LPROC(0x70000000),PT_HIPROC(0x7fffffff)]:该范围内的值用作预留;
 

PT_LOAD(1): 代码段

PT_LOAD(1): 数据段

PT_DYNAMIC(2): (如果 -staic静态编译,不包含该段)

PT_INTERP(3): 【就是一个字符串,程序解释器的绝对路径,如下图占用19(0x13)个字节存储该路径】

PT_NOTE(4)

PT_PHDR(6):【如下图:32 * 9 = 0x120个字节】

p_offset

当前段相对于 文件的offest

p_vaddr 该段映射到虚拟地址的起始地址
p_paddr

对于linux来说没用?

???
p_filesz 段在文件中占用的大小
p_memsz 段在内存中占用的大小
p_flags 段的相关标志
p_align 段加载到内存中,起始地址的对齐大小

linux 程序编译链接过程、动态库、静态库、elf文件_第5张图片

section header table
sh_name

节的名称在  .shstrtab 中的index

系统自带的section 的名称都是 . 开头。

自定义的section 不使用 . 开头。

sh_type

SHT_NULL(0):当前节是非活跃的,没有一个对应的具体的节内存;
SHT_PROGBITS(1):包含了程序的指令信息、数据等程序运行相关的信息;
SHT_SYMTAB(2):保存了符号信息,用于重定位;
此种类型节的sh_link存储相关字符串表的节索引,sh_info存储最后一个局部符号的符号表索引+1;
SHT_DYNSYM(11):保存共享库导入动态符号信息;
此种类型节的sh_link存储相关字符串表的节索引,sh_info存储最后一个局部符号的符号表索引+1;
SHT_STRTAB(3):一个字符串表,保存了每个节的节名称;
SHT_RELA(4):存储可重定位表项,可能会有附加内容,目标文件可能有多个可重定位表项;
此种类型节的sh_link存储相关符号表的节索引,sh_info存储重定位所使用节的索引;
SHT_HASH(5):存储符号哈希表,所有参与动态链接的目标只能包含一个哈希表,一个目标文件只能包含一个哈希表;
此种类型节的sh_link存储哈希表所使用的符号表的节索引,sh_info为0;
SHT_DYAMIC(6):存储包含动态链接的信息,一个目标文件只能包含一个;
此种类型的节的sh_link存储当前节中使用到的字符串表格的节的索引,sh_info为0;
SHT_NOTE(7):存储以某种形式标记文件的信息;
SHT_NOBITS(8):这种类型的节不占据文件空间,但是成员sh_offset依然会包含对应的偏移;
SHT_REL(9):包含可重定位表项,无附加内容,目标文件可能有多个可重定位表项;
此种类型节的sh_link存储相关符号表的节索引,sh_info存储重定位所使用节的索引;
SHT_SHLIB(10):保留区,包含此节的程序与ABI不兼容;
[SHT_LOPROC(0x70000000),SHT_HIPROC(0x7fffffff)]:留给处理器专用语义;
[SHT_LOUSER(0x80000000),SHT_HIUSER(0xffffffff)]:预留;
 

sh_flags

SHF_WRITE(0x1):当前节包含进程执行过程中可写的数据;

SHF_ALLOC(0x2):当前节在运行阶段占据内存;

SHF_EXECINSTR(0x4):当前节包含可执行的机器指令;

SHF_MASKPROC(0xf0000000):所有包含当前掩码都表示预留给特定处理器的;

sh_addr

如果当前节需要被装载到内存,则当前项存储当前节映射到内存的首地址,否则应该为0;

sh_offset 相对于的文件的offest
sh_size section 的大小
sh_link 该section 依赖的 section 的index
sh_info section 的附加信息
sh_addralign 地址对齐
sh_entsize 某些节是一个数组,此项表示数组每一项的大小 ES 列

linux 程序编译链接过程、动态库、静态库、elf文件_第6张图片

符号表

typedef struct elf32_sym{
  Elf32_Word	st_name;
  Elf32_Addr	st_value;
  Elf32_Word	st_size;
  unsigned char	st_info;
  unsigned char	st_other;
  Elf32_Half	st_shndx;
} Elf32_Sym;
elf32_sym
st_name 符号名称,是符号名在字符串表中的index;
st_value 对于应用程序和共享库来说,其为符号的链接地址。

对于.symtab里面的符号,编译链接时确定。

对于.dynsym里面的符号,动态链接时确定。

st_size 符号大小???
st_info 符号属性,符号类型
st_other
st_shndx 仅仅对于重定位文件有用


静态库( .a)

  • 静态库是一组 目标文件( .o)的集合,多个目标文件打包形成的文件;

  • linux静态库链接命名:  lib[real_name].a

生成静态库:

gcc -c function1.c function1.o    //生成REL (Relocatable file)
gcc -c function2.c function2.o    //生成REL (Relocatable file)
ar -crv libfunction.a  function1.o function2.o  //ar是打包工具

// .a  和 .o  文件  使用readelf工具查看, 都属于 REL (Relocatable file) 文件

动态链接库 ( .so)

  • 动态库在内存中只有一份,所有进程共享。(数据段每个进程都有一份,代码指令共享)
  • 动态库跨进程共享,需要内核的支持。进程的虚拟地址映射到相同的页面上,该页面存放了共享库。
  • linux默认的库文件搜索路径:  ./lib  /usr/lib
  • 配置动态链接库的搜索路径的环境变量:LD_LIBRARY_PATH,该环境变量不是默认缺省的配置。
    • LD_LIBRARY_PATH环境变量存放的也是目录列表,目录之间用冒号:分隔,最后的圆点.表示当前目录,与PATH的格式相同,export LD_LIBRARY_PATH=目录1:目录2:目录3:......目录n:.

  • 动态库的调用

    • 显示调用

    • 隐式调用

参考链接

1. https://blog.csdn.net/GrayOnDream/article/details/124564129

你可能感兴趣的:(Linux,linux,运维,服务器)