2

I have an object file compiled using gcc with -ffunction-sections option. I have access to the source file but iam not allowed to modify it.

file.c
void foo(void)
{
    bar();
}
void bar(void)
{
    abc();
}

What iam trying to achieve is to make all the references to bar take an absolute address(which I'll assign in the linker script) whereas bar will be placed at some other address by the linker.

A possible solution is to rename bar to file_bar without changing the call to bar inside foo(). I tried using objcopy -redefine-syms but it seems to rename even the calls to bar.

Solution provided by busybee solves the problem unless the functions are in the same compilation unit. foo1.c

#include <stdio.h>
extern void bar1();
void foo1(){
printf("foo1\n");
}
int main(){
printf("main\n");
foo1();
bar1();
}

bar1.c

#include <stdio.h>
void bar1(){
printf("bar1\n");
}

wrapper.c

#include <stdio.h>
void __wrap_foo1(){
printf("wrap_foo1\n");
}
void __wrap_bar1(){
printf("wrap_bar1\n");
}

Now,

$ gcc -c -ffunction-sections foo1.c bar1.c wrapper.c
$ gcc -Wl,--wrap=foo1 -Wl,--wrap=bar1 -o output foo1.o bar1.o wrapper.o
$ ./output
main
foo1
wrap_bar1
techgreed
  • 21
  • 5
  • 1
    If you rename a function `bar` to `file_bar` without changing calls and references to it, then linking should fail, because there will not be original `bar` in the source to link with, right? And the new function `file_bar` could be a unique symbol name, so the renamed function would be unused and probably removed. Why would you want to do that? Is this XY problem? In reality you want to replace the `bar` function content without changing anything else? – KamilCuk Oct 19 '19 at 10:08

1 Answers1

2

All functions to be redirected are in their own compilation unit

The linker has the option "--wrap" that replaces all references to the symbol "xxx" by "__wrap_xxx" and the symbol itself by "__real_xxx". It is used to put a wrapper function as an "interceptor" in between call and function.

But with this option you can do whatever you like with those symbols in your linker script. You just need to define "__wrap_xxx" with a symbol so that the references are resolvable.

Depending on your needs you can also write a dummy function named "__wrap_xxx()" that does not even call "__real_xxx()". Or you can place "__real_xxx" in a vector table, or... whatever you can think of.

All functions to be redirected are non-static ("global"), patching immediate values

I looked through the answers of the other question the OP posted in a comment. This gave me the idea to weaken the symbols in question and to override them with a value by the linker.

This example might give you some insight. I tested in on Linux which has address space layout randomization so all addresses are offsets from a random base. But for the OP's target system it should work as expected.

foo1.c

Because of arbitrary values for the redirected addresses the functions can't be called. But the program can print their addresses.

#include <stdio.h>

void foo1(void) {
}

extern void bar1(void);

int main(void) {
  printf("%p\n", main);
  printf("%p\n", foo1);
  printf("%p\n", bar1);
  return 0;
}

bar1.c

void bar1(void) {
}

wrapper.ld

This is the first alternative to give the linker the addresses to be used, an additional linker script. For the second one see below. The standard linker script will be augmented here, there is no need to copy and patch it. Because of the simple structure this is probably the most simple way to provide many redirected addresses which can be easily automated.

foo1 = 0x1000;
bar1 = 0x2000;

Note: This is not C! It is "linker script" syntax which happens to be quite similar.

How I built and tested

This command sequence can be automated and sorted for your liking. Especially the calls of objcopy could be done by some loop over a list.

gcc -c -ffunction-sections foo1.c
objcopy --weaken-symbol=foo1 foo1.o foo2.o

gcc -c -ffunction-sections bar1.c
objcopy --weaken-symbol=bar1 bar1.o bar2.o

gcc foo1.o bar1.o -o original
echo original
./original

gcc foo2.o bar2.o -o weakened
echo weakened
./weakened

gcc foo2.o bar2.o wrapper.ld -o redirected
echo redirected
./redirected

Instead of an additional linker script the symbol definitions can be given on the command line, too. This is the mentioned second alternative.

gcc foo2.o bar2.o -Wl,--defsym=foo1=0x1000 -Wl,--defsym=bar1=0x2000 -o redirected

BTW, the linker understands @file to read all arguments from the file file. So there's "no limit" on the size of the linker command.

All functions to be redirected are non-static ("global"), overwriting with new functions

Instead of providing immediate values you can of course just provide your alternative functions. This works like above but instead of the additional linker script or symbol definitions you write a source file.

wrapper.c

Yes, that's right, the names are equal to the names of the originals! Because we made the symbols of the original functions weak, we'll get no error message from the linker when it overwrites the references with the addresses of the new functions.

void foo1(void) {
}

void bar1(void) {
}

Build the redirected program like this (only new commands shown):

gcc -c -ffunction-sections wrapper.c

gcc foo2.o bar2.o wrapper.o -o redirected

A function to be redirected is static

Well, depending on your target architecture it will probably not be possible. This is because of the relocation entry of the reference. It will be some kind of relative, telling the linker to resolve by an offset into the section of the function instead to resolve by the symbol of the function.

I didn't investigate this further.

the busybee
  • 10,755
  • 3
  • 13
  • 30
  • Just wondering if there is a better way if we need to wrap lot of symbols instead of mentioning each one in the command line. – techgreed Oct 19 '19 at 13:02
  • So it boils down to the question: Why in the first place do you want to separate calls and functions? BTW, you should be able to automate this to generate the command line. About what number of symbols is it? – the busybee Oct 19 '19 at 13:12
  • It can be automated. There could be few hundreds of symbols. I was just checking if there is some way to mention the list of symbols to be wrapped in a file and mention the filename to the linker. Couldn't find something like that. Anyways your solution works perfectly fine for me. – techgreed Oct 19 '19 at 14:01
  • You might include the call to the linker in that file and call that as a script/batch. At least Linux accepts command lines of some kilobytes length. – the busybee Oct 19 '19 at 14:27
  • 1
    The solution doesn't work for functions in the same object file. There is a related post https://stackoverflow.com/questions/13961774/gnu-gcc-ld-wrapping-a-call-to-symbol-with-caller-and-callee-defined-in-the-sam but the soltion provided there makes use of the strong-weak symbol concept and not 'wrap'. – techgreed Oct 21 '19 at 07:28
  • Right, and it's good that you found a pointer to a better solution. - I'm still curious about your use case for the separation of references and definitions. ;-) It sounds a bit like an X-Y-problem. – the busybee Oct 21 '19 at 07:54
  • strong-weak solution is not the one for me as I wont have the wrapper implementation but will just assign the wrapper symbol with an absolute address. I still need to figure out a way to wrap the symbols in same source file – techgreed Oct 21 '19 at 10:19
  • I'm afraid that depending on the target's architecture the compiler will not generate relocatable references for calls in the same section of a compilation unit and instead insert relative calls which need no relocation. But as you compile with `-ffunction-sections` there might not happen. Do you have an example of such a reference/definition pair? – the busybee Oct 21 '19 at 11:10
  • Yes, I noticed, but I'm sorry to be quite limited in my spare time currently. In the meantime: Since you want to patch some software by absolute addresses it seems to me that your *real* target is *not* a PC. What is you target system? – the busybee Oct 22 '19 at 10:00
  • It's a custom asic based on arm v7-M – techgreed Oct 24 '19 at 03:06
  • Would you mind to check this with the target's compiler? On the PC (it seems you're using Linux like me) it's an issue with the relocation entry. Try `objdump -dr foo1.o` to see the disassembly with its relocation entries. The call of `foo1()` has an entry of type `R_X86_64_PC32` while the call of `bar1()` has `R_X86_64_PLT32`. Look at this on the target's compiled object file. You might investigate this further. – the busybee Oct 24 '19 at 17:05