/* * Elf-dll loader functions * * Copyright 1999 Bertho A. Stultiens */ #include #include #include #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" DEFAULT_DEBUG_CHANNEL(elfdll) #if defined(HAVE_DL_API) #include /*------------------ 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("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("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( "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("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("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("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("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