Nicholas Hein
Nicholas Hein

Reputation: 119

Linker script variable in library prevents dynamic linking

I have a project set up where I compile and link a shared library (libexample.so) with a linker script that looks like this:

SECTIONS
{
    .modules : {
        __MODULES_START = .;
        KEEP(*(.mod*));
        __MODULES_END = .;
    }
    ...
}

I use these in my code to load modules compiled into the library.

extern uint32_t __MODULES_START;
extern uint32_t __MODULES_END;

unsigned int init_mods (void) {
    void (*p)(void) = (void *)&__MODULES_START;
    ...
}

And when I compile the library in my Makefile

build/%.o: %.c
  gcc -o $@ -c $< -fPIC -g -Os -Iinclude

bin/libexample.so: $(OBJS)
  gcc -o $@ $^ -shared -fPIC -lc -T$(LINKER_SCRIPT)

It builds and links just fine, and it works when I try to link the library to another project that calls "init_mods".

build/%.o: %.c
  gcc -o $@ -c $< -fPIE -g -Os -Iinclude -I../libexample/include

bin/untitled-program: $(OBJS)
  gcc -o $@ $^ -fPIE -lc -lexample -Lbin '-Wl,-rpath,$$ORIGIN'

However, when I run the program where it can find the library, I receive the following linking error:

/bin/untitled-program: error while loading shared libraries: /blah/blah/libexample.so: unexpected PLT reloc type 0x08

When I readelf the shared library, I get the two definitions in my symbol table

Symbol table '.symtab' contains 223 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
...
   154: 0000000000000050     0 NOTYPE  GLOBAL DEFAULT    2 __MODULE_INIT_END
...
   222: 0000000000000028     0 NOTYPE  GLOBAL DEFAULT    2 __MODULE_INIT_START

So I'm wondering if my issue has to do with the NOTYPE, but I'm having trouble finding documentation on this.

To explain why I think my issue has to do with the linker script variables, when I run my program with linker debugging on, one of them is the last one to show up.

$ LD_DEBUG=all ./untitled-program
...
     23856: symbol=__MODULE_END;  lookup in file=./bin/untitled-program [0]
     23856: symbol=__MODULE_END;  lookup in file=/usr/lib/libc.so.6 [0]
     23856: symbol=__MODULE_END;  lookup in file=./bin/libexample.so [0]
     23856: binding file ./bin/libexample.so [0] to ./bin/libexample.so [0]: normal symbol `__MODULE_END'
...
     23856: symbol=__MODULE_START;  lookup in file=./bin/untitled-program [0]
     23856: symbol=__MODULE_START;  lookup in file=/usr/lib/libc.so.6 [0]
     23856: symbol=__MODULE_START;  lookup in file=./bin/libexample.so [0]
     23856: binding file ./bin/libexample.so [0] to ./bin/libexample.so [0]: normal symbol `__MODULE_START'
./bin/untitled-program: error while loading shared libraries: ./bin/libexample.so: unexpected PLT reloc type 0x08

But, that's the weird thing because it's able to bind one of the other linker script variables before it fails.

I have been working on this issue for too long, so I'm having trouble seeing the bigger picture. Maybe I'm thinking about this wrong and the issue is with another symbol. Any help or guidance would be appreciated!

Upvotes: 2

Views: 753

Answers (1)

None
None

Reputation: 291

Just mark your module init function with the GCC constructor function attribute (it has nothing to do with C++ constructors!), and it will include its address in the init_array section; the dynamic linker will then execute it before main(), or immediately when a dynamic library is loaded.

static void my_mod_init(void) __attribute__((constructor));
static void my_mod_init(void)
{
    /* Initialize this module, hook up to the library */
}

This has the benefit that because the dynamic linker executes these automatically, these are also run when you load a dynamic library with such modules with e.g. dlopen(path, RTLD_NOW | RTLD_GLOBAL).


If you want to replicate the functionality under your own control, then have each module declare an array of the init function addresses to a special section, say "mod_inits". Define some helper macros:

#define  MERGE2_(a, b)  a ## b
#define  MERGE2(a, b)   MERGE2_(a, b)
#define  MODULE_INIT(func)  \
    static void *MERGE2(_init_, __LINE__) \
    __attribute__((section ("mod_inits"), used)) = &func

Then, in your module source files, make some functions:

static void hello(void) {
    puts("Hello!");
}
MODULE_INIT(hello);

static void also(void) {
    puts("Also hello!");
}
MODULE_INIT(also);

In the library file, to scan and execute all functions in any compilation units marked with MODULE_INIT():

extern void *__start_mod_inits;
extern void *__stop_mod_inits;

void run_mod_inits(void)
{
    for (void **ptr = &__start_mod_inits; ptr < &__stop_mod_inits; ptr++) {
        void (*func)(void) = *ptr;

        func();  /* Could pass parameters, if they have the same prototype */

    }
}

You do not need any linker file for this, as gcc provides the __start_ and __stop_ symbols for all sections whose names are valid C identifiers.

Upvotes: 1

Related Questions