__attribute__((section(X)))

利用__attribute__((section(“name”)))构建初始化函数表

之前在linux内核代码中经常看到函数导出的语句,在阅读rt-thread的代码时,也看到了,因为rt-thread的代码更小,且有keil项目进行参考,所以写了一下记录,望以后的项目中可以借鉴

typedef int (*init_fn_t)(void);

#define INIT_EXPORT(fn, level)										\
            __attribute__((used)) const init_fn_t __lig_init_##fn	\
			__attribute__((section(".lig_init_fn." level))) = fn
/*展开例子:*/
static int start(void)
{
	printf("%s\n",__FUNCTION__);
	return 0;
}
INIT_EXPORT(start,"0");
//==>
__attribute__((used)) const init_fn_t __lig_init_start __attribute__((section(".lig_init_fn.0"))) = start;

这个宏可以将函数注册到一个固定的段,在调用时,获取这个段的起始,然后通过指针偏移进行访问,常用在初始化,或者较为灵活的模块加入

这个宏的作用是定义一个函数类型变量__rt_init_xxx=xxx,然后指定__rt_init_xxx这个变量存放于(.rti_fn.level)段中,在keil中,编译器根据段的名称,自动处理了程序中的各个数据段的存储的排序,在此不用指定这个段的起始位置;

以下对这个宏的各个部分进行详细说明

  • __attribute__((used)) : 标记这个变量被使用,因为在程序中,这个变量可能以指针遍历索引的形式被访问,而不是显式的被调用,这时如果不标记为used,可能会被编译器认为没有使用此变量而优化掉
  • const init_fn_t __rt_init_##fn: 定义一个函数指针变量,这个变量的名称是__rt_init_##fn,其中##起到字符拼接的功能
  • __attribute__((section(x))): 将变量放置到程序文件的x段

初始化函数表例子:

typedef int (*init_fn_t)(void);


#define INIT_EXPORT(fn, level)										\
            __attribute__((used)) const init_fn_t __lig_init_##fn	\
				__attribute__((section(".lig_init_fn." level))) = fn

static int start(void)
{
	printf("%s\n",__FUNCTION__);
	return 0;
}
INIT_EXPORT(start,"0");

static int end(void)
{
	printf("%s\n",__FUNCTION__);
	return 0;
}
INIT_EXPORT(end,"1.end");

static int init0(void)
{
	printf("%s\n",__FUNCTION__);
	return 0;
}
INIT_EXPORT(init0,"1");

static int init1(void)
{
	printf("%s\n",__FUNCTION__);
	return 0;
}
INIT_EXPORT(init1,"1");

static int init2(void)
{
	printf("%s\n",__FUNCTION__);
	return 0;
}
INIT_EXPORT(init2,"1");

static int init3(void)
{
	printf("%s\n",__FUNCTION__);
	return 0;
}
INIT_EXPORT(init3,"1");

static int init4(void)
{
	printf("%s\n",__FUNCTION__);
	return 0;
}
INIT_EXPORT(init4,"1");

int main(void)
{
	volatile const init_fn_t *fn_ptr;
    for (fn_ptr = &__lig_init_start; fn_ptr < &__lig_init_end; fn_ptr++)
    {
        (*fn_ptr)();
    }
    while(1);
}

在keil中编译后,在生成的map文件中,可以看到注册后的函数的存储地址

    __lig_init_start                         0x1a0011e0   Data           4  main.o(.lig_init_fn.0)
    __lig_init_init0                         0x1a0011e4   Data           4  main.o(.lig_init_fn.1)
    __lig_init_init1                         0x1a0011e8   Data           4  main.o(.lig_init_fn.1)
    __lig_init_init2                         0x1a0011ec   Data           4  main.o(.lig_init_fn.1)
    __lig_init_init3                         0x1a0011f0   Data           4  main.o(.lig_init_fn.1)
    __lig_init_init4                         0x1a0011f4   Data           4  main.o(.lig_init_fn.1)
    __lig_init_end                           0x1a0011f8   Data           4  main.o(.lig_init_fn.1.end)

打印数据

start
init0
init1
init2
init3
init4/*没有end*/

注意: 利用段的名称使start和end排序正常,这样不管代码中的顺序是怎样的,编译后都能生成有序的排列;除了start和end外,注册的函数应该是顺序无关的,否则应该对段做更多的分级,以区分顺序,如.lig_init_fn.1,.lig_init_fn.2,.lig_init_fn.3

还有一种用法

typedef int (*lig_app_fn_t)(void);
#define APP_TAB_EXPORT(fn)										\
            __attribute__((used)) const lig_app_fn_t __lig_app_##fn	\
				__attribute__((section("Lig_app_tab"))) = fn

extern const int Lig_app_tab$$Base;
extern const int Lig_app_tab$$Limit;

static int app1(void)
{
	printf("%s\r\n",__FUNCTION__);
	return 0;
}
APP_TAB_EXPORT(app1);

static int app2(void)
{
	printf("%s\r\n",__FUNCTION__);
	return 0;
}
APP_TAB_EXPORT(app2);

static int app3(void)
{
	printf("%s\r\n",__FUNCTION__);
	return 0;
}
APP_TAB_EXPORT(app3);


int main(void)
{
	volatile const lig_app_fn_t * appfn_ptr;
    for (appfn_ptr = (lig_app_fn_t *)&Lig_app_tab$$Base; appfn_ptr < (lig_app_fn_t *)&Lig_app_tab$$Limit; appfn_ptr++)
    {
        (*appfn_ptr)();
    }
    while (1) {
		lastime = SysTickCnt10Ms;
	}
}

map:

    Lig_app_tab$$Base                        0x1a001230   Number         0  main.o(Lig_app_tab)
    __lig_app_app1                           0x1a001230   Data           4  main.o(Lig_app_tab)
    __lig_app_app2                           0x1a001234   Data           4  main.o(Lig_app_tab)
    __lig_app_app3                           0x1a001238   Data           4  main.o(Lig_app_tab)
    Lig_app_tab$$Limit                       0x1a00123c   Number         0  main.o(Lig_app_tab)

打印数据:

app1
app2
app3

注意:从map中可以看出Lig_app_tab$$Base的位置和__lig_app_app1相同,是一个段的起始,而Lig_app_tab$$Limit__lig_app_app3不同,是一个段的结束;
这种$$Base,$$Limit用法好像不能对.xxx的段使用,编译不过去,不知道为什么,得找找相关文档看看

  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值