crashpad/snapshot/win/pe_image_resource_reader.cc
Mark Mentovai 20e5aba1af URL cleanups: switch to HTTPS, fix dead ones, use canonical ones
Change-Id: I4b247d7fae1a212350f8ffcf2bf5ba1fa730f5c1
Reviewed-on: https://chromium-review.googlesource.com/780339
Reviewed-by: Scott Graham <scottmg@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
2017-11-20 22:23:39 +00:00

280 lines
9.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2015 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/win/pe_image_resource_reader.h"
#include <algorithm>
#include <memory>
#include "base/logging.h"
namespace {
void AddLanguageAndNeutralSublanguage(std::vector<uint16_t>* languages,
uint16_t language) {
languages->push_back(language);
if (SUBLANGID(language) != SUBLANG_NEUTRAL) {
languages->push_back(MAKELANGID(PRIMARYLANGID(language), SUBLANG_NEUTRAL));
}
}
} // namespace
namespace crashpad {
PEImageResourceReader::PEImageResourceReader()
: resources_subrange_reader_(),
module_base_(0),
initialized_() {
}
PEImageResourceReader::~PEImageResourceReader() {}
bool PEImageResourceReader::Initialize(
const ProcessSubrangeReader& module_subrange_reader,
const IMAGE_DATA_DIRECTORY& resources_directory_entry) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
module_base_ = module_subrange_reader.Base();
if (!resources_subrange_reader_.InitializeSubrange(
module_subrange_reader,
module_base_ + resources_directory_entry.VirtualAddress,
resources_directory_entry.Size,
"resources")) {
return false;
}
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
bool PEImageResourceReader::FindResourceByID(uint16_t type,
uint16_t name,
uint16_t language,
WinVMAddress* address,
WinVMSize* size,
uint32_t* code_page) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// The root resource directory is at the beginning of the resources area
// within the module.
const uint32_t name_directory_offset =
GetEntryFromResourceDirectoryByID(0, type, true);
if (!name_directory_offset) {
return false;
}
const uint32_t language_directory_offset =
GetEntryFromResourceDirectoryByID(name_directory_offset, name, true);
if (!language_directory_offset) {
return false;
}
// The definition of IMAGE_RESOURCE_DIRECTORY_ENTRY in <winnt.h> has a comment
// saying that its offsets are relative to “the resource directory of the data
// associated with this directory entry”. That could be interpreted to mean
// that language_directory_offset is relative to name_directory_offset, since
// the language directory entry is found within the name directory. This is
// not correct. All resource offsets are relative to the resources area within
// the module.
const uint32_t data_offset = GetEntryFromResourceDirectoryByLanguage(
language_directory_offset, language);
if (!data_offset) {
return false;
}
IMAGE_RESOURCE_DATA_ENTRY data_entry;
if (!resources_subrange_reader_.ReadMemory(
resources_subrange_reader_.Base() + data_offset,
sizeof(data_entry),
&data_entry)) {
LOG(WARNING) << "could not read resource data entry from "
<< resources_subrange_reader_.name();
return false;
}
// The definition of IMAGE_RESOURCE_DATA_ENTRY in <winnt.h> has a comment
// saying that OffsetToData is relative to the beginning of the resource data.
// This is not correct. Its module-relative.
*address = module_base_ + data_entry.OffsetToData;
*size = data_entry.Size;
if (code_page) {
*code_page = data_entry.CodePage;
}
return true;
}
uint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByID(
uint32_t language_directory_offset,
uint16_t id,
bool want_subdirectory) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY> entries_by_id;
if (!ReadResourceDirectory(
language_directory_offset, nullptr, nullptr, &entries_by_id)) {
return 0;
}
const auto entry_it =
std::find_if(entries_by_id.begin(),
entries_by_id.end(),
[id](const IMAGE_RESOURCE_DIRECTORY_ENTRY& entry) {
return !entry.NameIsString && entry.Id == id;
});
if (entry_it != entries_by_id.end()) {
if ((entry_it->DataIsDirectory != 0) != want_subdirectory) {
LOG(WARNING) << "expected " << (want_subdirectory ? "" : "non-")
<< "directory for entry id " << id << " in "
<< resources_subrange_reader_.name();
return 0;
}
return entry_it->DataIsDirectory ? entry_it->OffsetToDirectory
: entry_it->OffsetToData;
}
return 0;
}
uint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByLanguage(
uint32_t resource_directory_offset,
uint16_t language) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY> entries_by_language;
if (!ReadResourceDirectory(
resource_directory_offset, nullptr, nullptr, &entries_by_language)) {
return 0;
}
if (entries_by_language.empty()) {
return 0;
}
// https://msdn.microsoft.com/library/cc194810.aspx
//
// TODO(mark): It seems like FindResourceEx() might do something more complex.
// It would be best to mimic its behavior.
std::vector<uint16_t> try_languages;
if (PRIMARYLANGID(language) != LANG_NEUTRAL) {
AddLanguageAndNeutralSublanguage(&try_languages, language);
} else {
if (SUBLANGID(language) != SUBLANG_SYS_DEFAULT) {
AddLanguageAndNeutralSublanguage(&try_languages,
LANGIDFROMLCID(GetThreadLocale()));
AddLanguageAndNeutralSublanguage(&try_languages,
LANGIDFROMLCID(GetUserDefaultLCID()));
}
if (SUBLANGID(language) != SUBLANG_DEFAULT) {
AddLanguageAndNeutralSublanguage(&try_languages,
LANGIDFROMLCID(GetSystemDefaultLCID()));
}
}
try_languages.push_back(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
try_languages.push_back(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT));
for (const auto try_language : try_languages) {
const auto entry_it = std::find_if(
entries_by_language.begin(),
entries_by_language.end(),
[try_language](const IMAGE_RESOURCE_DIRECTORY_ENTRY& entry) {
return !entry.NameIsString && entry.Id == try_language;
});
if (entry_it != entries_by_language.end()) {
if (entry_it->DataIsDirectory) {
LOG(WARNING) << "expected non-directory for entry language "
<< try_language << " in "
<< resources_subrange_reader_.name();
return 0;
}
return entry_it->OffsetToData;
}
}
// Fall back to the first entry in the list.
const auto& entry = entries_by_language.front();
if (entry.DataIsDirectory) {
LOG(WARNING) << "expected non-directory for entry in "
<< resources_subrange_reader_.name();
return 0;
}
return entry.OffsetToData;
}
bool PEImageResourceReader::ReadResourceDirectory(
uint32_t resource_directory_offset,
IMAGE_RESOURCE_DIRECTORY* resource_directory,
std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* named_entries,
std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* id_entries) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// resource_directory is optional, but its still needed locally even if the
// caller isnt interested in it.
std::unique_ptr<IMAGE_RESOURCE_DIRECTORY> local_resource_directory;
if (!resource_directory) {
local_resource_directory.reset(new IMAGE_RESOURCE_DIRECTORY);
resource_directory = local_resource_directory.get();
}
const WinVMAddress address =
resources_subrange_reader_.Base() + resource_directory_offset;
if (!resources_subrange_reader_.ReadMemory(
address, sizeof(*resource_directory), resource_directory)) {
LOG(WARNING) << "could not read resource directory from "
<< resources_subrange_reader_.name();
return false;
}
if (named_entries) {
named_entries->clear();
named_entries->resize(resource_directory->NumberOfNamedEntries);
if (!named_entries->empty() &&
!resources_subrange_reader_.ReadMemory(
address + sizeof(*resource_directory),
named_entries->size() * sizeof((*named_entries)[0]),
&(*named_entries)[0])) {
LOG(WARNING) << "could not read resource directory named entries from "
<< resources_subrange_reader_.name();
return false;
}
}
if (id_entries) {
id_entries->clear();
id_entries->resize(resource_directory->NumberOfIdEntries);
if (!id_entries->empty() &&
!resources_subrange_reader_.ReadMemory(
address + sizeof(*resource_directory) +
resource_directory->NumberOfNamedEntries *
sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY),
id_entries->size() * sizeof((*id_entries)[0]),
&(*id_entries)[0])) {
LOG(WARNING) << "could not read resource directory ID entries from "
<< resources_subrange_reader_.name();
return false;
}
}
return true;
}
} // namespace crashpad