C __链接到库时缺少开始部分和停止部分符号
我正在autotools C项目中使用自定义elf头,类似于此线程:。问题是,声明自定义节的c文件链接到一个静态库中,然后链接到最终应用程序 在此配置中,不会生成符号“开始”、“自定义”部分和“停止”、“自定义”部分。我对elf部分的定义如下:C __链接到库时缺少开始部分和停止部分符号,c,gcc,elf,C,Gcc,Elf,我正在autotools C项目中使用自定义elf头,类似于此线程:。问题是,声明自定义节的c文件链接到一个静态库中,然后链接到最终应用程序 在此配置中,不会生成符号“开始”、“自定义”部分和“停止”、“自定义”部分。我对elf部分的定义如下: struct mystruct __attribute((__section__("custom_section"))) __attribute((__used__) = { ... }; 如果我链接到对象文件而不是库,符号就会被创建,一切都会按预期进
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文件,以填充开始值和大小值。以下是基本步骤:
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该部分正在被使用,并且在链接时不优化它?我正在考虑放置少量未使用的数据,这些数据确实会被引用,并且总是存在,只是为了强制创建节,但这似乎是一种黑客行为……您是否尝试过提供语句