| /* |
| * UNIX dynamic loader |
| * |
| * Currently only supports stuff using the dl* API. |
| * |
| * Copyright 1998 Marcus Meissner |
| * |
| * FIXME: Small reentrancy problem. |
| * IDEA(s): could be used to split up shell32,comctl32... |
| */ |
| |
| #include "config.h" |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/types.h> |
| |
| #include "snoop.h" |
| #include "heap.h" |
| #include "file.h" |
| #include "module.h" |
| #include "debugtools.h" |
| #include "winerror.h" |
| #include "wine/port.h" |
| |
| DEFAULT_DEBUG_CHANNEL(win32); |
| |
| #ifdef HAVE_DL_API |
| |
| typedef struct { |
| WORD popl WINE_PACKED; /* 0x8f 0x05 */ |
| DWORD addr_popped WINE_PACKED;/* ... */ |
| BYTE pushl1 WINE_PACKED; /* 0x68 */ |
| DWORD newret WINE_PACKED; /* ... */ |
| BYTE pushl2 WINE_PACKED; /* 0x68 */ |
| DWORD origfun WINE_PACKED; /* original function */ |
| BYTE ret1 WINE_PACKED; /* 0xc3 */ |
| WORD addesp WINE_PACKED; /* 0x83 0xc4 */ |
| BYTE nrofargs WINE_PACKED; /* nr of arguments to add esp, */ |
| BYTE pushl3 WINE_PACKED; /* 0x68 */ |
| DWORD oldret WINE_PACKED; /* Filled out from popl above */ |
| BYTE ret2 WINE_PACKED; /* 0xc3 */ |
| } ELF_STDCALL_STUB; |
| |
| #define UNIX_DLL_ENDING "so" |
| |
| #define STUBSIZE 4095 |
| #define STUBOFFSET (sizeof(IMAGE_DOS_HEADER) + \ |
| sizeof(IMAGE_NT_HEADERS) + \ |
| sizeof(IMAGE_SECTION_HEADER)) |
| |
| static FARPROC ELF_FindExportedFunction( WINE_MODREF *wm, LPCSTR funcName, BOOL snoop ); |
| |
| static HMODULE ELF_CreateDummyModule( LPCSTR libname, LPCSTR modname ) |
| { |
| PIMAGE_DOS_HEADER dh; |
| PIMAGE_NT_HEADERS nth; |
| PIMAGE_SECTION_HEADER sh; |
| HMODULE hmod; |
| |
| hmod = (HMODULE)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, |
| sizeof(IMAGE_DOS_HEADER) + |
| sizeof(IMAGE_NT_HEADERS) + |
| sizeof(IMAGE_SECTION_HEADER) + STUBSIZE ); |
| dh = (PIMAGE_DOS_HEADER)hmod; |
| dh->e_magic = IMAGE_DOS_SIGNATURE; |
| dh->e_lfanew = sizeof(IMAGE_DOS_HEADER); |
| nth = PE_HEADER(hmod); |
| nth->Signature = IMAGE_NT_SIGNATURE; |
| nth->FileHeader.Machine = IMAGE_FILE_MACHINE_I386; |
| nth->FileHeader.NumberOfSections = 1; |
| nth->FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER); |
| nth->FileHeader.Characteristics = |
| IMAGE_FILE_RELOCS_STRIPPED|IMAGE_FILE_LINE_NUMS_STRIPPED| |
| IMAGE_FILE_LOCAL_SYMS_STRIPPED|IMAGE_FILE_32BIT_MACHINE| |
| IMAGE_FILE_DLL|IMAGE_FILE_DEBUG_STRIPPED; |
| nth->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC; |
| nth->OptionalHeader.SizeOfCode = 0; |
| nth->OptionalHeader.SizeOfInitializedData = 0; |
| nth->OptionalHeader.SizeOfUninitializedData = 0; |
| nth->OptionalHeader.AddressOfEntryPoint = 0; |
| nth->OptionalHeader.BaseOfCode = 0; |
| nth->OptionalHeader.MajorOperatingSystemVersion = 4; |
| nth->OptionalHeader.MajorImageVersion = 4; |
| nth->OptionalHeader.SizeOfImage = 0; |
| nth->OptionalHeader.SizeOfHeaders = 0; |
| nth->OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_NATIVE; |
| nth->OptionalHeader.DllCharacteristics = 0; |
| nth->OptionalHeader.NumberOfRvaAndSizes = 0; |
| |
| /* allocate one code section that crosses the whole process range |
| * (we could find out from internal tables ... hmm ) |
| */ |
| sh=(PIMAGE_SECTION_HEADER)(nth+1); |
| strcpy(sh->Name,".text"); |
| sh->Misc.VirtualSize = STUBSIZE; |
| sh->VirtualAddress = STUBOFFSET; /* so snoop can use it ... */ |
| sh->SizeOfRawData = STUBSIZE; |
| sh->PointerToRawData = 0; |
| sh->Characteristics = IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ; |
| return hmod; |
| } |
| |
| WINE_MODREF *ELF_LoadLibraryExA( LPCSTR libname, DWORD flags) |
| { |
| WINE_MODREF *wm; |
| HMODULE hmod; |
| char *modname,*s,*t,*x; |
| LPVOID *dlhandle; |
| char error[256]; |
| |
| t = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, |
| strlen(libname) + strlen("lib.so") + 1 ); |
| *t = '\0'; |
| /* copy path to tempvar ... */ |
| s=strrchr(libname,'/'); |
| if (!s) |
| s=strrchr(libname,'\\'); |
| if (s) { |
| s++; /* skip / or \ */ |
| /* copy everything up to s-1 */ |
| memcpy(t,libname,s-libname); |
| t[s-libname]= '\0'; |
| } else |
| s = (LPSTR)libname; |
| modname = s; |
| /* append "lib" foo ".so" */ |
| strcat(t,"lib"); |
| x = t+strlen(t); |
| strcat(t,s); |
| s = strchr(x,'.'); |
| if (s) { |
| while (s) { |
| if (!FILE_strcasecmp(s,".dll")) { |
| strcpy(s+1,UNIX_DLL_ENDING); |
| break; |
| } |
| s=strchr(s+1,'.'); |
| } |
| } else { |
| strcat(x,"."UNIX_DLL_ENDING); |
| } |
| |
| /* grab just the last piece of the path/filename |
| which should be the name of the library we are |
| looking to load. increment by 1 to skip the DOS slash */ |
| s = strrchr(t,'\\'); |
| s++; |
| |
| /* ... and open the library pointed by s, while t points |
| points to the ENTIRE DOS filename of the library |
| t is returned by HeapAlloc() above and so is also used |
| with HeapFree() below */ |
| dlhandle = wine_dlopen(s,RTLD_NOW,error,sizeof(error)); |
| if (!dlhandle) { |
| WARN("failed to load %s: %s\n", s, error); |
| HeapFree( GetProcessHeap(), 0, t ); |
| SetLastError( ERROR_FILE_NOT_FOUND ); |
| return NULL; |
| } |
| |
| hmod = ELF_CreateDummyModule( t, modname ); |
| |
| SNOOP_RegisterDLL(hmod,libname,0,STUBSIZE/sizeof(ELF_STDCALL_STUB)); |
| |
| wm = PE_CreateModule( hmod, libname, 0, 0, FALSE ); |
| wm->find_export = ELF_FindExportedFunction; |
| wm->dlhandle = dlhandle; |
| return wm; |
| } |
| |
| static FARPROC ELF_FindExportedFunction( WINE_MODREF *wm, LPCSTR funcName, BOOL snoop ) |
| { |
| LPVOID fun; |
| int i,nrofargs = 0; |
| ELF_STDCALL_STUB *stub, *first_stub; |
| char error[256]; |
| |
| if (!HIWORD(funcName)) { |
| ERR("Can't import from UNIX dynamic libs by ordinal, sorry.\n"); |
| return (FARPROC)0; |
| } |
| fun = wine_dlsym(wm->dlhandle,funcName,error,sizeof(error)); |
| if (!fun) |
| { |
| /* we sometimes have an excess '_' at the beginning of the name */ |
| if (funcName[0]=='_') |
| { |
| funcName++ ; |
| fun = wine_dlsym(wm->dlhandle,funcName,error,sizeof(error)); |
| } |
| } |
| if (!fun) { |
| /* Function@nrofargs usually marks a stdcall function |
| * with nrofargs bytes that are popped at the end |
| */ |
| if (strchr(funcName,'@')) { |
| LPSTR t,fn = HEAP_strdupA( GetProcessHeap(), 0, funcName ); |
| |
| t = strchr(fn,'@'); |
| *t = '\0'; |
| nrofargs = 0; |
| sscanf(t+1,"%d",&nrofargs); |
| fun = wine_dlsym(wm->dlhandle,fn,error,sizeof(error)); |
| HeapFree( GetProcessHeap(), 0, fn ); |
| } |
| } |
| /* We sometimes have Win32 dlls implemented using stdcall but UNIX |
| * dlls using cdecl. If we find out the number of args the function |
| * uses, we remove them from the stack using two small stubs. |
| */ |
| stub = first_stub = (ELF_STDCALL_STUB *)((char *)wm->module + STUBOFFSET); |
| for (i=0;i<STUBSIZE/sizeof(ELF_STDCALL_STUB);i++) { |
| if (!stub->origfun) |
| break; |
| if (stub->origfun == (DWORD)fun) |
| break; |
| stub++; |
| } |
| if (i==STUBSIZE/sizeof(ELF_STDCALL_STUB)) { |
| ERR("please report, that there are not enough slots for stdcall stubs in the ELF loader.\n"); |
| assert(i<STUBSIZE/sizeof(ELF_STDCALL_STUB)); |
| } |
| if (!stub->origfun) |
| stub->origfun=(DWORD)fun; /* just a marker */ |
| |
| if (fun && nrofargs) { /* we don't need it for 0 args */ |
| /* Selfmodifying entry/return stub for stdcall -> cdecl |
| * conversion. |
| * - Pop returnaddress directly into our return code |
| * popl <into code below> |
| * - Replace it by pointer to start of our returncode |
| * push $newret |
| * - And call the original function |
| * jmp $orgfun |
| * - Remove the arguments no longer needed |
| * newret: add esp, <nrofargs> |
| * - Push the original returnvalue on the stack |
| * pushl <poppedvalue> |
| * - And return to it. |
| * ret |
| */ |
| |
| /* FIXME: The function stub is not reentrant. */ |
| |
| ((LPBYTE)&(stub->popl))[0] = 0x8f; |
| ((LPBYTE)&(stub->popl))[1] = 0x05; |
| stub->addr_popped = (DWORD)&(stub->oldret); |
| stub->pushl1 = 0x68; |
| stub->newret = (DWORD)&(stub->addesp); |
| stub->pushl2 = 0x68; |
| stub->origfun = (DWORD)fun; |
| stub->ret1 = 0xc3; |
| ((LPBYTE)&(stub->addesp))[0]=0x83; |
| ((LPBYTE)&(stub->addesp))[1]=0xc4; |
| stub->nrofargs = nrofargs; |
| stub->pushl3 = 0x68; |
| /* filled out by entrycode */ |
| stub->oldret = 0xdeadbeef; |
| stub->ret2 = 0xc3; |
| fun=(FARPROC)stub; |
| } |
| if (!fun) { |
| FIXME("function %s not found: %s\n",funcName,error); |
| return fun; |
| } |
| fun = SNOOP_GetProcAddress(wm->module,funcName,stub-first_stub,fun); |
| return (FARPROC)fun; |
| } |
| |
| #else /* HAVE_DL_API */ |
| |
| WINE_MODREF *ELF_LoadLibraryExA( LPCSTR libname, DWORD flags) |
| { |
| return NULL; |
| } |
| |
| #endif /* HAVE_DL_API */ |