C/C++代码经过编译后会生成elf文件,里面包含了平台、代码、数据,调试信息等。
编译过程参考:https://blog.csdn.net/qq_39815222/article/details/108526580
1. elf文件格式
- ELF Header
链接后的elf文件有三种类型,分别是可重定位文件rel(包含适合于与其他目标文件链接来创建可执行文件,或者共享目标文件的代码和数据);可执行文件exec(包含适合于执行的的一个程序,此文件规定了exec()如何创建一个程序的进程映像);共享目标文件dyn(包含可在两种上下文链接的代码和数据,如linux中已.so结尾的文件)
简写为Ehdr, 包含以下信息:
e_ident[**] //elf标识
e_type; //elf类型(上述三种类型)
e_ machine; //目标文件体系类型,即运行架构,如x86、riscv、arm等
e_version; //目标文件版本
e_entry; //elf入口地址
e_ phoff; //程序头部偏移
e_shoff; //节区头部偏移
e_flags;
e_ehsize; //ELF格式头部大小
e_phentsize; //程序头部表项大小
e_phnum; //程序头表项个数,即segment数
e_shentsize; //节区头部表项大小
e_shnum; //节区表项个数,即section数
e_shstrndx;
- Program Header Table
简写为Phdr,包含以下信息:
p_type; //segment类型
p_offset; //segment在文件中的偏移
p_vaddr; //segment虚地址
p_paddr; //物理地址
p_filesz; //文件中segment字节数
p_memsz; //内存中segment字节数
p_flags;
p_align;
- segment
内容包括text segment,data segment等,segment包含多个section,
- .text
已编译程序的指令代码段 - .rodata
ro代表read only,表示只读数据 - .data
已初始化的C程序全局变量和静态局部变量。C程序普通变量在运行是被保存在堆栈中,既不在.data中,也不在.bss中,此外,如果变量初始化值为0,也可能会放到bss段。 - .bss
未初始化的C程序变量和静态局部变量。目标文件格式区分初始化和未初始化变量是为了空间效率,在ELF文件中.bss段不占据实际的存储器空间,仅仅是一个占位符。 - .debug
调试符号表,调试器用此段的信息帮助调试
简称sym,包含以下信息:
st_name;
st_value;
st_size;
st_info;
st_other;
st_shndx;
- section header table
简称Shdr,包含信息:
sh_name; //节区名称,字符串表索引值
sh_type; //节区种类,如rel*
sh_flags; //
sh_addr; /地址
sh_offset; //输出节区第一个字节偏移
sh_size; //节区大小
sh_link; //给出字节头部表索引链接
sh_info; //给出节区附加信息
sh_addralign; //对齐约束
sh_entsize; //给出对于某些有固定项目的大小,如符号表
2. elf的内存加载
实际上,编译后的elf文件的加载在不同平台上的情况是不一样的。
- 对于含有文件系统和动态加载功能操作系统的CPU
elf文件会通过某种方式(FTP或者网络文件系统)被写入到OS的文件系统。
首先先说明一下OS的启动。对该类型CPU来说,其操作系统kernel已经存储在硬盘中了,上电后bootloader会对CPU进行初始化,随后就会将操作系统镜像加载到SRAM特定的地址,随后CPU整个的控制权全部移交给kernel。
OS接管系统后,会进一步初始化并挂载文件系统,此时kernel便可加载文件系统中的elf文件。kernel首先将该文件加载到CPU中的SRAM,完成相应的初始化后将控制权交给应用程序。
- 对于简单操作系统和无操作系统的CPU
该类型的CPU由于不能提供加载功能,因此在生成elf文件后需要使用二进制处理工具objcopy将elf文件转换成bin文件,然后通过相应的烧写工具将该文件写入flash中。
若有操作系统,则需要将目标程序和OS并在一起编译和链接,目标程序只作为操作系统的一个内部函数调用。此情况下,操作系统的启动一般不需要单独的bootloader去加载,在上电完成初始化后就直接跳转到操作系统的代码。
- 对于静态链接的内存加载:
对于以静态链接形式编译的程序,由于其已经是一个完整的不依赖于任何其他东西的可执行程序,可以直接被执行。 - 对于动态链接的内存加载工作步骤:
loader程序将elf文件读入内存,然后启动链接器程序根据elf文件头的说明,将其所需要的其他程序段找到读入内存并装配在一起(相似段融合,然后重新编排符号表和重定位表,并根据重定位表来做重定位)。装配完后返回loader程序继续执行,然后loader直接跳转到装配完成的程序中的入口地址处执行。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/82504.html