2 * Creation of Wine fake dlls for apps that access the dll file directly.
4 * Copyright 2006 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
26 #ifdef HAVE_SYS_STAT_H
27 # include <sys/stat.h>
33 #define NONAMELESSSTRUCT
34 #define NONAMELESSUNION
36 #define WIN32_NO_STATUS
42 #include "wine/unicode.h"
43 #include "wine/library.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(setupapi
);
48 static const char fakedll_signature
[] = "Wine placeholder DLL";
50 static const unsigned int file_alignment
= 512;
51 static const unsigned int section_alignment
= 4096;
61 #define ALIGN(size,align) (((size) + (align) - 1) & ~((align) - 1))
63 /* contents of the dll sections */
65 static const BYTE dll_code_section
[] = { 0x31, 0xc0, /* xor %eax,%eax */
66 0xc2, 0x0c, 0x00 }; /* ret $12 */
68 static const BYTE exe_code_section
[] = { 0xb8, 0x01, 0x00, 0x00, 0x00, /* movl $1,%eax */
69 0xc2, 0x04, 0x00 }; /* ret $4 */
71 static const IMAGE_BASE_RELOCATION reloc_section
; /* empty relocs */
74 /* wrapper for WriteFile */
75 static inline BOOL
xwrite( struct dll_info
*info
, const void *data
, DWORD size
, DWORD offset
)
79 return (SetFilePointer( info
->handle
, offset
, NULL
, FILE_BEGIN
) != INVALID_SET_FILE_POINTER
&&
80 WriteFile( info
->handle
, data
, size
, &res
, NULL
) &&
84 /* add a new section to the dll NT header */
85 static void add_section( struct dll_info
*info
, const char *name
, DWORD size
, DWORD flags
)
87 IMAGE_SECTION_HEADER
*sec
= (IMAGE_SECTION_HEADER
*)(info
->nt
+ 1);
89 sec
+= info
->nt
->FileHeader
.NumberOfSections
;
90 memcpy( sec
->Name
, name
, min( strlen(name
), sizeof(sec
->Name
)) );
91 sec
->Misc
.VirtualSize
= ALIGN( size
, section_alignment
);
92 sec
->VirtualAddress
= info
->mem_pos
;
93 sec
->SizeOfRawData
= size
;
94 sec
->PointerToRawData
= info
->file_pos
;
95 sec
->Characteristics
= flags
;
96 info
->file_pos
+= ALIGN( size
, file_alignment
);
97 info
->mem_pos
+= ALIGN( size
, section_alignment
);
98 info
->nt
->FileHeader
.NumberOfSections
++;
101 /* add a data directory to the dll NT header */
102 static inline void add_directory( struct dll_info
*info
, unsigned int idx
, DWORD rva
, DWORD size
)
104 info
->nt
->OptionalHeader
.DataDirectory
[idx
].VirtualAddress
= rva
;
105 info
->nt
->OptionalHeader
.DataDirectory
[idx
].Size
= size
;
108 /* find the limits of the resource data */
109 static void get_resource_data( const IMAGE_RESOURCE_DIRECTORY
*dir
, const BYTE
*root
,
110 DWORD
*rva_start
, DWORD
*rva_end
)
112 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
115 entry
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
116 for (i
= 0; i
< dir
->NumberOfNamedEntries
+ dir
->NumberOfIdEntries
; i
++, entry
++)
118 const void *ptr
= root
+ entry
->u2
.s3
.OffsetToDirectory
;
119 if (entry
->u2
.s3
.DataIsDirectory
) get_resource_data( ptr
, root
, rva_start
, rva_end
);
122 const IMAGE_RESOURCE_DATA_ENTRY
*data
= ptr
;
123 *rva_start
= min( *rva_start
, data
->OffsetToData
);
124 *rva_end
= max( *rva_end
, data
->OffsetToData
+ data
->Size
);
129 /* fixup RVAs of resource data */
130 static void fixup_resources( IMAGE_RESOURCE_DIRECTORY
*dir
, BYTE
*root
, int delta
)
132 IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
135 entry
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
136 for (i
= 0; i
< dir
->NumberOfNamedEntries
+ dir
->NumberOfIdEntries
; i
++, entry
++)
138 void *ptr
= root
+ entry
->u2
.s3
.OffsetToDirectory
;
139 if (entry
->u2
.s3
.DataIsDirectory
) fixup_resources( ptr
, root
, delta
);
142 IMAGE_RESOURCE_DATA_ENTRY
*data
= ptr
;
143 if (data
->OffsetToData
) data
->OffsetToData
+= delta
;
148 /* add version resources to the dll by copying them from the source module */
149 static BOOL
add_version_resource( HMODULE module
, struct dll_info
*dll_info
)
152 DWORD rva_start
= ~0U, rva_end
= 0, dir_size
, total_size
;
153 const IMAGE_RESOURCE_DIRECTORY
*basedir
;
154 const BYTE
*data_start
, *root
;
157 if (!module
) return TRUE
;
158 if (LdrFindResourceDirectory_U( module
, NULL
, 0, &basedir
) != STATUS_SUCCESS
) return TRUE
;
159 root
= (const BYTE
*)basedir
;
160 get_resource_data( basedir
, root
, &rva_start
, &rva_end
);
161 data_start
= (const BYTE
*)module
+ rva_start
;
162 if (data_start
<= root
) return FALSE
;
163 dir_size
= data_start
- root
;
164 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, dir_size
))) return FALSE
;
165 memcpy( buffer
, root
, dir_size
);
166 fixup_resources( (IMAGE_RESOURCE_DIRECTORY
*)buffer
, buffer
, dll_info
->mem_pos
+ dir_size
- rva_start
);
167 if (!xwrite( dll_info
, buffer
, dir_size
, dll_info
->file_pos
)) goto done
;
168 if (!xwrite( dll_info
, data_start
, rva_end
- rva_start
, dll_info
->file_pos
+ dir_size
)) goto done
;
169 total_size
= dir_size
+ rva_end
- rva_start
;
170 add_directory( dll_info
, IMAGE_DIRECTORY_ENTRY_RESOURCE
, dll_info
->mem_pos
, total_size
);
171 add_section( dll_info
, ".rsrc", total_size
, IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
);
174 HeapFree( GetProcessHeap(), 0, buffer
);
178 /* build a complete fake dll, optionally using module as a source */
179 static BOOL
build_fake_dll( HANDLE file
, HMODULE module
)
181 IMAGE_DOS_HEADER
*dos
;
182 IMAGE_NT_HEADERS
*nt
;
183 const IMAGE_NT_HEADERS
*src_nt
= NULL
;
184 struct dll_info info
;
187 DWORD lfanew
= (sizeof(*dos
) + sizeof(fakedll_signature
) + 15) & ~15;
188 DWORD size
, header_size
= lfanew
+ sizeof(*nt
);
191 buffer
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, header_size
+ 8 * sizeof(IMAGE_SECTION_HEADER
) );
193 dos
= (IMAGE_DOS_HEADER
*)buffer
;
194 dos
->e_magic
= IMAGE_DOS_SIGNATURE
;
195 dos
->e_cblp
= sizeof(*dos
);
197 dos
->e_cparhdr
= lfanew
/ 16;
199 dos
->e_maxalloc
= 0xffff;
202 dos
->e_lfarlc
= lfanew
;
203 dos
->e_lfanew
= lfanew
;
204 memcpy( dos
+ 1, fakedll_signature
, sizeof(fakedll_signature
) );
206 nt
= info
.nt
= (IMAGE_NT_HEADERS
*)(buffer
+ lfanew
);
207 if (module
) src_nt
= RtlImageNtHeader( module
);
208 /* some fields are copied from the source dll */
209 #define SET(field,def) nt->field = src_nt ? src_nt->field : def
210 SET( FileHeader
.Machine
, IMAGE_FILE_MACHINE_I386
);
211 SET( FileHeader
.TimeDateStamp
, 0 );
212 SET( FileHeader
.Characteristics
, IMAGE_FILE_DLL
);
213 SET( OptionalHeader
.MajorLinkerVersion
, 1 );
214 SET( OptionalHeader
.MinorLinkerVersion
, 0 );
215 SET( OptionalHeader
.MajorOperatingSystemVersion
, 1 );
216 SET( OptionalHeader
.MinorOperatingSystemVersion
, 0 );
217 SET( OptionalHeader
.MajorImageVersion
, 1 );
218 SET( OptionalHeader
.MinorImageVersion
, 0 );
219 SET( OptionalHeader
.MajorSubsystemVersion
, 4 );
220 SET( OptionalHeader
.MinorSubsystemVersion
, 0 );
221 SET( OptionalHeader
.Win32VersionValue
, 0 );
222 SET( OptionalHeader
.Subsystem
, IMAGE_SUBSYSTEM_WINDOWS_GUI
);
223 SET( OptionalHeader
.DllCharacteristics
, 0 );
224 SET( OptionalHeader
.SizeOfStackReserve
, 0 );
225 SET( OptionalHeader
.SizeOfStackCommit
, 0 );
226 SET( OptionalHeader
.SizeOfHeapReserve
, 0 );
227 SET( OptionalHeader
.SizeOfHeapCommit
, 0 );
229 /* other fields have fixed values */
230 nt
->Signature
= IMAGE_NT_SIGNATURE
;
231 nt
->FileHeader
.NumberOfSections
= 0;
232 nt
->FileHeader
.SizeOfOptionalHeader
= IMAGE_SIZEOF_NT_OPTIONAL_HEADER
;
233 nt
->OptionalHeader
.Magic
= IMAGE_NT_OPTIONAL_HDR_MAGIC
;
234 nt
->OptionalHeader
.ImageBase
= 0x10000000;
235 nt
->OptionalHeader
.SectionAlignment
= section_alignment
;
236 nt
->OptionalHeader
.FileAlignment
= file_alignment
;
237 nt
->OptionalHeader
.NumberOfRvaAndSizes
= IMAGE_NUMBEROF_DIRECTORY_ENTRIES
;
239 header_size
= (BYTE
*)(nt
+ 1) - buffer
;
240 info
.mem_pos
= ALIGN( header_size
, section_alignment
);
241 info
.file_pos
= ALIGN( header_size
, file_alignment
);
243 nt
->OptionalHeader
.AddressOfEntryPoint
= info
.mem_pos
;
244 nt
->OptionalHeader
.BaseOfCode
= info
.mem_pos
;
246 if (nt
->FileHeader
.Characteristics
& IMAGE_FILE_DLL
)
248 size
= sizeof(dll_code_section
);
249 if (!xwrite( &info
, dll_code_section
, size
, info
.file_pos
)) goto done
;
253 size
= sizeof(exe_code_section
);
254 if (!xwrite( &info
, exe_code_section
, size
, info
.file_pos
)) goto done
;
256 nt
->OptionalHeader
.SizeOfCode
= size
;
257 add_section( &info
, ".text", size
, IMAGE_SCN_CNT_CODE
| IMAGE_SCN_MEM_EXECUTE
| IMAGE_SCN_MEM_READ
);
259 if (!xwrite( &info
, &reloc_section
, sizeof(reloc_section
), info
.file_pos
)) goto done
;
260 add_directory( &info
, IMAGE_DIRECTORY_ENTRY_BASERELOC
, info
.mem_pos
, sizeof(reloc_section
) );
261 add_section( &info
, ".reloc", sizeof(reloc_section
),
262 IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_DISCARDABLE
| IMAGE_SCN_MEM_READ
);
264 if (!add_version_resource( module
, &info
)) goto done
;
266 header_size
+= nt
->FileHeader
.NumberOfSections
* sizeof(IMAGE_SECTION_HEADER
);
267 nt
->OptionalHeader
.SizeOfHeaders
= ALIGN( header_size
, file_alignment
);
268 nt
->OptionalHeader
.SizeOfImage
= ALIGN( info
.mem_pos
, section_alignment
);
269 ret
= xwrite( &info
, buffer
, header_size
, 0 );
271 HeapFree( GetProcessHeap(), 0, buffer
);
275 /* check if an existing file is a fake dll so that we can overwrite it */
276 static BOOL
is_fake_dll( HANDLE h
)
278 IMAGE_DOS_HEADER
*dos
;
280 BYTE buffer
[sizeof(*dos
) + sizeof(fakedll_signature
)];
282 if (!ReadFile( h
, buffer
, sizeof(buffer
), &size
, NULL
) || size
!= sizeof(buffer
))
284 dos
= (IMAGE_DOS_HEADER
*)buffer
;
285 if (dos
->e_magic
!= IMAGE_DOS_SIGNATURE
) return FALSE
;
286 if (dos
->e_lfanew
< size
) return FALSE
;
287 return !memcmp( dos
+ 1, fakedll_signature
, sizeof(fakedll_signature
) );
290 /* create directories leading to a given file */
291 static void create_directories( const WCHAR
*name
)
295 /* create the directory/directories */
296 path
= HeapAlloc(GetProcessHeap(), 0, (strlenW(name
) + 1)*sizeof(WCHAR
));
299 p
= strchrW(path
, '\\');
303 if (!CreateDirectoryW(path
, NULL
))
304 TRACE("Couldn't create directory %s - error: %d\n", wine_dbgstr_w(path
), GetLastError());
306 p
= strchrW(p
+1, '\\');
308 HeapFree(GetProcessHeap(), 0, path
);
311 static inline char *prepend( char *buffer
, const char *str
, size_t len
)
313 return memcpy( buffer
- len
, str
, len
);
316 /* try to load a pre-compiled fake dll */
317 static void *load_fake_dll( const WCHAR
*name
, unsigned int *size
, void *buf
, unsigned int buf_size
)
319 const char *build_dir
= wine_get_build_dir();
322 char *file
, *ptr
, *buffer
= NULL
;
323 unsigned int i
, pos
, len
, namelen
, maxlen
= 0;
327 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
330 if (build_dir
) maxlen
= strlen(build_dir
) + sizeof("/programs/") + strlenW(name
);
331 while ((path
= wine_dll_enum_load_path( i
++ ))) maxlen
= max( maxlen
, strlen(path
) );
332 maxlen
+= sizeof("/fakedlls") + strlenW(name
) + 2;
334 if (!(file
= HeapAlloc( GetProcessHeap(), 0, maxlen
))) return NULL
;
336 len
= strlenW( name
);
337 pos
= maxlen
- len
- sizeof(".fake");
338 for (i
= 0; i
< len
; i
++)
340 /* we don't want to depend on the current codepage here */
342 if (name
[i
] > 127) goto done
;
343 if (c
>= 'A' && c
<= 'Z') c
+= 'a' - 'A';
350 strcpy( file
+ pos
+ len
+ 1, ".fake" );
355 if (namelen
> 4 && !memcmp( ptr
+ namelen
- 4, ".dll", 4 )) namelen
-= 4;
356 ptr
= prepend( ptr
, ptr
, namelen
);
357 ptr
= prepend( ptr
, "/dlls", sizeof("/dlls") - 1 );
358 ptr
= prepend( ptr
, build_dir
, strlen(build_dir
) );
359 if ((fd
= open( ptr
, O_RDONLY
| O_BINARY
)) != -1) goto found
;
361 /* now as a program */
364 if (namelen
> 4 && !memcmp( ptr
+ namelen
- 4, ".exe", 4 )) namelen
-= 4;
365 ptr
= prepend( ptr
, ptr
, namelen
);
366 ptr
= prepend( ptr
, "/programs", sizeof("/programs") - 1 );
367 ptr
= prepend( ptr
, build_dir
, strlen(build_dir
) );
368 if ((fd
= open( ptr
, O_RDONLY
| O_BINARY
)) != -1) goto found
;
371 file
[pos
+ len
+ 1] = 0;
372 for (i
= 0; (path
= wine_dll_enum_load_path( i
)); i
++)
374 ptr
= prepend( file
+ pos
, "/fakedlls", sizeof("/fakedlls") - 1 );
375 ptr
= prepend( ptr
, path
, strlen(path
) );
376 if ((fd
= open( ptr
, O_RDONLY
| O_BINARY
)) != -1) goto found
;
380 if (!fstat( fd
, &st
))
383 if (st
.st_size
<= buf_size
) buffer
= buf
;
384 else buffer
= HeapAlloc( GetProcessHeap(), 0, st
.st_size
);
387 if (read( fd
, buffer
, st
.st_size
) != st
.st_size
)
389 if (buffer
!= buf
) HeapFree( GetProcessHeap(), 0, buffer
);
392 else TRACE( "found %s %u bytes\n", ptr
, *size
);
398 HeapFree( GetProcessHeap(), 0, file
);
402 /***********************************************************************
405 BOOL
create_fake_dll( const WCHAR
*name
, const WCHAR
*source
)
409 unsigned int size
= 0;
410 char *temp_buffer
[4096];
413 /* check for empty name which means to only create the directory */
414 if (name
[strlenW(name
) - 1] == '\\')
416 create_directories( name
);
420 /* first check for an existing file */
421 h
= CreateFileW( name
, GENERIC_READ
|GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
422 if (h
!= INVALID_HANDLE_VALUE
)
424 if (!is_fake_dll( h
))
426 TRACE( "%s is not a fake dll, not overwriting it\n", debugstr_w(name
) );
430 /* truncate the file */
431 SetFilePointer( h
, 0, NULL
, FILE_BEGIN
);
436 if (GetLastError() == ERROR_PATH_NOT_FOUND
) create_directories( name
);
438 h
= CreateFileW( name
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, 0, NULL
);
439 if (h
== INVALID_HANDLE_VALUE
)
441 ERR( "failed to create %s (error=%u)\n", debugstr_w(name
), GetLastError() );
446 if ((buffer
= load_fake_dll( source
, &size
, temp_buffer
, sizeof(temp_buffer
) )))
450 ret
= (WriteFile( h
, buffer
, size
, &written
, NULL
) && written
== size
);
451 if (!ret
) ERR( "failed to write to %s (error=%u)\n", debugstr_w(name
), GetLastError() );
452 if (buffer
!= temp_buffer
) HeapFree( GetProcessHeap(), 0, buffer
);
456 HMODULE module
= LoadLibraryW( source
);
457 WARN( "fake dll %s not found for %s\n", debugstr_w(source
), debugstr_w(name
) );
458 ret
= build_fake_dll( h
, module
);
459 if (module
) FreeLibrary( module
);
463 if (!ret
) DeleteFileW( name
);