一. 链接
1.1. 每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局. 但你也可以用连接命令做一些其他事情.连接器有个默认的内置连接脚本, 可用ld –verbose查看. 连接选项-r和-N可以影响默认的连接脚本(如何影响?).-T选项用以指定自己的链接脚本, 它将代替默认的连接脚本
1.2. 示例:
以下脚本将输出文件的text section定位在0×10000, data section定位在0×8000000:
SECTIONS { .= 0×10000; .text : { *(.text) } .= 0×8000000; .data : { *(.data) } .bss : { *(.bss) } }
解释一下上述的例子:
. = 0×10000 : 把定位器符号置为0×10000 (若不指定, 则该符号的初始值为0).
.text : { *(.text) } : 将所有(*符号代表任意输入文件)输入文件的.text section合并成一个.text section, 该section的地址由定位器符号的值指定, 即0×10000.
. = 0×8000000 :把定位器符号置为0×8000000
.data : { *(.data) } : 将所有输入文件的.data section合并成一个.data section, 该section的地址被置为0×8000000.
.bss : { *(.bss) } : 将所有输入文件的.bss section合并成一个.bss section,该section的地址被置为0×8000000+.data section的大小.
连接器每读完一个section描述后, 将定位器符号的值*增加*该section的大小. 注意: 此处没有考虑对齐约束.
二. section构建初始化函数表
2.1. 和传统初始化对比
2.1.1. 传统的应用编写时,每添加一个模块,都需要在main中添加新模块的初始化
2.1.2. 使用__attribute__((section()))构建初始化函数表后,由模块告知main:“我要初始化“,添加新模块再也不需要在main代码中显式调用模块初始化接口
2.1.3. 内核module_init实现是构建初始化函数表
2.2. 链接文件
2.2.1. gcc默认链接文件
1. ld --verbose:打印出默认的链接脚本
2.2.2. 自定义链接文件
2.3. readelf
2.3.1. 查看section headers
2.3.2. 查看符合(-s(小写))
三. 实战
3.1. 源码
#include <stdio.h> #include <string.h> typedef struct static_cmd_function_struct { const char *cmd; void (*fp)(void); char *description; }__attribute__ ((aligned (16))) static_cmd_st; #define NR_USED __attribute__((used)) #define NR_SECTION(x) __attribute__((section(".rodata_nr_shell_cmd" x))) #define NR_SHELL_CMD_EXPORT_START(cmd, func) \ NR_USED const static_cmd_st _nr_cmd_start_ NR_SECTION("_start") = {#cmd, NULL} #define NR_SHELL_CMD_EXPORT(cmd, func) \ NR_USED const static_cmd_st _nr_cmd_##cmd NR_SECTION("") = {#cmd, func} #define NR_SHELL_CMD_EXPORT_END(cmd, func) \ NR_USED const static_cmd_st _nr_cmd_end_ NR_SECTION("_end") = {#cmd, NULL} NR_SHELL_CMD_EXPORT_START(start,NULL); NR_SHELL_CMD_EXPORT_END(end,NULL); static void func1(void) { printf("call %s\n", __FUNCTION__); } NR_SHELL_CMD_EXPORT(ls, func1); static void func2(void) { printf("call %s\n", __FUNCTION__); } NR_SHELL_CMD_EXPORT(ls2, func2); /*main.c*/ int main(int argc, char **argv) { const static_cmd_st *p0=&_nr_cmd_start_, *p1=&_nr_cmd_ls, *p2=&_nr_cmd_ls2,*p3=&_nr_cmd_end_; printf("sizeof=0x%lx\r\n",sizeof(static_cmd_st)); printf("addr:p0 %p\r\n",p0); printf("addr:p1 %p\r\n",p1); printf("addr:p2 %p\r\n",p2); printf("addr:p3 %p\r\n",p3); for(const static_cmd_st *p=&_nr_cmd_start_+1; p<&_nr_cmd_end_; p++) p->fp(); return 0; }