__attribute__((section("section_name")))其作用是将作用的函数或数据放入指定名为"section_name"输入段。
这里还要注意一下两个概念:输入段和输出段
输入段和输出段是相对于要生成最终的elf或binary时的Link过程说的,Link过程的输入大都是由源代码编绎生成的目标文件.o,那么这些.o文件中包含的段相对link过程来说就是输入段,而Link的输出一般是可执行文件elf或库等,这些输出文件中也包含有段,这些输出文件中的段就叫做输出段。输入段和输出段本来没有什么必然的联系,是互相独立,只是在Link过程中,Link程序会根据一定的规则(这些规则其实来源于Link Script),将不同的输入段重新组合到不同的输出段中,即使是段的名字,输入段和输出段可以完全不同。
我们今天会讲的是section的一种用法。这种用法是,将一些函数或者自定义格式的变量放在自己指定的段中(section),然后在程序中获取出来使用。
这种用法是实例有Linux内核驱动module_init一下就好了的原因就是把程序放在了指定的section里面,到指定的地方取出来用就行了;还有就是有些程序架构里的初始化话,也是将要初始化的函数放当指定的section里面,在初始化时逐一取出来调用就可以了。这样做就是当新添加和删除模块的初始化的时候,不用动main函数。把对应的函数新添加或取消添加进指定的section就行了。
废话一堆,一句没听懂。不如实操一下。我是在这里学到的,谢谢博主
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void want_money(void)
{
printf ("i want a lots of money, but...\n");
}
void want_power(void)
{
printf ("i want power to change the world, but...\n");
}
void want_woman(void)
{
printf ("i want a lots of women,but...\n");
}
typedef void (* init_func_yp)(void);
#define _my_section __attribute__((unused, section(".myinit")))
#define PUT_INTO_SECTION(func) init_func_yp _fn_##func _my_section = func
/* 定义3个变量,然后放在myinit section中 */
PUT_INTO_SECTION(want_money);
PUT_INTO_SECTION(want_power);
PUT_INTO_SECTION(want_woman);
/* 在链接脚本中定义 */
extern init_func_yp __init_start;
extern init_func_yp __init_end;
int main(int argc, char** argv)
{
init_func_yp* tmp = &__init_start;
for ( ; tmp < &__init_end; tmp++){
printf("init address: %p\n", tmp);
(*tmp)();
}
return 0;
}
具体操作如下:
1)准备连接脚本xxxx.lds
ld --verbose > script.lds
vi script.lds
1)删除script.lds中下面内容,==============也需要删除
GNU ld (GNU Binutils for Ubuntu) 2.30
Supported emulations:
elf_x86_64
elf32_x86_64
elf_i386
elf_iamcu
i386linux
elf_l1om
elf_k1om
i386pep
i386pe
using internal linker script:
==================================================
==================================================
2)增加script.lds里面的我们自定义的内容,加在__bss_start =.;上面。所以下面只需要添加前3行。
__init_start = .;
.myinit : {*(.myinit)}
__init_end = .;
__bss_start = .;
3)保存
2)编译开始
gcc -c example.c -o example.o
编译出来的example.o就有一个 myinit的段
3)连接一下
gcc -T script.lds example.o -o example
4)运行一下
就这么愉快的成功了。
师傅领进门,修为看个人,操作了一下后 可以重新理解一下输入段和输出段这个概念。
二)补充一个骚操作
就是利用c++类的构造函数,再配合__attribute__((init_priority(_pri_)))一起来实现自动初始化的目的。骚的不行。
#define CYGBLD_ATTRIB_INIT_PRI( _pri_ ) __attribute__((init_priority(_pri_)))
#define CYGBLD_ATTRIB_INIT_AFTER( _pri_ ) CYGBLD_ATTRIB_INIT_PRI(_pri_+100)
#define NET_INIT CYGBLD_ATTRIB_INIT_AFTER(CYG_INIT_LIBC)
void
cyg_net_init(void)
{
static int _init = false;
cyg_netdevtab_entry_t *t;
if (_init) return;
cyg_do_net_init(); // Just forces linking in the initializer/constructor
// Initialize interrupt "flags"
cyg_flag_init(&netint_flags);
// Done
_init = true;
}
void cyg_net_init(void);
class net_init_class {
public:
net_init_class(void) {
cyg_net_init();
}
};
// And here's an instance of the class just to make the code run
static net_init_class _net_init NET_INIT;
GNU C ++允许用户控制对象初始化的顺序 在命名空间范围中使用`init_priority'属性定义 指定相对PRIORITY,一个常数整数表达式目前约在101和65535之间。较低的数字表示较高的优先级。