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
23 #define NONAMELESSSTRUCT
24 #define NONAMELESSUNION
26 #define WIN32_NO_STATUS
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(setupapi
);
37 static const char fakedll_signature
[] = "Wine placeholder DLL";
39 static const unsigned int file_alignment
= 512;
40 static const unsigned int section_alignment
= 4096;
50 #define ALIGN(size,align) (((size) + (align) - 1) & ~((align) - 1))
52 /* contents of the dll sections */
54 static const BYTE dll_code_section
[] = { 0x31, 0xc0, /* xor %eax,%eax */
55 0xc2, 0x0c, 0x00 }; /* ret $12 */
57 static const BYTE exe_code_section
[] = { 0xb8, 0x01, 0x00, 0x00, 0x00, /* movl $1,%eax */
58 0xc2, 0x04, 0x00 }; /* ret $4 */
60 static const IMAGE_BASE_RELOCATION reloc_section
; /* empty relocs */
63 /* wrapper for WriteFile */
64 static inline BOOL
xwrite( struct dll_info
*info
, const void *data
, DWORD size
, DWORD offset
)
68 return (SetFilePointer( info
->handle
, offset
, NULL
, FILE_BEGIN
) != INVALID_SET_FILE_POINTER
&&
69 WriteFile( info
->handle
, data
, size
, &res
, NULL
) &&
73 /* add a new section to the dll NT header */
74 static void add_section( struct dll_info
*info
, const char *name
, DWORD size
, DWORD flags
)
76 IMAGE_SECTION_HEADER
*sec
= (IMAGE_SECTION_HEADER
*)(info
->nt
+ 1);
78 sec
+= info
->nt
->FileHeader
.NumberOfSections
;
79 memcpy( (char *)sec
->Name
, name
, min( strlen(name
), sizeof(sec
->Name
)) );
80 sec
->Misc
.VirtualSize
= ALIGN( size
, section_alignment
);
81 sec
->VirtualAddress
= info
->mem_pos
;
82 sec
->SizeOfRawData
= size
;
83 sec
->PointerToRawData
= info
->file_pos
;
84 sec
->Characteristics
= flags
;
85 info
->file_pos
+= ALIGN( size
, file_alignment
);
86 info
->mem_pos
+= ALIGN( size
, section_alignment
);
87 info
->nt
->FileHeader
.NumberOfSections
++;
90 /* add a data directory to the dll NT header */
91 static inline void add_directory( struct dll_info
*info
, unsigned int idx
, DWORD rva
, DWORD size
)
93 info
->nt
->OptionalHeader
.DataDirectory
[idx
].VirtualAddress
= rva
;
94 info
->nt
->OptionalHeader
.DataDirectory
[idx
].Size
= size
;
97 /* find the limits of the resource data */
98 static void get_resource_data( const IMAGE_RESOURCE_DIRECTORY
*dir
, const BYTE
*root
,
99 DWORD
*rva_start
, DWORD
*rva_end
)
101 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
104 entry
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
105 for (i
= 0; i
< dir
->NumberOfNamedEntries
+ dir
->NumberOfIdEntries
; i
++, entry
++)
107 const void *ptr
= root
+ entry
->u2
.s3
.OffsetToDirectory
;
108 if (entry
->u2
.s3
.DataIsDirectory
) get_resource_data( ptr
, root
, rva_start
, rva_end
);
111 const IMAGE_RESOURCE_DATA_ENTRY
*data
= ptr
;
112 *rva_start
= min( *rva_start
, data
->OffsetToData
);
113 *rva_end
= max( *rva_end
, data
->OffsetToData
+ data
->Size
);
118 /* fixup RVAs of resource data */
119 static void fixup_resources( IMAGE_RESOURCE_DIRECTORY
*dir
, BYTE
*root
, int delta
)
121 IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
124 entry
= (IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
125 for (i
= 0; i
< dir
->NumberOfNamedEntries
+ dir
->NumberOfIdEntries
; i
++, entry
++)
127 void *ptr
= root
+ entry
->u2
.s3
.OffsetToDirectory
;
128 if (entry
->u2
.s3
.DataIsDirectory
) fixup_resources( ptr
, root
, delta
);
131 IMAGE_RESOURCE_DATA_ENTRY
*data
= ptr
;
132 if (data
->OffsetToData
) data
->OffsetToData
+= delta
;
137 /* add version resources to the dll by copying them from the source module */
138 static BOOL
add_version_resource( HMODULE module
, struct dll_info
*dll_info
)
141 DWORD rva_start
= ~0U, rva_end
= 0, dir_size
, total_size
;
142 const IMAGE_RESOURCE_DIRECTORY
*basedir
;
143 const BYTE
*data_start
, *root
;
146 if (!module
) return TRUE
;
147 if (LdrFindResourceDirectory_U( module
, NULL
, 0, &basedir
) != STATUS_SUCCESS
) return TRUE
;
148 root
= (const BYTE
*)basedir
;
149 get_resource_data( basedir
, root
, &rva_start
, &rva_end
);
150 data_start
= (const BYTE
*)module
+ rva_start
;
151 if (data_start
<= root
) return FALSE
;
152 dir_size
= data_start
- root
;
153 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, dir_size
))) return FALSE
;
154 memcpy( buffer
, root
, dir_size
);
155 fixup_resources( (IMAGE_RESOURCE_DIRECTORY
*)buffer
, buffer
, dll_info
->mem_pos
+ dir_size
- rva_start
);
156 if (!xwrite( dll_info
, buffer
, dir_size
, dll_info
->file_pos
)) goto done
;
157 if (!xwrite( dll_info
, data_start
, rva_end
- rva_start
, dll_info
->file_pos
+ dir_size
)) goto done
;
158 total_size
= dir_size
+ rva_end
- rva_start
;
159 add_directory( dll_info
, IMAGE_DIRECTORY_ENTRY_RESOURCE
, dll_info
->mem_pos
, total_size
);
160 add_section( dll_info
, ".rsrc", total_size
, IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
);
163 HeapFree( GetProcessHeap(), 0, buffer
);
167 /* build a complete fake dll, optionally using module as a source */
168 static BOOL
build_fake_dll( HANDLE file
, HMODULE module
)
170 IMAGE_DOS_HEADER
*dos
;
171 IMAGE_NT_HEADERS
*nt
;
172 const IMAGE_NT_HEADERS
*src_nt
= NULL
;
173 struct dll_info info
;
176 DWORD lfanew
= (sizeof(*dos
) + sizeof(fakedll_signature
) + 15) & ~15;
177 DWORD size
, header_size
= lfanew
+ sizeof(*nt
);
180 buffer
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, header_size
+ 8 * sizeof(IMAGE_SECTION_HEADER
) );
182 dos
= (IMAGE_DOS_HEADER
*)buffer
;
183 dos
->e_magic
= IMAGE_DOS_SIGNATURE
;
184 dos
->e_cblp
= sizeof(*dos
);
186 dos
->e_cparhdr
= lfanew
/ 16;
188 dos
->e_maxalloc
= 0xffff;
191 dos
->e_lfarlc
= lfanew
;
192 dos
->e_lfanew
= lfanew
;
193 memcpy( dos
+ 1, fakedll_signature
, sizeof(fakedll_signature
) );
195 nt
= info
.nt
= (IMAGE_NT_HEADERS
*)(buffer
+ lfanew
);
196 src_nt
= RtlImageNtHeader( module
);
197 /* some fields are copied from the source dll */
198 #define SET(field,def) nt->field = src_nt ? src_nt->field : def
199 SET( FileHeader
.Machine
, IMAGE_FILE_MACHINE_I386
);
200 SET( FileHeader
.TimeDateStamp
, 0 );
201 SET( FileHeader
.Characteristics
, IMAGE_FILE_DLL
);
202 SET( OptionalHeader
.MajorLinkerVersion
, 1 );
203 SET( OptionalHeader
.MinorLinkerVersion
, 0 );
204 SET( OptionalHeader
.MajorOperatingSystemVersion
, 1 );
205 SET( OptionalHeader
.MinorOperatingSystemVersion
, 0 );
206 SET( OptionalHeader
.MajorImageVersion
, 1 );
207 SET( OptionalHeader
.MinorImageVersion
, 0 );
208 SET( OptionalHeader
.MajorSubsystemVersion
, 4 );
209 SET( OptionalHeader
.MinorSubsystemVersion
, 0 );
210 SET( OptionalHeader
.Win32VersionValue
, 0 );
211 SET( OptionalHeader
.Subsystem
, IMAGE_SUBSYSTEM_WINDOWS_GUI
);
212 SET( OptionalHeader
.DllCharacteristics
, 0 );
213 SET( OptionalHeader
.SizeOfStackReserve
, 0 );
214 SET( OptionalHeader
.SizeOfStackCommit
, 0 );
215 SET( OptionalHeader
.SizeOfHeapReserve
, 0 );
216 SET( OptionalHeader
.SizeOfHeapCommit
, 0 );
218 /* other fields have fixed values */
219 nt
->Signature
= IMAGE_NT_SIGNATURE
;
220 nt
->FileHeader
.NumberOfSections
= 0;
221 nt
->FileHeader
.SizeOfOptionalHeader
= IMAGE_SIZEOF_NT_OPTIONAL_HEADER
;
222 nt
->OptionalHeader
.Magic
= IMAGE_NT_OPTIONAL_HDR_MAGIC
;
223 nt
->OptionalHeader
.ImageBase
= 0x10000000;
224 nt
->OptionalHeader
.SectionAlignment
= section_alignment
;
225 nt
->OptionalHeader
.FileAlignment
= file_alignment
;
226 nt
->OptionalHeader
.NumberOfRvaAndSizes
= IMAGE_NUMBEROF_DIRECTORY_ENTRIES
;
228 header_size
= (BYTE
*)(nt
+ 1) - buffer
;
229 info
.mem_pos
= ALIGN( header_size
, section_alignment
);
230 info
.file_pos
= ALIGN( header_size
, file_alignment
);
232 nt
->OptionalHeader
.AddressOfEntryPoint
= info
.mem_pos
;
233 nt
->OptionalHeader
.BaseOfCode
= info
.mem_pos
;
235 if (nt
->FileHeader
.Characteristics
& IMAGE_FILE_DLL
)
237 size
= sizeof(dll_code_section
);
238 if (!xwrite( &info
, dll_code_section
, size
, info
.file_pos
)) goto done
;
242 size
= sizeof(exe_code_section
);
243 if (!xwrite( &info
, exe_code_section
, size
, info
.file_pos
)) goto done
;
245 nt
->OptionalHeader
.SizeOfCode
= size
;
246 add_section( &info
, ".text", size
, IMAGE_SCN_CNT_CODE
| IMAGE_SCN_MEM_EXECUTE
| IMAGE_SCN_MEM_READ
);
248 if (!xwrite( &info
, &reloc_section
, sizeof(reloc_section
), info
.file_pos
)) goto done
;
249 add_directory( &info
, IMAGE_DIRECTORY_ENTRY_BASERELOC
, info
.mem_pos
, sizeof(reloc_section
) );
250 add_section( &info
, ".reloc", sizeof(reloc_section
),
251 IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_DISCARDABLE
| IMAGE_SCN_MEM_READ
);
253 if (!add_version_resource( module
, &info
)) goto done
;
255 header_size
+= nt
->FileHeader
.NumberOfSections
* sizeof(IMAGE_SECTION_HEADER
);
256 nt
->OptionalHeader
.SizeOfHeaders
= ALIGN( header_size
, file_alignment
);
257 nt
->OptionalHeader
.SizeOfImage
= ALIGN( info
.mem_pos
, section_alignment
);
258 ret
= xwrite( &info
, buffer
, header_size
, 0 );
260 HeapFree( GetProcessHeap(), 0, buffer
);
264 /* check if an existing file is a fake dll so that we can overwrite it */
265 static BOOL
is_fake_dll( HANDLE h
)
267 IMAGE_DOS_HEADER
*dos
;
269 BYTE buffer
[sizeof(*dos
) + sizeof(fakedll_signature
)];
271 if (!ReadFile( h
, buffer
, sizeof(buffer
), &size
, NULL
) || size
!= sizeof(buffer
))
273 dos
= (IMAGE_DOS_HEADER
*)buffer
;
274 if (dos
->e_magic
!= IMAGE_DOS_SIGNATURE
) return FALSE
;
275 if (dos
->e_lfanew
< size
) return FALSE
;
276 return !memcmp( dos
+ 1, fakedll_signature
, sizeof(fakedll_signature
) );
279 /* create directories leading to a given file */
280 static void create_directories( const WCHAR
*name
)
284 /* create the directory/directories */
285 path
= HeapAlloc(GetProcessHeap(), 0, (strlenW(name
) + 1)*sizeof(WCHAR
));
288 p
= strchrW(path
, '\\');
292 if (!CreateDirectoryW(path
, NULL
))
293 TRACE("Couldn't create directory %s - error: %d\n", wine_dbgstr_w(path
), GetLastError());
295 p
= strchrW(p
+1, '\\');
297 HeapFree(GetProcessHeap(), 0, path
);
300 /***********************************************************************
303 BOOL
create_fake_dll( const WCHAR
*name
, const WCHAR
*source
)
309 /* check for empty name which means to only create the directory */
310 if (name
[strlenW(name
) - 1] == '\\')
312 create_directories( name
);
316 /* first check for an existing file */
317 h
= CreateFileW( name
, GENERIC_READ
|GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
318 if (h
!= INVALID_HANDLE_VALUE
)
320 if (!is_fake_dll( h
))
322 TRACE( "%s is not a fake dll, not overwriting it\n", debugstr_w(name
) );
326 /* truncate the file */
327 SetFilePointer( h
, 0, NULL
, FILE_BEGIN
);
332 if (GetLastError() == ERROR_PATH_NOT_FOUND
) create_directories( name
);
334 h
= CreateFileW( name
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, 0, NULL
);
335 if (h
== INVALID_HANDLE_VALUE
)
337 ERR( "failed to create %s (error=%u)\n", debugstr_w(name
), GetLastError() );
342 module
= LoadLibraryW( source
);
344 ret
= build_fake_dll( h
, module
);
347 if (module
) FreeLibrary( module
);
348 if (!ret
) DeleteFileW( name
);