Problemas con la interposición LD_PRELOAD y calloc () para ciertos ejecutables

Relacionado a una pregunta previa mía

He interpuesto con éxito malloc , pero calloc parece ser más problemático.

Con ciertos hosts, el calloc se bloquea en un bucle infinito con una posible llamada interna dentro de dlsym . Sin embargo, un host de prueba básico no presenta este comportamiento, pero el comando “ls” de mi sistema sí lo hace.

Aquí está mi código:

 // build with: g++ -O2 -Wall -fPIC -ldl -o libnano.so -shared Main.cc #include  #include  bool gNanoUp = false;// global // Function types typedef void* (*MallocFn)(size_t size); typedef void* (*CallocFn)(size_t elements, size_t size); struct MemoryFunctions { MallocFn mMalloc; CallocFn mCalloc; }; MemoryFunctions orgMemFuncs; // Save original methods. void __attribute__((constructor)) __nano_init(void) { fprintf(stderr, "NANO: init()\n"); // Get address of original functions orgMemFuncs.mMalloc = (MallocFn)dlsym(RTLD_NEXT, "malloc"); orgMemFuncs.mCalloc = (CallocFn)dlsym(RTLD_NEXT, "calloc"); fprintf(stderr, "NANO: malloc() found @%p\n", orgMemFuncs.mMalloc); fprintf(stderr, "NANO: calloc() found @%p\n", orgMemFuncs.mCalloc); gNanoUp = true; } // replacement functions extern "C" { void *malloc(size_t size) { if (!gNanoUp) __nano_init(); return orgMemFuncs.mMalloc(size); } void* calloc(size_t elements, size_t size) { if (!gNanoUp) __nano_init(); return orgMemFuncs.mCalloc(elements, size); } } 

Ahora, cuando hago lo siguiente, obtengo un bucle infinito seguido de una falla de seg, por ejemplo:

 % setenv LD_PRELOAD "./libnano.so" % ls ... NANO: init() NANO: init() NANO: init() Segmentation fault (core dumped) 

Sin embargo, si comento el intercalador de calloc , casi parece funcionar:

 % setenv LD_PRELOAD "./libnano.so" % ls NANO: init() NANO: malloc() found @0x3b36274dc0 NANO: calloc() found @0x3b362749e0 NANO: init() NANO: malloc() found @0x3b36274dc0 NANO: calloc() found @0x3b362749e0  ... 

Así que algunas cosas con “ls” significa que se llama dos veces a init() .

EDITAR Tenga en cuenta que el siguiente progtwig host funciona correctamente: init() solo se llama una vez, y el calloc se interpone con éxito, como se puede ver en la salida.

 // build with: g++ test.cc -o test #include  #include  int main(int argc, char* argv[]) { void* p = malloc(123); printf("HOST p=%p\n", p); free(p); char* c = new char; printf("HOST c=%p\n", c); delete c; void* ca = calloc(10,10); printf("HOST ca=%p\n", ca); free(ca); } % setenv LD_PRELOAD "./libnano.so" % ./test NANO: init() NANO: malloc() found @0x3b36274dc0 NANO: calloc() found @0x3b362749e0 HOST p=0x601010 HOST c=0x601010 HOST ca=0x601030 

Con respecto a __nano_init() se llama dos veces: ha declarado la función como un constructor, por lo que se llama cuando se carga la biblioteca y se llama explícitamente una segunda vez cuando se llama por primera vez a las implementaciones de malloc() y calloc() . Elegir uno.

Con respecto a la interposición de calloc() bloquea su aplicación: algunas de las funciones que está utilizando, como dlsym() y fprintf() , pueden estar intentando asignar memoria, llamando a las funciones de su interposición. Considera las consecuencias, y actúa en consecuencia.

El uso de dlsym basados ​​en dlsym puede provocar lockings, ya que dlsym vuelve a llamar al asignador de memoria. En su lugar, use ganchos malloc , como sugerí en su pregunta anterior ; estos se pueden instalar sin invocar realmente dlsym en absoluto.

Sé que llego un poco tarde (6 años). Pero quería anular calloc() hoy y enfrenté un problema porque dlsym() usa internamente calloc() . Lo resolví usando una técnica simple y pensé en compartirlo aquí:

 static unsigned char buffer[8192]; void *calloc(size_t nmemb, size_t size) { if (calloc_ptr == NULL) // obtained from dlsym return buffer; init(); // uses dlsym() to find address of the real calloc() return calloc_ptr(len); } void free(void *in) { if (in == buffer) return; free_ptr(in); } 

buffer satisface la necesidad de dlsym() hasta que se haya localizado el calloc() real calloc() y se calloc_ptr inicializado el puntero de mi función calloc_ptr .

Puede salirse con un calloc deficiente preliminar que simplemente devuelve NULL. Esto realmente funciona en Linux, YMMV.

 static void* poor_calloc(size_t nmemb, size_t size) { // So dlsym uses calloc internally, which will lead to infinite recursion, since our calloc calls dlsym. // Apparently dlsym can cope with slightly wrong calloc, see for further explanation: // http://blog.bigpixel.ro/2010/09/interposing-calloc-on-linux return NULL; // This is a poor implementation of calloc! }