2022-09-06 19:14:07 -04:00
|
|
|
// Copyright 2017 The Crashpad Authors
|
2017-07-05 18:23:32 -07:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2017-09-08 12:49:55 -07:00
|
|
|
#include "snapshot/elf/elf_image_reader.h"
|
2017-07-05 18:23:32 -07:00
|
|
|
|
2017-07-14 16:00:21 -04:00
|
|
|
#include <stddef.h>
|
|
|
|
|
2017-12-05 11:21:14 -08:00
|
|
|
#include <algorithm>
|
2017-07-05 18:23:32 -07:00
|
|
|
#include <limits>
|
2017-12-05 11:21:14 -08:00
|
|
|
#include <utility>
|
2017-07-14 16:00:21 -04:00
|
|
|
#include <vector>
|
2017-07-05 18:23:32 -07:00
|
|
|
|
2023-10-18 12:08:10 -07:00
|
|
|
#include "base/check_op.h"
|
2017-07-05 18:23:32 -07:00
|
|
|
#include "base/logging.h"
|
2019-05-27 10:26:15 -07:00
|
|
|
#include "base/numerics/safe_math.h"
|
2017-07-28 21:39:29 -04:00
|
|
|
#include "build/build_config.h"
|
2017-09-12 16:49:35 -07:00
|
|
|
#include "util/numeric/checked_vm_address_range.h"
|
2017-07-05 18:23:32 -07:00
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
|
|
|
|
class ElfImageReader::ProgramHeaderTable {
|
|
|
|
public:
|
|
|
|
virtual ~ProgramHeaderTable() {}
|
|
|
|
|
2018-07-26 08:27:31 -07:00
|
|
|
virtual bool VerifyLoadSegments(bool verbose) const = 0;
|
2017-07-05 18:23:32 -07:00
|
|
|
virtual size_t Size() const = 0;
|
2017-09-12 16:49:35 -07:00
|
|
|
virtual bool GetDynamicSegment(VMAddress* address, VMSize* size) const = 0;
|
2018-07-26 08:27:31 -07:00
|
|
|
virtual bool GetPreferredElfHeaderAddress(VMAddress* address,
|
|
|
|
bool verbose) const = 0;
|
2017-09-12 16:49:35 -07:00
|
|
|
virtual bool GetPreferredLoadedMemoryRange(VMAddress* address,
|
2018-07-26 08:27:31 -07:00
|
|
|
VMSize* size,
|
|
|
|
bool verbose) const = 0;
|
2017-07-05 18:23:32 -07:00
|
|
|
|
2017-12-05 11:21:14 -08:00
|
|
|
// Locate the next PT_NOTE segment starting at segment index start_index. If a
|
|
|
|
// PT_NOTE segment is found, start_index is set to the next index after the
|
|
|
|
// found segment.
|
|
|
|
virtual bool GetNoteSegment(size_t* start_index,
|
|
|
|
VMAddress* address,
|
|
|
|
VMSize* size) const = 0;
|
|
|
|
|
2017-07-05 18:23:32 -07:00
|
|
|
protected:
|
|
|
|
ProgramHeaderTable() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename PhdrType>
|
|
|
|
class ElfImageReader::ProgramHeaderTableSpecific
|
|
|
|
: public ElfImageReader::ProgramHeaderTable {
|
|
|
|
public:
|
2022-09-30 14:07:07 +02:00
|
|
|
ProgramHeaderTableSpecific() {}
|
2021-09-20 12:55:12 -07:00
|
|
|
|
2022-09-30 14:07:07 +02:00
|
|
|
ProgramHeaderTableSpecific(
|
2021-09-20 12:55:12 -07:00
|
|
|
const ProgramHeaderTableSpecific<PhdrType>&) = delete;
|
|
|
|
ProgramHeaderTableSpecific<PhdrType>& operator=(
|
|
|
|
const ProgramHeaderTableSpecific<PhdrType>&) = delete;
|
|
|
|
|
2022-09-30 14:07:07 +02:00
|
|
|
~ProgramHeaderTableSpecific() {}
|
2017-07-05 18:23:32 -07:00
|
|
|
|
|
|
|
bool Initialize(const ProcessMemoryRange& memory,
|
2017-09-12 16:49:35 -07:00
|
|
|
VMAddress address,
|
2018-07-26 08:27:31 -07:00
|
|
|
VMSize num_segments,
|
|
|
|
bool verbose) {
|
2017-07-05 18:23:32 -07:00
|
|
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|
|
|
table_.resize(num_segments);
|
|
|
|
if (!memory.Read(address, sizeof(PhdrType) * num_segments, table_.data())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-26 08:27:31 -07:00
|
|
|
if (!VerifyLoadSegments(verbose)) {
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-26 08:27:31 -07:00
|
|
|
bool VerifyLoadSegments(bool verbose) const override {
|
2017-07-05 18:23:32 -07:00
|
|
|
constexpr bool is_64_bit = std::is_same<PhdrType, Elf64_Phdr>::value;
|
2017-09-12 16:49:35 -07:00
|
|
|
VMAddress last_vaddr;
|
2017-07-05 18:23:32 -07:00
|
|
|
bool load_found = false;
|
|
|
|
for (const auto& header : table_) {
|
|
|
|
if (header.p_type == PT_LOAD) {
|
2017-09-12 16:49:35 -07:00
|
|
|
CheckedVMAddressRange load_range(
|
2017-07-05 18:23:32 -07:00
|
|
|
is_64_bit, header.p_vaddr, header.p_memsz);
|
|
|
|
|
|
|
|
if (!load_range.IsValid()) {
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "bad load range";
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (load_found && header.p_vaddr <= last_vaddr) {
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "out of order load segments";
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
load_found = true;
|
|
|
|
last_vaddr = header.p_vaddr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t Size() const override { return sizeof(PhdrType) * table_.size(); }
|
|
|
|
|
2018-07-26 08:27:31 -07:00
|
|
|
bool GetPreferredElfHeaderAddress(VMAddress* address,
|
|
|
|
bool verbose) const override {
|
2017-07-05 18:23:32 -07:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
for (const auto& header : table_) {
|
|
|
|
if (header.p_type == PT_LOAD && header.p_offset == 0) {
|
|
|
|
*address = header.p_vaddr;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "no preferred header address";
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
bool GetPreferredLoadedMemoryRange(VMAddress* base,
|
2018-07-26 08:27:31 -07:00
|
|
|
VMSize* size,
|
|
|
|
bool verbose) const override {
|
2017-07-05 18:23:32 -07:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
VMAddress preferred_base = 0;
|
|
|
|
VMAddress preferred_end = 0;
|
2017-07-05 18:23:32 -07:00
|
|
|
bool load_found = false;
|
|
|
|
for (const auto& header : table_) {
|
|
|
|
if (header.p_type == PT_LOAD) {
|
|
|
|
if (!load_found) {
|
|
|
|
preferred_base = header.p_vaddr;
|
|
|
|
load_found = true;
|
|
|
|
}
|
|
|
|
preferred_end = header.p_vaddr + header.p_memsz;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (load_found) {
|
|
|
|
*base = preferred_base;
|
|
|
|
*size = preferred_end - preferred_base;
|
|
|
|
return true;
|
|
|
|
}
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "no load segments";
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
bool GetDynamicSegment(VMAddress* address, VMSize* size) const override {
|
2017-07-05 18:23:32 -07:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
const PhdrType* phdr;
|
|
|
|
if (!GetProgramHeader(PT_DYNAMIC, &phdr)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*address = phdr->p_vaddr;
|
|
|
|
*size = phdr->p_memsz;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GetProgramHeader(uint32_t type, const PhdrType** header_out) const {
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
for (const auto& header : table_) {
|
|
|
|
if (header.p_type == type) {
|
|
|
|
*header_out = &header;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-12-05 11:21:14 -08:00
|
|
|
bool GetNoteSegment(size_t* start_index,
|
|
|
|
VMAddress* address,
|
|
|
|
VMSize* size) const override {
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
for (size_t index = *start_index; index < table_.size(); ++index) {
|
2018-02-28 13:15:49 -08:00
|
|
|
if (table_[index].p_type == PT_NOTE && table_[index].p_vaddr != 0) {
|
2017-12-05 11:21:14 -08:00
|
|
|
*start_index = index + 1;
|
|
|
|
*address = table_[index].p_vaddr;
|
|
|
|
*size = table_[index].p_memsz;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-07-05 18:23:32 -07:00
|
|
|
private:
|
|
|
|
std::vector<PhdrType> table_;
|
|
|
|
InitializationStateDcheck initialized_;
|
|
|
|
};
|
|
|
|
|
2017-12-05 11:21:14 -08:00
|
|
|
ElfImageReader::NoteReader::~NoteReader() = default;
|
|
|
|
|
|
|
|
ElfImageReader::NoteReader::Result ElfImageReader::NoteReader::NextNote(
|
|
|
|
std::string* name,
|
|
|
|
NoteType* type,
|
2018-11-06 13:19:06 -08:00
|
|
|
std::string* desc,
|
|
|
|
VMAddress* desc_address) {
|
2017-12-05 11:21:14 -08:00
|
|
|
if (!is_valid_) {
|
|
|
|
LOG(ERROR) << "invalid note reader";
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result result = Result::kError;
|
|
|
|
do {
|
|
|
|
while (current_address_ == segment_end_address_) {
|
|
|
|
VMSize segment_size;
|
|
|
|
if (!phdr_table_->GetNoteSegment(
|
|
|
|
&phdr_index_, ¤t_address_, &segment_size)) {
|
|
|
|
return Result::kNoMoreNotes;
|
|
|
|
}
|
|
|
|
current_address_ += elf_reader_->GetLoadBias();
|
|
|
|
segment_end_address_ = current_address_ + segment_size;
|
|
|
|
segment_range_ = std::make_unique<ProcessMemoryRange>();
|
|
|
|
if (!segment_range_->Initialize(*range_) ||
|
|
|
|
!segment_range_->RestrictRange(current_address_, segment_size)) {
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
retry_ = false;
|
2018-11-06 13:19:06 -08:00
|
|
|
result = range_->Is64Bit()
|
|
|
|
? ReadNote<Elf64_Nhdr>(name, type, desc, desc_address)
|
|
|
|
: ReadNote<Elf32_Nhdr>(name, type, desc, desc_address);
|
2017-12-05 11:21:14 -08:00
|
|
|
} while (retry_);
|
|
|
|
|
|
|
|
if (result == Result::kSuccess) {
|
|
|
|
return Result::kSuccess;
|
|
|
|
}
|
|
|
|
is_valid_ = false;
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
|
2019-05-24 12:49:28 -07:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
// The maximum size the user can specify for maximum note size. Clamping this
|
|
|
|
// ensures that buffer allocations cannot be wildly large. It is not expected
|
|
|
|
// that a note would be larger than ~1k in normal usage.
|
|
|
|
constexpr size_t kMaxMaxNoteSize = 16384;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2017-12-05 11:21:14 -08:00
|
|
|
ElfImageReader::NoteReader::NoteReader(const ElfImageReader* elf_reader,
|
|
|
|
const ProcessMemoryRange* range,
|
|
|
|
const ProgramHeaderTable* phdr_table,
|
2019-05-24 12:49:28 -07:00
|
|
|
size_t max_note_size,
|
2017-12-05 11:21:14 -08:00
|
|
|
const std::string& name_filter,
|
|
|
|
NoteType type_filter,
|
|
|
|
bool use_filter)
|
|
|
|
: current_address_(0),
|
|
|
|
segment_end_address_(0),
|
|
|
|
elf_reader_(elf_reader),
|
|
|
|
range_(range),
|
|
|
|
phdr_table_(phdr_table),
|
|
|
|
segment_range_(),
|
|
|
|
phdr_index_(0),
|
2019-05-24 12:49:28 -07:00
|
|
|
max_note_size_(std::min(kMaxMaxNoteSize, max_note_size)),
|
2017-12-05 11:21:14 -08:00
|
|
|
name_filter_(name_filter),
|
|
|
|
type_filter_(type_filter),
|
|
|
|
use_filter_(use_filter),
|
|
|
|
is_valid_(true),
|
2019-05-24 12:49:28 -07:00
|
|
|
retry_(false) {
|
|
|
|
DCHECK_LT(max_note_size, kMaxMaxNoteSize);
|
|
|
|
}
|
2017-12-05 11:21:14 -08:00
|
|
|
|
|
|
|
template <typename NhdrType>
|
|
|
|
ElfImageReader::NoteReader::Result ElfImageReader::NoteReader::ReadNote(
|
|
|
|
std::string* name,
|
|
|
|
NoteType* type,
|
2018-11-06 13:19:06 -08:00
|
|
|
std::string* desc,
|
|
|
|
VMAddress* desc_address) {
|
2017-12-05 11:21:14 -08:00
|
|
|
static_assert(sizeof(*type) >= sizeof(NhdrType::n_namesz),
|
|
|
|
"Note field size mismatch");
|
|
|
|
DCHECK_LT(current_address_, segment_end_address_);
|
|
|
|
|
|
|
|
NhdrType note_info;
|
|
|
|
if (!segment_range_->Read(current_address_, sizeof(note_info), ¬e_info)) {
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
current_address_ += sizeof(note_info);
|
|
|
|
|
|
|
|
constexpr size_t align = sizeof(note_info.n_namesz);
|
2019-05-27 10:26:15 -07:00
|
|
|
|
|
|
|
#define CHECKED_PAD(x, into) \
|
|
|
|
base::CheckAnd(base::CheckAdd(x, align - 1), ~(align - 1)) \
|
|
|
|
.AssignIfValid(&into)
|
|
|
|
|
|
|
|
size_t padded_namesz;
|
|
|
|
if (!CHECKED_PAD(note_info.n_namesz, padded_namesz)) {
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
size_t padded_descsz;
|
|
|
|
if (!CHECKED_PAD(note_info.n_descsz, padded_descsz)) {
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t note_size;
|
|
|
|
if (!base::CheckAdd(padded_namesz, padded_descsz).AssignIfValid(¬e_size)) {
|
|
|
|
return Result::kError;
|
|
|
|
}
|
2017-12-05 11:21:14 -08:00
|
|
|
|
|
|
|
// Notes typically have 4-byte alignment. However, .note.android.ident may
|
|
|
|
// inadvertently use 2-byte alignment.
|
|
|
|
// https://android-review.googlesource.com/c/platform/bionic/+/554986/
|
|
|
|
// We can still find .note.android.ident if it appears first in a note segment
|
|
|
|
// but there may be 4-byte aligned notes following it. If this note was
|
|
|
|
// aligned at less than 4-bytes, expect that the next note will be aligned at
|
|
|
|
// 4-bytes and add extra padding, if necessary.
|
2019-05-27 10:26:15 -07:00
|
|
|
|
|
|
|
VMAddress end_of_note_candidate;
|
|
|
|
if (!base::CheckAdd(current_address_, note_size)
|
|
|
|
.AssignIfValid(&end_of_note_candidate)) {
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
VMAddress end_of_note;
|
|
|
|
if (!CHECKED_PAD(end_of_note_candidate, end_of_note)) {
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
end_of_note = std::min(end_of_note, segment_end_address_);
|
|
|
|
|
|
|
|
#undef CHECKED_PAD
|
2017-12-05 11:21:14 -08:00
|
|
|
|
2019-05-24 12:49:28 -07:00
|
|
|
if (note_size > max_note_size_) {
|
2017-12-05 11:21:14 -08:00
|
|
|
current_address_ = end_of_note;
|
|
|
|
retry_ = true;
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (use_filter_ && note_info.n_type != type_filter_) {
|
|
|
|
current_address_ = end_of_note;
|
|
|
|
retry_ = true;
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string local_name(note_info.n_namesz, '\0');
|
|
|
|
if (!segment_range_->Read(
|
|
|
|
current_address_, note_info.n_namesz, &local_name[0])) {
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
if (!local_name.empty()) {
|
|
|
|
if (local_name.back() != '\0') {
|
|
|
|
LOG(ERROR) << "unterminated note name";
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
local_name.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (use_filter_ && local_name != name_filter_) {
|
|
|
|
current_address_ = end_of_note;
|
|
|
|
retry_ = true;
|
|
|
|
return Result::kError;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_address_ += padded_namesz;
|
|
|
|
|
|
|
|
std::string local_desc(note_info.n_descsz, '\0');
|
|
|
|
if (!segment_range_->Read(
|
|
|
|
current_address_, note_info.n_descsz, &local_desc[0])) {
|
|
|
|
return Result::kError;
|
|
|
|
}
|
2018-11-06 13:19:06 -08:00
|
|
|
*desc_address = current_address_;
|
2017-12-05 11:21:14 -08:00
|
|
|
|
|
|
|
current_address_ = end_of_note;
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
name->swap(local_name);
|
|
|
|
}
|
|
|
|
if (type) {
|
|
|
|
*type = note_info.n_type;
|
|
|
|
}
|
|
|
|
desc->swap(local_desc);
|
|
|
|
return Result::kSuccess;
|
|
|
|
}
|
|
|
|
|
2017-07-05 18:23:32 -07:00
|
|
|
ElfImageReader::ElfImageReader()
|
|
|
|
: header_64_(),
|
|
|
|
ehdr_address_(0),
|
|
|
|
load_bias_(0),
|
|
|
|
memory_(),
|
|
|
|
program_headers_(),
|
|
|
|
dynamic_array_(),
|
|
|
|
symbol_table_(),
|
|
|
|
initialized_(),
|
|
|
|
dynamic_array_initialized_(),
|
|
|
|
symbol_table_initialized_() {}
|
|
|
|
|
|
|
|
ElfImageReader::~ElfImageReader() {}
|
|
|
|
|
|
|
|
bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
|
2018-07-26 08:27:31 -07:00
|
|
|
VMAddress address,
|
|
|
|
bool verbose) {
|
2017-07-05 18:23:32 -07:00
|
|
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|
|
|
ehdr_address_ = address;
|
|
|
|
if (!memory_.Initialize(memory)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t e_ident[EI_NIDENT];
|
|
|
|
if (!memory_.Read(ehdr_address_, EI_NIDENT, e_ident)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
|
|
|
|
e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "Incorrect ELF magic number";
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS64) &&
|
|
|
|
!(!memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS32)) {
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "unexpected bitness";
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(ARCH_CPU_LITTLE_ENDIAN)
|
|
|
|
constexpr uint8_t expected_encoding = ELFDATA2LSB;
|
|
|
|
#elif defined(ARCH_CPU_BIG_ENDIAN)
|
|
|
|
constexpr uint8_t expected_encoding = ELFDATA2MSB;
|
|
|
|
#endif
|
|
|
|
if (e_ident[EI_DATA] != expected_encoding) {
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "unexpected encoding";
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e_ident[EI_VERSION] != EV_CURRENT) {
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "unexpected version";
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(memory_.Is64Bit()
|
|
|
|
? memory_.Read(ehdr_address_, sizeof(header_64_), &header_64_)
|
|
|
|
: memory_.Read(ehdr_address_, sizeof(header_32_), &header_32_))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define VERIFY_HEADER(header) \
|
|
|
|
do { \
|
|
|
|
if (header.e_type != ET_EXEC && header.e_type != ET_DYN) { \
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "unexpected image type"; \
|
2017-07-05 18:23:32 -07:00
|
|
|
return false; \
|
|
|
|
} \
|
|
|
|
if (header.e_version != EV_CURRENT) { \
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "unexpected version"; \
|
2017-07-05 18:23:32 -07:00
|
|
|
return false; \
|
|
|
|
} \
|
|
|
|
if (header.e_ehsize != sizeof(header)) { \
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "unexpected header size"; \
|
2017-07-05 18:23:32 -07:00
|
|
|
return false; \
|
|
|
|
} \
|
|
|
|
} while (false);
|
|
|
|
|
|
|
|
if (memory_.Is64Bit()) {
|
|
|
|
VERIFY_HEADER(header_64_);
|
|
|
|
} else {
|
|
|
|
VERIFY_HEADER(header_32_);
|
|
|
|
}
|
|
|
|
|
2018-07-26 08:27:31 -07:00
|
|
|
if (!InitializeProgramHeaders(verbose)) {
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
VMAddress preferred_ehdr_address;
|
2017-07-05 18:23:32 -07:00
|
|
|
if (!program_headers_.get()->GetPreferredElfHeaderAddress(
|
2018-07-26 08:27:31 -07:00
|
|
|
&preferred_ehdr_address, verbose)) {
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
load_bias_ = ehdr_address_ - preferred_ehdr_address;
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
VMAddress base_address;
|
|
|
|
VMSize loaded_size;
|
2018-07-26 08:27:31 -07:00
|
|
|
if (!program_headers_.get()->GetPreferredLoadedMemoryRange(
|
|
|
|
&base_address, &loaded_size, verbose)) {
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
base_address += load_bias_;
|
|
|
|
|
|
|
|
if (!memory_.RestrictRange(base_address, loaded_size)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
VMSize ehdr_size;
|
|
|
|
VMAddress phdr_address;
|
2017-07-05 18:23:32 -07:00
|
|
|
if (memory_.Is64Bit()) {
|
|
|
|
ehdr_size = sizeof(header_64_);
|
|
|
|
phdr_address = ehdr_address_ + header_64_.e_phoff;
|
|
|
|
} else {
|
|
|
|
ehdr_size = sizeof(header_32_);
|
|
|
|
phdr_address = ehdr_address_ + header_32_.e_phoff;
|
|
|
|
}
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
CheckedVMAddressRange range(memory_.Is64Bit(), base_address, loaded_size);
|
|
|
|
if (!range.ContainsRange(
|
|
|
|
CheckedVMAddressRange(memory_.Is64Bit(), ehdr_address_, ehdr_size))) {
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "ehdr out of range";
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
2017-09-12 16:49:35 -07:00
|
|
|
if (!range.ContainsRange(CheckedVMAddressRange(
|
2017-07-05 18:23:32 -07:00
|
|
|
memory.Is64Bit(), phdr_address, program_headers_->Size()))) {
|
2018-07-26 08:27:31 -07:00
|
|
|
LOG_IF(ERROR, verbose) << "phdrs out of range";
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t ElfImageReader::FileType() const {
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
return memory_.Is64Bit() ? header_64_.e_type : header_32_.e_type;
|
|
|
|
}
|
|
|
|
|
2018-09-20 10:13:58 -07:00
|
|
|
bool ElfImageReader::SoName(std::string* name) {
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
if (!InitializeDynamicArray()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
VMSize offset;
|
|
|
|
if (!dynamic_array_->GetValue(DT_SONAME, true, &offset)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ReadDynamicStringTableAtOffset(offset, name);
|
|
|
|
}
|
|
|
|
|
2017-07-05 18:23:32 -07:00
|
|
|
bool ElfImageReader::GetDynamicSymbol(const std::string& name,
|
2017-09-12 16:49:35 -07:00
|
|
|
VMAddress* address,
|
|
|
|
VMSize* size) {
|
2017-07-05 18:23:32 -07:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
if (!InitializeDynamicSymbolTable()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ElfSymbolTableReader::SymbolInformation info;
|
|
|
|
if (!symbol_table_->GetSymbol(name, &info)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (info.shndx == SHN_UNDEF || info.shndx == SHN_COMMON) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (info.binding) {
|
|
|
|
case STB_GLOBAL:
|
|
|
|
case STB_WEAK:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STB_LOCAL:
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (info.type) {
|
|
|
|
case STT_OBJECT:
|
|
|
|
case STT_FUNC:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STT_COMMON:
|
|
|
|
case STT_NOTYPE:
|
|
|
|
case STT_SECTION:
|
|
|
|
case STT_FILE:
|
|
|
|
case STT_TLS:
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info.shndx != SHN_ABS) {
|
|
|
|
info.address += GetLoadBias();
|
|
|
|
}
|
|
|
|
|
|
|
|
*address = info.address;
|
|
|
|
*size = info.size;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
bool ElfImageReader::ReadDynamicStringTableAtOffset(VMSize offset,
|
2017-07-05 18:23:32 -07:00
|
|
|
std::string* string) {
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
if (!InitializeDynamicArray()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
VMAddress string_table_address;
|
|
|
|
VMSize string_table_size;
|
Read either DT_HASH or DT_GNU_HASH to determine the size of DT_SYMTAB
Without the section headers for the symbol table, there's no direct way
to calculate the number of entries in the table.
DT_HASH and DT_GNU_HASH are auxiliary tables that are designed to make
symbol lookup faster. DT_HASH is the original and is theoretically
mandatory. DT_GNU_HASH is the new-and-improved, but is more complex.
In practice, however, an Android build (at least vs. API 16) has only
DT_HASH, and not DT_GNU_HASH, and a Fuchsia build has only DT_GNU_HASH
but not DT_HASH. So, both are tried.
This change does not actually use the data in these tables to improve
the speed of symbol lookup, but instead only uses them to correctly
terminate the linear search.
DT_HASH contains the total number of symbols in the symbol table fairly
directly because there is an entry for each symbol table entry in the
hash table, so the number is the same.
DT_GNU_HASH regrettably does not. Instead, it's necessary to walk the
buckets and chain structure to find the largest entry.
DT_GNU_HASH doesn't appear in any "real" documentation that I'm aware
of, other than the binutils code (at least as far as I know). Some
more-and-less-useful references:
- https://flapenguin.me/2017/04/24/elf-lookup-dt-hash/
- https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/
- http://deroko.phearless.org/dt_gnu_hash.txt
- https://sourceware.org/ml/binutils/2006-10/msg00377.html
Change-Id: I7cfc4372f29efc37446f0931d22a1f790e44076f
Bug: crashpad:213, crashpad:196
Reviewed-on: https://chromium-review.googlesource.com/876879
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
2018-01-30 14:30:50 -08:00
|
|
|
if (!GetAddressFromDynamicArray(DT_STRTAB, true, &string_table_address) ||
|
|
|
|
!dynamic_array_->GetValue(DT_STRSZ, true, &string_table_size)) {
|
2017-07-05 18:23:32 -07:00
|
|
|
LOG(ERROR) << "missing string table info";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (offset >= string_table_size) {
|
|
|
|
LOG(ERROR) << "bad offset";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-06-04 15:05:27 -07:00
|
|
|
// GNU ld.so doesn't adjust the vdso's dynamic array entries by the load bias.
|
|
|
|
// If the address is too small to point into the loaded module range and is
|
|
|
|
// small enough to be an offset from the base of the module, adjust it now.
|
|
|
|
if (string_table_address < memory_.Base() &&
|
|
|
|
string_table_address < memory_.Size()) {
|
|
|
|
string_table_address += GetLoadBias();
|
|
|
|
}
|
|
|
|
|
2017-07-05 18:23:32 -07:00
|
|
|
if (!memory_.ReadCStringSizeLimited(
|
|
|
|
string_table_address + offset, string_table_size - offset, string)) {
|
|
|
|
LOG(ERROR) << "missing nul-terminator";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
bool ElfImageReader::GetDebugAddress(VMAddress* debug) {
|
2017-07-05 18:23:32 -07:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
if (!InitializeDynamicArray()) {
|
|
|
|
return false;
|
|
|
|
}
|
Read either DT_HASH or DT_GNU_HASH to determine the size of DT_SYMTAB
Without the section headers for the symbol table, there's no direct way
to calculate the number of entries in the table.
DT_HASH and DT_GNU_HASH are auxiliary tables that are designed to make
symbol lookup faster. DT_HASH is the original and is theoretically
mandatory. DT_GNU_HASH is the new-and-improved, but is more complex.
In practice, however, an Android build (at least vs. API 16) has only
DT_HASH, and not DT_GNU_HASH, and a Fuchsia build has only DT_GNU_HASH
but not DT_HASH. So, both are tried.
This change does not actually use the data in these tables to improve
the speed of symbol lookup, but instead only uses them to correctly
terminate the linear search.
DT_HASH contains the total number of symbols in the symbol table fairly
directly because there is an entry for each symbol table entry in the
hash table, so the number is the same.
DT_GNU_HASH regrettably does not. Instead, it's necessary to walk the
buckets and chain structure to find the largest entry.
DT_GNU_HASH doesn't appear in any "real" documentation that I'm aware
of, other than the binutils code (at least as far as I know). Some
more-and-less-useful references:
- https://flapenguin.me/2017/04/24/elf-lookup-dt-hash/
- https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/
- http://deroko.phearless.org/dt_gnu_hash.txt
- https://sourceware.org/ml/binutils/2006-10/msg00377.html
Change-Id: I7cfc4372f29efc37446f0931d22a1f790e44076f
Bug: crashpad:213, crashpad:196
Reviewed-on: https://chromium-review.googlesource.com/876879
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
2018-01-30 14:30:50 -08:00
|
|
|
return GetAddressFromDynamicArray(DT_DEBUG, true, debug);
|
2017-07-05 18:23:32 -07:00
|
|
|
}
|
|
|
|
|
2018-07-26 08:27:31 -07:00
|
|
|
bool ElfImageReader::GetDynamicArrayAddress(VMAddress* address) {
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
VMAddress dyn_segment_address;
|
|
|
|
VMSize dyn_segment_size;
|
|
|
|
if (!program_headers_.get()->GetDynamicSegment(&dyn_segment_address,
|
|
|
|
&dyn_segment_size)) {
|
|
|
|
LOG(ERROR) << "no dynamic segment";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*address = dyn_segment_address + GetLoadBias();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
VMAddress ElfImageReader::GetProgramHeaderTableAddress() {
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
return ehdr_address_ +
|
|
|
|
(memory_.Is64Bit() ? header_64_.e_phoff : header_32_.e_phoff);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ElfImageReader::InitializeProgramHeaders(bool verbose) {
|
|
|
|
#define INITIALIZE_PROGRAM_HEADERS(PhdrType, header) \
|
|
|
|
do { \
|
|
|
|
if (header.e_phentsize != sizeof(PhdrType)) { \
|
|
|
|
LOG_IF(ERROR, verbose) << "unexpected phdr size"; \
|
|
|
|
return false; \
|
|
|
|
} \
|
|
|
|
auto phdrs = new ProgramHeaderTableSpecific<PhdrType>(); \
|
|
|
|
program_headers_.reset(phdrs); \
|
|
|
|
if (!phdrs->Initialize(memory_, \
|
|
|
|
ehdr_address_ + header.e_phoff, \
|
|
|
|
header.e_phnum, \
|
|
|
|
verbose)) { \
|
|
|
|
return false; \
|
|
|
|
} \
|
2017-07-05 18:23:32 -07:00
|
|
|
} while (false);
|
|
|
|
|
|
|
|
if (memory_.Is64Bit()) {
|
|
|
|
INITIALIZE_PROGRAM_HEADERS(Elf64_Phdr, header_64_);
|
|
|
|
} else {
|
|
|
|
INITIALIZE_PROGRAM_HEADERS(Elf32_Phdr, header_32_);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ElfImageReader::InitializeDynamicArray() {
|
|
|
|
if (dynamic_array_initialized_.is_valid()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!dynamic_array_initialized_.is_uninitialized()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
dynamic_array_initialized_.set_invalid();
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
VMAddress dyn_segment_address;
|
|
|
|
VMSize dyn_segment_size;
|
2017-07-05 18:23:32 -07:00
|
|
|
if (!program_headers_.get()->GetDynamicSegment(&dyn_segment_address,
|
|
|
|
&dyn_segment_size)) {
|
|
|
|
LOG(ERROR) << "no dynamic segment";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
dyn_segment_address += GetLoadBias();
|
|
|
|
|
|
|
|
dynamic_array_.reset(new ElfDynamicArrayReader());
|
|
|
|
if (!dynamic_array_->Initialize(
|
|
|
|
memory_, dyn_segment_address, dyn_segment_size)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
dynamic_array_initialized_.set_valid();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ElfImageReader::InitializeDynamicSymbolTable() {
|
|
|
|
if (symbol_table_initialized_.is_valid()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!symbol_table_initialized_.is_uninitialized()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
symbol_table_initialized_.set_invalid();
|
|
|
|
|
|
|
|
if (!InitializeDynamicArray()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-12 16:49:35 -07:00
|
|
|
VMAddress symbol_table_address;
|
Read either DT_HASH or DT_GNU_HASH to determine the size of DT_SYMTAB
Without the section headers for the symbol table, there's no direct way
to calculate the number of entries in the table.
DT_HASH and DT_GNU_HASH are auxiliary tables that are designed to make
symbol lookup faster. DT_HASH is the original and is theoretically
mandatory. DT_GNU_HASH is the new-and-improved, but is more complex.
In practice, however, an Android build (at least vs. API 16) has only
DT_HASH, and not DT_GNU_HASH, and a Fuchsia build has only DT_GNU_HASH
but not DT_HASH. So, both are tried.
This change does not actually use the data in these tables to improve
the speed of symbol lookup, but instead only uses them to correctly
terminate the linear search.
DT_HASH contains the total number of symbols in the symbol table fairly
directly because there is an entry for each symbol table entry in the
hash table, so the number is the same.
DT_GNU_HASH regrettably does not. Instead, it's necessary to walk the
buckets and chain structure to find the largest entry.
DT_GNU_HASH doesn't appear in any "real" documentation that I'm aware
of, other than the binutils code (at least as far as I know). Some
more-and-less-useful references:
- https://flapenguin.me/2017/04/24/elf-lookup-dt-hash/
- https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/
- http://deroko.phearless.org/dt_gnu_hash.txt
- https://sourceware.org/ml/binutils/2006-10/msg00377.html
Change-Id: I7cfc4372f29efc37446f0931d22a1f790e44076f
Bug: crashpad:213, crashpad:196
Reviewed-on: https://chromium-review.googlesource.com/876879
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
2018-01-30 14:30:50 -08:00
|
|
|
if (!GetAddressFromDynamicArray(DT_SYMTAB, true, &symbol_table_address)) {
|
2017-07-05 18:23:32 -07:00
|
|
|
LOG(ERROR) << "no symbol table";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Read either DT_HASH or DT_GNU_HASH to determine the size of DT_SYMTAB
Without the section headers for the symbol table, there's no direct way
to calculate the number of entries in the table.
DT_HASH and DT_GNU_HASH are auxiliary tables that are designed to make
symbol lookup faster. DT_HASH is the original and is theoretically
mandatory. DT_GNU_HASH is the new-and-improved, but is more complex.
In practice, however, an Android build (at least vs. API 16) has only
DT_HASH, and not DT_GNU_HASH, and a Fuchsia build has only DT_GNU_HASH
but not DT_HASH. So, both are tried.
This change does not actually use the data in these tables to improve
the speed of symbol lookup, but instead only uses them to correctly
terminate the linear search.
DT_HASH contains the total number of symbols in the symbol table fairly
directly because there is an entry for each symbol table entry in the
hash table, so the number is the same.
DT_GNU_HASH regrettably does not. Instead, it's necessary to walk the
buckets and chain structure to find the largest entry.
DT_GNU_HASH doesn't appear in any "real" documentation that I'm aware
of, other than the binutils code (at least as far as I know). Some
more-and-less-useful references:
- https://flapenguin.me/2017/04/24/elf-lookup-dt-hash/
- https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/
- http://deroko.phearless.org/dt_gnu_hash.txt
- https://sourceware.org/ml/binutils/2006-10/msg00377.html
Change-Id: I7cfc4372f29efc37446f0931d22a1f790e44076f
Bug: crashpad:213, crashpad:196
Reviewed-on: https://chromium-review.googlesource.com/876879
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
2018-01-30 14:30:50 -08:00
|
|
|
// Try both DT_HASH and DT_GNU_HASH. They're completely different, but both
|
|
|
|
// circuitously offer a way to find the number of entries in the symbol table.
|
|
|
|
// DT_HASH is specifically checked first, because depending on the linker, the
|
|
|
|
// count maybe be incorrect for zero-export cases. In practice, it is believed
|
|
|
|
// that the zero-export case is probably not particularly useful, so this
|
|
|
|
// incorrect count will only occur in constructed test cases (see
|
|
|
|
// ElfImageReader.DtHashAndDtGnuHashMatch).
|
|
|
|
VMSize number_of_symbol_table_entries;
|
|
|
|
if (!GetNumberOfSymbolEntriesFromDtHash(&number_of_symbol_table_entries) &&
|
|
|
|
!GetNumberOfSymbolEntriesFromDtGnuHash(&number_of_symbol_table_entries)) {
|
|
|
|
LOG(ERROR) << "could not retrieve number of symbol table entries";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
symbol_table_.reset(new ElfSymbolTableReader(
|
|
|
|
&memory_, this, symbol_table_address, number_of_symbol_table_entries));
|
2017-07-05 18:23:32 -07:00
|
|
|
symbol_table_initialized_.set_valid();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag,
|
Read either DT_HASH or DT_GNU_HASH to determine the size of DT_SYMTAB
Without the section headers for the symbol table, there's no direct way
to calculate the number of entries in the table.
DT_HASH and DT_GNU_HASH are auxiliary tables that are designed to make
symbol lookup faster. DT_HASH is the original and is theoretically
mandatory. DT_GNU_HASH is the new-and-improved, but is more complex.
In practice, however, an Android build (at least vs. API 16) has only
DT_HASH, and not DT_GNU_HASH, and a Fuchsia build has only DT_GNU_HASH
but not DT_HASH. So, both are tried.
This change does not actually use the data in these tables to improve
the speed of symbol lookup, but instead only uses them to correctly
terminate the linear search.
DT_HASH contains the total number of symbols in the symbol table fairly
directly because there is an entry for each symbol table entry in the
hash table, so the number is the same.
DT_GNU_HASH regrettably does not. Instead, it's necessary to walk the
buckets and chain structure to find the largest entry.
DT_GNU_HASH doesn't appear in any "real" documentation that I'm aware
of, other than the binutils code (at least as far as I know). Some
more-and-less-useful references:
- https://flapenguin.me/2017/04/24/elf-lookup-dt-hash/
- https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/
- http://deroko.phearless.org/dt_gnu_hash.txt
- https://sourceware.org/ml/binutils/2006-10/msg00377.html
Change-Id: I7cfc4372f29efc37446f0931d22a1f790e44076f
Bug: crashpad:213, crashpad:196
Reviewed-on: https://chromium-review.googlesource.com/876879
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
2018-01-30 14:30:50 -08:00
|
|
|
bool log,
|
2017-09-12 16:49:35 -07:00
|
|
|
VMAddress* address) {
|
Read either DT_HASH or DT_GNU_HASH to determine the size of DT_SYMTAB
Without the section headers for the symbol table, there's no direct way
to calculate the number of entries in the table.
DT_HASH and DT_GNU_HASH are auxiliary tables that are designed to make
symbol lookup faster. DT_HASH is the original and is theoretically
mandatory. DT_GNU_HASH is the new-and-improved, but is more complex.
In practice, however, an Android build (at least vs. API 16) has only
DT_HASH, and not DT_GNU_HASH, and a Fuchsia build has only DT_GNU_HASH
but not DT_HASH. So, both are tried.
This change does not actually use the data in these tables to improve
the speed of symbol lookup, but instead only uses them to correctly
terminate the linear search.
DT_HASH contains the total number of symbols in the symbol table fairly
directly because there is an entry for each symbol table entry in the
hash table, so the number is the same.
DT_GNU_HASH regrettably does not. Instead, it's necessary to walk the
buckets and chain structure to find the largest entry.
DT_GNU_HASH doesn't appear in any "real" documentation that I'm aware
of, other than the binutils code (at least as far as I know). Some
more-and-less-useful references:
- https://flapenguin.me/2017/04/24/elf-lookup-dt-hash/
- https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/
- http://deroko.phearless.org/dt_gnu_hash.txt
- https://sourceware.org/ml/binutils/2006-10/msg00377.html
Change-Id: I7cfc4372f29efc37446f0931d22a1f790e44076f
Bug: crashpad:213, crashpad:196
Reviewed-on: https://chromium-review.googlesource.com/876879
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
2018-01-30 14:30:50 -08:00
|
|
|
if (!dynamic_array_->GetValue(tag, log, address)) {
|
2017-07-05 18:23:32 -07:00
|
|
|
return false;
|
|
|
|
}
|
2023-06-08 21:08:54 +00:00
|
|
|
|
|
|
|
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || \
|
|
|
|
(defined(__GLIBC__) && defined(ARCH_CPU_RISCV64))
|
|
|
|
// The GNU loader updates the dynamic array according to the load bias (except
|
|
|
|
// for RISC-V: https://sourceware.org/bugzilla/show_bug.cgi?id=24484).
|
2018-01-19 14:03:34 -08:00
|
|
|
// The Android and Fuchsia loaders only update the debug address.
|
2017-07-05 18:23:32 -07:00
|
|
|
if (tag != DT_DEBUG) {
|
|
|
|
*address += GetLoadBias();
|
|
|
|
}
|
2022-01-19 15:00:24 -05:00
|
|
|
#endif // BUILDFLAG(IS_ANDROID)
|
2017-07-05 18:23:32 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Read either DT_HASH or DT_GNU_HASH to determine the size of DT_SYMTAB
Without the section headers for the symbol table, there's no direct way
to calculate the number of entries in the table.
DT_HASH and DT_GNU_HASH are auxiliary tables that are designed to make
symbol lookup faster. DT_HASH is the original and is theoretically
mandatory. DT_GNU_HASH is the new-and-improved, but is more complex.
In practice, however, an Android build (at least vs. API 16) has only
DT_HASH, and not DT_GNU_HASH, and a Fuchsia build has only DT_GNU_HASH
but not DT_HASH. So, both are tried.
This change does not actually use the data in these tables to improve
the speed of symbol lookup, but instead only uses them to correctly
terminate the linear search.
DT_HASH contains the total number of symbols in the symbol table fairly
directly because there is an entry for each symbol table entry in the
hash table, so the number is the same.
DT_GNU_HASH regrettably does not. Instead, it's necessary to walk the
buckets and chain structure to find the largest entry.
DT_GNU_HASH doesn't appear in any "real" documentation that I'm aware
of, other than the binutils code (at least as far as I know). Some
more-and-less-useful references:
- https://flapenguin.me/2017/04/24/elf-lookup-dt-hash/
- https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/
- http://deroko.phearless.org/dt_gnu_hash.txt
- https://sourceware.org/ml/binutils/2006-10/msg00377.html
Change-Id: I7cfc4372f29efc37446f0931d22a1f790e44076f
Bug: crashpad:213, crashpad:196
Reviewed-on: https://chromium-review.googlesource.com/876879
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
2018-01-30 14:30:50 -08:00
|
|
|
bool ElfImageReader::GetNumberOfSymbolEntriesFromDtHash(
|
|
|
|
VMSize* number_of_symbol_table_entries) {
|
|
|
|
if (!InitializeDynamicArray()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
VMAddress dt_hash_address;
|
|
|
|
if (!GetAddressFromDynamicArray(DT_HASH, false, &dt_hash_address)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct {
|
|
|
|
uint32_t nbucket;
|
|
|
|
uint32_t nchain;
|
|
|
|
} header;
|
|
|
|
|
|
|
|
if (!memory_.Read(dt_hash_address, sizeof(header), &header)) {
|
|
|
|
LOG(ERROR) << "failed to read DT_HASH header";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
*number_of_symbol_table_entries = header.nchain;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ElfImageReader::GetNumberOfSymbolEntriesFromDtGnuHash(
|
|
|
|
VMSize* number_of_symbol_table_entries) {
|
|
|
|
if (!InitializeDynamicArray()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
VMAddress dt_gnu_hash_address;
|
|
|
|
if (!GetAddressFromDynamicArray(DT_GNU_HASH, false, &dt_gnu_hash_address)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ and
|
|
|
|
// https://sourceware.org/ml/binutils/2006-10/msg00377.html.
|
|
|
|
struct {
|
|
|
|
uint32_t nbuckets;
|
|
|
|
uint32_t symoffset;
|
|
|
|
uint32_t bloom_size;
|
|
|
|
uint32_t bloom_shift;
|
|
|
|
} header;
|
|
|
|
if (!memory_.Read(dt_gnu_hash_address, sizeof(header), &header)) {
|
|
|
|
LOG(ERROR) << "failed to read DT_GNU_HASH header";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<uint32_t> buckets(header.nbuckets);
|
|
|
|
const size_t kNumBytesForBuckets = sizeof(buckets[0]) * buckets.size();
|
|
|
|
const size_t kWordSize =
|
|
|
|
memory_.Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t);
|
|
|
|
const VMAddress buckets_address =
|
|
|
|
dt_gnu_hash_address + sizeof(header) + (kWordSize * header.bloom_size);
|
|
|
|
if (!memory_.Read(buckets_address, kNumBytesForBuckets, buckets.data())) {
|
|
|
|
LOG(ERROR) << "read buckets";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Locate the chain that handles the largest index bucket.
|
|
|
|
uint32_t last_symbol = 0;
|
|
|
|
for (uint32_t i = 0; i < header.nbuckets; ++i) {
|
|
|
|
last_symbol = std::max(buckets[i], last_symbol);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last_symbol < header.symoffset) {
|
|
|
|
*number_of_symbol_table_entries = header.symoffset;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk the bucket's chain to add the chain length to the total.
|
|
|
|
const VMAddress chains_base_address = buckets_address + kNumBytesForBuckets;
|
|
|
|
for (;;) {
|
|
|
|
uint32_t chain_entry;
|
|
|
|
if (!memory_.Read(chains_base_address + (last_symbol - header.symoffset) *
|
|
|
|
sizeof(chain_entry),
|
|
|
|
sizeof(chain_entry),
|
|
|
|
&chain_entry)) {
|
|
|
|
LOG(ERROR) << "read chain entry";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
++last_symbol;
|
|
|
|
|
|
|
|
// If the low bit is set, this entry is the end of the chain.
|
|
|
|
if (chain_entry & 1)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*number_of_symbol_table_entries = last_symbol;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-05 11:21:14 -08:00
|
|
|
std::unique_ptr<ElfImageReader::NoteReader> ElfImageReader::Notes(
|
2019-05-24 12:49:28 -07:00
|
|
|
size_t max_note_size) {
|
2017-12-05 11:21:14 -08:00
|
|
|
return std::make_unique<NoteReader>(
|
|
|
|
this, &memory_, program_headers_.get(), max_note_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<ElfImageReader::NoteReader>
|
|
|
|
ElfImageReader::NotesWithNameAndType(const std::string& name,
|
|
|
|
NoteReader::NoteType type,
|
2019-05-24 12:49:28 -07:00
|
|
|
size_t max_note_size) {
|
2017-12-05 11:21:14 -08:00
|
|
|
return std::make_unique<NoteReader>(
|
|
|
|
this, &memory_, program_headers_.get(), max_note_size, name, type, true);
|
|
|
|
}
|
|
|
|
|
2018-01-30 17:37:00 -08:00
|
|
|
const ProcessMemoryRange* ElfImageReader::Memory() const {
|
|
|
|
return &memory_;
|
|
|
|
}
|
|
|
|
|
2017-07-05 18:23:32 -07:00
|
|
|
} // namespace crashpad
|