原文地址:__attribute__之section详解

前言

第一次接触 “section” 是在公司的一个STM32的项目代码中,前工程师将所有的初始化函数都使用的“section”进行设定了属性。当时知道其目的,但是不知道原因。然后到后来在接触了Linux的驱动程序的时候,发现linux的驱动注册的宏定义层层解析以后,也是使用的“section”进行修饰,但是当时看教程以为必须限定到内存的特定位置中,以及经验不足,所以没有深究。然现在在写Linux应用程序的的时候,发现在SDK中也有使用“section”进行一类程序的修饰,然后我就专门花了几个小时时间去查阅各种论坛,进行了一次算还算比较深入的学习吧。

使用section可以使我们如在初始化函数时,不用在主函数中去添加一个新的初始化程序,只需要在自己的函数模块内注册就好了。或者在实现某些命令时,添加或删除该命令的支持,会方便很多。

使用方法

"section"关键字会将被修饰的变量或函数编译到特定的一块位置,不是物理存储器上的特定位置,而是在可执行文件的特定段内。在编译好的程序中我们可以使用命令:

seven@root:~/section/$ readelf -S a.out
There are 37 section headers, starting at offset 0x201c: Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 080481ac 0001ac 00002c 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481d8 0001d8 000050 10 A 6 1 4
[ 6] .dynstr STRTAB 08048228 000228 00004c 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048274 000274 00000a 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 08048280 000280 000020 00 A 6 1 4
[ 9] .rel.dyn REL 080482a0 0002a0 000018 08 A 5 0 4
[10] .init PROGBITS 080482b8 0002b8 000023 00 AX 0 0 4
[11] .plt PROGBITS 080482e0 0002e0 000010 04 AX 0 0 16
[12] .plt.got PROGBITS 080482f0 0002f0 000018 00 AX 0 0 8
[13] .text PROGBITS 08048310 000310 000212 00 AX 0 0 16
[14] .fini PROGBITS 08048524 000524 000014 00 AX 0 0 4
[15] .rodata PROGBITS 08048538 000538 00007f 00 A 0 0 4
[16] .eh_frame_hdr PROGBITS 080485b8 0005b8 000044 00 A 0 0 4
[17] .eh_frame PROGBITS 080485fc 0005fc 00012c 00 A 0 0 4
[18] .init_array INIT_ARRAY 08049f08 000f08 000004 00 WA 0 0 4
[19] .fini_array FINI_ARRAY 08049f0c 000f0c 000004 00 WA 0 0 4
[20] .jcr PROGBITS 08049f10 000f10 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 08049f14 000f14 0000e0 08 WA 6 0 4
[22] .got PROGBITS 08049ff4 000ff4 00000c 04 WA 0 0 4
[23] .got.plt PROGBITS 0804a000 001000 00000c 04 WA 0 0 4
[24] .data PROGBITS 0804a00c 00100c 000008 00 WA 0 0 4
[25] .application_init PROGBITS 0804a014 001014 00000c 00 WA 0 0 4
[26] .bss NOBITS 0804a020 001020 000004 00 WA 0 0 1
[27] .comment PROGBITS 00000000 001020 000035 01 MS 0 0 1
[28] .debug_aranges PROGBITS 00000000 001055 000020 00 0 0 1
[29] .debug_info PROGBITS 00000000 001075 000200 00 0 0 1
[30] .debug_abbrev PROGBITS 00000000 001275 0000e3 00 0 0 1
[31] .debug_line PROGBITS 00000000 001358 000079 00 0 0 1
[32] .debug_str PROGBITS 00000000 0013d1 0001c9 01 MS 0 0 1
[33] .debug_loc PROGBITS 00000000 00159a 0000f1 00 0 0 1
[34] .shstrtab STRTAB 00000000 001ebd 00015e 00 0 0 1
[35] .symtab SYMTAB 00000000 00168c 000560 10 36 59 4
[36] .strtab STRTAB 00000000 001bec 0002d1 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)

如上,可以看到程序被分成了很多的段,其中“.application_init”为稍后步骤中自定义的一个段。

测试源代码如下:

#include <stdio.h>
#include <string.h> struct _s_application_init {
int(*function)(void);
}; struct _s_application_init _init_start;//段".application_init"的起始地址,在*.lds文件中定义
struct _s_application_init _init_end;//段".application_init"的末尾地址,在*.lds文件中定义
#define __app_init_section __attribute__((section(".application_init")))
#define __application_init(function) \
struct _s_application_init _s_a_init_##function __app_init_section = {function} static int application_init_a(void)
{
printf("execute funtion : %s\n", __FUNCTION__);
return 0;
}
__application_init(application_init_a); static int application_init_b(void)
{
printf("execute funtion : %s\n", __FUNCTION__);
return 0;
}
__application_init(application_init_b); static int application_init_c(void)
{
printf("execute funtion : %s\n", __FUNCTION__);
return 0;
}
__application_init(application_init_c); int main(int argc, char **argv)
{
/*
* 从段的起始地址开始获取数据,直到末尾地址
*/
struct _s_application_init *pf_init = &_init_start;
do {
printf("Load init function from address %p\n", pf_init);
pf_init->function();
++pf_init;
} while (pf_init < &_init_end);
return 0;
}

然后我们还需要编写lds文件,首先使用命令生成默认的文件:

seven@root:~/section/$ ld --verbose > main.lds
seven@root:~/section/$ cat main.lds /* Script for -z combreloc: combine and sort reloc sections */
/* Copyright (C) 2014-2015 Free Software Foundation, Inc.
Copying and distribution of this script, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. */
OUTPUT_FORMAT("elf32-i386", "elf32-i386",
"elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib/i386-linux-gnu"); SEARCH_DIR("=/lib/i386-linux-gnu"); SEARCH_DIR("=/usr/lib/i386-linux-gnu"); SEARCH_DIR("=/usr/local/lib32"); SEARCH_DIR("=/lib32"); SEARCH_DIR("=/usr/lib32"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/i686-linux-gnu/lib32"); SEARCH_DIR("=/usr/i686-linux-gnu/lib");
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000)); . = SEGMENT_START("text-segment", 0x08048000) + SIZEOF_HEADERS;
.interp : { *(.interp) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.dyn :
{
*(.rel.init)
*(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
*(.rel.fini)
*(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
*(.rel.data.rel.ro .rel.data.rel.ro.* .rel.gnu.linkonce.d.rel.ro.*)
*(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
*(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
*(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
*(.rel.ctors)
*(.rel.dtors)
*(.rel.got)
*(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
*(.rel.ifunc)
}
.rel.plt :
{
*(.rel.plt)
PROVIDE_HIDDEN (__rel_iplt_start = .);
*(.rel.iplt)
PROVIDE_HIDDEN (__rel_iplt_end = .);
}
.init :
{
KEEP (*(SORT_NONE(.init)))
}
.plt : { *(.plt) *(.iplt) }
.plt.got : { *(.plt.got) }
.text :
{
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
}
.fini :
{
KEEP (*(SORT_NONE(.fini)))
}
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table
.gcc_except_table.*) }
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) }
/* These sections are generated by the Sun/Oracle C++ compiler. */
.exception_ranges : ONLY_IF_RO { *(.exception_ranges
.exception_ranges*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gnu_extab : ONLY_IF_RW { *(.gnu_extab) }
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
}
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
.dynamic : { *(.dynamic) }
.got : { *(.got) *(.igot) }
. = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 12 ? 12 : 0, .);
.got.plt : { *(.got.plt) *(.igot.plt) }
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
. = .;
__bss_start = .;
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 32 / 8 : 1);
}
. = ALIGN(32 / 8);
. = SEGMENT_START("ldata-segment", .);
. = ALIGN(32 / 8);
_end = .; PROVIDE (end = .);
. = DATA_SEGMENT_END (.);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
/* DWARF Extension. */
.debug_macro 0 : { *(.debug_macro) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}

然后我们修改这个文件:

首先我们需要将默认文件的首尾“==================================================”包含这一行要删除,不然会报格式错误

/usr/bin/ld:test.lds:1: syntax error

collect2: error: ld returned 1 exit status

然后选择在“__bss_start”前添加我们自己的段

  ...

  . = .;

  _init_start = .;/* 获取当前的地址赋值给__init_start,在源码中有使用到,指向“.application_init”段的起始地址 */
.application_init : { *(.application_init) }/* 将“.application_init”的所有内容放在这一段 */
_init_end = .;/* 获取当前的地址赋值给__init_end,表示“.application_init”段的结束地址 */ __bss_start = .; ...

然后我们在链接的时候使用一下命令:

seven@root:~/section/$ gcc main.c -Tmain.lds

如无意外的情况下,即可编译出最原始的“a.out”,如果出现错误,请烧香拜佛。

seven@root:~/section$ ./a.out
Load init function from address 0x804a014
execute funtion : application_init_a
Load init function from address 0x804a018
execute funtion : application_init_b
Load init function from address 0x804a01c
execute funtion : application_init_c

可以看到依次的执行了三个初始化函数。

注:

  1. 初始化的宏放置的位置,直接影响初始化函数执行的顺序,同一文件中行号越小越优先。(是注册的顺序,不是函数的位置)
  2. 多文件中的注册,应该也会存在一定规律,如果初始化顺序有特定的先后顺序的,如需要先初始化GPIO,再初始化LED灯等,则可以采用其他方法进行顺序限定(如优先级值和链表)。
  3. 在参考的文档中,段的地址貌似可以指定,但是我没有尝试。

参考链接

(转)__attribute__之section 分析详解的更多相关文章

  1. Memcache的使用和协议分析详解

    Memcache的使用和协议分析详解 作者:heiyeluren博客:http://blog.csdn.NET/heiyeshuwu时间:2006-11-12关键字:PHP Memcache Linu ...

  2. wav文件格式分析详解

    wav文件格式分析详解 文章转载自:http://blog.csdn.net/BlueSoal/article/details/932395 一.综述    WAVE文件作为多媒体中使用的声波文件格式 ...

  3. 线程组ThreadGroup分析详解 多线程中篇(三)

    线程组,顾名思义,就是线程的组,逻辑类似项目组,用于管理项目成员,线程组就是用来管理线程. 每个线程都会有一个线程组,如果没有设置将会有些默认的初始化设置 而在java中线程组则是使用类ThreadG ...

  4. HanLP中人名识别分析详解

    HanLP中人名识别分析详解 在看源码之前,先看几遍论文<基于角色标注的中国人名自动识别研究> 关于命名识别的一些问题,可参考下列一些issue: l ·名字识别的问题 #387 l ·机 ...

  5. html5中section元素详解

    html5中section元素详解 一.总结 一句话总结: section元素 用来定义文章中的章节(通常应该有标题和段落内容) section元素的作用就是给内容分段,给页面分区 1.section ...

  6. GC日志分析详解

    点击返回上层目录 原创声明:作者:Arnold.zhao 博客园地址:https://www.cnblogs.com/zh94 GC日志分析详解 以ParallelGC为例,YoungGC日志解释如下 ...

  7. HashMap实现原理分析(详解)

    1. HashMap的数据结构 http://blog.csdn.net/gaopu12345/article/details/50831631   ??看一下 数据结构中有数组和链表来实现对数据的存 ...

  8. MongoDB执行计划分析详解

    要保证数据库处于高效.稳定的状态,除了良好的硬件基础.高效高可用的数据库架构.贴合业务的数据模型之外,高效的查询语句也是不可少的.那么,如何查看并判断我们的执行计划呢?我们今天就来谈论下MongoDB ...

  9. 15.linux-LCD层次分析(详解)

    如果我们的系统要用GUI(图形界面接口),这时LCD设备驱动程序就应该编写成frambuffer接口,而不是像之前那样只编写操作底层的LCD控制器接口. 什么是frambuffer设备? frambu ...

随机推荐

  1. selenium的定位方法-多元素定位

    在实际工作中,有些时候定位元素使用ID.NAME.CLASS_NMAE.XPATH等方法无法定位到具体元素,会发现元素属性有很多一致的,这个时候使用单元素定位方法无法准确定位到具体元素,例如,百度首页 ...

  2. Java:不得不知的Object类

    目录 一.equals 1.equals与==有啥区别? 2.equals方法的规范 3.instanceof 和getClass() 4.其他总结 二.hashCode 1.hashCode的规范 ...

  3. python正则匹配次数,贪婪和非贪婪

    贪婪模式  {m,n}表示匹配子串的次数>=m and <=n,再此分为内匹配次数尽可能的多 贪婪模式  {,n}表示 >=0 and <=n 贪婪模式  {m,} 表示> ...

  4. Linux Centos7文件系统

    上期教大家创建分区,刚分区完成后没有文件系统,分区不能使用.本期教大家创建文件系统,(文件系统:操作系统通过文件系统管理文件及数据,创建文件系统的过程俗称格式化.)没有文件系统的设备称之为裸(raw) ...

  5. ES+VBA 实现批量添加网络图片

    需求:通过自动读取相对应列的图片网址,自动添加到图片列,从而完成添加图片 案例:需要将备注列的图片网址添加到图片列的内容 关键代码 '引入API Private Declare Function UR ...

  6. Java-POJ1000-A+B Problem

  7. 计算几何-RC-poj2187

    This article is made by Jason-Cow.Welcome to reprint.But please post the article's address. 今天学习一下旋( ...

  8. EF Code Frist 配置

    通过Nuget安装Entity Framework, 创建model;[Table("")] 代表在数据库对应的名称,可以自定义规则,[Key]代表是主键 [Table(" ...

  9. MSYS2与mingw32和mingw64的安装

    由于编译OpenBLAS接触到MSYS2. 下载MSYS:https://mirror.tuna.tsinghua.edu.cn/help/msys2/ 安装,并按照下面的配置,然后可以安装mingw ...

  10. 2019冬季PAT甲级第四题

    #define HAVE_STRUCT_TIMESPEC #include<bits/stdc++.h> using namespace std; typedef struct node{ ...