Improve performance of registering font preferences
[chromium-blink-merge.git] / base / win / pe_image.cc
blobfcf03c1d4c1e2984c7468ef5438b579137411e12
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // This file implements PEImage, a generic class to manipulate PE files.
6 // This file was adapted from GreenBorder's Code.
8 #include "base/win/pe_image.h"
10 namespace base {
11 namespace win {
13 #if defined(_WIN64) && !defined(NACL_WIN64)
14 // TODO(rvargas): Bug 27218. Make sure this is ok.
15 #error This code is not tested on x64. Please make sure all the base unit tests\
16 pass before doing any real work. The current unit tests don't test the\
17 differences between 32- and 64-bits implementations. Bugs may slip through.\
18 You need to improve the coverage before continuing.
19 #endif
21 // Structure to perform imports enumerations.
22 struct EnumAllImportsStorage {
23 PEImage::EnumImportsFunction callback;
24 PVOID cookie;
27 namespace {
29 // Compare two strings byte by byte on an unsigned basis.
30 // if s1 == s2, return 0
31 // if s1 < s2, return negative
32 // if s1 > s2, return positive
33 // Exception if inputs are invalid.
34 int StrCmpByByte(LPCSTR s1, LPCSTR s2) {
35 while (*s1 != '\0' && *s1 == *s2) {
36 ++s1;
37 ++s2;
40 return (*reinterpret_cast<const unsigned char*>(s1) -
41 *reinterpret_cast<const unsigned char*>(s2));
44 } // namespace
46 // Callback used to enumerate imports. See EnumImportChunksFunction.
47 bool ProcessImportChunk(const PEImage &image, LPCSTR module,
48 PIMAGE_THUNK_DATA name_table,
49 PIMAGE_THUNK_DATA iat, PVOID cookie) {
50 EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
51 cookie);
53 return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
54 storage.cookie);
57 // Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
58 bool ProcessDelayImportChunk(const PEImage &image,
59 PImgDelayDescr delay_descriptor,
60 LPCSTR module, PIMAGE_THUNK_DATA name_table,
61 PIMAGE_THUNK_DATA iat, PIMAGE_THUNK_DATA bound_iat,
62 PIMAGE_THUNK_DATA unload_iat, PVOID cookie) {
63 EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
64 cookie);
66 return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
67 module, name_table, iat, bound_iat,
68 unload_iat, storage.cookie);
71 void PEImage::set_module(HMODULE module) {
72 module_ = module;
75 PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
76 return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
79 PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
80 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
82 return reinterpret_cast<PIMAGE_NT_HEADERS>(
83 reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
86 PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const {
87 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
88 PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers);
90 if (section < nt_headers->FileHeader.NumberOfSections)
91 return first_section + section;
92 else
93 return NULL;
96 WORD PEImage::GetNumSections() const {
97 return GetNTHeaders()->FileHeader.NumberOfSections;
100 DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
101 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
103 return nt_headers->OptionalHeader.DataDirectory[directory].Size;
106 PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
107 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
109 return RVAToAddr(
110 nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
113 PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
114 PBYTE target = reinterpret_cast<PBYTE>(address);
115 PIMAGE_SECTION_HEADER section;
117 for (UINT i = 0; NULL != (section = GetSectionHeader(i)); i++) {
118 // Don't use the virtual RVAToAddr.
119 PBYTE start = reinterpret_cast<PBYTE>(
120 PEImage::RVAToAddr(section->VirtualAddress));
122 DWORD size = section->Misc.VirtualSize;
124 if ((start <= target) && (start + size > target))
125 return section;
128 return NULL;
131 PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
132 LPCSTR section_name) const {
133 if (NULL == section_name)
134 return NULL;
136 PIMAGE_SECTION_HEADER ret = NULL;
137 int num_sections = GetNumSections();
139 for (int i = 0; i < num_sections; i++) {
140 PIMAGE_SECTION_HEADER section = GetSectionHeader(i);
141 if (0 == _strnicmp(reinterpret_cast<LPCSTR>(section->Name), section_name,
142 sizeof(section->Name))) {
143 ret = section;
144 break;
148 return ret;
151 PDWORD PEImage::GetExportEntry(LPCSTR name) const {
152 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
154 if (NULL == exports)
155 return NULL;
157 WORD ordinal = 0;
158 if (!GetProcOrdinal(name, &ordinal))
159 return NULL;
161 PDWORD functions = reinterpret_cast<PDWORD>(
162 RVAToAddr(exports->AddressOfFunctions));
164 return functions + ordinal - exports->Base;
167 FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
168 PDWORD export_entry = GetExportEntry(function_name);
169 if (NULL == export_entry)
170 return NULL;
172 PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
174 PBYTE exports = reinterpret_cast<PBYTE>(
175 GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
176 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
178 // Check for forwarded exports as a special case.
179 if (exports <= function && exports + size > function)
180 #pragma warning(push)
181 #pragma warning(disable: 4312)
182 // This cast generates a warning because it is 32 bit specific.
183 return reinterpret_cast<FARPROC>(0xFFFFFFFF);
184 #pragma warning(pop)
186 return reinterpret_cast<FARPROC>(function);
189 bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const {
190 if (NULL == ordinal)
191 return false;
193 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
195 if (NULL == exports)
196 return false;
198 if (IsOrdinal(function_name)) {
199 *ordinal = ToOrdinal(function_name);
200 } else {
201 PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
202 PDWORD lower = names;
203 PDWORD upper = names + exports->NumberOfNames;
204 int cmp = -1;
206 // Binary Search for the name.
207 while (lower != upper) {
208 PDWORD middle = lower + (upper - lower) / 2;
209 LPCSTR name = reinterpret_cast<LPCSTR>(RVAToAddr(*middle));
211 // This may be called by sandbox before MSVCRT dll loads, so can't use
212 // CRT function here.
213 cmp = StrCmpByByte(function_name, name);
215 if (cmp == 0) {
216 lower = middle;
217 break;
220 if (cmp > 0)
221 lower = middle + 1;
222 else
223 upper = middle;
226 if (cmp != 0)
227 return false;
230 PWORD ordinals = reinterpret_cast<PWORD>(
231 RVAToAddr(exports->AddressOfNameOrdinals));
233 *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
236 return true;
239 bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const {
240 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
241 UINT num_sections = nt_headers->FileHeader.NumberOfSections;
242 PIMAGE_SECTION_HEADER section = GetSectionHeader(0);
244 for (UINT i = 0; i < num_sections; i++, section++) {
245 PVOID section_start = RVAToAddr(section->VirtualAddress);
246 DWORD size = section->Misc.VirtualSize;
248 if (!callback(*this, section, section_start, size, cookie))
249 return false;
252 return true;
255 bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
256 PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
257 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
259 // Check if there are any exports at all.
260 if (NULL == directory || 0 == size)
261 return true;
263 PIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
264 directory);
265 UINT ordinal_base = exports->Base;
266 UINT num_funcs = exports->NumberOfFunctions;
267 UINT num_names = exports->NumberOfNames;
268 PDWORD functions = reinterpret_cast<PDWORD>(RVAToAddr(
269 exports->AddressOfFunctions));
270 PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
271 PWORD ordinals = reinterpret_cast<PWORD>(RVAToAddr(
272 exports->AddressOfNameOrdinals));
274 for (UINT count = 0; count < num_funcs; count++) {
275 PVOID func = RVAToAddr(functions[count]);
276 if (NULL == func)
277 continue;
279 // Check for a name.
280 LPCSTR name = NULL;
281 UINT hint;
282 for (hint = 0; hint < num_names; hint++) {
283 if (ordinals[hint] == count) {
284 name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
285 break;
289 if (name == NULL)
290 hint = 0;
292 // Check for forwarded exports.
293 LPCSTR forward = NULL;
294 if (reinterpret_cast<char*>(func) >= reinterpret_cast<char*>(directory) &&
295 reinterpret_cast<char*>(func) <= reinterpret_cast<char*>(directory) +
296 size) {
297 forward = reinterpret_cast<LPCSTR>(func);
298 func = 0;
301 if (!callback(*this, ordinal_base + count, hint, name, func, forward,
302 cookie))
303 return false;
306 return true;
309 bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const {
310 PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC);
311 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC);
312 PIMAGE_BASE_RELOCATION base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
313 directory);
315 if (directory == NULL || size < sizeof(IMAGE_BASE_RELOCATION))
316 return true;
318 while (base->SizeOfBlock) {
319 PWORD reloc = reinterpret_cast<PWORD>(base + 1);
320 UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
321 sizeof(WORD);
323 for (UINT i = 0; i < num_relocs; i++, reloc++) {
324 WORD type = *reloc >> 12;
325 PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
327 if (!callback(*this, type, address, cookie))
328 return false;
331 base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
332 reinterpret_cast<char*>(base) + base->SizeOfBlock);
335 return true;
338 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
339 PVOID cookie) const {
340 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
341 PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
343 if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
344 return true;
346 for (; import->FirstThunk; import++) {
347 LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
348 PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
349 RVAToAddr(import->OriginalFirstThunk));
350 PIMAGE_THUNK_DATA iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
351 RVAToAddr(import->FirstThunk));
353 if (!callback(*this, module_name, name_table, iat, cookie))
354 return false;
357 return true;
360 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
361 LPCSTR module_name,
362 PIMAGE_THUNK_DATA name_table,
363 PIMAGE_THUNK_DATA iat, PVOID cookie) const {
364 if (NULL == name_table)
365 return false;
367 for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
368 LPCSTR name = NULL;
369 WORD ordinal = 0;
370 WORD hint = 0;
372 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
373 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
374 } else {
375 PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
376 RVAToAddr(name_table->u1.ForwarderString));
378 hint = import->Hint;
379 name = reinterpret_cast<LPCSTR>(&import->Name);
382 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
383 return false;
386 return true;
389 bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
390 EnumAllImportsStorage temp = { callback, cookie };
391 return EnumImportChunks(ProcessImportChunk, &temp);
394 bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
395 PVOID cookie) const {
396 PVOID directory = GetImageDirectoryEntryAddr(
397 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
398 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
399 PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
401 if (directory == NULL || size == 0)
402 return true;
404 for (; delay_descriptor->rvaHmod; delay_descriptor++) {
405 PIMAGE_THUNK_DATA name_table;
406 PIMAGE_THUNK_DATA iat;
407 PIMAGE_THUNK_DATA bound_iat; // address of the optional bound IAT
408 PIMAGE_THUNK_DATA unload_iat; // address of optional copy of original IAT
409 LPCSTR module_name;
411 // check if VC7-style imports, using RVAs instead of
412 // VC6-style addresses.
413 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
415 if (rvas) {
416 module_name = reinterpret_cast<LPCSTR>(
417 RVAToAddr(delay_descriptor->rvaDLLName));
418 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
419 RVAToAddr(delay_descriptor->rvaINT));
420 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
421 RVAToAddr(delay_descriptor->rvaIAT));
422 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
423 RVAToAddr(delay_descriptor->rvaBoundIAT));
424 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
425 RVAToAddr(delay_descriptor->rvaUnloadIAT));
426 } else {
427 #pragma warning(push)
428 #pragma warning(disable: 4312)
429 // These casts generate warnings because they are 32 bit specific.
430 module_name = reinterpret_cast<LPCSTR>(delay_descriptor->rvaDLLName);
431 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
432 delay_descriptor->rvaINT);
433 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(delay_descriptor->rvaIAT);
434 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
435 delay_descriptor->rvaBoundIAT);
436 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
437 delay_descriptor->rvaUnloadIAT);
438 #pragma warning(pop)
441 if (!callback(*this, delay_descriptor, module_name, name_table, iat,
442 bound_iat, unload_iat, cookie))
443 return false;
446 return true;
449 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
450 PImgDelayDescr delay_descriptor,
451 LPCSTR module_name,
452 PIMAGE_THUNK_DATA name_table,
453 PIMAGE_THUNK_DATA iat,
454 PIMAGE_THUNK_DATA bound_iat,
455 PIMAGE_THUNK_DATA unload_iat,
456 PVOID cookie) const {
457 UNREFERENCED_PARAMETER(bound_iat);
458 UNREFERENCED_PARAMETER(unload_iat);
460 for (; name_table->u1.Ordinal; name_table++, iat++) {
461 LPCSTR name = NULL;
462 WORD ordinal = 0;
463 WORD hint = 0;
465 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
466 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
467 } else {
468 PIMAGE_IMPORT_BY_NAME import;
469 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
471 if (rvas) {
472 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
473 RVAToAddr(name_table->u1.ForwarderString));
474 } else {
475 #pragma warning(push)
476 #pragma warning(disable: 4312)
477 // This cast generates a warning because it is 32 bit specific.
478 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
479 name_table->u1.ForwarderString);
480 #pragma warning(pop)
483 hint = import->Hint;
484 name = reinterpret_cast<LPCSTR>(&import->Name);
487 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
488 return false;
491 return true;
494 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
495 PVOID cookie) const {
496 EnumAllImportsStorage temp = { callback, cookie };
497 return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
500 bool PEImage::VerifyMagic() const {
501 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
503 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
504 return false;
506 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
508 if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
509 return false;
511 if (nt_headers->FileHeader.SizeOfOptionalHeader !=
512 sizeof(IMAGE_OPTIONAL_HEADER))
513 return false;
515 if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
516 return false;
518 return true;
521 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
522 LPVOID address = RVAToAddr(rva);
523 return ImageAddrToOnDiskOffset(address, on_disk_offset);
526 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
527 DWORD *on_disk_offset) const {
528 if (NULL == address)
529 return false;
531 // Get the section that this address belongs to.
532 PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
533 if (NULL == section_header)
534 return false;
536 #pragma warning(push)
537 #pragma warning(disable: 4311)
538 // These casts generate warnings because they are 32 bit specific.
539 // Don't follow the virtual RVAToAddr, use the one on the base.
540 DWORD offset_within_section = reinterpret_cast<DWORD>(address) -
541 reinterpret_cast<DWORD>(PEImage::RVAToAddr(
542 section_header->VirtualAddress));
543 #pragma warning(pop)
545 *on_disk_offset = section_header->PointerToRawData + offset_within_section;
546 return true;
549 PVOID PEImage::RVAToAddr(DWORD rva) const {
550 if (rva == 0)
551 return NULL;
553 return reinterpret_cast<char*>(module_) + rva;
556 PVOID PEImageAsData::RVAToAddr(DWORD rva) const {
557 if (rva == 0)
558 return NULL;
560 PVOID in_memory = PEImage::RVAToAddr(rva);
561 DWORD disk_offset;
563 if (!ImageAddrToOnDiskOffset(in_memory, &disk_offset))
564 return NULL;
566 return PEImage::RVAToAddr(disk_offset);
569 } // namespace win
570 } // namespace base