[DELAYIMP] Fix 2 Clang-Cl warnings about __pfnDliNotifyHook2Default and __pfnDliFailu...
[reactos.git] / sdk / 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 %lx name %lx lang %04lx level %lu\n",
222 BaseAddress, ResourceInfo->Type,
223 Level > 1 ? ResourceInfo->Name : 0,
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 /* Get the resource string */
312 ResourceString = (PIMAGE_RESOURCE_DIR_STRING_U)(ResourceData +
313 Entry->NameOffset);
314
315 /* Get the string length */
316 ResourceStringLength = ResourceString->Length;
317
318 String1 = ResourceString->NameString;
319 String2 = (PWSTR)CompareName;
320
321 /* Loop all characters of the resource string */
322 while (ResourceStringLength--)
323 {
324 /* Get the next characters */
325 Char1 = *String1++;
326 Char2 = *String2++;
327
328 /* Check if they don't match, or if the compare string ends */
329 if ((Char1 != Char2) || (Char2 == 0))
330 {
331 /* They don't match, fail */
332 return Char2 - Char1;
333 }
334 }
335
336 /* All characters match, check if the compare string ends here */
337 return (*String2 == 0) ? 0 : 1;
338 }
339 }
340
341 NTSTATUS
342 NTAPI
343 LdrEnumResources(
344 _In_ PVOID ImageBase,
345 _In_ PLDR_RESOURCE_INFO ResourceInfo,
346 _In_ ULONG Level,
347 _Inout_ ULONG *ResourceCount,
348 _Out_writes_to_(*ResourceCount,*ResourceCount) LDR_ENUM_RESOURCE_INFO *Resources)
349 {
350 PUCHAR ResourceData;
351 NTSTATUS Status;
352 ULONG i, j, k;
353 ULONG NumberOfTypeEntries, NumberOfNameEntries, NumberOfLangEntries;
354 ULONG Count, MaxResourceCount;
355 PIMAGE_RESOURCE_DIRECTORY TypeDirectory, NameDirectory, LangDirectory;
356 PIMAGE_RESOURCE_DIRECTORY_ENTRY TypeEntry, NameEntry, LangEntry;
357 PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
358 ULONG Size;
359 LONG Result;
360
361 /* If the caller wants data, get the maximum count of entries */
362 MaxResourceCount = (Resources != NULL) ? *ResourceCount : 0;
363
364 /* Default to 0 */
365 *ResourceCount = 0;
366
367 /* Locate the resource directory */
368 ResourceData = RtlImageDirectoryEntryToData(ImageBase,
369 TRUE,
370 IMAGE_DIRECTORY_ENTRY_RESOURCE,
371 &Size);
372 if (ResourceData == NULL)
373 return STATUS_RESOURCE_DATA_NOT_FOUND;
374
375 /* The type directory is at the root, followed by the entries */
376 TypeDirectory = (PIMAGE_RESOURCE_DIRECTORY)ResourceData;
377 TypeEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(TypeDirectory + 1);
378
379 /* Get the number of entries in the type directory */
380 NumberOfTypeEntries = TypeDirectory->NumberOfNamedEntries +
381 TypeDirectory->NumberOfIdEntries;
382
383 /* Start with 0 resources and status success */
384 Status = STATUS_SUCCESS;
385 Count = 0;
386
387 /* Loop all entries in the type directory */
388 for (i = 0; i < NumberOfTypeEntries; ++i, ++TypeEntry)
389 {
390 /* Check if comparison of types is requested */
391 if (Level > RESOURCE_TYPE_LEVEL)
392 {
393 /* Compare the type with the requested Type */
394 Result = LdrpCompareResourceNames_U(ResourceData,
395 TypeEntry,
396 ResourceInfo->Type);
397
398 /* Not equal, continue with next entry */
399 if (Result != 0) continue;
400 }
401
402 /* The entry must point to the name directory */
403 if (!TypeEntry->DataIsDirectory)
404 {
405 return STATUS_INVALID_IMAGE_FORMAT;
406 }
407
408 /* Get a pointer to the name subdirectory and it's first entry */
409 NameDirectory = (PIMAGE_RESOURCE_DIRECTORY)(ResourceData +
410 TypeEntry->OffsetToDirectory);
411 NameEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(NameDirectory + 1);
412
413 /* Get the number of entries in the name directory */
414 NumberOfNameEntries = NameDirectory->NumberOfNamedEntries +
415 NameDirectory->NumberOfIdEntries;
416
417 /* Loop all entries in the name directory */
418 for (j = 0; j < NumberOfNameEntries; ++j, ++NameEntry)
419 {
420 /* Check if comparison of names is requested */
421 if (Level > RESOURCE_NAME_LEVEL)
422 {
423 /* Compare the name with the requested name */
424 Result = LdrpCompareResourceNames_U(ResourceData,
425 NameEntry,
426 ResourceInfo->Name);
427
428 /* Not equal, continue with next entry */
429 if (Result != 0) continue;
430 }
431
432 /* The entry must point to the language directory */
433 if (!NameEntry->DataIsDirectory)
434 {
435 return STATUS_INVALID_IMAGE_FORMAT;
436 }
437
438 /* Get a pointer to the language subdirectory and it's first entry */
439 LangDirectory = (PIMAGE_RESOURCE_DIRECTORY)(ResourceData +
440 NameEntry->OffsetToDirectory);
441 LangEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(LangDirectory + 1);
442
443 /* Get the number of entries in the language directory */
444 NumberOfLangEntries = LangDirectory->NumberOfNamedEntries +
445 LangDirectory->NumberOfIdEntries;
446
447 /* Loop all entries in the language directory */
448 for (k = 0; k < NumberOfLangEntries; ++k, ++LangEntry)
449 {
450 /* Check if comparison of languages is requested */
451 if (Level > RESOURCE_LANGUAGE_LEVEL)
452 {
453 /* Compare the language with the requested language */
454 Result = LdrpCompareResourceNames_U(ResourceData,
455 LangEntry,
456 ResourceInfo->Language);
457
458 /* Not equal, continue with next entry */
459 if (Result != 0) continue;
460 }
461
462 /* This entry must point to data */
463 if (LangEntry->DataIsDirectory)
464 {
465 return STATUS_INVALID_IMAGE_FORMAT;
466 }
467
468 /* Get a pointer to the data entry */
469 DataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)(ResourceData +
470 LangEntry->OffsetToData);
471
472 /* Check if there is still space to store the data */
473 if (Count < MaxResourceCount)
474 {
475 /* There is, fill the entry */
476 Resources[Count].Type =
477 NAME_FROM_RESOURCE_ENTRY(ResourceData, TypeEntry);
478 Resources[Count].Name =
479 NAME_FROM_RESOURCE_ENTRY(ResourceData, NameEntry);
480 Resources[Count].Language =
481 NAME_FROM_RESOURCE_ENTRY(ResourceData, LangEntry);
482 Resources[Count].Data = (PUCHAR)ImageBase + DataEntry->OffsetToData;
483 Resources[Count].Reserved = 0;
484 Resources[Count].Size = DataEntry->Size;
485 }
486 else
487 {
488 /* There is not enough space, save error status */
489 Status = STATUS_INFO_LENGTH_MISMATCH;
490 }
491
492 /* Count this resource */
493 ++Count;
494 }
495 }
496 }
497
498 /* Return the number of matching resources */
499 *ResourceCount = Count;
500 return Status;
501 }