Binding failure with objcopy --redefine-syms
Asked Answered
P

1

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?

Prescind answered 23/1, 2019 at 17:39 Comment(0)
M
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.
Mika answered 29/1, 2019 at 14:16 Comment(5)
Do you have a citation for the objcopy limitations you mentioned?Prescind
Mostly very old ones like objcopy --redefine-sym on .dynsym section, 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.Mika
Looks like it's probably the right answer but I'm looking for an official reference to reward the bounty.Prescind
I suspect that's going to be hard. I looked at the source code, 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. Perhaps that's a sign that it can't be done with objcopy.Mika
@Prescind 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.Mika

© 2022 - 2024 — McMap. All rights reserved.