Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Re-export Shared Library Symbols from Other Library (OS X / POSIX)

My question is fairly OS X on x86-64 specific but a universal solution that works on other POSIX OSes is even more appreciated.

Given a list of symbol names of some shared library (called original library in the following) and I want my shared library to re-export these symbols. Re-export as in if someone tries to resolve the symbol against my library I either provide my version of this symbol or (if my library doesn't have this symbol) forward to the original library's symbol.

I don't know the types of the symbols, I only know whether they are functions (type T in nm output) or other symbols (type S in nm output).

For functions, I already have a solution: For every function I want to re-export I generate an assembly stub that does dynamically resolve the symbol (using dlsym()) and then jumps into the resolved function with the very same environment (registers rdi, rsi, rdx, rcx, r8, r9, stack pointer, ...). I'm basically generating universal proxy functions. Using some macro trickery that can be generated fairly easy without writing code for each and every symbol.

For non-function symbols the problem seems to be harder because I cannot generate this universal proxy function, because the resolving party does never call a function.

Using a constructor function static void init(void) __attribute__((constructor)); I can execute code whenever someone loads my library, that would be a good point to resolve and re-export all non-function symbols if that's possible.

In other words, I'd like to write the symbol table of my library to point to the respective symbols of another shared library. Doing the rewriting at compile or run time is okay (run time preferred). Or put yet another way, the behaviour of DYLD_INSERT_LIBRARIES (LD_PRELOAD) is exactly what I need but I don't want to insert a new library, I want to replace one (in the file system). EDIT: The reason I don't want/can't use DYLD_INSERT_LIBRARIES or any other environment variable of the DYLD_* family is that they are ignored for code signed, restricted, ... binaries.

I'm aware of the -reexport-l, -reexport_library and -reexported_symbols_list linker flags but I could not get them to work, especially when my library is a "replacement" for frameworks that are part of umbrella frameworks (example: /System/Library/Frameworks/CoreServices.framework/Frameworks/SearchKit.framework/SearchKit) because ld forbids to link directly against parts of umbrella frameworks.

EDIT: Because I explained it somewhat ambiguously: I can't change the way the actual program is linked. The goal is to produce a shared library that is a replacement for the original library. (Apparently called filter library.)

like image 269
Johannes Weiss Avatar asked Nov 16 '13 16:11

Johannes Weiss


2 Answers

You could link against both libraries and use the link order to make sure to link against the right symbols. This works on both OS X and Linux:

cc -o executable -lmylib -loriglib

Where origlib is the original library and mylib contains symbols that are supposed to overwrite symbols in origlib. Then the executable will be linked against your symbols from mylib first and all unresolved symbols will be linked against origlib.

This works in the same way when linking against OS X frameworks. Just link against your library that replaces symbols first and against the framework after.

cc -o executable -lmylib -framework SomeFramework

Edit: If you just want to replace symbols at runtime then you can use LD_PRELOAD in the same way:

cc -o executable -framework SomeFramework
LD_PRELOAD=libmylib.dylib ./executable
like image 40
Sergey L. Avatar answered Oct 05 '22 23:10

Sergey L.


Found it out now (OS X specific): clang -o replacement-lib.dylib ... -Xlinker -reexport_library PATH_TO_ORIGINAL_LIB does the trick. PATH_TO_ORIGINAL_LIB could for example be /System/Library/Frameworks/CoreServices.framework/Frameworks/SearchKit.framework/Versions/Current/SearchKit.

If PATH_TO_ORIGINAL_LIB is a library that is part of an umbrella framework (as in the example above), then replace PATH_TO_ORIGINAL_LIB by the path of some other lib (I created a lib empty.dylib for that) and as a second step do

install_name_tool -change /usr/local/lib/empty.dylib PATH_TO_ORIGINAL_LIB replacement-lib.dylib

To see if the actual reexporting worked use:

otool -l replacement-lib.dylib | grep -A2 LC_REEXPORT_DYLIB

The output should look like

  cmd LC_REEXPORT_DYLIB
  cmdsize XX
     name empty.dylib (offset YY)

After launching the install_name_tool it could be

      cmd LC_REEXPORT_DYLIB
  cmdsize XX
     name /System/Library/Frameworks/CoreServices.framework/Frameworks/SearchKit.framework/Versions/Current/SearchKit (offset YY)
like image 194
Johannes Weiss Avatar answered Oct 05 '22 23:10

Johannes Weiss