Synchronize with trunk r58457.
[reactos.git] / lib / rtl / res.c
1 /*
2 * PE file resources
3 *
4 * Copyright 1995 Thomas Sandford
5 * Copyright 1996 Martin von Loewis
6 * Copyright 2003 Alexandre Julliard
7 * Copyright 1993 Robert J. Amstadt
8 * Copyright 1997 Marcus Meissner
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 /* INCLUDES *****************************************************************/
26
27 #include <rtl.h>
28
29 #define NDEBUG
30 #include <debug.h>
31
32 NTSTATUS find_entry( PVOID BaseAddress, LDR_RESOURCE_INFO *info,
33 ULONG level, void **ret, int want_dir );
34
35 /* FUNCTIONS ****************************************************************/
36
37 int page_fault(ULONG ExceptionCode)
38 {
39 if (ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
40 ExceptionCode == EXCEPTION_PRIV_INSTRUCTION)
41 return EXCEPTION_EXECUTE_HANDLER;
42 return EXCEPTION_CONTINUE_SEARCH;
43 }
44
45 /**********************************************************************
46 * is_data_file_module
47 *
48 * Check if a module handle is for a LOAD_LIBRARY_AS_DATAFILE module.
49 */
50 static int is_data_file_module( PVOID BaseAddress )
51 {
52 return (ULONG_PTR)BaseAddress & 1;
53 }
54
55
56 /**********************************************************************
57 * push_language
58 *
59 * push a language in the list of languages to try
60 */
61 int push_language( USHORT *list, ULONG pos, WORD lang )
62 {
63 ULONG i;
64 for (i = 0; i < pos; i++) if (list[i] == lang) return pos;
65 list[pos++] = lang;
66 return pos;
67 }
68
69
70 /**********************************************************************
71 * find_first_entry
72 *
73 * Find the first suitable entry in a resource directory
74 */
75 IMAGE_RESOURCE_DIRECTORY *find_first_entry( IMAGE_RESOURCE_DIRECTORY *dir,
76 void *root, int want_dir )
77 {
78 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
79 int pos;
80
81 for (pos = 0; pos < dir->NumberOfNamedEntries + dir->NumberOfIdEntries; pos++)
82 {
83 if (!entry[pos].DataIsDirectory == !want_dir)
84 return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].OffsetToDirectory);
85 }
86 return NULL;
87 }
88
89
90 /**********************************************************************
91 * find_entry_by_id
92 *
93 * Find an entry by id in a resource directory
94 */
95 IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( IMAGE_RESOURCE_DIRECTORY *dir,
96 WORD id, void *root, int want_dir )
97 {
98 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
99 int min, max, pos;
100
101 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
102 min = dir->NumberOfNamedEntries;
103 max = min + dir->NumberOfIdEntries - 1;
104 while (min <= max)
105 {
106 pos = (min + max) / 2;
107 if (entry[pos].Id == id)
108 {
109 if (!entry[pos].DataIsDirectory == !want_dir)
110 {
111 DPRINT("root %p dir %p id %04x ret %p\n",
112 root, dir, id, (const char*)root + entry[pos].OffsetToDirectory);
113 return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].OffsetToDirectory);
114 }
115 break;
116 }
117 if (entry[pos].Id > id) max = pos - 1;
118 else min = pos + 1;
119 }
120 DPRINT("root %p dir %p id %04x not found\n", root, dir, id );
121 return NULL;
122 }
123
124
125 /**********************************************************************
126 * find_entry_by_name
127 *
128 * Find an entry by name in a resource directory
129 */
130 IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( IMAGE_RESOURCE_DIRECTORY *dir,
131 LPCWSTR name, void *root,
132 int want_dir )
133 {
134 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
135 const IMAGE_RESOURCE_DIR_STRING_U *str;
136 int min, max, res, pos;
137 size_t namelen;
138
139 if (!((ULONG_PTR)name & 0xFFFF0000)) return find_entry_by_id( dir, (ULONG_PTR)name & 0xFFFF, root, want_dir );
140 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
141 namelen = wcslen(name);
142 min = 0;
143 max = dir->NumberOfNamedEntries - 1;
144 while (min <= max)
145 {
146 pos = (min + max) / 2;
147 str = (const IMAGE_RESOURCE_DIR_STRING_U *)((const char *)root + entry[pos].NameOffset);
148 res = _wcsnicmp( name, str->NameString, str->Length );
149 if (!res && namelen == str->Length)
150 {
151 if (!entry[pos].DataIsDirectory == !want_dir)
152 {
153 DPRINT("root %p dir %p name %ws ret %p\n",
154 root, dir, name, (const char*)root + entry[pos].OffsetToDirectory);
155 return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].OffsetToDirectory);
156 }
157 break;
158 }
159 if (res < 0) max = pos - 1;
160 else min = pos + 1;
161 }
162 DPRINT("root %p dir %p name %ws not found\n", root, dir, name);
163 return NULL;
164 }
165
166 #ifdef __i386__
167 NTSTATUS NTAPI LdrpAccessResource( PVOID BaseAddress, IMAGE_RESOURCE_DATA_ENTRY *entry,
168 void **ptr, ULONG *size )
169 #else
170 static NTSTATUS LdrpAccessResource( PVOID BaseAddress, IMAGE_RESOURCE_DATA_ENTRY *entry,
171 void **ptr, ULONG *size )
172 #endif
173 {
174 NTSTATUS status = STATUS_SUCCESS;
175
176 _SEH2_TRY
177 {
178 ULONG dirsize;
179
180 if (!RtlImageDirectoryEntryToData( BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &dirsize ))
181 status = STATUS_RESOURCE_DATA_NOT_FOUND;
182 else
183 {
184 if (ptr)
185 {
186 if (is_data_file_module(BaseAddress))
187 {
188 PVOID mod = (PVOID)((ULONG_PTR)BaseAddress & ~1);
189 *ptr = RtlImageRvaToVa( RtlImageNtHeader(mod), mod, entry->OffsetToData, NULL );
190 }
191 else *ptr = (char *)BaseAddress + entry->OffsetToData;
192 }
193 if (size) *size = entry->Size;
194 }
195 }
196 _SEH2_EXCEPT(page_fault(_SEH2_GetExceptionCode()))
197 {
198 status = _SEH2_GetExceptionCode();
199 }
200 _SEH2_END;
201 return status;
202 }
203
204
205 /*
206 * @implemented
207 */
208 NTSTATUS NTAPI
209 LdrFindResource_U(PVOID BaseAddress,
210 PLDR_RESOURCE_INFO ResourceInfo,
211 ULONG Level,
212 PIMAGE_RESOURCE_DATA_ENTRY* ResourceDataEntry)
213 {
214 void *res;
215 NTSTATUS status = STATUS_SUCCESS;
216
217 _SEH2_TRY
218 {
219 if (ResourceInfo)
220 {
221 DPRINT( "module %p type %ws name %ws lang %04lx level %lu\n",
222 BaseAddress, (LPCWSTR)ResourceInfo->Type,
223 Level > 1 ? (LPCWSTR)ResourceInfo->Name : L"",
224 Level > 2 ? ResourceInfo->Language : 0, Level );
225 }
226
227 status = find_entry( BaseAddress, ResourceInfo, Level, &res, FALSE );
228 if (NT_SUCCESS(status))
229 *ResourceDataEntry = res;
230 }
231 _SEH2_EXCEPT(page_fault(_SEH2_GetExceptionCode()))
232 {
233 status = _SEH2_GetExceptionCode();
234 }
235 _SEH2_END;
236 return status;
237 }
238
239 #ifndef __i386__
240 /*
241 * @implemented
242 */
243 NTSTATUS NTAPI
244 LdrAccessResource(IN PVOID BaseAddress,
245 IN PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
246 OUT PVOID* Resource OPTIONAL,
247 OUT PULONG Size OPTIONAL)
248 {
249 return LdrpAccessResource( BaseAddress, ResourceDataEntry, Resource, Size );
250 }
251 #endif
252
253 /*
254 * @implemented
255 */
256 NTSTATUS NTAPI
257 LdrFindResourceDirectory_U(IN PVOID BaseAddress,
258 IN PLDR_RESOURCE_INFO info,
259 IN ULONG level,
260 OUT PIMAGE_RESOURCE_DIRECTORY* addr)
261 {
262 void *res;
263 NTSTATUS status = STATUS_SUCCESS;
264
265 _SEH2_TRY
266 {
267 if (info)
268 {
269 DPRINT( "module %p type %ws name %ws lang %04lx level %lu\n",
270 BaseAddress, (LPCWSTR)info->Type,
271 level > 1 ? (LPCWSTR)info->Name : L"",
272 level > 2 ? info->Language : 0, level );
273 }
274
275 status = find_entry( BaseAddress, info, level, &res, TRUE );
276 if (NT_SUCCESS(status))
277 *addr = res;
278 }
279 _SEH2_EXCEPT(page_fault(_SEH2_GetExceptionCode()))
280 {
281 status = _SEH2_GetExceptionCode();
282 }
283 _SEH2_END;
284 return status;
285 }
286
287
288 #define NAME_FROM_RESOURCE_ENTRY(RootDirectory, Entry) \
289 ((Entry)->NameIsString ? (ULONG_PTR)(RootDirectory) + (Entry)->NameOffset : (Entry)->Id)
290
291 static
292 LONG
293 LdrpCompareResourceNames_U(
294 _In_ PUCHAR ResourceData,
295 _In_ PIMAGE_RESOURCE_DIRECTORY_ENTRY Entry,
296 _In_ ULONG_PTR CompareName)
297 {
298 PIMAGE_RESOURCE_DIR_STRING_U ResourceString;
299 PWSTR String1, String2;
300 USHORT ResourceStringLength;
301 WCHAR Char1, Char2;
302
303 /* Check if the resource name is an ID */
304 if (CompareName <= USHRT_MAX)
305 {
306 /* Just compare the 2 IDs */
307 return (CompareName - Entry->Id);
308 }
309 else
310 {
311 /* Fail if ResourceName2 is an ID */
312 if (Entry->Id <= USHRT_MAX) return -1;
313
314 /* Get the resource string */
315 ResourceString = (PIMAGE_RESOURCE_DIR_STRING_U)(ResourceData +
316 Entry->NameOffset);
317
318 /* Get the string length */
319 ResourceStringLength = ResourceString->Length;
320
321 String1 = ResourceString->NameString;
322 String2 = (PWSTR)CompareName;
323
324 /* Loop all characters of the resource string */
325 while (ResourceStringLength--)
326 {
327 /* Get the next characters */
328 Char1 = *String1++;
329 Char2 = *String2++;
330
331 /* Check if they don't match, or if the compare string ends */
332 if ((Char1 != Char2) || (Char2 == 0))
333 {
334 /* They don't match, fail */
335 return Char2 - Char1;
336 }
337 }
338
339 /* All characters match, check if the compare string ends here */
340 return (*String2 == 0) ? 0 : 1;
341 }
342 }
343
344 NTSTATUS
345 NTAPI
346 LdrEnumResources(
347 _In_ PVOID ImageBase,
348 _In_ PLDR_RESOURCE_INFO ResourceInfo,
349 _In_ ULONG Level,
350 _Inout_ ULONG *ResourceCount,
351 _Out_writes_to_(*ResourceCount,*ResourceCount) LDR_ENUM_RESOURCE_INFO *Resources)
352 {
353 PUCHAR ResourceData;
354 NTSTATUS Status;
355 ULONG i, j, k;
356 ULONG NumberOfTypeEntries, NumberOfNameEntries, NumberOfLangEntries;
357 ULONG Count, MaxResourceCount;
358 PIMAGE_RESOURCE_DIRECTORY TypeDirectory, NameDirectory, LangDirectory;
359 PIMAGE_RESOURCE_DIRECTORY_ENTRY TypeEntry, NameEntry, LangEntry;
360 PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
361 ULONG Size;
362 LONG Result;
363
364 /* If the caller wants data, get the maximum count of entries */
365 MaxResourceCount = (Resources != NULL) ? *ResourceCount : 0;
366
367 /* Default to 0 */
368 *ResourceCount = 0;
369
370 /* Locate the resource directory */
371 ResourceData = RtlImageDirectoryEntryToData(ImageBase,
372 TRUE,
373 IMAGE_DIRECTORY_ENTRY_RESOURCE,
374 &Size);
375 if (ResourceData == NULL)
376 return STATUS_RESOURCE_DATA_NOT_FOUND;
377
378 /* The type directory is at the root, followed by the entries */
379 TypeDirectory = (PIMAGE_RESOURCE_DIRECTORY)ResourceData;
380 TypeEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(TypeDirectory + 1);
381
382 /* Get the number of entries in the type directory */
383 NumberOfTypeEntries = TypeDirectory->NumberOfNamedEntries +
384 TypeDirectory->NumberOfIdEntries;
385
386 /* Start with 0 resources and status success */
387 Status = STATUS_SUCCESS;
388 Count = 0;
389
390 /* Loop all entries in the type directory */
391 for (i = 0; i < NumberOfTypeEntries; ++i, ++TypeEntry)
392 {
393 /* Check if comparison of types is requested */
394 if (Level > RESOURCE_TYPE_LEVEL)
395 {
396 /* Compare the type with the requested Type */
397 Result = LdrpCompareResourceNames_U(ResourceData,
398 TypeEntry,
399 ResourceInfo->Type);
400
401 /* Not equal, continue with next entry */
402 if (Result != 0) continue;
403 }
404
405 /* The entry must point to the name directory */
406 if (!TypeEntry->DataIsDirectory)
407 {
408 return STATUS_INVALID_IMAGE_FORMAT;
409 }
410
411 /* Get a pointer to the name subdirectory and it's first entry */
412 NameDirectory = (PIMAGE_RESOURCE_DIRECTORY)(ResourceData +
413 TypeEntry->OffsetToDirectory);
414 NameEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(NameDirectory + 1);
415
416 /* Get the number of entries in the name directory */
417 NumberOfNameEntries = NameDirectory->NumberOfNamedEntries +
418 NameDirectory->NumberOfIdEntries;
419
420 /* Loop all entries in the name directory */
421 for (j = 0; j < NumberOfNameEntries; ++j, ++NameEntry)
422 {
423 /* Check if comparison of names is requested */
424 if (Level > RESOURCE_NAME_LEVEL)
425 {
426 /* Compare the name with the requested name */
427 Result = LdrpCompareResourceNames_U(ResourceData,
428 NameEntry,
429 ResourceInfo->Name);
430
431 /* Not equal, continue with next entry */
432 if (Result != 0) continue;
433 }
434
435 /* The entry must point to the language directory */
436 if (!NameEntry->DataIsDirectory)
437 {
438 return STATUS_INVALID_IMAGE_FORMAT;
439 }
440
441 /* Get a pointer to the language subdirectory and it's first entry */
442 LangDirectory = (PIMAGE_RESOURCE_DIRECTORY)(ResourceData +
443 NameEntry->OffsetToDirectory);
444 LangEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(LangDirectory + 1);
445
446 /* Get the number of entries in the language directory */
447 NumberOfLangEntries = LangDirectory->NumberOfNamedEntries +
448 LangDirectory->NumberOfIdEntries;
449
450 /* Loop all entries in the language directory */
451 for (k = 0; k < NumberOfLangEntries; ++k, ++LangEntry)
452 {
453 /* Check if comparison of languages is requested */
454 if (Level > RESOURCE_LANGUAGE_LEVEL)
455 {
456 /* Compare the language with the requested language */
457 Result = LdrpCompareResourceNames_U(ResourceData,
458 LangEntry,
459 ResourceInfo->Language);
460
461 /* Not equal, continue with next entry */
462 if (Result != 0) continue;
463 }
464
465 /* This entry must point to data */
466 if (LangEntry->DataIsDirectory)
467 {
468 return STATUS_INVALID_IMAGE_FORMAT;
469 }
470
471 /* Get a pointer to the data entry */
472 DataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)(ResourceData +
473 LangEntry->OffsetToData);
474
475 /* Check if there is still space to store the data */
476 if (Count < MaxResourceCount)
477 {
478 /* There is, fill the entry */
479 Resources[Count].Type =
480 NAME_FROM_RESOURCE_ENTRY(ResourceData, TypeEntry);
481 Resources[Count].Name =
482 NAME_FROM_RESOURCE_ENTRY(ResourceData, NameEntry);
483 Resources[Count].Language =
484 NAME_FROM_RESOURCE_ENTRY(ResourceData, LangEntry);
485 Resources[Count].Data = (PUCHAR)ImageBase + DataEntry->OffsetToData;
486 Resources[Count].Reserved = 0;
487 Resources[Count].Size = DataEntry->Size;
488 }
489 else
490 {
491 /* There is not enough space, save error status */
492 Status = STATUS_INFO_LENGTH_MISMATCH;
493 }
494
495 /* Count this resource */
496 ++Count;
497 }
498 }
499 }
500
501 /* Return the number of matching resources */
502 *ResourceCount = Count;
503 return Status;
504 }