Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding failure with objcopy --redefine-syms

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?

like image 714
BrianTheLion Avatar asked Jan 23 '19 17:01

BrianTheLion


1 Answers

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.
like image 57
Ted Lyngmo Avatar answered Sep 29 '22 04:09

Ted Lyngmo