Skip to content

Commit

Permalink
Add Sys.dlpath() to map strings or dlopen handles to absolute pathnames
Browse files Browse the repository at this point in the history
Currently implemented for OSX and Linux only
  • Loading branch information
staticfloat committed Dec 20, 2013
1 parent 2381c9f commit d14356e
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 1 deletion.
11 changes: 10 additions & 1 deletion base/sysinfo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
107 changes: 107 additions & 0 deletions src/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <sys/wait.h>
#include <unistd.h>
#include <sys/mman.h>
#include <dlfcn.h>
#endif
#include <errno.h>
#include <signal.h>
Expand All @@ -24,6 +25,15 @@ char *dirname(char *);
#endif
#include <fcntl.h>

#ifdef __APPLE__
#include <mach-o/dyld.h>
#include <mach-o/nlist.h>
#endif

#ifdef _OS_LINUX_
#include <link.h>
#endif

#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#include <llvm-c/Target.h>
Expand Down Expand Up @@ -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;
}

0 comments on commit d14356e

Please sign in to comment.