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"
29 #ifdef HAVE_SYS_STAT_H
30 # include <sys/stat.h>
36 #define NONAMELESSSTRUCT
37 #define NONAMELESSUNION
39 #define WIN32_NO_STATUS
45 #include "wine/unicode.h"
46 #include "wine/library.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(setupapi
);
51 static const char fakedll_signature
[] = "Wine placeholder DLL";
53 static const unsigned int file_alignment
= 512;
54 static const unsigned int section_alignment
= 4096;
55 static const unsigned int max_dll_name_len
= 64;
57 static void *file_buffer
;
58 static size_t file_buffer_size
;
59 static unsigned int handled_count
;
60 static unsigned int handled_total
;
61 static char **handled_dlls
;
71 #define ALIGN(size,align) (((size) + (align) - 1) & ~((align) - 1))
73 /* contents of the dll sections */
75 static const BYTE dll_code_section
[] = { 0x31, 0xc0, /* xor %eax,%eax */
76 0xc2, 0x0c, 0x00 }; /* ret $12 */
78 static const BYTE exe_code_section
[] = { 0xb8, 0x01, 0x00, 0x00, 0x00, /* movl $1,%eax */
79 0xc2, 0x04, 0x00 }; /* ret $4 */
81 static const IMAGE_BASE_RELOCATION reloc_section
; /* empty relocs */
84 /* wrapper for WriteFile */
85 static inline BOOL
xwrite( struct dll_info
*info
, const void *data
, DWORD size
, DWORD offset
)
89 return (SetFilePointer( info
->handle
, offset
, NULL
, FILE_BEGIN
) != INVALID_SET_FILE_POINTER
&&
90 WriteFile( info
->handle
, data
, size
, &res
, NULL
) &&
94 /* add a new section to the dll NT header */
95 static void add_section( struct dll_info
*info
, const char *name
, DWORD size
, DWORD flags
)
97 IMAGE_SECTION_HEADER
*sec
= (IMAGE_SECTION_HEADER
*)(info
->nt
+ 1);
99 sec
+= info
->nt
->FileHeader
.NumberOfSections
;
100 memcpy( sec
->Name
, name
, min( strlen(name
), sizeof(sec
->Name
)) );
101 sec
->Misc
.VirtualSize
= ALIGN( size
, section_alignment
);
102 sec
->VirtualAddress
= info
->mem_pos
;
103 sec
->SizeOfRawData
= size
;
104 sec
->PointerToRawData
= info
->file_pos
;
105 sec
->Characteristics
= flags
;
106 info
->file_pos
+= ALIGN( size
, file_alignment
);
107 info
->mem_pos
+= ALIGN( size
, section_alignment
);
108 info
->nt
->FileHeader
.NumberOfSections
++;
111 /* add a data directory to the dll NT header */
112 static inline void add_directory( struct dll_info
*info
, unsigned int idx
, DWORD rva
, DWORD size
)
114 info
->nt
->OptionalHeader
.DataDirectory
[idx
].VirtualAddress
= rva
;
115 info
->nt
->OptionalHeader
.DataDirectory
[idx
].Size
= size
;
118 /* convert a dll name W->A without depending on the current codepage */
119 static char *dll_name_WtoA( char *nameA
, const WCHAR
*nameW
, unsigned int len
)
123 for (i
= 0; i
< len
; i
++)
126 if (nameW
[i
] > 127) return NULL
;
127 if (c
>= 'A' && c
<= 'Z') c
+= 'a' - 'A';
134 /* convert a dll name A->W without depending on the current codepage */
135 static WCHAR
*dll_name_AtoW( WCHAR
*nameW
, const char *nameA
, unsigned int len
)
139 for (i
= 0; i
< len
; i
++) nameW
[i
] = nameA
[i
];
144 /* add a dll to the list of dll that have been taken care of */
145 static BOOL
add_handled_dll( const WCHAR
*name
)
147 unsigned int len
= strlenW( name
);
148 int i
, min
, max
, pos
, res
;
151 if (!(nameA
= HeapAlloc( GetProcessHeap(), 0, len
+ 1 ))) return FALSE
;
152 if (!dll_name_WtoA( nameA
, name
, len
)) goto failed
;
155 max
= handled_count
- 1;
158 pos
= (min
+ max
) / 2;
159 res
= strcmp( handled_dlls
[pos
], nameA
);
160 if (!res
) goto failed
; /* already in the list */
161 if (res
< 0) min
= pos
+ 1;
165 if (handled_count
>= handled_total
)
168 unsigned int new_count
= max( 64, handled_total
* 2 );
170 if (handled_dlls
) new_dlls
= HeapReAlloc( GetProcessHeap(), 0, handled_dlls
,
171 new_count
* sizeof(*handled_dlls
) );
172 else new_dlls
= HeapAlloc( GetProcessHeap(), 0, new_count
* sizeof(*handled_dlls
) );
173 if (!new_dlls
) goto failed
;
174 handled_dlls
= new_dlls
;
175 handled_total
= new_count
;
178 for (i
= handled_count
; i
> min
; i
--) handled_dlls
[i
] = handled_dlls
[i
- 1];
179 handled_dlls
[i
] = nameA
;
184 HeapFree( GetProcessHeap(), 0, nameA
);
188 /* read in the contents of a file into the global file buffer */
189 /* return 1 on success, 0 on nonexistent file, -1 on other error */
190 static int read_file( const char *name
, void **data
, size_t *size
)
192 static char static_file_buffer
[4096];
194 void *buffer
= static_file_buffer
;
197 IMAGE_DOS_HEADER
*dos
;
198 IMAGE_NT_HEADERS
*nt
;
199 const size_t min_size
= sizeof(*dos
) + sizeof(fakedll_signature
) +
200 FIELD_OFFSET( IMAGE_NT_HEADERS
, OptionalHeader
.MajorLinkerVersion
);
202 if ((fd
= open( name
, O_RDONLY
| O_BINARY
)) == -1) return 0;
203 if (fstat( fd
, &st
) == -1) goto done
;
205 if (st
.st_size
> sizeof(static_file_buffer
))
207 if (!file_buffer
|| st
.st_size
> file_buffer_size
)
209 HeapFree( GetProcessHeap(), 0, file_buffer
);
210 if (!(file_buffer
= HeapAlloc( GetProcessHeap(), 0, st
.st_size
))) goto done
;
211 file_buffer_size
= st
.st_size
;
213 buffer
= file_buffer
;
216 /* check for valid fake dll file */
218 if (st
.st_size
< min_size
) goto done
;
219 header_size
= min( st
.st_size
, 4096 );
220 if (pread( fd
, buffer
, header_size
, 0 ) != header_size
) goto done
;
222 if (dos
->e_magic
!= IMAGE_DOS_SIGNATURE
) goto done
;
223 if (dos
->e_lfanew
< sizeof(fakedll_signature
)) goto done
;
224 if (memcmp( dos
+ 1, fakedll_signature
, sizeof(fakedll_signature
) )) goto done
;
225 if (dos
->e_lfanew
+ FIELD_OFFSET(IMAGE_NT_HEADERS
,OptionalHeader
.MajorLinkerVersion
) > header_size
)
227 nt
= (IMAGE_NT_HEADERS
*)((char *)buffer
+ dos
->e_lfanew
);
228 if (nt
->Signature
== IMAGE_NT_SIGNATURE
&& nt
->OptionalHeader
.Magic
!= IMAGE_NT_OPTIONAL_HDR_MAGIC
)
230 /* wrong 32/64 type, pretend it doesn't exist */
234 if (st
.st_size
== header_size
||
235 pread( fd
, (char *)buffer
+ header_size
,
236 st
.st_size
- header_size
, header_size
) == st
.st_size
- header_size
)
246 /* build a complete fake dll from scratch */
247 static BOOL
build_fake_dll( HANDLE file
)
249 IMAGE_DOS_HEADER
*dos
;
250 IMAGE_NT_HEADERS
*nt
;
251 struct dll_info info
;
254 DWORD lfanew
= (sizeof(*dos
) + sizeof(fakedll_signature
) + 15) & ~15;
255 DWORD size
, header_size
= lfanew
+ sizeof(*nt
);
258 buffer
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, header_size
+ 8 * sizeof(IMAGE_SECTION_HEADER
) );
260 dos
= (IMAGE_DOS_HEADER
*)buffer
;
261 dos
->e_magic
= IMAGE_DOS_SIGNATURE
;
262 dos
->e_cblp
= sizeof(*dos
);
264 dos
->e_cparhdr
= lfanew
/ 16;
266 dos
->e_maxalloc
= 0xffff;
269 dos
->e_lfarlc
= lfanew
;
270 dos
->e_lfanew
= lfanew
;
271 memcpy( dos
+ 1, fakedll_signature
, sizeof(fakedll_signature
) );
273 nt
= info
.nt
= (IMAGE_NT_HEADERS
*)(buffer
+ lfanew
);
274 /* some fields are copied from the source dll */
275 nt
->FileHeader
.Machine
= IMAGE_FILE_MACHINE_I386
;
276 nt
->FileHeader
.TimeDateStamp
= 0;
277 nt
->FileHeader
.Characteristics
= IMAGE_FILE_DLL
;
278 nt
->OptionalHeader
.MajorLinkerVersion
= 1;
279 nt
->OptionalHeader
.MinorLinkerVersion
= 0;
280 nt
->OptionalHeader
.MajorOperatingSystemVersion
= 1;
281 nt
->OptionalHeader
.MinorOperatingSystemVersion
= 0;
282 nt
->OptionalHeader
.MajorImageVersion
= 1;
283 nt
->OptionalHeader
.MinorImageVersion
= 0;
284 nt
->OptionalHeader
.MajorSubsystemVersion
= 4;
285 nt
->OptionalHeader
.MinorSubsystemVersion
= 0;
286 nt
->OptionalHeader
.Win32VersionValue
= 0;
287 nt
->OptionalHeader
.Subsystem
= IMAGE_SUBSYSTEM_WINDOWS_GUI
;
288 nt
->OptionalHeader
.DllCharacteristics
= 0;
289 nt
->OptionalHeader
.SizeOfStackReserve
= 0;
290 nt
->OptionalHeader
.SizeOfStackCommit
= 0;
291 nt
->OptionalHeader
.SizeOfHeapReserve
= 0;
292 nt
->OptionalHeader
.SizeOfHeapCommit
= 0;
293 /* other fields have fixed values */
294 nt
->Signature
= IMAGE_NT_SIGNATURE
;
295 nt
->FileHeader
.NumberOfSections
= 0;
296 nt
->FileHeader
.SizeOfOptionalHeader
= IMAGE_SIZEOF_NT_OPTIONAL_HEADER
;
297 nt
->OptionalHeader
.Magic
= IMAGE_NT_OPTIONAL_HDR_MAGIC
;
298 nt
->OptionalHeader
.ImageBase
= 0x10000000;
299 nt
->OptionalHeader
.SectionAlignment
= section_alignment
;
300 nt
->OptionalHeader
.FileAlignment
= file_alignment
;
301 nt
->OptionalHeader
.NumberOfRvaAndSizes
= IMAGE_NUMBEROF_DIRECTORY_ENTRIES
;
303 header_size
= (BYTE
*)(nt
+ 1) - buffer
;
304 info
.mem_pos
= ALIGN( header_size
, section_alignment
);
305 info
.file_pos
= ALIGN( header_size
, file_alignment
);
307 nt
->OptionalHeader
.AddressOfEntryPoint
= info
.mem_pos
;
308 nt
->OptionalHeader
.BaseOfCode
= info
.mem_pos
;
310 if (nt
->FileHeader
.Characteristics
& IMAGE_FILE_DLL
)
312 size
= sizeof(dll_code_section
);
313 if (!xwrite( &info
, dll_code_section
, size
, info
.file_pos
)) goto done
;
317 size
= sizeof(exe_code_section
);
318 if (!xwrite( &info
, exe_code_section
, size
, info
.file_pos
)) goto done
;
320 nt
->OptionalHeader
.SizeOfCode
= size
;
321 add_section( &info
, ".text", size
, IMAGE_SCN_CNT_CODE
| IMAGE_SCN_MEM_EXECUTE
| IMAGE_SCN_MEM_READ
);
323 if (!xwrite( &info
, &reloc_section
, sizeof(reloc_section
), info
.file_pos
)) goto done
;
324 add_directory( &info
, IMAGE_DIRECTORY_ENTRY_BASERELOC
, info
.mem_pos
, sizeof(reloc_section
) );
325 add_section( &info
, ".reloc", sizeof(reloc_section
),
326 IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_DISCARDABLE
| IMAGE_SCN_MEM_READ
);
328 header_size
+= nt
->FileHeader
.NumberOfSections
* sizeof(IMAGE_SECTION_HEADER
);
329 nt
->OptionalHeader
.SizeOfHeaders
= ALIGN( header_size
, file_alignment
);
330 nt
->OptionalHeader
.SizeOfImage
= ALIGN( info
.mem_pos
, section_alignment
);
331 ret
= xwrite( &info
, buffer
, header_size
, 0 );
333 HeapFree( GetProcessHeap(), 0, buffer
);
337 /* check if an existing file is a fake dll so that we can overwrite it */
338 static BOOL
is_fake_dll( HANDLE h
)
340 IMAGE_DOS_HEADER
*dos
;
342 BYTE buffer
[sizeof(*dos
) + sizeof(fakedll_signature
)];
344 if (!ReadFile( h
, buffer
, sizeof(buffer
), &size
, NULL
) || size
!= sizeof(buffer
))
346 dos
= (IMAGE_DOS_HEADER
*)buffer
;
347 if (dos
->e_magic
!= IMAGE_DOS_SIGNATURE
) return FALSE
;
348 if (dos
->e_lfanew
< size
) return FALSE
;
349 return !memcmp( dos
+ 1, fakedll_signature
, sizeof(fakedll_signature
) );
352 /* create directories leading to a given file */
353 static void create_directories( const WCHAR
*name
)
357 /* create the directory/directories */
358 path
= HeapAlloc(GetProcessHeap(), 0, (strlenW(name
) + 1)*sizeof(WCHAR
));
361 p
= strchrW(path
, '\\');
365 if (!CreateDirectoryW(path
, NULL
))
366 TRACE("Couldn't create directory %s - error: %d\n", wine_dbgstr_w(path
), GetLastError());
368 p
= strchrW(p
+1, '\\');
370 HeapFree(GetProcessHeap(), 0, path
);
373 static inline char *prepend( char *buffer
, const char *str
, size_t len
)
375 return memcpy( buffer
- len
, str
, len
);
378 /* try to load a pre-compiled fake dll */
379 static void *load_fake_dll( const WCHAR
*name
, size_t *size
)
381 const char *build_dir
= wine_get_build_dir();
385 unsigned int i
, pos
, len
, namelen
, maxlen
= 0;
389 if ((p
= strrchrW( name
, '\\' ))) name
= p
+ 1;
392 if (build_dir
) maxlen
= strlen(build_dir
) + sizeof("/programs/") + strlenW(name
);
393 while ((path
= wine_dll_enum_load_path( i
++ ))) maxlen
= max( maxlen
, strlen(path
) );
394 maxlen
+= sizeof("/fakedlls") + strlenW(name
) + 2;
396 if (!(file
= HeapAlloc( GetProcessHeap(), 0, maxlen
))) return NULL
;
398 len
= strlenW( name
);
399 pos
= maxlen
- len
- sizeof(".fake");
400 if (!dll_name_WtoA( file
+ pos
, name
, len
)) goto done
;
405 strcpy( file
+ pos
+ len
+ 1, ".fake" );
410 if (namelen
> 4 && !memcmp( ptr
+ namelen
- 4, ".dll", 4 )) namelen
-= 4;
411 ptr
= prepend( ptr
, ptr
, namelen
);
412 ptr
= prepend( ptr
, "/dlls", sizeof("/dlls") - 1 );
413 ptr
= prepend( ptr
, build_dir
, strlen(build_dir
) );
414 if ((res
= read_file( ptr
, &data
, size
))) goto done
;
416 /* now as a program */
419 if (namelen
> 4 && !memcmp( ptr
+ namelen
- 4, ".exe", 4 )) namelen
-= 4;
420 ptr
= prepend( ptr
, ptr
, namelen
);
421 ptr
= prepend( ptr
, "/programs", sizeof("/programs") - 1 );
422 ptr
= prepend( ptr
, build_dir
, strlen(build_dir
) );
423 if ((res
= read_file( ptr
, &data
, size
))) goto done
;
426 file
[pos
+ len
+ 1] = 0;
427 for (i
= 0; (path
= wine_dll_enum_load_path( i
)); i
++)
429 ptr
= prepend( file
+ pos
, "/fakedlls", sizeof("/fakedlls") - 1 );
430 ptr
= prepend( ptr
, path
, strlen(path
) );
431 if ((res
= read_file( ptr
, &data
, size
))) break;
435 HeapFree( GetProcessHeap(), 0, file
);
436 if (res
== 1) return data
;
440 /* create the fake dll destination file */
441 static HANDLE
create_dest_file( const WCHAR
*name
)
443 /* first check for an existing file */
444 HANDLE h
= CreateFileW( name
, GENERIC_READ
|GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
445 if (h
!= INVALID_HANDLE_VALUE
)
447 if (!is_fake_dll( h
))
449 TRACE( "%s is not a fake dll, not overwriting it\n", debugstr_w(name
) );
453 /* truncate the file */
454 SetFilePointer( h
, 0, NULL
, FILE_BEGIN
);
459 if (GetLastError() == ERROR_PATH_NOT_FOUND
) create_directories( name
);
461 h
= CreateFileW( name
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, 0, NULL
);
462 if (h
== INVALID_HANDLE_VALUE
)
463 ERR( "failed to create %s (error=%u)\n", debugstr_w(name
), GetLastError() );
468 /* copy a fake dll file to the dest directory */
469 static void install_fake_dll( WCHAR
*dest
, char *file
, const char *ext
)
475 WCHAR
*destname
= dest
+ strlenW(dest
);
476 char *name
= strrchr( file
, '/' ) + 1;
477 char *end
= name
+ strlen(name
);
479 if (ext
) strcpy( end
, ext
);
480 if (!(ret
= read_file( file
, &data
, &size
))) return;
482 if (end
> name
+ 2 && !strncmp( end
- 2, "16", 2 )) end
-= 2; /* remove "16" suffix */
483 dll_name_AtoW( destname
, name
, end
- name
);
484 if (!add_handled_dll( destname
)) ret
= -1;
488 HANDLE h
= create_dest_file( dest
);
490 if (h
&& h
!= INVALID_HANDLE_VALUE
)
492 TRACE( "%s -> %s\n", debugstr_a(file
), debugstr_w(dest
) );
494 ret
= (WriteFile( h
, data
, size
, &written
, NULL
) && written
== size
);
495 if (!ret
) ERR( "failed to write to %s (error=%u)\n", debugstr_w(dest
), GetLastError() );
497 if (!ret
) DeleteFileW( dest
);
500 *destname
= 0; /* restore it for next file */
503 /* find and install all fake dlls in a given lib directory */
504 static void install_lib_dir( WCHAR
*dest
, char *file
, const char *default_ext
)
510 if (!(dir
= opendir( file
))) return;
511 name
= file
+ strlen(file
);
513 while ((de
= readdir( dir
)))
515 if (strlen( de
->d_name
) > max_dll_name_len
) continue;
516 if (!strcmp( de
->d_name
, "." )) continue;
517 if (!strcmp( de
->d_name
, ".." )) continue;
518 strcpy( name
, de
->d_name
);
519 if (default_ext
) /* inside build dir */
522 strcat( name
, de
->d_name
);
523 if (!strchr( de
->d_name
, '.' )) strcat( name
, default_ext
);
524 install_fake_dll( dest
, file
, ".fake" );
526 else install_fake_dll( dest
, file
, NULL
);
531 /* create fake dlls in dirname for all the files we can find */
532 static BOOL
create_wildcard_dlls( const WCHAR
*dirname
)
534 const char *build_dir
= wine_get_build_dir();
536 unsigned int i
, maxlen
= 0;
540 if (build_dir
) maxlen
= strlen(build_dir
) + sizeof("/programs/");
541 for (i
= 0; (path
= wine_dll_enum_load_path(i
)); i
++) maxlen
= max( maxlen
, strlen(path
) );
542 maxlen
+= 2 * max_dll_name_len
+ 2 + sizeof(".dll.fake");
543 if (!(file
= HeapAlloc( GetProcessHeap(), 0, maxlen
))) return FALSE
;
545 if (!(dest
= HeapAlloc( GetProcessHeap(), 0, (strlenW(dirname
) + max_dll_name_len
) * sizeof(WCHAR
) )))
547 HeapFree( GetProcessHeap(), 0, file
);
550 strcpyW( dest
, dirname
);
551 dest
[strlenW(dest
) - 1] = 0; /* remove wildcard */
555 strcpy( file
, build_dir
);
556 strcat( file
, "/dlls" );
557 install_lib_dir( dest
, file
, ".dll" );
558 strcpy( file
, build_dir
);
559 strcat( file
, "/programs" );
560 install_lib_dir( dest
, file
, ".exe" );
562 for (i
= 0; (path
= wine_dll_enum_load_path( i
)); i
++)
564 strcpy( file
, path
);
565 strcat( file
, "/fakedlls" );
566 install_lib_dir( dest
, file
, NULL
);
568 HeapFree( GetProcessHeap(), 0, file
);
569 HeapFree( GetProcessHeap(), 0, dest
);
573 /***********************************************************************
576 BOOL
create_fake_dll( const WCHAR
*name
, const WCHAR
*source
)
581 const WCHAR
*filename
;
584 if (!(filename
= strrchrW( name
, '\\' ))) filename
= name
;
587 /* check for empty name which means to only create the directory */
590 create_directories( name
);
593 if (filename
[0] == '*' && !filename
[1]) return create_wildcard_dlls( name
);
595 add_handled_dll( filename
);
597 if (!(h
= create_dest_file( name
))) return TRUE
; /* not a fake dll */
598 if (h
== INVALID_HANDLE_VALUE
) return FALSE
;
600 if (source
[0] == '-' && !source
[1])
602 /* '-' source means delete the file */
603 TRACE( "deleting %s\n", debugstr_w(name
) );
606 else if ((buffer
= load_fake_dll( source
, &size
)))
610 ret
= (WriteFile( h
, buffer
, size
, &written
, NULL
) && written
== size
);
611 if (!ret
) ERR( "failed to write to %s (error=%u)\n", debugstr_w(name
), GetLastError() );
615 WARN( "fake dll %s not found for %s\n", debugstr_w(source
), debugstr_w(name
) );
616 ret
= build_fake_dll( h
);
620 if (!ret
) DeleteFileW( name
);
625 /***********************************************************************
628 void cleanup_fake_dlls(void)
630 HeapFree( GetProcessHeap(), 0, file_buffer
);
632 HeapFree( GetProcessHeap(), 0, handled_dlls
);
633 handled_count
= handled_total
= 0;