10
votes

I have a elf binary which has been statically linked to libc. I do not have access to its C code. I would like to use OpenOnload library, which has implementation of sockets in user-space and therefore provides lower latency compared to standard libc versions. OpenOnload implements standard socket api, and overrides libc version using LD_PRELOAD. But, since this elf binary is statically linked, it cannot use the OpenOnload version of the socket API.

I believe that converting this binary to dynamically link with OpenOnload is possible, with the following steps:

  1. Add new Program headers: PT_INTERP, PT_DYNAMIC and PT_LOAD.
  2. Add entries in PT_DYNAMIC to list dependency with libc.
  3. Add PLT stubs for required libc functions in the new PT_LOAD section.
  4. Modify existing binary code for libc functions to jump to corresponding PLT stubs.

As a first cut, I tried just adding 3 PT_LOAD segments. New segment headers were added after existing PT_LOAD segment headers. Also, vm_addr of existing segments was not modified. File offsets of existing segments were shifted below to next aligned address based on p_align. New PT_LOAD segments were added in the file at end of the file.

After re-writing the file, when I ran it, it was loaded properly by the kernel, but then it immediately seg-faulted.

My questions are:

  1. If I just shift the file-offsets in the elf binary, without modifying the vm_addresses, can it cause any error while running the binary?
  2. Is it possible to do what I am attempting? Has anybody attempted it?
1
In theory this can be made to work, but I think you're almost certainly looking at an unbounded number of bugs -- the big problem you haven't even hit yet is, glibc expects to have only one copy of itself in any given address space, and important things (like malloc) will crash unpredictably if that expectation is violated. You may be better off running the entire binary through a disassembler, manually pruning out all of libc, and then reassembling and relinking it.zwol
I'd probably just write a thin wrapper for the library..Jari Komppa
I agree with Zack. You'll spend an unreasonable amount of time doing that, and it probably won't work as well as you want (the devil is in the details). So don't do that!Basile Starynkevitch
Zack, does glibc expect only one copy to be loaded or only one copy to be used? What I mean to say is that if I redirect all the calls to glibc functions from the statically linked glibc to one that is dynamically loaded, can it work?javed
Zack, can you elaborate a little more on the disassembler. How can I go about doing it? I mean which disassembler can I use on Linux?javed

1 Answers

5
votes

What you are attempting is not possible in any automated way. At the time of static linking, all relocation information identifying calls to libc as calls to libc has been resolved and removed. If debugging symbols exist in the binary, it's possible to identify "this range of bytes in the text segment corresponds to such-and-such libc function", but there is no way to identify references to the function, which will be embedded in the instruction byte stream with no markup to identify them. You could use heuristics based on disassembly, but they would be incomplete and unreliable (possibility of both false negatives and false positives).

As far as shifting offsets, you absolutely cannot change anything about the load addresses for a static linked binary. If you need to insert headers before the load segments, you'd have to insert a whole page, and update the file offsets in the program header table (adding 1 page to them) while leaving the virtual address load offsets the same. However, since what you're trying to do is not possible overall, the offset-shifting issue is the least of your worries.

Perhaps, if the program doesn't require high performance, you could run it under qemu app-level emulation, with qemu going through the sockets emulation/wrapper.