【问题标题】:How do you get the start and end addresses of a custom ELF section?如何获取自定义 ELF 部分的开始和结束地址?
【发布时间】:2013-05-09 07:07:09
【问题描述】:

我在 Linux 上使用 C 语言工作。我已经看到使用 gcc __section__ attribute(尤其是在 Linux 内核中)将数据(通常是函数指针)收集到自定义 ELF 部分中。如何检索和使用放在这些自定义部分中的“东西”?

【问题讨论】:

标签: c gcc elf


【解决方案1】:

你好:像这样。

extern const struct pseudo_ta_head __start_ta_head_section;
extern const struct pseudo_ta_head __stop_ta_head_section;    

const struct pseudo_ta_head *start = &__start_ta_head_section;
const struct pseudo_ta_head *end = &__stop_ta_head_section;

【讨论】:

    【解决方案2】:

    从各种答案中收集信息,这是一个工作示例,说明如何将信息收集到自定义链接器部分,然后在 C 程序中使用魔术变量 __start_SECTION__stop_SECTION 从该部分读取信息,其中SECTION 是链接映射中部分的名称。

    __start_SECTION__stop_SECTION 变量由链接器提供,因此当从 C 代码中使用这些变量时,需要为这些变量创建显式的 extern 引用。

    如果编译器用于计算指针/数组偏移量的对齐方式与链接器在每个部分中打包的对象的对齐方式不同,也会出现一些问题。一种解决方案(在此示例中使用)是仅在链接器部分中存储指向数据的指针。

    #include <stdio.h>
    
    struct thing {
        int val;
        const char* str;
        int another_val;
    };
    struct thing data1 = {1, "one"};
    struct thing data2 = {2, "two"};
    
    /* The following two pointers will be placed in "my_custom_section".
     * Store pointers (instead of structs) in "my_custom_section" to ensure
     * matching alignment when accessed using iterator in main(). */
    struct thing *p_one __attribute__((section("my_custom_section"))) = &data1; 
    struct thing *p_two __attribute__((section("my_custom_section"))) = &data2;
    
    /* The linker automatically creates these symbols for "my_custom_section". */
    extern struct thing *__start_my_custom_section;
    extern struct thing *__stop_my_custom_section;
    
    int main(void) {
        struct thing **iter = &__start_my_custom_section;
        for ( ; iter < &__stop_my_custom_section; ++iter) {
            printf("Have thing %d: '%s'\n", (*iter)->val, (*iter)->str);
        }
        return 0;
    }
    

    【讨论】:

    • 如果节名不是 C 中的有效变量名怎么办
    • @ToddFreed 你有一个段名在 C 中不是有效变量名的例子吗?
    • ".note.gnu.build-id" 不是有效的变量名。这是 ld 在指定 --build-id 选项时创建的部分,请参阅linux.die.net/man/1/ld
    • 愚蠢的问题:如何让链接器将 Stop 符号放在实际部分的末尾,而不是在 Start 符号之后?
    【解决方案3】:

    所以上面的答案__start_SECTION__stop_SECTION 将起作用,但是为了让程序能够使用来自链接器的信息,您需要将这些变量声明为extern char* __start_SECTION。享受吧!

    extern char * __start_blobby;
    
    ...
    printf("This section starts at %p\n", (unsigned int)&__start_blobby);
    ...
    

    【讨论】:

    • extern &lt;any valid type&gt; __start_SECTION 有效,我看到extern int __start_SECTIONextern char __start_SECTION 用得最多。
    【解决方案4】:

    链接器可以使用代码中定义的符号,并且如果您使用链接器脚本中的确切名称,则可以为其分配初始值:

    _smysection = .;
    *(.mysection)
    *(.mysection*)
    _emysection = .;
    

    只需在 C 代码中定义一个变量:

    const void * _smysection;
    

    然后您可以将其作为常规变量访问。

    u32 someVar = (u32)&_smysection;
    

    【讨论】:

    • 这是我需要的答案。我已经使用链接器部分有一段时间了,但想开始对它们进行细分(例如.init.subsection)。节名称中的句点阻止链接器继续通用__start/stop 标签。但是直接在链接描述文件中提供标签是解决这个问题的方法。
    【解决方案5】:

    只要节名产生一个有效的 C 变量名,gcc(而是ld)就会生成两个魔术变量:__start_SECTION__stop_SECTION。这些可用于检索节的开始和结束地址,如下所示:

    /**
     * Assuming you've tagged some stuff earlier with:
     * __attribute((__section__("my_custom_section")))
     */
    
    struct thing *iter = &__start_my_custom_section;
    
    for ( ; iter < &__stop_my_custom_section; ++iter) {
        /* do something with *iter */
    }
    

    我找不到任何有关此功能的正式文档,只有一些晦涩的邮件列表引用。如果您知道文档在哪里,请发表评论!

    如果您使用自己的链接器脚本(如 Linux 内核那样),则必须自己添加魔法变量(参见 vmlinux.lds.[Sh]this SO answer)。

    有关使用自定义 ELF 部分的另一个示例,请参阅 here

    【讨论】:

    • 你也可以使用 Objdump -d
    • 注意:我猜想使用asm("__start_invalid.c.variable.name")可以避免“有效的C变量名”问题,但事实证明它不起作用。
    • 愚蠢的问题:如何让链接器将 Stop 符号放在实际部分的末尾,而不是在 Start 符号之后?
    猜你喜欢
    • 1970-01-01
    • 2011-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-27
    • 2014-06-08
    • 1970-01-01
    相关资源
    最近更新 更多