mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 15:32:10 +08:00
6278690abe
sed -i '' -E -e 's/Copyright (.+) The Crashpad Authors\. All rights reserved\.$/Copyright \1 The Crashpad Authors/' $(git grep -El 'Copyright (.+) The Crashpad Authors\. All rights reserved\.$') Bug: chromium:1098010 Change-Id: I8d6138469ddbe3d281a5d83f64cf918ec2491611 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3878262 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Mark Mentovai <mark@chromium.org>
280 lines
9.6 KiB
C++
280 lines
9.6 KiB
C++
// Copyright 2015 The Crashpad Authors
|
||
//
|
||
// 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. It’s 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 it’s still needed locally even if the
|
||
// caller isn’t 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
|