Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C __链接到库时缺少开始部分和停止部分符号_C_Gcc_Elf - Fatal编程技术网

C __链接到库时缺少开始部分和停止部分符号

C __链接到库时缺少开始部分和停止部分符号,c,gcc,elf,C,Gcc,Elf,我正在autotools C项目中使用自定义elf头,类似于此线程:。问题是,声明自定义节的c文件链接到一个静态库中,然后链接到最终应用程序 在此配置中,不会生成符号“开始”、“自定义”部分和“停止”、“自定义”部分。我对elf部分的定义如下: struct mystruct __attribute((__section__("custom_section"))) __attribute((__used__) = { ... }; 如果我链接到对象文件而不是库,符号就会被创建,一切都会按预期进

我正在autotools C项目中使用自定义elf头,类似于此线程:。问题是,声明自定义节的c文件链接到一个静态库中,然后链接到最终应用程序

在此配置中,不会生成符号“开始”、“自定义”部分和“停止”、“自定义”部分。我对elf部分的定义如下:

struct mystruct __attribute((__section__("custom_section"))) __attribute((__used__) = {
...
};

如果我链接到对象文件而不是库,符号就会被创建,一切都会按预期进行。不过,这不是一个可扩展的解决方案,因为我希望新模块只通过将它们编译到模块库中来工作。知道链接器为什么不创建这些特殊符号吗?当节存在于库中而不是单个对象文件中时?

我最近做了类似的事情,我的解决方案不依赖于任何特定于编译器的实现、内部未记录的符号等。但是,它确实需要更多的工作:)

背景

通过了解磁盘上的ELF二进制文件的格式并使用提供给我们的两种结构,可以非常轻松地加载和解析磁盘上的ELF二进制文件:。您可以遍历它的每个段和节(段是节的容器)。如果执行此操作,则可以计算分区的相对开始/结束虚拟地址。根据这种逻辑,您可能认为可以在运行时通过迭代加载的内存版本ELF二进制文件的段和节来执行相同的操作。但是遗憾的是,您只能通过段本身(via)进行迭代,并且所有的段元数据都丢失了

那么,我们如何保留节元数据呢?自己储存

解决方案

如果有一个名为“.mycustom”的自定义节,则定义一个元数据结构,该结构至少应存储两个数字,以指示“.mycustom”节的相对起始地址和大小。创建此元数据结构的全局实例,该实例将在另一个名为“.mycustom\u meta”的自定义节中独立运行

例如:

typedef struct
{
    unsigned long ulStart;
    unsinged long ulSize;
} CustomSectionMeta;

__attribute((__section__(".mycustom_meta"))) CustomSectionMeta g_customSectionMeta = { 0, 0 };
#include <dlfcn.h>
Dl_info dlInfo;
if (dladdr(&g_customSectionMeta, &dlInfo) != 0)
{
    void * vpBase = dlInfo.dli_fbase;
    void * vpMyCustomStart = vpBase + g_customSectionMeta.ulStart;
    void * vpMyCustomEnd = vpMyCustomStart + g_customSectionMeta.ulSize;
}
您可以看到,我们的struct实例的起始值和大小值都初始化为零。编译此代码时,对象文件将包含一个名为“.mycustom_meta”的节,对于32位编译,该节的大小为8字节(对于64位编译,为16字节),并且值将全部为零。在上面运行objdump,您将看到同样多的内容。如果需要,可以将其放入静态库(.a),在其上运行readelf,您将看到完全相同的结果。如果需要,将其构建到共享对象(.so)中,在其上运行readelf,您将再次看到相同的内容。把它构建成一个可执行程序,在上面运行readelf,瞧,它还在那里

现在把戏来了。您需要编写一个小的可执行文件(我们称之为MetaWriter),它将更新磁盘上的ELF文件,以填充开始值和大小值。以下是基本步骤:

  • 以二进制模式打开ELF文件(.o、.so或可执行文件),并将其读入连续数组。或者,您可以将其映射到内存中以实现相同的效果
  • 使用上面列出的ELF链接中的头结构和指令通读二进制文件
  • 找到您的“.mycustom”部分并阅读section.sh_addr和section.sh_size
  • 找到您的“.mycustom\u meta”部分。使用步骤3中的开始值和大小值创建CustomSectionMeta的实例。memcpy()在现有“.mycustom_meta”节数据的顶部创建结构,该节数据到目前为止都是零
  • 将ELF数据保存回原始文件。它现在应该完全没有修改,除了写入“.mycustom_meta”部分的几个字节之外
  • 我所做的是在我的Makefile中作为构建过程的一部分执行这个MetaWriter程序。因此,您可以构建.So或可执行文件,然后在其上运行MetaWriter以填充meta部分。在那之后,它就可以开始了

    现在,当.so或可执行文件中的代码运行时,它可以从g_customSectionMeta读取,其中将填充“.mycustom”节的起始地址偏移量,以及它的大小,当然,它可以用来轻松地计算结束。必须将此起始偏移量添加到加载的ELF二进制文件的基址。有几种方法可以实现这一点,但我发现的最简单的方法是对我知道存在于二进制文件中的符号(例如g_customSectionMeta!)运行dladdr,并使用dli_fbase的结果值来知道模块的基址

    例如:

    typedef struct
    {
        unsigned long ulStart;
        unsinged long ulSize;
    } CustomSectionMeta;
    
    __attribute((__section__(".mycustom_meta"))) CustomSectionMeta g_customSectionMeta = { 0, 0 };
    
    #include <dlfcn.h>
    Dl_info dlInfo;
    if (dladdr(&g_customSectionMeta, &dlInfo) != 0)
    {
        void * vpBase = dlInfo.dli_fbase;
        void * vpMyCustomStart = vpBase + g_customSectionMeta.ulStart;
        void * vpMyCustomEnd = vpMyCustomStart + g_customSectionMeta.ulSize;
    }
    
    #包括
    Dl_info dlInfo;
    if(dladdr(&g_customSectionMeta,&dlInfo)!=0)
    {
    void*vpBase=dlInfo.dli\u fbase;
    void*vpMyCustomStart=vpBase+g_customSectionMeta.ulStart;
    void*vpMyCustomEnd=vpMyCustomStart+g_customSectionMeta.ulSize;
    }
    

    发布完成所有这些工作所需的全部代码,特别是MetaWriter中ELF二进制文件的解析,可能有点过火。但是,如果您需要帮助,请随时联系我。

    在我的情况下,代码中没有引用该变量,并且在释放模式(-O2)下对该部分进行了优化。添加
    used
    属性解决了问题。例如:

    static const unsigned char unused_var[] __attribute__((used, section("foo"))) = {
        0xCA, 0xFE, 0xBA, 0xBE
    };
    

    我试过-Wl,-没有gc节,-Wl,-整个归档都没有成功。我得出结论,该节正在消失,因为ld正在优化它,因为没有直接引用将进入该节的数据结构。仍然不确定为什么我直接在对象文件中链接时没有得到优化。。。库不只是对象的存档吗?我如何告诉ld该部分正在被使用,并且在链接时不优化它?我正在考虑放置少量未使用的数据,这些数据确实会被引用,并且总是存在,只是为了强制创建节,但这似乎是一种黑客行为……您是否尝试过提供语句