Reputation: 3212
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.
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;
}
misc.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;
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
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
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