OlivierM
OlivierM

Reputation: 3212

Merging dedicated ELF sections from libraries into application dedicated ELF section

Here is my test. I have a main application composed of the sources main.c and misc.c and a static library made of lib.c.

GOAL: I want to declare all my struct module declarations in an ELF section .modules.

ISSUE: I can only see the struct module declarations from the main application. Here is the output I can see with the following code:

Hello World
- module:module_a
- module:module_b

If I call my_lib() into main() then I see:

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

But I am not interested to directly call module's function into my main application.

Here are some ELF information:

$ 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

Upvotes: 1

Views: 617

Answers (2)

JATothrim
JATothrim

Reputation: 862

This question prompt me to answer as I was trying to do the exact same thing. I have solution that doesn't need linker script for standard executable:

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

The GNU LD linker recognizes these extern symbols prefixed with __start_ and __stop_ and places them at begin and end of module section thus no need for custom linker script for normal executable unless you need something special.

The static libraries have to be linked with -Wl,--whole-archive or the symbols in module sections will be discarded as pointed out.

Example code: 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)

Note: I needed the linker to collect bunch of callbacks into an array and I'm fine with the memory being read-write and in random location. If anybody knows how to make the module section RODATA or merge the collected array into .rodata section I'm happy to update this answer.

Upvotes: 0

Florian Weimer
Florian Weimer

Reputation: 33727

If there are no references to an object file in a static library, by default that object file is not included in the link. With the binutils linker, you can disable this optimization using the --whole-archive option.

Upvotes: 3

Related Questions