1

I have some function, say int foo(int x) which I get from a DLL (using dlsym()). So, currently my code looks something like this:

void foo(int x) {
    void (*foo)(int x);
    foo = dlsym(dll_handle, "foo");
    int y = foo(x);
    printf("y is %d", y);
}

What I want to is for (something like) this code to work:

void bar(int x) {
    int y = foo(x);
    printf("y is %d", y);
}

So that foo() is a stub which calls the dll function (but does not have to search the DLL every time).

  1. What's the best approach to achieving this for a single function?
  2. For the case of many functions, how would I avoid writing a bunch of copy-paste stubs? A macro solution might be tricky, considering the signature. Perhaps a C++11-based variadic-arg template-based thing?

I have a basic idea for a solution for 1. in an answer below, but I'm not too sure about it, I'd like to adopt the 'best practice' approach here.

yugr
  • 19,769
  • 3
  • 51
  • 96
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • That will certainly work, but it will never unload the shared library with `dlclose(3)` when it's no longer needed. That may be ok, but if you want to be able to unload the library, you need a deinitialization function which will call `dlclose()` and set the `dll_foo` variable back to `NULL`. – Adam Rosenfield Jan 08 '14 at 18:10

3 Answers3

1

You have answered yourself in the question. A small improvement may be to check for "updates" of dll, if any.

int foo(int x) {
     static void (*dll_foo)(int x) = NULL;
     static void *foo_dll_handle = NULL;
     if (dll_foo == NULL || foo_dll_handle != dll_handle) {
          dll_foo = dlsym(dll_handle, "foo");
          foo_dll_handle = dll_handle;
     }
     return dll_foo(x);
}
Marian
  • 7,402
  • 2
  • 22
  • 34
1

For the case of many functions, how would I avoid writing a bunch of copy-paste stubs?

It has been a long time but for completeness, you can use Implib.so to generate such wrappers automatically:

$ implib-gen.py mylib.so

This will generate two files, mylib.so.tramp.S and mylib.so.init.c. Assembly file contains wrappers for library functions (which optionally dlopen library and use dlsym to locate proper implementation):

// Wrapper for bar symbol
  .globl bar
bar:
  .cfi_startproc
  // Check if library function address is resolved
  cmp $0, _libtest_so_tramp_table+0(%rip)
  je 2f
1:
  // Fast path
  jmp *_libtest_so_tramp_table+0
2:
  // Slow path
  pushq $0
  .cfi_adjust_cfa_offset 8
  call save_regs_and_resolve
  addq $8, %rsp
  .cfi_adjust_cfa_offset -8
  jmp 1b
  .cfi_endproc

Generated C code handles dlopen and dlsym part:

void _libtest_so_tramp_resolve(int i) {
  assert(i < sizeof(sym_names) / sizeof(sym_names[0]) - 1);
  if(!lib_handle) {
    lib_handle = dlopen("libtest.so", RTLD_LAZY | RTLD_GLOBAL);
  }
  CHECK(lib_handle, "failed to load library 'libtest.so': %s", dlerror());

  // Can be sped up by manually parsing library symtab...
  _libtest_so_tramp_table[i] = dlsym(lib_handle, sym_names[i]);
  CHECK(_libtest_so_tramp_table[i], "failed to resolve symbol '%s' in library 'libtest.so'", sym_names[i]);
}

For more info, check projects Github page.

yugr
  • 19,769
  • 3
  • 51
  • 96
  • 1. Can you give an example of some generated stubs? 2. What's the .S file for? – einpoklum Nov 29 '17 at 14:07
  • @einpoklum Sure, attached. The .S file is to generate the fastest possible stub functions (they are as fast as normal PLT calls). – yugr Nov 29 '17 at 16:05
  • In the C example, where is lib_handle defined? Is it a global? Also, the C example seems to always take the "slow path". In the assembly file fast path, what does the "+0" do? – einpoklum Nov 29 '17 at 16:42
  • @einpoklum First of all note that I didn't copy-paste all autogenerated code as it's big and probly not too interesting. For a smaller example check [this anwer](https://stackoverflow.com/a/47221180/2170527) (from which Implib.so originated). – yugr Nov 29 '17 at 16:48
  • @einpoklum "In the C example, where is lib_handle defined? Is it a global?" - yes, it's a static variable. – yugr Nov 29 '17 at 16:48
  • @einpoklum "Also, the C example seems to always take the slow path" - C code is only called on first call to every stub function (to call `dlsym` and resolve the address library). – yugr Nov 29 '17 at 16:49
  • @einpoklum "In the assembly file fast path, what does the "+0" do?" - it indexes the pointer table (each stub has it's own pointer, in this example `bar` happened to have pointer stored at index 0). – yugr Nov 29 '17 at 16:50
  • Oh, so the 0 is a literal here because it's the first function. Ok. Anyway, you have your +1... – einpoklum Nov 29 '17 at 16:51
0

For the case of a single function, I was thinking something like this should be right:

int foo(int x) {
     static void (*dll_foo)(int x) = NULL;
     if (dll_foo == NULL) {
          dll_foo = dlsym(dll_handle, "foo");
     }
     return dll_foo(x);
}
einpoklum
  • 118,144
  • 57
  • 340
  • 684