添加自定义的section

一、基本知识点

编译出来的程序(o so exe ko等等)都是以elf格式进行排列保存的

elf文件分析情况:https://blog.csdn.net/edonlii/article/details/8779075

二、系统连接器会自带许多section,当然也可以添加自定义的section

2.1、以下命令可以查看连接器默认的section名

ld --verbose > section.lds

2.2、如何实现自定义的section

2.2.1、使用__section__函数实现自定义

__attribute__((used, __section__("xxxx")))

自定义一个结构体

#define SECTION_START(_type,_name)			\
static const struct mydyh_desc __mydyh_desc_##_type	\
	__attribute__((used, __section__(".dyh.info.init"))) = {	\
		.nr		= MY_DYH_##_type,		\
		.name	= _name,
		
#define SECTION_END						\
};

2.2.2、在链接脚本中添加section名和起始结束位置

 2.2.3、怎样才能获取到section里面的数据?

通过nm命令查看可执行程序的符号表,发现存在__dyh_info_begin,__dyh_info_end符号

nm exe_name | grep __dyh_info

因此,可以通过extern方式,获取符号地址

extern struct mydyh_desc __dyh_info_begin[], __dyh_info_end[];

2.3、编译代码

gcc section.c -T section.lds -o section

demo程序:https://github.com/dyh-git/section_custom

三、有什么用途?

假如在main函数中需要实现许多初始化功能,每当添加一个初始化功能,就需要把初始化函数添加到main函数中,然后重新编译。往往初始化函数的入参和返回值都一致。

如果使用自定义section模式,不需要把初始化函数添加到main函数中,遍历__dyh_info_begin到 __dyh_info_end地址就可以实现,详见demo程序。内核在module_init加载驱动时就是采用的这种方式。

四、module_init函数分析

4.1、module_init调用流程

#define module_init(x)	__initcall(x);

#define __initcall(fn) __define_initcall("1", fn)

#define __define_initcall(level,fn) \
        static initcall_t __initcall_##fn __used \
        __attribute__((__section__(".initcall" level ".init"))) = fn

可以看到,最终会调用__section__函数创建.initcall的section

4.2、查看linux链接脚本vmlinux.lds可以找到上面定义的section

 4.3、执行流程,do_initcall_level遍历的数组initcall_levels对应的就是section的起始位置

static initcall_t *initcall_levels[] __initdata = {
        __initcall0_start,
        __initcall1_start,
        __initcall2_start,
        __initcall3_start,
        __initcall4_start,
        __initcall5_start,
        __initcall6_start,
        __initcall7_start,
        __initcall_end,
};

int __init_or_module do_one_initcall(initcall_t fn)
{
        int ret;

        if (initcall_debug)
                ret = do_one_initcall_debug(fn);
        else
                ret = fn();

        return ret;
}

static void __init do_initcall_level(int level)
{
        initcall_t *fn;

        for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
                do_one_initcall(*fn);
}

static void __init do_initcalls(void)
{
        int level;

        for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
                do_initcall_level(level);
}

参考书籍:《程序员的自我修养—链接、装载与库》

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值