From d14356eb43bbff7bd85aee9609b6310fc8f54a23 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Fri, 20 Dec 2013 00:47:32 -0800 Subject: [PATCH] Add Sys.dlpath() to map strings or dlopen handles to absolute pathnames Currently implemented for OSX and Linux only --- base/sysinfo.jl | 11 ++++- src/sys.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/base/sysinfo.jl b/base/sysinfo.jl index c4350f2662989..4041344ba076c 100644 --- a/base/sysinfo.jl +++ b/base/sysinfo.jl @@ -12,7 +12,8 @@ export CPU_CORES, free_memory, total_memory, shlib_ext, - shlib_list + shlib_list, + dlpath import ..Base: WORD_SIZE, OS_NAME, ARCH, MACHINE import ..Base: show, uv_error @@ -192,4 +193,12 @@ function shlib_list() dynamic_libraries end +function dlpath( handle::Ptr{Void} ) + return bytestring(ccall( :jl_pathname_for_handle, Ptr{Uint8}, (Ptr{Void},), handle )) +end + +function dlpath( libname::String ) + return dlpath( dlopen(libname) ) +end + end diff --git a/src/sys.c b/src/sys.c index 396c40a0d0dbb..46d1582ea5c1c 100644 --- a/src/sys.c +++ b/src/sys.c @@ -13,6 +13,7 @@ #include #include #include +#include #endif #include #include @@ -24,6 +25,15 @@ char *dirname(char *); #endif #include +#ifdef __APPLE__ +#include +#include +#endif + +#ifdef _OS_LINUX_ +#include +#endif + #define __STDC_CONSTANT_MACROS #define __STDC_LIMIT_MACROS #include @@ -517,3 +527,100 @@ DLLEXPORT long jl_SC_CLK_TCK(void) return 0; #endif } + +// Dynamic Library interrogation + +#ifdef __APPLE__ +// This code gratefully lifted from: http://stackoverflow.com/questions/20481058 +#ifdef __LP64__ +typedef struct mach_header_64 mach_header_t; +typedef struct segment_command_64 segment_command_t; +typedef struct nlist_64 nlist_t; +#else +typedef struct mach_header mach_header_t; +typedef struct segment_command segment_command_t; +typedef struct nlist nlist_t; +#endif + +static const char * first_external_symbol_for_image(const mach_header_t *header) +{ + Dl_info info; + if (dladdr(header, &info) == 0) + return NULL; + + segment_command_t *seg_linkedit = NULL; + segment_command_t *seg_text = NULL; + struct symtab_command *symtab = NULL; + + struct load_command *cmd = (struct load_command *)((intptr_t)header + sizeof(mach_header_t)); + for (uint32_t i = 0; i < header->ncmds; i++, cmd = (struct load_command *)((intptr_t)cmd + cmd->cmdsize)) + { + switch(cmd->cmd) + { + case LC_SEGMENT: + case LC_SEGMENT_64: + if (!strcmp(((segment_command_t *)cmd)->segname, SEG_TEXT)) + seg_text = (segment_command_t *)cmd; + else if (!strcmp(((segment_command_t *)cmd)->segname, SEG_LINKEDIT)) + seg_linkedit = (segment_command_t *)cmd; + break; + + case LC_SYMTAB: + symtab = (struct symtab_command *)cmd; + break; + } + } + + if ((seg_text == NULL) || (seg_linkedit == NULL) || (symtab == NULL)) + return NULL; + + intptr_t file_slide = ((intptr_t)seg_linkedit->vmaddr - (intptr_t)seg_text->vmaddr) - seg_linkedit->fileoff; + intptr_t strings = (intptr_t)header + (symtab->stroff + file_slide); + nlist_t *sym = (nlist_t *)((intptr_t)header + (symtab->symoff + file_slide)); + + for (uint32_t i = 0; i < symtab->nsyms; i++, sym++) + { + if ((sym->n_type & N_EXT) != N_EXT || !sym->n_value) + continue; + + return (const char *)strings + sym->n_un.n_strx; + } + + return NULL; +} +#endif + + +// Takes a handle (as returned from dlopen()) and returns the absolute path to the image loaded +DLLEXPORT const char * jl_pathname_for_handle(uv_lib_t * uv_lib) +{ + if( !uv_lib ) + return NULL; + + void * handle = uv_lib->handle; +#ifdef __APPLE__ + for (int32_t i = _dyld_image_count(); i >= 0 ; i--) { + const char *first_symbol = first_external_symbol_for_image((const mach_header_t *)_dyld_get_image_header(i)); + if (first_symbol && strlen(first_symbol) > 1) { + handle = (void *)((intptr_t)handle | 1); // in order to trigger findExportedSymbol instead of findExportedSymbolInImageOrDependentImages. See `dlsym` implementation at http://opensource.apple.com/source/dyld/dyld-239.3/src/dyldAPIs.cpp + first_symbol++; // in order to remove the leading underscore + void *address = dlsym(handle, first_symbol); + Dl_info info; + if (dladdr(address, &info)) + return info.dli_fname; + } + } +#endif + +#ifdef _OS_LINUX_ + struct link_map * map; + dlinfo( handle, RTLD_DI_LINKMAP, &map ); + if (map) + return map->l_name; +#endif + +#ifdef _OS_WINDOWS_ + // Not supported yet... +#endif + return NULL; +}