Index: snapshot/win/pe_image_resource_reader.cc |
diff --git a/snapshot/win/pe_image_resource_reader.cc b/snapshot/win/pe_image_resource_reader.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3a754a9757596a63aa4153b8b6f8914e3db34511 |
--- /dev/null |
+++ b/snapshot/win/pe_image_resource_reader.cc |
@@ -0,0 +1,279 @@ |
+// 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 "base/logging.h" |
+#include "base/memory/scoped_ptr.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/en-us/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. |
+ scoped_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 |