I would like to get the start and end pointers of a section in a library, in a way that it can be overridden from the program to which the program is being linked.
This allows me to specify in the program some parameters as to how the library should load. Here's a concrete example:
foo.c
, the library:
#include <stdio.h>
typedef void (*fptr)();
void lib_function();
void dummy()
{
printf("NO -- I should be overriden by prog_function\n");
}
fptr section_fptrlist __attribute__((weak, section("fptrlist"))) = (fptr)dummy;
extern fptr __start_fptrlist;
extern fptr __stop_fptrlist;
void __attribute__((constructor)) setup()
{
// setup library: call pre-init functions;
for (fptr *f = &__start_fptrlist; f != &__stop_fptrlist; f++)
(*f)();
}
void lib_function()
{
}
bar.c
, the program:
#include <stdio.h>
void lib_function();
typedef void (*fptr)();
void pre_init()
{
printf("OK -- run me from library constructor\n");
}
fptr section_fptrlist __attribute__((section("fptrlist"))) = (fptr)pre_init;
int main()
{
lib_function();
return 0;
}
I build libfoo.so
from foo.c
and then a test program from bar.c
and libfoo.so
, for example:
gcc -g -O0 -fPIC -shared foo.c -o libfoo.so
gcc -g -O0 bar.c -L. -lfoo -o test
This used to work fine, i.e. with ld version 2.26.1 I get as expected:
$ ./test
OK -- run me from library constructor
Now with ld version 2.29.1 I get:
$ ./test
NO -- I should be overriden by prog_function
I have compiled everything on one machine, and only changed the linker step by copying the object file to a different machine, running ld -shared foo.o -o libfoo.so
and copying the library back, so as far as I can tell the linker is the only difference between this working and not working.
I further use gcc 7.2.0 and glibc 2.22-62 but as stated above that doesn't seem to be decisive. The differences in the linker scripts seem minor and using one instead of the other does not seem to make any difference to the result so far (2.26 with 2.29's script does work as excepted, 2.29 with 2.26's script does not). Here's the diff anyway:
--- ld_script_v2.26 2018-02-02 21:52:56.038573732 +0100
+++ ld_script_v2.29 2018-02-02 21:52:41.154504340 +0100
@@ -1,4 +1,4 @@
@@ -53,5 +53,5 @@ SECTIONS
.plt : { *(.plt) *(.iplt) }
.plt.got : { *(.plt.got) }
-.plt.bnd : { *(.plt.bnd) }
+.plt.sec : { *(.plt.sec) }
.text :
{
@@ -226,4 +226,5 @@ SECTIONS
/* DWARF Extension. */
.debug_macro 0 : { *(.debug_macro) }
+ .debug_addr 0 : { *(.debug_addr) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
Looking at the dynamic symbol table (with readelf -Ws
) I noticed that in the 2.29 versions the symbols are now protected:
with ld 2.29> readelf -Ws libfoo.so | grep fptr
8: 0000000000201028 0 NOTYPE GLOBAL PROTECTED 24 __start_fptrlist
14: 0000000000201028 8 OBJECT WEAK DEFAULT 24 section_fptrlist
16: 0000000000201030 0 NOTYPE GLOBAL PROTECTED 24 __stop_fptrlist
54: 0000000000201028 8 OBJECT WEAK DEFAULT 24 section_fptrlist
58: 0000000000201028 0 NOTYPE GLOBAL PROTECTED 24 __start_fptrlist
62: 0000000000201030 0 NOTYPE GLOBAL PROTECTED 24 __stop_fptrlist
whith ld 2.26> readelf -Ws libfoo.so | grep fptrlist
9: 0000000000201028 0 NOTYPE GLOBAL DEFAULT 23 __start_fptrlist
15: 0000000000201028 8 OBJECT WEAK DEFAULT 23 section_fptrlist
17: 0000000000201030 0 NOTYPE GLOBAL DEFAULT 23 __stop_fptrlist
53: 0000000000201028 8 OBJECT WEAK DEFAULT 23 section_fptrlist
57: 0000000000201028 0 NOTYPE GLOBAL DEFAULT 23 __start_fptrlist
61: 0000000000201030 0 NOTYPE GLOBAL DEFAULT 23 __stop_fptrlist
I am aware, from this answer, that the feature I was relying on is more shady that well defined. I was able to track down the fact that this change was intentional. What is now the best way for me to achieve the goal of calling program function(s) from my library setup? Can I still make this approach work? Is there a way to un-protect those symbols for example?
Even though this example is small, this problem actually happens in a pretty big C++ project, so the less changes the better.
I think the problem is
for (fptr *f = &__start_fptrlist; f != &__stop_fptrlist; f++)
(*f)();
here the for-loop is expected to go through __start_fptrlist
and __stop_fptrlist
defined in your app. While the .protected
makes those symbols resolved from the .so
itself.
A simple workaround will be:
// foo.c
/* ... */
fptr *my_start = &__start_fptrlist;
fptr *my_stop = &__stop_fptrlist;
void __attribute__((constructor)) setup()
{
// setup library: call pre-init functions;
for (fptr *f = my_start; f != my_stop; f++)
(*f)();
}
Here the exact value of my_*
function is not important, because these 2 names are supposed to be bound to the value from app.
// bar.c
/* ... */
fptr section_fptrlist __attribute__((section("fptrlist"))) = (fptr)pre_init;
extern fptr __start_fptrlist;
extern fptr __stop_fptrlist;
fptr *my_start = &__start_fptrlist;
fptr *my_stop = &__stop_fptrlist;
This will force your for-loop go through the addresses from your app instead of your .so
. Because my_*
symbols are global and they will be first resolved from app.
Warning: Code not tested, since I don't have the environment as you described. Please let me know whether this approach works on your machine.