diff options
Diffstat (limited to 'rts')
-rw-r--r-- | rts/CheckUnload.c | 45 | ||||
-rw-r--r-- | rts/Linker.c | 196 | ||||
-rw-r--r-- | rts/LinkerInternals.h | 33 | ||||
-rw-r--r-- | rts/linker/LoadArchive.c | 2 |
4 files changed, 261 insertions, 15 deletions
diff --git a/rts/CheckUnload.c b/rts/CheckUnload.c index fcbe0f6156..8f834d13fa 100644 --- a/rts/CheckUnload.c +++ b/rts/CheckUnload.c @@ -238,21 +238,45 @@ static void reserveOCSectionIndices(OCSectionIndices *s_indices, int len) // state. void insertOCSectionIndices(ObjectCode *oc) { - reserveOCSectionIndices(global_s_indices, oc->n_sections); + // after we finish the section table will no longer be sorted. global_s_indices->sorted = false; - int s_i = global_s_indices->n_sections; - for (int i = 0; i < oc->n_sections; i++) { - if (oc->sections[i].kind != SECTIONKIND_OTHER) { - global_s_indices->indices[s_i].start = (W_)oc->sections[i].start; - global_s_indices->indices[s_i].end = (W_)oc->sections[i].start - + oc->sections[i].size; - global_s_indices->indices[s_i].oc = oc; + if (oc->type == DYNAMIC_OBJECT) { + // First count the ranges + int n_ranges = 0; + for (NativeCodeRange *ncr = oc->nc_ranges; ncr != NULL; ncr = ncr->next) { + n_ranges++; + } + + // Next reserve the appropriate number of table entries... + reserveOCSectionIndices(global_s_indices, n_ranges); + + // Now insert the new ranges... + int s_i = global_s_indices->n_sections; + for (NativeCodeRange *ncr = oc->nc_ranges; ncr != NULL; ncr = ncr->next) { + OCSectionIndex *ent = &global_s_indices->indices[s_i]; + ent->start = (W_)ncr->start; + ent->end = (W_)ncr->end; + ent->oc = oc; s_i++; } - } - global_s_indices->n_sections = s_i; + global_s_indices->n_sections = s_i; + } else { + reserveOCSectionIndices(global_s_indices, oc->n_sections); + int s_i = global_s_indices->n_sections; + for (int i = 0; i < oc->n_sections; i++) { + if (oc->sections[i].kind != SECTIONKIND_OTHER) { + OCSectionIndex *ent = &global_s_indices->indices[s_i]; + ent->start = (W_)oc->sections[i].start; + ent->end = (W_)oc->sections[i].start + oc->sections[i].size; + ent->oc = oc; + s_i++; + } + } + + global_s_indices->n_sections = s_i; + } // Add object to 'objects' list if (objects != NULL) { @@ -446,6 +470,7 @@ void checkUnload() ObjectCode *next = NULL; for (ObjectCode *oc = old_objects; oc != NULL; oc = next) { next = oc->next; + ASSERT(oc->status == OBJECT_UNLOADED); removeOCSectionIndices(s_indices, oc); diff --git a/rts/Linker.c b/rts/Linker.c index f6a38e08dd..96d25fb741 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -64,6 +64,7 @@ # include "linker/Elf.h" # include <regex.h> // regex is already used by dlopen() so this is OK // to use here without requiring an additional lib +# include <link.h> #elif defined(OBJFORMAT_PEi386) # include "linker/PEi386.h" # include <windows.h> @@ -170,6 +171,8 @@ Mutex linker_mutex; /* Generic wrapper function to try and Resolve and RunInit oc files */ int ocTryLoad( ObjectCode* oc ); +static void freeNativeCode_ELF (ObjectCode *nc); + /* Link objects into the lower 2Gb on x86_64 and AArch64. GHC assumes the * small memory model on this architecture (see gcc docs, * -mcmodel=small). @@ -1246,6 +1249,16 @@ freePreloadObjectFile (ObjectCode *oc) */ void freeObjectCode (ObjectCode *oc) { + if (oc->type == DYNAMIC_OBJECT) { +#if defined(OBJFORMAT_ELF) + ACQUIRE_LOCK(&dl_mutex); + freeNativeCode_ELF(oc); + RELEASE_LOCK(&dl_mutex); +#else + barf("freeObjectCode: This shouldn't happen"); +#endif + } + freePreloadObjectFile(oc); if (oc->symbols != NULL) { @@ -1328,7 +1341,7 @@ void freeObjectCode (ObjectCode *oc) } ObjectCode* -mkOc( pathchar *path, char *image, int imageSize, +mkOc( ObjectType type, pathchar *path, char *image, int imageSize, bool mapped, pathchar *archiveMemberName, int misalignment ) { ObjectCode* oc; @@ -1336,6 +1349,7 @@ mkOc( pathchar *path, char *image, int imageSize, oc = stgMallocBytes(sizeof(ObjectCode), "mkOc(oc)"); oc->info = NULL; + oc->type = type; # if defined(OBJFORMAT_ELF) oc->formatName = "ELF"; @@ -1396,6 +1410,10 @@ mkOc( pathchar *path, char *image, int imageSize, oc->rx_m32 = m32_allocator_new(true); #endif + oc->l_addr = NULL; + oc->nc_ranges = NULL; + oc->dlopen_handle = NULL; + IF_DEBUG(linker, debugBelch("mkOc: done\n")); return oc; } @@ -1524,7 +1542,7 @@ preloadObjectFile (pathchar *path) IF_DEBUG(linker, debugBelch("loadObj: preloaded image at %p\n", (void *) image)); /* FIXME (AP): =mapped= parameter unconditionally set to true */ - oc = mkOc(path, image, fileSize, true, NULL, misalignment); + oc = mkOc(STATIC_OBJECT, path, image, fileSize, true, NULL, misalignment); #if defined(OBJFORMAT_MACHO) if (ocVerifyImage_MachO( oc )) @@ -1943,6 +1961,180 @@ addSection (Section *s, SectionKind kind, SectionAlloc alloc, size, kind )); } + +# if defined(OBJFORMAT_ELF) +static int loadNativeObjCb_(struct dl_phdr_info *info, + size_t _size GNUC3_ATTRIBUTE(__unused__), void *data) { + ObjectCode* nc = (ObjectCode*) data; + + // This logic mimicks _dl_addr_inside_object from glibc + // For reference: + // int + // internal_function + // _dl_addr_inside_object (struct link_map *l, const ElfW(Addr) addr) + // { + // int n = l->l_phnum; + // const ElfW(Addr) reladdr = addr - l->l_addr; + // + // while (--n >= 0) + // if (l->l_phdr[n].p_type == PT_LOAD + // && reladdr - l->l_phdr[n].p_vaddr >= 0 + // && reladdr - l->l_phdr[n].p_vaddr < l->l_phdr[n].p_memsz) + // return 1; + // return 0; + // } + + if ((void*) info->dlpi_addr == nc->l_addr) { + int n = info->dlpi_phnum; + while (--n >= 0) { + if (info->dlpi_phdr[n].p_type == PT_LOAD) { + NativeCodeRange* ncr = + stgMallocBytes(sizeof(NativeCodeRange), "loadNativeObjCb_"); + ncr->start = (void*) ((char*) nc->l_addr + info->dlpi_phdr[n].p_vaddr); + ncr->end = (void*) ((char*) ncr->start + info->dlpi_phdr[n].p_memsz); + + ncr->next = nc->nc_ranges; + nc->nc_ranges = ncr; + } + } + } + return 0; +} + +static void copyErrmsg(char** errmsg_dest, char* errmsg) { + if (errmsg == NULL) errmsg = "loadNativeObj_ELF: unknown error"; + *errmsg_dest = stgMallocBytes(strlen(errmsg)+1, "loadNativeObj_ELF"); + strcpy(*errmsg_dest, errmsg); +} + +// need dl_mutex +static void freeNativeCode_ELF (ObjectCode *nc) { + dlclose(nc->dlopen_handle); + + NativeCodeRange *ncr = nc->nc_ranges; + while (ncr) { + NativeCodeRange* last_ncr = ncr; + ncr = ncr->next; + stgFree(last_ncr); + } +} + +static void * loadNativeObj_ELF (pathchar *path, char **errmsg) +{ + ObjectCode* nc; + void *hdl, *retval; + + IF_DEBUG(linker, debugBelch("loadNativeObj_ELF %" PATH_FMT "\n", path)); + + retval = NULL; + ACQUIRE_LOCK(&dl_mutex); + + nc = mkOc(DYNAMIC_OBJECT, path, NULL, 0, true, NULL, 0); + + foreignExportsLoadingObject(nc); + hdl = dlopen(path, RTLD_NOW|RTLD_LOCAL); + foreignExportsFinishedLoadingObject(); + if (hdl == NULL) { + /* dlopen failed; save the message in errmsg */ + copyErrmsg(errmsg, dlerror()); + goto dlopen_fail; + } + + struct link_map *map; + if (dlinfo(hdl, RTLD_DI_LINKMAP, &map) == -1) { + /* dlinfo failed; save the message in errmsg */ + copyErrmsg(errmsg, dlerror()); + goto dlinfo_fail; + } + + nc->l_addr = (void*) map->l_addr; + nc->dlopen_handle = hdl; + hdl = NULL; // pass handle ownership to nc + + dl_iterate_phdr(loadNativeObjCb_, nc); + if (!nc->nc_ranges) { + copyErrmsg(errmsg, "dl_iterate_phdr failed to find obj"); + goto dl_iterate_phdr_fail; + } + + insertOCSectionIndices(nc); + + nc->next_loaded_object = loaded_objects; + loaded_objects = nc; + + retval = nc->dlopen_handle; + goto success; + +dl_iterate_phdr_fail: + // already have dl_mutex + freeNativeCode_ELF(nc); +dlinfo_fail: + if (hdl) dlclose(hdl); +dlopen_fail: +success: + + RELEASE_LOCK(&dl_mutex); + IF_DEBUG(linker, debugBelch("loadNativeObj_ELF result=%p\n", retval)); + + return retval; +} + +# endif + +#define UNUSED(x) (void)(x) + +void * loadNativeObj (pathchar *path, char **errmsg) +{ +#if defined(OBJFORMAT_ELF) + ACQUIRE_LOCK(&linker_mutex); + void *r = loadNativeObj_ELF(path, errmsg); + RELEASE_LOCK(&linker_mutex); + return r; +#else + UNUSED(path); + UNUSED(errmsg); + barf("loadNativeObj: not implemented on this platform"); +#endif +} + +HsInt unloadNativeObj (void *handle) +{ + bool unloadedAnyObj = false; + + IF_DEBUG(linker, debugBelch("unloadNativeObj: %p\n", handle)); + + ObjectCode *prev = NULL, *next; + for (ObjectCode *nc = loaded_objects; nc; nc = next) { + next = nc->next_loaded_object; // we might move nc + + if (nc->type == DYNAMIC_OBJECT && nc->dlopen_handle == handle) { + nc->status = OBJECT_UNLOADED; + n_unloaded_objects += 1; + + // dynamic objects have no symbols + ASSERT(nc->symbols == NULL); + freeOcStablePtrs(nc); + + // Remove object code from root set + if (prev == NULL) { + loaded_objects = nc->next_loaded_object; + } else { + prev->next_loaded_object = nc->next_loaded_object; + } + unloadedAnyObj = true; + } else { + prev = nc; + } + } + + if (unloadedAnyObj) { + return 1; + } else { + errorBelch("unloadObjNativeObj_ELF: can't find `%p' to unload", handle); + return 0; + } +} + /* ----------------------------------------------------------------------------- * Segment management */ diff --git a/rts/LinkerInternals.h b/rts/LinkerInternals.h index 6b726f7a27..93e949f2f2 100644 --- a/rts/LinkerInternals.h +++ b/rts/LinkerInternals.h @@ -31,6 +31,13 @@ typedef struct _Symbol SymbolAddr *addr; } Symbol_t; +typedef struct NativeCodeRange_ { + void *start, *end; + + /* Allow a chain of these things */ + struct NativeCodeRange_ *next; +} NativeCodeRange; + /* Indication of section kinds for loaded objects. Needed by the GC for deciding whether or not a pointer on the stack is a code pointer. @@ -157,6 +164,13 @@ typedef struct { #endif } SymbolExtra; +typedef enum { + /* Objects that were loaded by this linker */ + STATIC_OBJECT, + + /* Objects that were loaded by dlopen */ + DYNAMIC_OBJECT, +} ObjectType; /* Top-level structure for an object module. One of these is allocated * for each object file in use. @@ -165,7 +179,8 @@ typedef struct _ObjectCode { OStatus status; pathchar *fileName; int fileSize; /* also mapped image size when using mmap() */ - char* formatName; /* eg "ELF32", "DLL", "COFF", etc. */ + char* formatName; /* e.g. "ELF32", "DLL", "COFF", etc. */ + ObjectType type; /* who loaded this object? */ /* If this object is a member of an archive, archiveMemberName is * like "libarchive.a(object.o)". Otherwise it's NULL. @@ -267,6 +282,19 @@ typedef struct _ObjectCode { * (read-only/executable) code. */ m32_allocator *rw_m32, *rx_m32; #endif + + /* + * The following are only valid if .type == DYNAMIC_OBJECT + */ + + /* handle returned from dlopen */ + void *dlopen_handle; + + /* base virtual address of the loaded code */ + void *l_addr; + + /* virtual memory ranges of loaded code */ + NativeCodeRange *nc_ranges; } ObjectCode; #define OC_INFORMATIVE_FILENAME(OC) \ @@ -275,6 +303,7 @@ typedef struct _ObjectCode { (OC)->fileName \ ) + #if defined(THREADED_RTS) extern Mutex linker_mutex; #endif @@ -360,7 +389,7 @@ resolveSymbolAddr (pathchar* buffer, int size, HsInt isAlreadyLoaded( pathchar *path ); HsInt loadOc( ObjectCode* oc ); -ObjectCode* mkOc( pathchar *path, char *image, int imageSize, +ObjectCode* mkOc( ObjectType type, pathchar *path, char *image, int imageSize, bool mapped, pathchar *archiveMemberName, int misalignment ); diff --git a/rts/linker/LoadArchive.c b/rts/linker/LoadArchive.c index 0ad3d94725..55081489f5 100644 --- a/rts/linker/LoadArchive.c +++ b/rts/linker/LoadArchive.c @@ -521,7 +521,7 @@ static HsInt loadArchive_ (pathchar *path) pathprintf(archiveMemberName, size, WSTR("%" PATH_FMT "(%.*s)"), path, (int)thisFileNameSize, fileName); - ObjectCode *oc = mkOc(path, image, memberSize, false, archiveMemberName, + ObjectCode *oc = mkOc(STATIC_OBJECT, path, image, memberSize, false, archiveMemberName, misalignment); #if defined(OBJFORMAT_MACHO) ocInit_MachO( oc ); |