14

Preconditions

A third-party has provided a C++ executable fooapp that uses a shared object libfoo.so. The library also comes with a header foo.hpp so developers can build other applications:

/* foo.hpp */
namespace foo {
  void bar(int a, int b);
  // More code below here <--- NOTE!!!
}

Success example

This is a standard LD_PRELOAD-based function interposition workflow.

First, I write my own version of the library, myfoo.cpp that exactly mirrors part of foo.hpp:

/* myfoo.hpp */
# include <ofstream>
namespace foo {
  void bar(int a, int b) {
    std::cout << a << "," << b << std::endl;
  }
  // NOTHING below here <-- NOTE!!!
}

Then I compile my library into libmyfoo.so and see the following:

$ nm libfoo.so -C | fgrep bar
0000000000021fc0 T foo::bar(int, int)
$ nm libmyfoo.so -C | fgrep bar
0000000000010c30 T foo::bar(int, int)
$ LD_DEBUG=bindings LD_DEBUG_OUTPUT=ld_debug.log LD_PRELOAD=./libmyfoo.so fooapp

Success! ld_debug.log shows binding as expected, and bar(...) generates output to the console.

Failure example

For the failure example, I'm going to (1) change one character in myfoo.hpp and (2) then fix that character in the binary using objcopy:

/* myfoo.hpp */
# include <ofstream>
namespace foq { // <-- NAME CHANGE!
  void bar(int a, int b) {
    std::cout << a << "," << b << std::endl;
  }
  // NOTHING below here <-- NOTE!!!
}

When I compile my library into libmyfoq.so I see the following:

$ nm libfoo.so -C | fgrep bar
0000000000021fc0 T foo::bar(int, int) # <-- Sames as before
$ nm libmyfoq.so -C | fgrep bar
0000000000010c30 T foq::bar(int, int) # <-- New name as expected
$ objcopy --redefine-syms=sym.map libmyfoq.so libmyfoo.so # <-- NEW STEP!
$ nm libmyfoo.so -C | fgrep bar
0000000000010c30 T foo::bar(int, int) # <-- SUCCESSful name update
$ LD_DEBUG=bindings LD_DEBUG_OUTPUT=ld_debug.log LD_PRELOAD=./libmyfoo.so fooapp

Failure! ld_debug.log shows NO binding of fooapp symbols to libmyfoo.so.

(PS -- If you're curious, sym.map contains a mapping between the mangled name of foq::bar and the mangled name of foo::bar. You can see these if you drop the -C from the nm command. See man objcopy for additional details.)

WHY?

In summary:

  • objcopy is renaming the symbol correctly.
  • The symbol name hasn't changed size.
  • BUT the loader is ignoring it at load time.

What's the story here?

BrianTheLion
  • 2,618
  • 2
  • 29
  • 46

1 Answers1

10

WHY? What's the story here?

objcopy -redefine-syms will only redefine the debugging symbols in the .symtab and .strtab symbol tables, not the symbols in the .dynsym and .dynstr symbol tables that is used for dynamic loading.

If you examine the library you created with objcopy with nm -D or readelf -s you'll see that the name used by the dynamic loader is still _ZN3foq3barEii, or with nm -DC, foq::bar(int, int).

So, that's what's going on.

The most official references I could find indicating that objcopy can't be used to update the dynamic symbols is this unassigned bugzilla entry from 2010:

Bug 11386 - objcopy should be able to update dynamic symbols visibility

And my own query on the official binutils mailing list:

Redefining dynamic symbols


As a side note, I don't think there's a tool available to redefine the dynamic symbols (properly), but I could be wrong.

The cjacker/elfhash project from 2015 seems to make an attempt. I tried that and it seems to actually rename the dynamic symbol name - but loading the .so afterwards fails and the original libs symbol is used instead.

% elfhash -f _ZN3foq3barEii -t _ZN3foo3barEii libmyfoq.so
% mv libmyfoq.so libmyfoo.so
% LD_DEBUG=bindings LD_DEBUG_OUTPUT=ld_debug.log LD_PRELOAD=./libmyfoo.so ./fooapp
ERROR: ld.so: object './libmyfoo.so' from LD_PRELOAD cannot be preloaded (cannot change memory protections): ignored.
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • Do you have a citation for the objcopy limitations you mentioned? – BrianTheLion Jan 29 '19 at 14:34
  • Mostly very old ones like [objcopy --redefine-sym on .dynsym section](http://sourceware-org.1504.n7.nabble.com/objcopy-redefine-sym-on-dynsym-section-td119610.html), but now that you know what's happening, searching for _ELF renaming symbols in the dynamic symbol table_ (or similar) will probably give the all those I found. – Ted Lyngmo Jan 29 '19 at 14:38
  • Looks like it's probably the right answer but I'm looking for an official reference to reward the bounty. – BrianTheLion Jan 29 '19 at 14:40
  • I suspect that's going to be hard. I looked at the source code, [objcopy.c](https://chromium.googlesource.com/native_client/nacl-toolchain/+/vendor-src/binutils/binutils/objcopy.c) and could only find `SHT_SYMTAB` mentioned in there, not `SHT_DYNSYM` that you can find in tools just reading files, like [readelf.c](https://chromium.googlesource.com/native_client/nacl-binutils/+/upstream/master/binutils/readelf.c). Perhaps that's a sign that it can't be done with `objcopy`. – Ted Lyngmo Jan 29 '19 at 15:09
  • @BrianTheLion I added a link to a query I made on the binutils mailing list. Perhaps someone will give an official statement there some day. I've also cloned the latest and greatest version of binutils/objcopy but haven't found anything in there suggesting it can be done. – Ted Lyngmo Jan 31 '19 at 15:32