@@ -306,75 +306,76 @@ posix_memalign(void** memptr, size_t alignment, size_t size) noexcept
306
306
return ret;
307
307
}
308
308
309
- // We need to override dlopen/dlclose to account for new shared libraries being
310
- // loaded in the process memory space. This is needed so we can correctly track
311
- // allocations in those libraries by overriding their PLT entries and also so we
312
- // can properly map the addresses of the symbols in those libraries when we
313
- // resolve later native traces. Unfortunately, we can't just override dlopen
314
- // directly because of the following edge case: when a shared library dlopen's
315
- // another by name (e.g. dlopen("libfoo.so")), the dlopen call will honor the
316
- // RPATH/RUNPATH of the calling library if it's set. Some libraries set an
317
- // RPATH/RUNPATH based on $ORIGIN (the path of the calling library) to load
318
- // dependencies from a relative directory based on the location of the calling
319
- // library. This means that if we override dlopen, we'll end up loading the
320
- // library from the wrong path or more likely, not loading it at all because the
321
- // dynamic loader will think the memray extenion it's the calling library and
322
- // the RPATH of the real calling library will not be honoured.
323
- //
324
- // To work around this, we override dlsym instead and override the symbols in
325
- // the loaded libraries only the first time we have seen a handle passed to
326
- // dlsym. This works because for a symbol from a given dlopen-ed library to
327
- // appear in a call stack, *something* from that library has to be dlsym-ed
328
- // first. The only exception to this are static initializers, but we cannot
329
- // track those anyway by overriding dlopen as they run within the dlopen call
330
- // itself.
331
- // There's another set of cases we would miss: if library A has a static initializer
332
- // that passes a pointer to one of its functions to library B, and library B stores
333
- // that function pointer, then we could see calls into library A via the function pointer
334
- // held by library B, even though dlsym was never called on library A. This should be
335
- // very rare and will be corrected the next time library B calls dlsym so this should
336
- // not be a problem in practice.
337
-
338
- class DlsymCache
339
- {
340
- public:
341
- auto insert (const void * handle)
342
- {
343
- std::unique_lock lock (mutex_);
344
- return d_handles.insert (handle);
345
- }
346
-
347
- void erase (const void * handle)
348
- {
349
- std::unique_lock lock (mutex_);
350
- d_handles.erase (handle);
351
- }
352
-
353
- private:
354
- mutable std::mutex mutex_;
355
- std::unordered_set<const void *> d_handles;
356
- };
357
-
358
- static DlsymCache dlsym_cache;
359
-
360
309
void *
361
- dlsym ( void * handle, const char * symbol ) noexcept
310
+ dlopen ( const char * filename, int flag ) noexcept
362
311
{
363
- assert (MEMRAY_ORIG (dlsym ));
364
- void * ret;
312
+ assert (MEMRAY_ORIG (dlopen ));
313
+ void * ret = nullptr ;
365
314
{
366
315
tracking_api::RecursionGuard guard;
367
- ret = MEMRAY_ORIG (dlsym)(handle, symbol);
316
+ #if defined(__GLIBC__)
317
+ if (filename != nullptr && filename[0 ] != ' \0 ' && std::strchr (filename, ' /' ) == nullptr ) {
318
+ void * const callerAddr = __builtin_extract_return_addr (__builtin_return_address (0 ));
319
+
320
+ Dl_info info;
321
+ if (dladdr (callerAddr, &info)) {
322
+ const char * dlname = info.dli_fname ;
323
+ {
324
+ struct stat dlstat, exestat;
325
+ // Check fi we are being called from the main executable
326
+ void * main_sym = dlsym (dlopen (nullptr , RTLD_LAZY | RTLD_NOLOAD), " main" );
327
+ if (main_sym && dladdr (main_sym, &main_info)
328
+ && strcmp (main_info.dli_fname , info.dli_fname ) == 0 )
329
+ {
330
+ dlname = nullptr ;
331
+ }
332
+ }
333
+
334
+ void * caller = MEMRAY_ORIG (dlopen)(dlname, RTLD_LAZY | RTLD_NOLOAD);
335
+ if (caller != nullptr ) {
336
+ Dl_serinfo size;
337
+ if (dlinfo (caller, RTLD_DI_SERINFOSIZE, &size) == 0 ) {
338
+ auto * paths = reinterpret_cast <Dl_serinfo*>(new char [size.dls_size ]);
339
+ *paths = size;
340
+ if (dlinfo (caller, RTLD_DI_SERINFO, paths) == 0 ) {
341
+ for (unsigned i = 0 ; i != paths->dls_cnt ; ++i) {
342
+ const char * name = paths->dls_serpath [i].dls_name ;
343
+ std::string path;
344
+ if (name == nullptr || name[0 ] == ' \0 ' ) {
345
+ // In the dynamic linking search path, an
346
+ // empty entry typically represents the
347
+ // current working directory ($PWD).
348
+ path = filename;
349
+ }
350
+ path = name;
351
+ if (path.back () != ' /' ) {
352
+ path += ' /' ;
353
+ }
354
+ path += filename;
355
+ ret = MEMRAY_ORIG (dlopen)(path.c_str (), flag);
356
+ if (ret) {
357
+ break ;
358
+ }
359
+ }
360
+ }
361
+ delete[] reinterpret_cast <char *>(paths);
362
+ }
363
+ dlclose (caller);
364
+ }
365
+ }
366
+ }
367
+ #endif
368
+ // Fallback if we found nothing
369
+ if (ret == nullptr ) {
370
+ ret = MEMRAY_ORIG (dlopen)(filename, flag);
371
+ }
368
372
}
369
373
if (ret) {
370
- auto [_, inserted] = dlsym_cache.insert (handle);
371
- if (inserted) {
372
- tracking_api::Tracker::invalidate_module_cache ();
373
- if (symbol
374
- && (0 == strcmp (symbol, " PyInit_greenlet" ) || 0 == strcmp (symbol, " PyInit__greenlet" )))
375
- {
376
- tracking_api::Tracker::beginTrackingGreenlets ();
377
- }
374
+ tracking_api::Tracker::invalidate_module_cache ();
375
+ if (filename
376
+ && (nullptr != strstr (filename, " /_greenlet." ) || nullptr != strstr (filename, " /greenlet." )))
377
+ {
378
+ tracking_api::Tracker::beginTrackingGreenlets ();
378
379
}
379
380
}
380
381
return ret;
@@ -390,7 +391,6 @@ dlclose(void* handle) noexcept
390
391
tracking_api::RecursionGuard guard;
391
392
ret = MEMRAY_ORIG (dlclose)(handle);
392
393
}
393
- dlsym_cache.erase (handle);
394
394
tracking_api::NativeTrace::flushCache ();
395
395
if (!ret) tracking_api::Tracker::invalidate_module_cache ();
396
396
return ret;
0 commit comments