Search code examples
clinuxlinkerldsymbols

Manually specify remapping of specific linking symbols


Without modifying these two source files, is there a way to take the object files produced by compiling them, and convincing a linker to link foo in main_v1.c to bar in bar.c?

main_v1.c

void foo(void);

int main(void)
{
    foo();
}

bar.c

#include <stdio.h>

void bar(void)
{
    puts("bar()!");
}

Modifying the object files themselves is fair game but assume that we might not even have the source code available. The platform is Linux.


By insisting on a modest change to main_v1.c, and linking in an additional "mapping" object file, here's a way to almost get the desired result with just standard C.

main_v2.c

extern void (*const foo)(void);

int main(void)
{
    foo();
}

bar.c is unchanged.

map.c

void bar(void);
void (*const foo)(void) = bar;

If the object files are compiled with lto, the function pointer dereference is even elided (using a recent gcc). This is a pretty good result but if main() is modified to have a direct call to bar(), then bar() itself is inlined after linking so there's room for improvement.


Solution

  • This would be a job for the GNU ld --wrap option

    You have, we assume, a main_v1.o compiled and perhaps unchangeable from your main_v1.c and likewise a bar.o compiled from your bar.c.

    Now write another source file:

    wrap.c

    extern void bar(void);
    
    void __wrap_foo(void)
    {
        bar();
    }
    

    Compile it to wrap.o and link it with the prior object files like so:

    $ gcc -c wrap.c
    $ gcc -o prog -Wl,--wrap=foo main_v1.o bar.o wrap.o
    

    Then:

    $ ./prog
    bar()!