attribute的section属性

官方文档

Normally, the compiler places the objects it generates in sectionslike data and bss. Sometimes, however, you need additional sections,or you need certain particular variables to appear in specialsections, for example to map to special hardware. The section attribute specifies that a variable (or function) lives in a particular section.

通常,编译器将它生成的对象放在像.data和.bss段中。但是有时需要自己定义的部分,或者需要某些特定的变量出现在特殊的部分中,例如映射到特殊的硬件。section属性指定一个变量(或函数)位于特定的section段中。

语法

_attribute_((section(name)))

作用

常用于初始化模块中,添加新的模块不需要修改之前的代码。

传统做法:

如果我需要在主函数的初始化中添加新功能C的初始化,那么我必然需要修改主函数。
在这里插入图片描述

使用__attribute__((section()))构建初始化函数表后的做法:

在这里插入图片描述

那么我们只需要完成功能C的初始化,然后添加到对应的初始化表中。main函数中初始化函数表的时候,就会自动完成模块的初始化。
那么可能有人问了,第一种明显简单易懂为啥需要使用__attribute__呢?
如果程序比较简单全部是自己写的自然可以使用传统方法,但是如果需要去耦合或者接触到linux编程时会发现有很多__attribute__ section的用法。

举个栗子

练习一下使用__attribute__ section来初始化函数。
环境:ubuntu18.04
gcc:gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
代码来源:
廖威雄 Linux阅码场

例子源码

#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
typedef void (*init_call)(void);
/*
 * These two variables are defined in link script.
 */
extern init_call _init_start;//使用外部变量这个变量会在连接脚本中被定义
extern init_call _init_end;  //使用外部变量这个变量会在连接脚本中被定义
#define _init __attribute__((unused, section(".myinit")))
#define DECLARE_INIT(func) init_call _fn_##func _init = func
static void A_init(void)
{
    write(1, "A_init\n", sizeof("A_init\n"));
}
DECLARE_INIT(A_init);
static void B_init(void)
{
    printf("B_init\n");
}
DECLARE_INIT(B_init);
static void C_init(void)
{
    printf("C_init\n");
}
DECLARE_INIT(C_init);
/*
 * DECLARE_INIT like below:
 *  static init_call _fn_A_init __attribute__((unused, section(".myinit"))) = A_init;
 *  static init_call _fn_C_init __attribute__((unused, section(".myinit"))) = C_init;
 *  static init_call _fn_B_init __attribute__((unused, section(".myinit"))) = B_init;
 */
void do_initcalls(void)

{
    init_call *init_ptr = &_init_start;
    for (; init_ptr < &_init_end; init_ptr++) {
        printf("init address: %p\n", init_ptr);
        (*init_ptr)();//初始化_init_start到_init_end段之间所有函数
    }
}

int main(void)
{
    do_initcalls();
    return 0;
}

命名程序为section.c
实现的功能:

  1. 定义DECLARE_INIT(func)宏,使用gcc编译器的__attribute__((section()))将初始化代码定义到myinit段
  2. 使用DECLARE_INIT(func)初始化A_init/B_init/C_init三个函数
  3. 集成外部变量extern init_call _init_start;extern init_call _init_end;,这两个变量会在编译脚本中定义,
  4. 在main函数中

修改连接脚本

  • 获取默认连接脚本ld --verbose
    可以得到如下所示代码和图片:
    在这里插入图片描述
 - GNU ld (GNU Binutils for Ubuntu) 2.30
  支持的仿真:
   elf_x86_64
   elf32_x86_64
   elf_i386
   elf_iamcu
   i386linux
   elf_l1om
   elf_k1om
   i386pep
   i386pe
使用内部链接脚本:
==================================================
.......(需要复制的内容)
==================================================

复制“==================================================”之间的内容,然后使用
vim ldscript.lds自己创建一个连接脚本,粘贴刚刚复制的内容

在.bss段之前添加

_init_start = .;

.myinit : { *(.myinit) }

_init_end = .;

如下图所示
在这里插入图片描述

编译

使用命令:gcc -c section.c -o section.o便于.c文件
查看短信息:readelf -S section.o

在这里插入图片描述

链接

执行:gcc -T ldscript.lds section.o -o section使用刚刚编辑过的链接脚本,生成可执行文件
执行:readelf -S section查看可执行文件段分布
在这里插入图片描述

执行结果

使用命令./section执行可执行文件
在这里插入图片描述

参考

廖威雄 Linux阅码场

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柒妖71

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值