| /* |
| * Elf-dll loader functions |
| * |
| * Copyright 1999 Bertho A. Stultiens |
| */ |
| |
| #include <string.h> |
| #include <ctype.h> |
| #include <stdlib.h> |
| |
| #include "config.h" |
| #include "windef.h" |
| #include "global.h" |
| #include "process.h" |
| #include "module.h" |
| #include "neexe.h" |
| #include "heap.h" |
| #include "wine/winbase16.h" |
| #include "elfdll.h" |
| #include "debugtools.h" |
| #include "winerror.h" |
| |
| DECLARE_DEBUG_CHANNEL(elfdll) |
| |
| #if defined(HAVE_DL_API) |
| #include <dlfcn.h> |
| |
| /*------------------ HACKS -----------------*/ |
| extern DWORD fixup_imports(WINE_MODREF *wm); |
| extern void dump_exports(HMODULE hModule); |
| /*---------------- END HACKS ---------------*/ |
| |
| char *extra_ld_library_path = NULL; /* The extra search-path set in wine.conf */ |
| |
| struct elfdll_image |
| { |
| HMODULE pe_module_start; |
| DWORD pe_module_size; |
| NE_MODULE *ne_module_start; |
| DWORD ne_module_size; |
| }; |
| |
| |
| /**************************************************************************** |
| * ELFDLL_dlopen |
| * |
| * Wrapper for dlopen to search the EXTRA_LD_LIBRARY_PATH from wine.conf |
| * manually because libdl.so caches the environment and does not accept our |
| * changes. |
| */ |
| void *ELFDLL_dlopen(const char *libname, int flags) |
| { |
| char buffer[256]; |
| int namelen; |
| void *handle; |
| char *ldpath; |
| |
| /* First try the default path search of dlopen() */ |
| handle = dlopen(libname, flags); |
| if(handle) |
| return handle; |
| |
| /* Now try to construct searches through our extra search-path */ |
| namelen = strlen(libname); |
| ldpath = extra_ld_library_path; |
| while(ldpath && *ldpath) |
| { |
| int len; |
| char *cptr; |
| char *from; |
| |
| from = ldpath; |
| cptr = strchr(ldpath, ':'); |
| if(!cptr) |
| { |
| len = strlen(ldpath); |
| ldpath = NULL; |
| } |
| else |
| { |
| len = cptr - ldpath; |
| ldpath = cptr + 1; |
| } |
| |
| if(len + namelen + 1 >= sizeof(buffer)) |
| { |
| ERR_(elfdll)("Buffer overflow! Check EXTRA_LD_LIBRARY_PATH or increase buffer size.\n"); |
| return NULL; |
| } |
| |
| strncpy(buffer, from, len); |
| if(len) |
| { |
| buffer[len] = '/'; |
| strcpy(buffer + len + 1, libname); |
| } |
| else |
| strcpy(buffer + len, libname); |
| |
| TRACE_(elfdll)("Trying dlopen('%s', %d)\n", buffer, flags); |
| |
| handle = dlopen(buffer, flags); |
| if(handle) |
| return handle; |
| } |
| return NULL; |
| } |
| |
| |
| /**************************************************************************** |
| * get_sobasename (internal) |
| * |
| */ |
| static LPSTR get_sobasename(LPCSTR path, LPSTR name) |
| { |
| char *cptr; |
| |
| /* Strip the path from the library name */ |
| if((cptr = strrchr(path, '/'))) |
| { |
| char *cp = strrchr(cptr+1, '\\'); |
| if(cp && cp > cptr) |
| cptr = cp; |
| } |
| else |
| cptr = strrchr(path, '\\'); |
| |
| if(!cptr) |
| cptr = (char *)path; /* No '/' nor '\\' in path */ |
| else |
| cptr++; |
| |
| strcpy(name, cptr); |
| cptr = strrchr(name, '.'); |
| if(cptr) |
| *cptr = '\0'; /* Strip extension */ |
| |
| /* Convert to lower case. |
| * This must be done manually because it is not sure that |
| * other modules are accessible. |
| */ |
| for(cptr = name; *cptr; cptr++) |
| *cptr = tolower(*cptr); |
| |
| return name; |
| } |
| |
| |
| /**************************************************************************** |
| * ELFDLL_CreateModref (internal) |
| * |
| * INPUT |
| * hModule - the header from the elf-dll's data-segment |
| * path - requested path from original call |
| * |
| * OUTPUT |
| * A WINE_MODREF pointer to the new object |
| * |
| * BUGS |
| * - Does not handle errors due to dependencies correctly |
| * - path can be wrong |
| */ |
| #define RVA(base, va) (((DWORD)base) + ((DWORD)va)) |
| |
| static WINE_MODREF *ELFDLL_CreateModref(HMODULE hModule, LPCSTR path) |
| { |
| IMAGE_NT_HEADERS *nt = PE_HEADER(hModule); |
| IMAGE_DATA_DIRECTORY *dir; |
| IMAGE_IMPORT_DESCRIPTOR *pe_import = NULL; |
| WINE_MODREF *wm; |
| int len; |
| HANDLE procheap = GetProcessHeap(); |
| |
| wm = (WINE_MODREF *)HeapAlloc(procheap, HEAP_ZERO_MEMORY, sizeof(*wm)); |
| if(!wm) |
| return NULL; |
| |
| wm->module = hModule; |
| wm->type = MODULE32_PE; /* FIXME */ |
| |
| dir = nt->OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_EXPORT; |
| if(dir->Size) |
| wm->binfmt.pe.pe_export = (PIMAGE_EXPORT_DIRECTORY)RVA(hModule, dir->VirtualAddress); |
| |
| dir = nt->OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_IMPORT; |
| if(dir->Size) |
| pe_import = wm->binfmt.pe.pe_import = (PIMAGE_IMPORT_DESCRIPTOR)RVA(hModule, dir->VirtualAddress); |
| |
| dir = nt->OptionalHeader.DataDirectory+IMAGE_DIRECTORY_ENTRY_RESOURCE; |
| if(dir->Size) |
| wm->binfmt.pe.pe_resource = (PIMAGE_RESOURCE_DIRECTORY)RVA(hModule, dir->VirtualAddress); |
| |
| |
| wm->filename = HEAP_strdupA( procheap, 0, path ); |
| wm->modname = strrchr( wm->filename, '\\' ); |
| if (!wm->modname) wm->modname = wm->filename; |
| else wm->modname++; |
| |
| len = GetShortPathNameA( wm->filename, NULL, 0 ); |
| wm->short_filename = (char *)HeapAlloc( procheap, 0, len+1 ); |
| GetShortPathNameA( wm->filename, wm->short_filename, len+1 ); |
| wm->short_modname = strrchr( wm->short_filename, '\\' ); |
| if (!wm->short_modname) wm->short_modname = wm->short_filename; |
| else wm->short_modname++; |
| |
| /* Link MODREF into process list */ |
| |
| EnterCriticalSection( &PROCESS_Current()->crit_section ); |
| |
| wm->next = PROCESS_Current()->modref_list; |
| PROCESS_Current()->modref_list = wm; |
| if ( wm->next ) wm->next->prev = wm; |
| |
| if ( !( nt->FileHeader.Characteristics & IMAGE_FILE_DLL ) |
| && !( wm->flags & WINE_MODREF_LOAD_AS_DATAFILE ) ) |
| |
| { |
| if ( PROCESS_Current()->exe_modref ) |
| FIXME_(elfdll)( "Trying to load second .EXE file: %s\n", path ); |
| else |
| PROCESS_Current()->exe_modref = wm; |
| } |
| |
| LeaveCriticalSection( &PROCESS_Current()->crit_section ); |
| |
| /* Fixup Imports */ |
| |
| if ( pe_import |
| && !( wm->flags & WINE_MODREF_LOAD_AS_DATAFILE ) |
| && !( wm->flags & WINE_MODREF_DONT_RESOLVE_REFS ) |
| && fixup_imports( wm ) ) |
| { |
| /* remove entry from modref chain */ |
| EnterCriticalSection( &PROCESS_Current()->crit_section ); |
| |
| if ( !wm->prev ) |
| PROCESS_Current()->modref_list = wm->next; |
| else |
| wm->prev->next = wm->next; |
| |
| if ( wm->next ) wm->next->prev = wm->prev; |
| wm->next = wm->prev = NULL; |
| |
| LeaveCriticalSection( &PROCESS_Current()->crit_section ); |
| |
| /* FIXME: there are several more dangling references |
| * left. Including dlls loaded by this dll before the |
| * failed one. Unrolling is rather difficult with the |
| * current structure and we can leave it them lying |
| * around with no problems, so we don't care. |
| * As these might reference our wm, we don't free it. |
| */ |
| return NULL; |
| } |
| |
| return wm; |
| } |
| |
| |
| /*********************************************************************** |
| * ELFDLL_CreateNEModule |
| * |
| * Create a dummy NE module for the win32 elf-dll based on the supplied |
| * NE header in the elf-dll. |
| */ |
| static HMODULE16 ELFDLL_CreateNEModule(NE_MODULE *ne_image, DWORD size) |
| { |
| NE_MODULE *pModule; |
| HMODULE16 hModule = GLOBAL_CreateBlock(GMEM_MOVEABLE, ne_image, size, 0, |
| FALSE, FALSE, FALSE, NULL); |
| if(!hModule) |
| return (HMODULE16)0; |
| |
| FarSetOwner16(hModule, hModule); |
| pModule = (NE_MODULE *)GlobalLock16(hModule); |
| pModule->self = hModule; |
| NE_RegisterModule(pModule); |
| return hModule; |
| } |
| |
| |
| /**************************************************************************** |
| * ELFDLL_LoadLibraryExA (internal) |
| * |
| * Implementation of elf-dll loading for PE modules |
| */ |
| WINE_MODREF *ELFDLL_LoadLibraryExA(LPCSTR path, DWORD flags, DWORD *err) |
| { |
| LPVOID dlhandle; |
| struct elfdll_image *image; |
| char name[129]; |
| char soname[129]; |
| HMODULE16 hmod16; |
| WINE_MODREF *wm; |
| |
| get_sobasename(path, name); |
| strcpy(soname, name); |
| strcat(soname, ".so"); |
| |
| /* Try to open the elf-dll */ |
| dlhandle = ELFDLL_dlopen(soname, RTLD_LAZY); |
| if(!dlhandle) |
| { |
| WARN_(elfdll)("Could not load %s (%s)\n", soname, dlerror()); |
| *err = ERROR_FILE_NOT_FOUND; |
| return NULL; |
| } |
| |
| /* Get the 'dllname_elfdll_image' variable */ |
| strcpy(soname, name); |
| strcat(soname, "_elfdll_image"); |
| image = (struct elfdll_image *)dlsym(dlhandle, soname); |
| if(!image) |
| { |
| ERR_(elfdll)("Could not get elfdll image descriptor %s (%s)\n", soname, dlerror()); |
| dlclose(dlhandle); |
| *err = ERROR_BAD_FORMAT; |
| return NULL; |
| } |
| |
| /* Create a win16 dummy module */ |
| hmod16 = ELFDLL_CreateNEModule(image->ne_module_start, image->ne_module_size); |
| if(!hmod16) |
| { |
| ERR_(elfdll)("Could not create win16 dummy module for %s\n", path); |
| dlclose(dlhandle); |
| *err = ERROR_OUTOFMEMORY; |
| return NULL; |
| } |
| |
| image->ne_module_start->module32 = image->pe_module_start; |
| |
| wm = ELFDLL_CreateModref(image->pe_module_start, path); |
| if(!wm) |
| { |
| ERR_(elfdll)("Could not create WINE_MODREF for %s\n", path); |
| GLOBAL_FreeBlock((HGLOBAL16)hmod16); |
| dlclose(dlhandle); |
| *err = ERROR_OUTOFMEMORY; |
| return NULL; |
| } |
| |
| dump_exports(image->pe_module_start); |
| |
| *err = 0; |
| return wm; |
| } |
| |
| |
| /**************************************************************************** |
| * ELFDLL_UnloadLibrary (internal) |
| * |
| * Unload an elf-dll completely from memory and deallocate the modref |
| */ |
| void ELFDLL_UnloadLibrary(WINE_MODREF *wm) |
| { |
| } |
| |
| |
| /**************************************************************************** |
| * ELFDLL_LoadModule16 (internal) |
| * |
| * Implementation of elf-dll loading for NE modules |
| */ |
| HINSTANCE16 ELFDLL_LoadModule16(LPCSTR libname) |
| { |
| return (HINSTANCE16)ERROR_FILE_NOT_FOUND; |
| } |
| |
| #else |
| |
| /* |
| * No elfdlls possible |
| * Just put stubs in here. |
| */ |
| |
| WINE_MODREF *ELFDLL_LoadLibraryExA(LPCSTR libname, DWORD flags, DWORD *err) |
| { |
| *err = ERROR_FILE_NOT_FOUND; |
| return NULL; |
| } |
| |
| void ELFDLL_UnloadLibrary(WINE_MODREF *wm) |
| { |
| } |
| |
| HINSTANCE16 ELFDLL_LoadModule16(LPCSTR libname) |
| { |
| return (HINSTANCE16)ERROR_FILE_NOT_FOUND; |
| } |
| |
| #endif |