利用section构建初始化函数表
一. 链接
1.1. 每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局. 但你也可以用连接命令做一些其他事情.连接器有个默认的内置连接脚本, 可用ld –verbose查看. 连接选项-r和-N可以影响默认的连接脚本(如何影响?).-T选项用以指定自己的链接脚本, 它将代替默认的连接脚本
1.2. 示例:
SECTIONS { .= 0×10000; .text : { *(.text) } .= 0×8000000; .data : { *(.data) } .bss : { *(.bss) } }
二. 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; }
/*script.lds*/ SECTIONS { . = ALIGN(16); .rodata_nr_shell_cmd_start : { *(.rodata_nr_shell_cmd_start) } .rodata_nr_shell_cmd : { *(.rodata_nr_shell_cmd) } .rodata_nr_shell_cmd_end : { *(.rodata_nr_shell_cmd_end) } } INSERT AFTER .rodata
CC := gcc DGBFLAG := -g -T script.lds SRC := main.c TARGET_I := main.i TARGET := main .PHONY: all all: $(CC) $(DGBFLAG) $(SRC) -E -o $(TARGET_I) $(CC) $(DGBFLAG) $(SRC) -o $(TARGET) clean: @rm -f $(TARGET) $(TARGET_I)
3.2. 测试
PS: 如果结构体不是16字节对齐的话,运行结果会出错,结果如下
所以需要16/32/6字节对齐即可。
参考文档:https://blog.csdn.net/qq_41822235/article/details/81272999
参考文档:https://www.cnblogs.com/sky-heaven/p/8275303.html
参考文档:https://blog.csdn.net/weixin_37571125/article/details/78665184