Implemented DrawPrimitiveStrided and DrawIndexPrimitiveStrided.
[wine] / loader / elf.c
1 /*
2  *      UNIX dynamic loader
3  *
4  * Currently only supports stuff using the dl* API.
5  *
6  * Copyright 1998 Marcus Meissner
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * FIXME:       Small reentrancy problem.
23  * IDEA(s):     could be used to split up shell32,comctl32...
24  */
25
26 #include "config.h"
27 #include "wine/port.h"
28
29 #include <assert.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <sys/types.h>
33
34 #include "snoop.h"
35 #include "file.h"
36 #include "wine/debug.h"
37 #include "winerror.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(win32);
40
41 typedef struct {
42         WORD    popl    WINE_PACKED;    /* 0x8f 0x05 */
43         DWORD   addr_popped WINE_PACKED;/* ...  */
44         BYTE    pushl1  WINE_PACKED;    /* 0x68 */
45         DWORD   newret WINE_PACKED;     /* ...  */
46         BYTE    pushl2  WINE_PACKED;    /* 0x68 */
47         DWORD   origfun WINE_PACKED;    /* original function */
48         BYTE    ret1    WINE_PACKED;    /* 0xc3 */
49         WORD    addesp  WINE_PACKED;    /* 0x83 0xc4 */
50         BYTE    nrofargs WINE_PACKED;   /* nr of arguments to add esp, */
51         BYTE    pushl3  WINE_PACKED;    /* 0x68 */
52         DWORD   oldret  WINE_PACKED;    /* Filled out from popl above  */
53         BYTE    ret2    WINE_PACKED;    /* 0xc3 */
54 } ELF_STDCALL_STUB;
55
56 #define UNIX_DLL_ENDING         "so"
57
58 #define STUBSIZE                4095
59 #define STUBOFFSET  (sizeof(IMAGE_DOS_HEADER) + \
60                      sizeof(IMAGE_NT_HEADERS) + \
61                      sizeof(IMAGE_SECTION_HEADER))
62
63 static FARPROC ELF_FindExportedFunction( WINE_MODREF *wm, LPCSTR funcName, int hint, BOOL snoop );
64
65 static HMODULE ELF_CreateDummyModule( LPCSTR libname, LPCSTR modname )
66 {
67         PIMAGE_DOS_HEADER       dh;
68         PIMAGE_NT_HEADERS       nth;
69         PIMAGE_SECTION_HEADER   sh;
70         HMODULE hmod;
71
72         hmod = (HMODULE)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
73                                      sizeof(IMAGE_DOS_HEADER) +
74                                      sizeof(IMAGE_NT_HEADERS) +
75                                      sizeof(IMAGE_SECTION_HEADER) + STUBSIZE );
76         dh = (PIMAGE_DOS_HEADER)hmod;
77         dh->e_magic = IMAGE_DOS_SIGNATURE;
78         dh->e_lfanew = sizeof(IMAGE_DOS_HEADER);
79         nth = (IMAGE_NT_HEADERS *)(dh + 1);
80         nth->Signature = IMAGE_NT_SIGNATURE;
81         nth->FileHeader.Machine = IMAGE_FILE_MACHINE_I386;
82         nth->FileHeader.NumberOfSections = 1;
83         nth->FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER);
84         nth->FileHeader.Characteristics =
85                 IMAGE_FILE_RELOCS_STRIPPED|IMAGE_FILE_LINE_NUMS_STRIPPED|
86                 IMAGE_FILE_LOCAL_SYMS_STRIPPED|IMAGE_FILE_32BIT_MACHINE|
87                 IMAGE_FILE_DLL|IMAGE_FILE_DEBUG_STRIPPED;
88         nth->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC;
89         nth->OptionalHeader.SizeOfCode = 0;
90         nth->OptionalHeader.SizeOfInitializedData = 0;
91         nth->OptionalHeader.SizeOfUninitializedData = 0;
92         nth->OptionalHeader.AddressOfEntryPoint = 0;
93         nth->OptionalHeader.BaseOfCode          = 0;
94         nth->OptionalHeader.MajorOperatingSystemVersion = 4;
95         nth->OptionalHeader.MajorImageVersion   = 4;
96         nth->OptionalHeader.SizeOfImage         = 0;
97         nth->OptionalHeader.SizeOfHeaders       = 0;
98         nth->OptionalHeader.Subsystem           = IMAGE_SUBSYSTEM_NATIVE;
99         nth->OptionalHeader.DllCharacteristics  = 0;
100         nth->OptionalHeader.NumberOfRvaAndSizes = 0;
101
102         /* allocate one code section that crosses the whole process range
103          * (we could find out from internal tables ... hmm )
104          */
105         sh=(PIMAGE_SECTION_HEADER)(nth+1);
106         strcpy(sh->Name,".text");
107         sh->Misc.VirtualSize    = STUBSIZE;
108         sh->VirtualAddress      = STUBOFFSET; /* so snoop can use it ... */
109         sh->SizeOfRawData       = STUBSIZE;
110         sh->PointerToRawData    = 0;
111         sh->Characteristics     = IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ;
112         return hmod;
113 }
114
115 WINE_MODREF *ELF_LoadLibraryExA( LPCSTR libname, DWORD flags)
116 {
117         WINE_MODREF     *wm;
118         HMODULE         hmod;
119         char            *modname,*s,*t,*x;
120         LPVOID          *dlhandle;
121         char            error[256];
122
123         t = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
124                        strlen(libname) + strlen("lib.so") + 1 );
125         *t = '\0';
126         /* copy path to tempvar ... */
127         s=strrchr(libname,'/');
128         if (!s)
129                 s=strrchr(libname,'\\');
130         if (s) {
131                 s++; /* skip / or \ */
132                 /* copy everything up to s-1 */
133                 memcpy(t,libname,s-libname);
134                 t[s-libname]= '\0';
135         } else
136                 s = (LPSTR)libname;
137         modname = s;
138         /* append "lib" foo ".so" */
139         strcat(t,"lib");
140         x = t+strlen(t);
141         strcat(t,s);
142         s = strchr(x,'.');
143         if (s) {
144             while (s) {
145                 if (!FILE_strcasecmp(s,".dll")) {
146                     strcpy(s+1,UNIX_DLL_ENDING);
147                     break;
148                 }
149                 s=strchr(s+1,'.');
150             }
151         } else {
152                 strcat(x,"."UNIX_DLL_ENDING);
153         }
154
155         /* grab just the last piece of the path/filename
156            which should be the name of the library we are
157            looking to load. increment by 1 to skip the DOS slash */
158         s = strrchr(t,'\\');
159         s++;
160
161        /* ... and open the library pointed by s, while t points
162          points to the ENTIRE DOS filename of the library
163          t is returned by HeapAlloc() above and so is also used
164          with HeapFree() below */
165         dlhandle = wine_dlopen(s,RTLD_NOW,error,sizeof(error));
166         if (!dlhandle) {
167                 WARN("failed to load %s: %s\n", s, error);
168                 HeapFree( GetProcessHeap(), 0, t );
169                 SetLastError( ERROR_FILE_NOT_FOUND );
170                 return NULL;
171         }
172
173         hmod = ELF_CreateDummyModule( t, modname );
174
175         SNOOP_RegisterDLL(hmod,libname,0,STUBSIZE/sizeof(ELF_STDCALL_STUB));
176
177         wm = PE_CreateModule( hmod, libname, 0, 0, FALSE );
178         wm->find_export = ELF_FindExportedFunction;
179         wm->dlhandle = dlhandle;
180         return wm;
181 }
182
183 static FARPROC ELF_FindExportedFunction( WINE_MODREF *wm, LPCSTR funcName, int hint, BOOL snoop )
184 {
185         LPVOID                  fun;
186         int                     i,nrofargs = 0;
187         ELF_STDCALL_STUB        *stub, *first_stub;
188         char                    error[256];
189
190         if (!HIWORD(funcName)) {
191                 ERR("Can't import from UNIX dynamic libs by ordinal, sorry.\n");
192                 return (FARPROC)0;
193         }
194         fun = wine_dlsym(wm->dlhandle,funcName,error,sizeof(error));
195         if (!fun)
196         {
197             /* we sometimes have an excess '_' at the beginning of the name */
198             if (funcName[0]=='_')
199             {
200                 funcName++ ;
201                 fun = wine_dlsym(wm->dlhandle,funcName,error,sizeof(error));
202             }
203         }
204         if (!fun) {
205                 /* Function@nrofargs usually marks a stdcall function
206                  * with nrofargs bytes that are popped at the end
207                  */
208             LPCSTR t;
209             if ((t = strchr(funcName,'@')))
210             {
211                 LPSTR fn = HeapAlloc( GetProcessHeap(), 0, t - funcName + 1 );
212                 memcpy( fn, funcName, t - funcName );
213                 fn[t - funcName] = 0;
214                 nrofargs = 0;
215                 sscanf(t+1,"%d",&nrofargs);
216                 fun = wine_dlsym(wm->dlhandle,fn,error,sizeof(error));
217                 HeapFree( GetProcessHeap(), 0, fn );
218             }
219         }
220         /* We sometimes have Win32 dlls implemented using stdcall but UNIX
221          * dlls using cdecl. If we find out the number of args the function
222          * uses, we remove them from the stack using two small stubs.
223          */
224         stub = first_stub = (ELF_STDCALL_STUB *)((char *)wm->module + STUBOFFSET);
225         for (i=0;i<STUBSIZE/sizeof(ELF_STDCALL_STUB);i++) {
226                 if (!stub->origfun)
227                         break;
228                 if (stub->origfun == (DWORD)fun)
229                         break;
230                 stub++;
231         }
232         if (i==STUBSIZE/sizeof(ELF_STDCALL_STUB)) {
233                 ERR("please report, that there are not enough slots for stdcall stubs in the ELF loader.\n");
234                 assert(i<STUBSIZE/sizeof(ELF_STDCALL_STUB));
235         }
236         if (!stub->origfun)
237                 stub->origfun=(DWORD)fun; /* just a marker */
238
239         if (fun && nrofargs) { /* we don't need it for 0 args */
240                 /* Selfmodifying entry/return stub for stdcall -> cdecl
241                  * conversion.
242                  *  - Pop returnaddress directly into our return code
243                  *              popl <into code below>
244                  *  - Replace it by pointer to start of our returncode
245                  *              push $newret
246                  *  - And call the original function
247                  *              jmp $orgfun
248                  *  - Remove the arguments no longer needed
249                  * newret:      add esp, <nrofargs>
250                  *  - Push the original returnvalue on the stack
251                  *              pushl <poppedvalue>
252                  *  - And return to it.
253                  *              ret
254                  */
255
256                 /* FIXME: The function stub is not reentrant. */
257
258                 ((LPBYTE)&(stub->popl))[0]        = 0x8f;
259                 ((LPBYTE)&(stub->popl))[1]        = 0x05;
260                 stub->addr_popped = (DWORD)&(stub->oldret);
261                 stub->pushl1      = 0x68;
262                 stub->newret      = (DWORD)&(stub->addesp);
263                 stub->pushl2      = 0x68;
264                 stub->origfun     = (DWORD)fun;
265                 stub->ret1        = 0xc3;
266                 ((LPBYTE)&(stub->addesp))[0]=0x83;
267                 ((LPBYTE)&(stub->addesp))[1]=0xc4;
268                 stub->nrofargs    = nrofargs;
269                 stub->pushl3      = 0x68;
270                         /* filled out by entrycode */
271                 stub->oldret      = 0xdeadbeef;
272                 stub->ret2        = 0xc3;
273                 fun=(FARPROC)stub;
274         }
275         if (!fun) {
276                 FIXME("function %s not found: %s\n",funcName,error);
277                 return fun;
278         }
279         fun = SNOOP_GetProcAddress(wm->module,funcName,stub-first_stub,fun);
280         return (FARPROC)fun;
281 }