C语言 将库中的专用ELF部分合并到应用程序专用ELF部分

xwmevbvl  于 2023-06-21  发布在  其他
关注(0)|答案(2)|浏览(102)

这是我的测试。我有一个main应用程序,它由main.cmisc.c源代码以及一个由lib.c构成的静态库组成。

**目标:**我想在一个ELF节.modules中声明我所有的struct module声明。
**问题:**我只能看到主应用的struct module声明。下面是我可以用下面的代码看到的输出:

Hello World
- module:module_a
- module:module_b

如果我调用my_lib()main(),那么我会看到:

Hello World
MyLib
- module:module_a
- module:module_b
- module:module_lib

但我不感兴趣直接调用模块的功能到我的主应用程序。

*CMakeLists.txt

add_executable(main main.c misc.c)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")

set(LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/linker.ld")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T ${LINKER_SCRIPT}")

add_library(static_lib STATIC lib.c)

target_link_libraries(main static_lib)

*main.c

#include "module.h"

extern const struct module modules_start[];
extern const struct module modules_end[];

struct module __attribute__ ((section (".modules"))) module_a = {
    .name = "module_a",
};

int main(void) {
    puts("Hello World");

    const struct module *m = modules_start;
    while (m < modules_end) {
        printf("- module:%s\n", m->name);
        m++;
    }

    return 0;
}

*其他c

#include "module.h"

struct module __attribute__ ((section (".modules"))) module_b = {
    .name = "module_b",
};

*module.h

#include <stdio.h>

struct module {
    const char* name;
};

*lib.c

#include "module.h"

struct module __attribute__ ((section (".modules"))) __attribute__ ((used)) module_lib = {
    .name = "module_lib",
};

int my_lib(void) {
    puts("MyLib");
    return 0;
}

*linker.ld

SECTIONS
{
    .modules : {
        modules_start = .;
        KEEP(*(.modules))
        modules_end = .;
    }
}
INSERT AFTER .rodata;

以下是一些ELF信息:

$ readelf --sections libstatic_lib.a | grep -A 1 modules
  [ 5] .modules          PROGBITS         0000000000000000  00000058
       0000000000000008  0000000000000000  WA       0     0     8
  [ 6] .rela.modules     RELA             0000000000000000  00000278
       0000000000000018  0000000000000018   I      13     5     8

$ readelf --sections main | grep -A 1 modules
  [17] .modules          PROGBITS         00000000000009c0  000009c0
       0000000000000010  0000000000000000  WA       0     0     8

$ nm libstatic_lib.a | grep module
0000000000000000 D module_lib

$ nm main | grep module
00000000000009c0 D module_a
00000000000009c8 D module_b
00000000000009d0 D modules_end
00000000000009c0 D modules_start
ht4b089n

ht4b089n1#

如果静态库中没有对某个对象文件的引用,则默认情况下该对象文件不包含在链接中。使用binutils链接器,可以使用--whole-archive选项禁用此优化。

e3bfsja2

e3bfsja22#

这个问题促使我回答,因为我正试图做同样的事情。我有解决方案,不需要标准可执行链接器脚本:

extern const void* __start_module;
extern const void* __stop_module;

GNU LD链接器识别这些以__start___stop_为前缀的外部符号,并将它们放置在module部分的开始和结束处,因此不需要为普通可执行文件定制链接器脚本,除非您需要一些特殊的东西。
静态库必须与-Wl,--whole-archive链接,否则module部分中的符号将被丢弃。
示例代码:main.cpp

#include <cstdio>

typedef void(*callback_t)(void);

// get the function pointers via linker provided array.
extern const callback_t __start_module;
extern const callback_t __stop_module;

int main() {
    // the pointers start at address of __start_benchmark and end at __stop_benchmark
    const callback_t* itr = (callback_t*)&__start_module;
    const callback_t* stop = (callback_t*)&__stop_module;
    while(itr != stop) {
        std::printf("calling %p\n", itr);
        (*itr)();
        ++itr;
    }
}

hello.cpp:

#include <cstdio>
typedef void(*callback_t)(void);
void func() {
    std::printf("Hello!\n");
}
// assign  functions into .benchmark section
const callback_t entry __attribute__ ((section ("module"))) = &func;

CMakeLists.txt:

add_library(test STATIC hello.cpp)
add_executable(prog main.cpp)
target_link_libraries(prog PUBLIC
    -Wl,--whole-archive
    test
    -Wl,--no-whole-archive)

注意:我需要链接器来收集一堆回调到一个数组中,我对内存的读写和随机位置很满意。如果有人知道如何使module部分RODATA或合并收集到的数组到.rodata部分,我很乐意更新这个答案。

相关问题