ServiceWorker: Consolidate version manipulation functions in SWProviderContext
[chromium-blink-merge.git] / base / win / pe_image.cc
blob572b4d927e2eaf65cf147dc584fd70ea50166236
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 // TODO(jschuh): crbug.com/167707 Make sure this code works on 64-bit.
15 // Structure to perform imports enumerations.
16 struct EnumAllImportsStorage {
17 PEImage::EnumImportsFunction callback;
18 PVOID cookie;
21 namespace {
23 // Compare two strings byte by byte on an unsigned basis.
24 // if s1 == s2, return 0
25 // if s1 < s2, return negative
26 // if s1 > s2, return positive
27 // Exception if inputs are invalid.
28 int StrCmpByByte(LPCSTR s1, LPCSTR s2) {
29 while (*s1 != '\0' && *s1 == *s2) {
30 ++s1;
31 ++s2;
34 return (*reinterpret_cast<const unsigned char*>(s1) -
35 *reinterpret_cast<const unsigned char*>(s2));
38 } // namespace
40 // Callback used to enumerate imports. See EnumImportChunksFunction.
41 bool ProcessImportChunk(const PEImage &image, LPCSTR module,
42 PIMAGE_THUNK_DATA name_table,
43 PIMAGE_THUNK_DATA iat, PVOID cookie) {
44 EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
45 cookie);
47 return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
48 storage.cookie);
51 // Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
52 bool ProcessDelayImportChunk(const PEImage &image,
53 PImgDelayDescr delay_descriptor,
54 LPCSTR module, PIMAGE_THUNK_DATA name_table,
55 PIMAGE_THUNK_DATA iat, PIMAGE_THUNK_DATA bound_iat,
56 PIMAGE_THUNK_DATA unload_iat, PVOID cookie) {
57 EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
58 cookie);
60 return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
61 module, name_table, iat, bound_iat,
62 unload_iat, storage.cookie);
65 void PEImage::set_module(HMODULE module) {
66 module_ = module;
69 PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
70 return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
73 PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
74 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
76 return reinterpret_cast<PIMAGE_NT_HEADERS>(
77 reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
80 PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const {
81 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
82 PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers);
84 if (section < nt_headers->FileHeader.NumberOfSections)
85 return first_section + section;
86 else
87 return NULL;
90 WORD PEImage::GetNumSections() const {
91 return GetNTHeaders()->FileHeader.NumberOfSections;
94 DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
95 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
97 return nt_headers->OptionalHeader.DataDirectory[directory].Size;
100 PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
101 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
103 return RVAToAddr(
104 nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
107 PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
108 PBYTE target = reinterpret_cast<PBYTE>(address);
109 PIMAGE_SECTION_HEADER section;
111 for (UINT i = 0; NULL != (section = GetSectionHeader(i)); i++) {
112 // Don't use the virtual RVAToAddr.
113 PBYTE start = reinterpret_cast<PBYTE>(
114 PEImage::RVAToAddr(section->VirtualAddress));
116 DWORD size = section->Misc.VirtualSize;
118 if ((start <= target) && (start + size > target))
119 return section;
122 return NULL;
125 PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
126 LPCSTR section_name) const {
127 if (NULL == section_name)
128 return NULL;
130 PIMAGE_SECTION_HEADER ret = NULL;
131 int num_sections = GetNumSections();
133 for (int i = 0; i < num_sections; i++) {
134 PIMAGE_SECTION_HEADER section = GetSectionHeader(i);
135 if (0 == _strnicmp(reinterpret_cast<LPCSTR>(section->Name), section_name,
136 sizeof(section->Name))) {
137 ret = section;
138 break;
142 return ret;
145 PDWORD PEImage::GetExportEntry(LPCSTR name) const {
146 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
148 if (NULL == exports)
149 return NULL;
151 WORD ordinal = 0;
152 if (!GetProcOrdinal(name, &ordinal))
153 return NULL;
155 PDWORD functions = reinterpret_cast<PDWORD>(
156 RVAToAddr(exports->AddressOfFunctions));
158 return functions + ordinal - exports->Base;
161 FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
162 PDWORD export_entry = GetExportEntry(function_name);
163 if (NULL == export_entry)
164 return NULL;
166 PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
168 PBYTE exports = reinterpret_cast<PBYTE>(
169 GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
170 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
172 // Check for forwarded exports as a special case.
173 if (exports <= function && exports + size > function)
174 #pragma warning(push)
175 #pragma warning(disable: 4312)
176 // This cast generates a warning because it is 32 bit specific.
177 return reinterpret_cast<FARPROC>(0xFFFFFFFF);
178 #pragma warning(pop)
180 return reinterpret_cast<FARPROC>(function);
183 bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const {
184 if (NULL == ordinal)
185 return false;
187 PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
189 if (NULL == exports)
190 return false;
192 if (IsOrdinal(function_name)) {
193 *ordinal = ToOrdinal(function_name);
194 } else {
195 PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
196 PDWORD lower = names;
197 PDWORD upper = names + exports->NumberOfNames;
198 int cmp = -1;
200 // Binary Search for the name.
201 while (lower != upper) {
202 PDWORD middle = lower + (upper - lower) / 2;
203 LPCSTR name = reinterpret_cast<LPCSTR>(RVAToAddr(*middle));
205 // This may be called by sandbox before MSVCRT dll loads, so can't use
206 // CRT function here.
207 cmp = StrCmpByByte(function_name, name);
209 if (cmp == 0) {
210 lower = middle;
211 break;
214 if (cmp > 0)
215 lower = middle + 1;
216 else
217 upper = middle;
220 if (cmp != 0)
221 return false;
224 PWORD ordinals = reinterpret_cast<PWORD>(
225 RVAToAddr(exports->AddressOfNameOrdinals));
227 *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
230 return true;
233 bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const {
234 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
235 UINT num_sections = nt_headers->FileHeader.NumberOfSections;
236 PIMAGE_SECTION_HEADER section = GetSectionHeader(0);
238 for (UINT i = 0; i < num_sections; i++, section++) {
239 PVOID section_start = RVAToAddr(section->VirtualAddress);
240 DWORD size = section->Misc.VirtualSize;
242 if (!callback(*this, section, section_start, size, cookie))
243 return false;
246 return true;
249 bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
250 PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
251 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
253 // Check if there are any exports at all.
254 if (NULL == directory || 0 == size)
255 return true;
257 PIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
258 directory);
259 UINT ordinal_base = exports->Base;
260 UINT num_funcs = exports->NumberOfFunctions;
261 UINT num_names = exports->NumberOfNames;
262 PDWORD functions = reinterpret_cast<PDWORD>(RVAToAddr(
263 exports->AddressOfFunctions));
264 PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
265 PWORD ordinals = reinterpret_cast<PWORD>(RVAToAddr(
266 exports->AddressOfNameOrdinals));
268 for (UINT count = 0; count < num_funcs; count++) {
269 PVOID func = RVAToAddr(functions[count]);
270 if (NULL == func)
271 continue;
273 // Check for a name.
274 LPCSTR name = NULL;
275 UINT hint;
276 for (hint = 0; hint < num_names; hint++) {
277 if (ordinals[hint] == count) {
278 name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
279 break;
283 if (name == NULL)
284 hint = 0;
286 // Check for forwarded exports.
287 LPCSTR forward = NULL;
288 if (reinterpret_cast<char*>(func) >= reinterpret_cast<char*>(directory) &&
289 reinterpret_cast<char*>(func) <= reinterpret_cast<char*>(directory) +
290 size) {
291 forward = reinterpret_cast<LPCSTR>(func);
292 func = 0;
295 if (!callback(*this, ordinal_base + count, hint, name, func, forward,
296 cookie))
297 return false;
300 return true;
303 bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const {
304 PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC);
305 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC);
306 PIMAGE_BASE_RELOCATION base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
307 directory);
309 if (!directory)
310 return true;
312 while (size >= sizeof(IMAGE_BASE_RELOCATION) && base->SizeOfBlock &&
313 size >= base->SizeOfBlock) {
314 PWORD reloc = reinterpret_cast<PWORD>(base + 1);
315 UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
316 sizeof(WORD);
318 for (UINT i = 0; i < num_relocs; i++, reloc++) {
319 WORD type = *reloc >> 12;
320 PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
322 if (!callback(*this, type, address, cookie))
323 return false;
326 size -= base->SizeOfBlock;
327 base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
328 reinterpret_cast<char*>(base) + base->SizeOfBlock);
331 return true;
334 bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
335 PVOID cookie) const {
336 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
337 PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
339 if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
340 return true;
342 for (; import->FirstThunk; import++) {
343 LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
344 PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
345 RVAToAddr(import->OriginalFirstThunk));
346 PIMAGE_THUNK_DATA iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
347 RVAToAddr(import->FirstThunk));
349 if (!callback(*this, module_name, name_table, iat, cookie))
350 return false;
353 return true;
356 bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
357 LPCSTR module_name,
358 PIMAGE_THUNK_DATA name_table,
359 PIMAGE_THUNK_DATA iat, PVOID cookie) const {
360 if (NULL == name_table)
361 return false;
363 for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
364 LPCSTR name = NULL;
365 WORD ordinal = 0;
366 WORD hint = 0;
368 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
369 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
370 } else {
371 PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
372 RVAToAddr(name_table->u1.ForwarderString));
374 hint = import->Hint;
375 name = reinterpret_cast<LPCSTR>(&import->Name);
378 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
379 return false;
382 return true;
385 bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
386 EnumAllImportsStorage temp = { callback, cookie };
387 return EnumImportChunks(ProcessImportChunk, &temp);
390 bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
391 PVOID cookie) const {
392 PVOID directory = GetImageDirectoryEntryAddr(
393 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
394 DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
395 PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
397 if (directory == NULL || size == 0)
398 return true;
400 for (; delay_descriptor->rvaHmod; delay_descriptor++) {
401 PIMAGE_THUNK_DATA name_table;
402 PIMAGE_THUNK_DATA iat;
403 PIMAGE_THUNK_DATA bound_iat; // address of the optional bound IAT
404 PIMAGE_THUNK_DATA unload_iat; // address of optional copy of original IAT
405 LPCSTR module_name;
407 // check if VC7-style imports, using RVAs instead of
408 // VC6-style addresses.
409 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
411 if (rvas) {
412 module_name = reinterpret_cast<LPCSTR>(
413 RVAToAddr(delay_descriptor->rvaDLLName));
414 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
415 RVAToAddr(delay_descriptor->rvaINT));
416 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
417 RVAToAddr(delay_descriptor->rvaIAT));
418 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
419 RVAToAddr(delay_descriptor->rvaBoundIAT));
420 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
421 RVAToAddr(delay_descriptor->rvaUnloadIAT));
422 } else {
423 #pragma warning(push)
424 #pragma warning(disable: 4312)
425 // These casts generate warnings because they are 32 bit specific.
426 module_name = reinterpret_cast<LPCSTR>(delay_descriptor->rvaDLLName);
427 name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
428 delay_descriptor->rvaINT);
429 iat = reinterpret_cast<PIMAGE_THUNK_DATA>(delay_descriptor->rvaIAT);
430 bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
431 delay_descriptor->rvaBoundIAT);
432 unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
433 delay_descriptor->rvaUnloadIAT);
434 #pragma warning(pop)
437 if (!callback(*this, delay_descriptor, module_name, name_table, iat,
438 bound_iat, unload_iat, cookie))
439 return false;
442 return true;
445 bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
446 PImgDelayDescr delay_descriptor,
447 LPCSTR module_name,
448 PIMAGE_THUNK_DATA name_table,
449 PIMAGE_THUNK_DATA iat,
450 PIMAGE_THUNK_DATA bound_iat,
451 PIMAGE_THUNK_DATA unload_iat,
452 PVOID cookie) const {
453 UNREFERENCED_PARAMETER(bound_iat);
454 UNREFERENCED_PARAMETER(unload_iat);
456 for (; name_table->u1.Ordinal; name_table++, iat++) {
457 LPCSTR name = NULL;
458 WORD ordinal = 0;
459 WORD hint = 0;
461 if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
462 ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
463 } else {
464 PIMAGE_IMPORT_BY_NAME import;
465 bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
467 if (rvas) {
468 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
469 RVAToAddr(name_table->u1.ForwarderString));
470 } else {
471 #pragma warning(push)
472 #pragma warning(disable: 4312)
473 // This cast generates a warning because it is 32 bit specific.
474 import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
475 name_table->u1.ForwarderString);
476 #pragma warning(pop)
479 hint = import->Hint;
480 name = reinterpret_cast<LPCSTR>(&import->Name);
483 if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
484 return false;
487 return true;
490 bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
491 PVOID cookie) const {
492 EnumAllImportsStorage temp = { callback, cookie };
493 return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
496 bool PEImage::VerifyMagic() const {
497 PIMAGE_DOS_HEADER dos_header = GetDosHeader();
499 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
500 return false;
502 PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
504 if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
505 return false;
507 if (nt_headers->FileHeader.SizeOfOptionalHeader !=
508 sizeof(IMAGE_OPTIONAL_HEADER))
509 return false;
511 if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
512 return false;
514 return true;
517 bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
518 LPVOID address = RVAToAddr(rva);
519 return ImageAddrToOnDiskOffset(address, on_disk_offset);
522 bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
523 DWORD *on_disk_offset) const {
524 if (NULL == address)
525 return false;
527 // Get the section that this address belongs to.
528 PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
529 if (NULL == section_header)
530 return false;
532 #pragma warning(push)
533 #pragma warning(disable: 4311)
534 // These casts generate warnings because they are 32 bit specific.
535 // Don't follow the virtual RVAToAddr, use the one on the base.
536 DWORD offset_within_section = reinterpret_cast<DWORD>(address) -
537 reinterpret_cast<DWORD>(PEImage::RVAToAddr(
538 section_header->VirtualAddress));
539 #pragma warning(pop)
541 *on_disk_offset = section_header->PointerToRawData + offset_within_section;
542 return true;
545 PVOID PEImage::RVAToAddr(DWORD rva) const {
546 if (rva == 0)
547 return NULL;
549 return reinterpret_cast<char*>(module_) + rva;
552 PVOID PEImageAsData::RVAToAddr(DWORD rva) const {
553 if (rva == 0)
554 return NULL;
556 PVOID in_memory = PEImage::RVAToAddr(rva);
557 DWORD disk_offset;
559 if (!ImageAddrToOnDiskOffset(in_memory, &disk_offset))
560 return NULL;
562 return PEImage::RVAToAddr(disk_offset);
565 } // namespace win
566 } // namespace base