Search code examples
clinuxnamespacesshared-librariesdlopen

dlmopen() can't resolve defined function symbol in created namespace


I am using dlmopen in C to load shared libraries into an isolated namespace. My goal is to make symbols in that namespace all belong to the loaded libraries. So they can't access symbols in my main program. Flowing is the pseu·do·code of the main function to show the process.

// when first .so is loaded, load it into a new namespace and store that namespace 
// function a() is defined in it
void* handle1 = dlmopen(LM_ID_NEWLM, new_so_name1, RTLD_NOW);
// store the namespace in lmid
dlinfo(handle1,  RTLD_DI_LMID, &lmid); 
// when second .so is loaded, load it in lmid
// function b() is defined in it
void* handle2 = dlmopen(lmid, new_so_name2, RTLD_NOW);

Works pretty good. And it seems that both a() and b() are loaded into the namespace. But When I load a third shared library, something strange happens:

// If I make a call a() in the third library
void* handle3 = dlmopen(lmid, new_so_name3, RTLD_NOW);
// call a() using dlsym() 

The loading succeed and I can call that function a(). But if I make a call b() in the third library and load it into the same namespace:

void* handle3 = dlmopen(lmid, new_so_name3, RTLD_NOW);
printf("error:%s\n", dlerror());

It gives the following error:

erroe:undefined symbol: b

It seems that the second library weren't loaded in the namespace successfully. But As I've shown, the second load succeeded and a non-null handle2 is returned. So why does this happen, what's wrong with my loading process? I just want all loaded libraries can share symbols in that specific namespace, to avoid using symbols in my main program.

EDIT: Appendix

As suggested by @CharlieBurns. I post the smallest program below to show my problem.

#define _GNU_SOURCE
#include <stdio.h>
#include <link.h>
#include <dlfcn.h>
void* handle1;
void* handle2;
void* handle3;
Lmid_t lmid;
int main() {
  // handle1 contains: int a(){return 0;}
  handle1 = dlmopen(LM_ID_NEWLM, "/tmp/lib-o1i6Yd.so", RTLD_NOW);
  dlinfo(handle1, RTLD_DI_LMID, &lmid);
  // handle2 contains: int b(){return 1;}
  handle2 = dlmopen(lmid, "/tmp/lib-vSyM4y.so", RTLD_NOW);
#ifdef RUN_A
  int (*A)(void);
  handle3 = dlmopen(lmid, "/tmp/lib-lAGjb2.so", RTLD_NOW);
  A = (int(*)(void))dlsym(handle3, "__wrapper_lAGjb2");
/*
  int __wrapper_lAGjb2() {
    return a();
  }
*/
  printf("%d\n", (*A)()); // output 0 as expected
  return 0;
#endif 

#ifdef RUN_B
  int (*B)(void);
  handle3 = dlmopen(lmid, "/tmp/lib-CO1YAD.so", RTLD_NOW);
  // error: /tmp/lib-CO1YAD.so: undefined symbol: b
  printf("error: %s\n", dlerror()); 
  B = (int(*)(void))dlsym(handle3, "__wrapper_CO1YAD");
  // error: ./a.out: undefined symbol: __wrapper_CO1YAD
  printf("error: %s\n", dlerror()); // 
/*
  int __wrapper_CO1YAD() {
    return b();
  }
*/
  printf("%d\n", (*B)()); // segmentation fault (core dumped)
  return 0;
#endif
}

This is test.c file. I compile by using gcc test.c -ldl -DRUN_A/B, then ./a.out to run it.

EDIT: /tmp/.so files

They are created manually. For example, to create /tmp/lib-o1i6Yd.so file using gcc: write code in /tmp/lib-o1i6Yd.c:

int a(){return 0;}

compile to .o object file:

gcc -c -fpic /tmp/lib-o1i6Yd.c -o /tmp/lib-o1i6Yd.o

then create .so file:

gcc -shared -o /tmp/lib-o1i6Yd.so /tmp/lib-o1i6Yd.o

Solution

  • Manaul doesn't clearly state the namespace problem(or I didn't understand it clearly) Newly loaded shared libraries using dlmopen() will be in RTLD_LOCAL state default. That is, the latest loaded function can't call functions or using variables that loaded previously. See this post for more information.