From afa6b8e93a0dc0de33c9d404945c7c5621e20b1a Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 12 Sep 2014 18:38:24 -0700 Subject: [PATCH] ART: Make elf loading not abort Changes elf_file code to use less CHECKs and instead return error values (usually nullptr). This avoids aborts. In oat_file, when loading an oat file fails, try to unlink at. If this succeeds, on the next run we may compile again. Bug: 17491333 Change-Id: I50fdd2edacd86f25d4dacf2180ce2a6105eaf4af --- compiler/elf_fixup.cc | 50 ++--- compiler/elf_patcher.cc | 2 +- compiler/elf_stripper.cc | 36 ++-- dex2oat/dex2oat.cc | 1 + patchoat/patchoat.cc | 18 +- runtime/elf_file.cc | 465 +++++++++++++++++++++++++++++++++++------------ runtime/elf_file.h | 63 ++++--- runtime/oat_file.cc | 9 + 8 files changed, 456 insertions(+), 188 deletions(-) diff --git a/compiler/elf_fixup.cc b/compiler/elf_fixup.cc index 60f76efed..0155c8268 100644 --- a/compiler/elf_fixup.cc +++ b/compiler/elf_fixup.cc @@ -85,17 +85,18 @@ bool ElfFixup::FixupDynamic(ElfFile& elf_file, uintptr_t base_address) { bool ElfFixup::FixupSectionHeaders(ElfFile& elf_file, uintptr_t base_address) { for (Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) { - Elf32_Shdr& sh = elf_file.GetSectionHeader(i); + Elf32_Shdr* sh = elf_file.GetSectionHeader(i); + CHECK(sh != nullptr); // 0 implies that the section will not exist in the memory of the process - if (sh.sh_addr == 0) { + if (sh->sh_addr == 0) { continue; } if (DEBUG_FIXUP) { LOG(INFO) << StringPrintf("In %s moving Elf32_Shdr[%d] from 0x%08x to 0x%08" PRIxPTR, elf_file.GetFile().GetPath().c_str(), i, - sh.sh_addr, sh.sh_addr + base_address); + sh->sh_addr, sh->sh_addr + base_address); } - sh.sh_addr += base_address; + sh->sh_addr += base_address; } return true; } @@ -103,18 +104,19 @@ bool ElfFixup::FixupSectionHeaders(ElfFile& elf_file, uintptr_t base_address) { bool ElfFixup::FixupProgramHeaders(ElfFile& elf_file, uintptr_t base_address) { // TODO: ELFObjectFile doesn't have give to Elf32_Phdr, so we do that ourselves for now. for (Elf32_Word i = 0; i < elf_file.GetProgramHeaderNum(); i++) { - Elf32_Phdr& ph = elf_file.GetProgramHeader(i); - CHECK_EQ(ph.p_vaddr, ph.p_paddr) << elf_file.GetFile().GetPath() << " i=" << i; - CHECK((ph.p_align == 0) || (0 == ((ph.p_vaddr - ph.p_offset) & (ph.p_align - 1)))) + Elf32_Phdr* ph = elf_file.GetProgramHeader(i); + CHECK(ph != nullptr); + CHECK_EQ(ph->p_vaddr, ph->p_paddr) << elf_file.GetFile().GetPath() << " i=" << i; + CHECK((ph->p_align == 0) || (0 == ((ph->p_vaddr - ph->p_offset) & (ph->p_align - 1)))) << elf_file.GetFile().GetPath() << " i=" << i; if (DEBUG_FIXUP) { LOG(INFO) << StringPrintf("In %s moving Elf32_Phdr[%d] from 0x%08x to 0x%08" PRIxPTR, elf_file.GetFile().GetPath().c_str(), i, - ph.p_vaddr, ph.p_vaddr + base_address); + ph->p_vaddr, ph->p_vaddr + base_address); } - ph.p_vaddr += base_address; - ph.p_paddr += base_address; - CHECK((ph.p_align == 0) || (0 == ((ph.p_vaddr - ph.p_offset) & (ph.p_align - 1)))) + ph->p_vaddr += base_address; + ph->p_paddr += base_address; + CHECK((ph->p_align == 0) || (0 == ((ph->p_vaddr - ph->p_offset) & (ph->p_align - 1)))) << elf_file.GetFile().GetPath() << " i=" << i; } return true; @@ -124,20 +126,21 @@ bool ElfFixup::FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dyna Elf32_Word section_type = dynamic ? SHT_DYNSYM : SHT_SYMTAB; // TODO: Unfortunate ELFObjectFile has protected symbol access, so use ElfFile Elf32_Shdr* symbol_section = elf_file.FindSectionByType(section_type); - if (symbol_section == NULL) { + if (symbol_section == nullptr) { // file is missing optional .symtab CHECK(!dynamic) << elf_file.GetFile().GetPath(); return true; } for (uint32_t i = 0; i < elf_file.GetSymbolNum(*symbol_section); i++) { - Elf32_Sym& symbol = elf_file.GetSymbol(section_type, i); - if (symbol.st_value != 0) { + Elf32_Sym* symbol = elf_file.GetSymbol(section_type, i); + CHECK(symbol != nullptr); + if (symbol->st_value != 0) { if (DEBUG_FIXUP) { LOG(INFO) << StringPrintf("In %s moving Elf32_Sym[%d] from 0x%08x to 0x%08" PRIxPTR, elf_file.GetFile().GetPath().c_str(), i, - symbol.st_value, symbol.st_value + base_address); + symbol->st_value, symbol->st_value + base_address); } - symbol.st_value += base_address; + symbol->st_value += base_address; } } return true; @@ -145,10 +148,11 @@ bool ElfFixup::FixupSymbols(ElfFile& elf_file, uintptr_t base_address, bool dyna bool ElfFixup::FixupRelocations(ElfFile& elf_file, uintptr_t base_address) { for (Elf32_Word i = 0; i < elf_file.GetSectionHeaderNum(); i++) { - Elf32_Shdr& sh = elf_file.GetSectionHeader(i); - if (sh.sh_type == SHT_REL) { - for (uint32_t i = 0; i < elf_file.GetRelNum(sh); i++) { - Elf32_Rel& rel = elf_file.GetRel(sh, i); + Elf32_Shdr* sh = elf_file.GetSectionHeader(i); + CHECK(sh != nullptr); + if (sh->sh_type == SHT_REL) { + for (uint32_t i = 0; i < elf_file.GetRelNum(*sh); i++) { + Elf32_Rel& rel = elf_file.GetRel(*sh, i); if (DEBUG_FIXUP) { LOG(INFO) << StringPrintf("In %s moving Elf32_Rel[%d] from 0x%08x to 0x%08" PRIxPTR, elf_file.GetFile().GetPath().c_str(), i, @@ -156,9 +160,9 @@ bool ElfFixup::FixupRelocations(ElfFile& elf_file, uintptr_t base_address) { } rel.r_offset += base_address; } - } else if (sh.sh_type == SHT_RELA) { - for (uint32_t i = 0; i < elf_file.GetRelaNum(sh); i++) { - Elf32_Rela& rela = elf_file.GetRela(sh, i); + } else if (sh->sh_type == SHT_RELA) { + for (uint32_t i = 0; i < elf_file.GetRelaNum(*sh); i++) { + Elf32_Rela& rela = elf_file.GetRela(*sh, i); if (DEBUG_FIXUP) { LOG(INFO) << StringPrintf("In %s moving Elf32_Rela[%d] from 0x%08x to 0x%08" PRIxPTR, elf_file.GetFile().GetPath().c_str(), i, diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc index ea4517f51..e4c957af0 100644 --- a/compiler/elf_patcher.cc +++ b/compiler/elf_patcher.cc @@ -273,7 +273,7 @@ bool ElfPatcher::WriteOutPatchData() { << "We got more patches than anticipated"; CHECK_LE(reinterpret_cast(elf_file_->Begin()) + shdr->sh_offset + shdr->sh_size, reinterpret_cast(elf_file_->End())) << "section is too large"; - CHECK(shdr == &elf_file_->GetSectionHeader(elf_file_->GetSectionHeaderNum() - 1) || + CHECK(shdr == elf_file_->GetSectionHeader(elf_file_->GetSectionHeaderNum() - 1) || shdr->sh_offset + shdr->sh_size <= (shdr + 1)->sh_offset) << "Section overlaps onto next section"; // It's mmap'd so we can just memcpy. diff --git a/compiler/elf_stripper.cc b/compiler/elf_stripper.cc index 0b86ad071..457d8a094 100644 --- a/compiler/elf_stripper.cc +++ b/compiler/elf_stripper.cc @@ -72,13 +72,15 @@ bool ElfStripper::Strip(File* file, std::string* error_msg) { section_headers.reserve(elf_file->GetSectionHeaderNum()); - Elf32_Shdr& string_section = elf_file->GetSectionNameStringSection(); + Elf32_Shdr* string_section = elf_file->GetSectionNameStringSection(); + CHECK(string_section != nullptr); for (Elf32_Word i = 0; i < elf_file->GetSectionHeaderNum(); i++) { - Elf32_Shdr& sh = elf_file->GetSectionHeader(i); - const char* name = elf_file->GetString(string_section, sh.sh_name); - if (name == NULL) { + Elf32_Shdr* sh = elf_file->GetSectionHeader(i); + CHECK(sh != nullptr); + const char* name = elf_file->GetString(*string_section, sh->sh_name); + if (name == nullptr) { CHECK_EQ(0U, i); - section_headers.push_back(sh); + section_headers.push_back(*sh); section_headers_original_indexes.push_back(0); continue; } @@ -87,32 +89,34 @@ bool ElfStripper::Strip(File* file, std::string* error_msg) { || (strcmp(name, ".symtab") == 0)) { continue; } - section_headers.push_back(sh); + section_headers.push_back(*sh); section_headers_original_indexes.push_back(i); } CHECK_NE(0U, section_headers.size()); CHECK_EQ(section_headers.size(), section_headers_original_indexes.size()); // section 0 is the NULL section, sections start at offset of first section - Elf32_Off offset = elf_file->GetSectionHeader(1).sh_offset; + CHECK(elf_file->GetSectionHeader(1) != nullptr); + Elf32_Off offset = elf_file->GetSectionHeader(1)->sh_offset; for (size_t i = 1; i < section_headers.size(); i++) { Elf32_Shdr& new_sh = section_headers[i]; - Elf32_Shdr& old_sh = elf_file->GetSectionHeader(section_headers_original_indexes[i]); - CHECK_EQ(new_sh.sh_name, old_sh.sh_name); - if (old_sh.sh_addralign > 1) { - offset = RoundUp(offset, old_sh.sh_addralign); + Elf32_Shdr* old_sh = elf_file->GetSectionHeader(section_headers_original_indexes[i]); + CHECK(old_sh != nullptr); + CHECK_EQ(new_sh.sh_name, old_sh->sh_name); + if (old_sh->sh_addralign > 1) { + offset = RoundUp(offset, old_sh->sh_addralign); } - if (old_sh.sh_offset == offset) { + if (old_sh->sh_offset == offset) { // already in place - offset += old_sh.sh_size; + offset += old_sh->sh_size; continue; } // shift section earlier memmove(elf_file->Begin() + offset, - elf_file->Begin() + old_sh.sh_offset, - old_sh.sh_size); + elf_file->Begin() + old_sh->sh_offset, + old_sh->sh_size); new_sh.sh_offset = offset; - offset += old_sh.sh_size; + offset += old_sh->sh_size; } Elf32_Off shoff = offset; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index bd39e67df..f0d345578 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1019,6 +1019,7 @@ static int dex2oat(int argc, char** argv) { include_debug_symbols = true; } else if (option == "--no-include-debug-symbols" || option == "--strip-symbols") { include_debug_symbols = false; + generate_gdb_information = false; // Depends on debug symbols, see above. } else if (option.starts_with("--profile-file=")) { profile_file = option.substr(strlen("--profile-file=")).data(); VLOG(compiler) << "dex2oat: profile file is " << profile_file; diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 55e81415b..74036607c 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -525,14 +525,15 @@ bool PatchOat::PatchElf() { t.NewTiming("Fixup Elf Headers"); // Fixup Phdr's for (unsigned int i = 0; i < oat_file_->GetProgramHeaderNum(); i++) { - Elf32_Phdr& hdr = oat_file_->GetProgramHeader(i); - if (hdr.p_vaddr != 0 && hdr.p_vaddr != hdr.p_offset) { + Elf32_Phdr* hdr = oat_file_->GetProgramHeader(i); + CHECK(hdr != nullptr); + if (hdr->p_vaddr != 0 && hdr->p_vaddr != hdr->p_offset) { need_fixup = true; - hdr.p_vaddr += delta_; + hdr->p_vaddr += delta_; } - if (hdr.p_paddr != 0 && hdr.p_paddr != hdr.p_offset) { + if (hdr->p_paddr != 0 && hdr->p_paddr != hdr->p_offset) { need_fixup = true; - hdr.p_paddr += delta_; + hdr->p_paddr += delta_; } } if (!need_fixup) { @@ -542,9 +543,10 @@ bool PatchOat::PatchElf() { } t.NewTiming("Fixup Section Headers"); for (unsigned int i = 0; i < oat_file_->GetSectionHeaderNum(); i++) { - Elf32_Shdr& hdr = oat_file_->GetSectionHeader(i); - if (hdr.sh_addr != 0) { - hdr.sh_addr += delta_; + Elf32_Shdr* hdr = oat_file_->GetSectionHeader(i); + CHECK(hdr != nullptr); + if (hdr->sh_addr != 0) { + hdr->sh_addr += delta_; } } diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index f6e89213a..0ab6394b5 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -202,12 +202,25 @@ bool ElfFile::Setup(int prot, int flags, std::string* error_msg) { } } - // Either way, the program header is relative to the elf header - program_headers_start_ = Begin() + GetHeader().e_phoff; + if (program_header_only_) { + program_headers_start_ = Begin() + GetHeader().e_phoff; + } else { + if (!CheckAndSet(GetHeader().e_phoff, "program headers", &program_headers_start_, error_msg)) { + return false; + } - if (!program_header_only_) { // Setup section headers. - section_headers_start_ = Begin() + GetHeader().e_shoff; + if (!CheckAndSet(GetHeader().e_shoff, "section headers", §ion_headers_start_, error_msg)) { + return false; + } + + // Find shstrtab. + Elf32_Shdr* shstrtab_section_header = GetSectionNameStringSection(); + if (shstrtab_section_header == nullptr) { + *error_msg = StringPrintf("Failed to find shstrtab section header in ELF file: '%s'", + file_->GetPath().c_str()); + return false; + } // Find .dynamic section info from program header dynamic_program_header_ = FindProgamHeaderByType(PT_DYNAMIC); @@ -217,48 +230,84 @@ bool ElfFile::Setup(int prot, int flags, std::string* error_msg) { return false; } - dynamic_section_start_ - = reinterpret_cast(Begin() + GetDynamicProgramHeader().p_offset); + if (!CheckAndSet(GetDynamicProgramHeader().p_offset, "dynamic section", + reinterpret_cast(&dynamic_section_start_), error_msg)) { + return false; + } // Find other sections from section headers for (Elf32_Word i = 0; i < GetSectionHeaderNum(); i++) { - Elf32_Shdr& section_header = GetSectionHeader(i); - byte* section_addr = Begin() + section_header.sh_offset; - switch (section_header.sh_type) { + Elf32_Shdr* section_header = GetSectionHeader(i); + if (section_header == nullptr) { + *error_msg = StringPrintf("Failed to find section header for section %d in ELF file: '%s'", + i, file_->GetPath().c_str()); + return false; + } + switch (section_header->sh_type) { case SHT_SYMTAB: { - symtab_section_start_ = reinterpret_cast(section_addr); + if (!CheckAndSet(section_header->sh_offset, "symtab", + reinterpret_cast(&symtab_section_start_), error_msg)) { + return false; + } break; } case SHT_DYNSYM: { - dynsym_section_start_ = reinterpret_cast(section_addr); + if (!CheckAndSet(section_header->sh_offset, "dynsym", + reinterpret_cast(&dynsym_section_start_), error_msg)) { + return false; + } break; } case SHT_STRTAB: { // TODO: base these off of sh_link from .symtab and .dynsym above - if ((section_header.sh_flags & SHF_ALLOC) != 0) { - dynstr_section_start_ = reinterpret_cast(section_addr); + if ((section_header->sh_flags & SHF_ALLOC) != 0) { + // Check that this is named ".dynstr" and ignore otherwise. + const char* header_name = GetString(*shstrtab_section_header, section_header->sh_name); + if (strncmp(".dynstr", header_name, 8) == 0) { + if (!CheckAndSet(section_header->sh_offset, "dynstr", + reinterpret_cast(&dynstr_section_start_), error_msg)) { + return false; + } + } } else { - strtab_section_start_ = reinterpret_cast(section_addr); + // Check that this is named ".strtab" and ignore otherwise. + const char* header_name = GetString(*shstrtab_section_header, section_header->sh_name); + if (strncmp(".strtab", header_name, 8) == 0) { + if (!CheckAndSet(section_header->sh_offset, "strtab", + reinterpret_cast(&strtab_section_start_), error_msg)) { + return false; + } + } } break; } case SHT_DYNAMIC: { - if (reinterpret_cast(dynamic_section_start_) != section_addr) { + if (reinterpret_cast(dynamic_section_start_) != + Begin() + section_header->sh_offset) { LOG(WARNING) << "Failed to find matching SHT_DYNAMIC for PT_DYNAMIC in " << file_->GetPath() << ": " << std::hex << reinterpret_cast(dynamic_section_start_) - << " != " << reinterpret_cast(section_addr); + << " != " << reinterpret_cast(Begin() + section_header->sh_offset); return false; } break; } case SHT_HASH: { - hash_section_start_ = reinterpret_cast(section_addr); + if (!CheckAndSet(section_header->sh_offset, "hash section", + reinterpret_cast(&hash_section_start_), error_msg)) { + return false; + } break; } } } + + // Check for the existence of some sections. + if (!CheckSectionsExist(error_msg)) { + return false; + } } + return true; } @@ -272,6 +321,117 @@ ElfFile::~ElfFile() { } } +bool ElfFile::CheckAndSet(Elf32_Off offset, const char* label, + byte** target, std::string* error_msg) { + if (Begin() + offset >= End()) { + *error_msg = StringPrintf("Offset %d is out of range for %s in ELF file: '%s'", offset, label, + file_->GetPath().c_str()); + return false; + } + *target = Begin() + offset; + return true; +} + +bool ElfFile::CheckSectionsLinked(const byte* source, const byte* target) const { + // Only works in whole-program mode, as we need to iterate over the sections. + // Note that we normally can't search by type, as duplicates are allowed for most section types. + if (program_header_only_) { + return true; + } + + Elf32_Shdr* source_section = nullptr; + Elf32_Word target_index = 0; + bool target_found = false; + for (Elf32_Word i = 0; i < GetSectionHeaderNum(); i++) { + Elf32_Shdr* section_header = GetSectionHeader(i); + + if (Begin() + section_header->sh_offset == source) { + // Found the source. + source_section = section_header; + if (target_index) { + break; + } + } else if (Begin() + section_header->sh_offset == target) { + target_index = i; + target_found = true; + if (source_section != nullptr) { + break; + } + } + } + + return target_found && source_section != nullptr && source_section->sh_link == target_index; +} + +bool ElfFile::CheckSectionsExist(std::string* error_msg) const { + if (!program_header_only_) { + // If in full mode, need section headers. + if (section_headers_start_ == nullptr) { + *error_msg = StringPrintf("No section headers in ELF file: '%s'", file_->GetPath().c_str()); + return false; + } + } + + // This is redundant, but defensive. + if (dynamic_program_header_ == nullptr) { + *error_msg = StringPrintf("Failed to find PT_DYNAMIC program header in ELF file: '%s'", + file_->GetPath().c_str()); + return false; + } + + // Need a dynamic section. This is redundant, but defensive. + if (dynamic_section_start_ == nullptr) { + *error_msg = StringPrintf("Failed to find dynamic section in ELF file: '%s'", + file_->GetPath().c_str()); + return false; + } + + // Symtab validation. These is not really a hard failure, as we are currently not using the + // symtab internally, but it's nice to be defensive. + if (symtab_section_start_ != nullptr) { + // When there's a symtab, there should be a strtab. + if (strtab_section_start_ == nullptr) { + *error_msg = StringPrintf("No strtab for symtab in ELF file: '%s'", file_->GetPath().c_str()); + return false; + } + + // The symtab should link to the strtab. + if (!CheckSectionsLinked(reinterpret_cast(symtab_section_start_), + reinterpret_cast(strtab_section_start_))) { + *error_msg = StringPrintf("Symtab is not linked to the strtab in ELF file: '%s'", + file_->GetPath().c_str()); + return false; + } + } + + // We always need a dynstr & dynsym. + if (dynstr_section_start_ == nullptr) { + *error_msg = StringPrintf("No dynstr in ELF file: '%s'", file_->GetPath().c_str()); + return false; + } + if (dynsym_section_start_ == nullptr) { + *error_msg = StringPrintf("No dynsym in ELF file: '%s'", file_->GetPath().c_str()); + return false; + } + + // Need a hash section for dynamic symbol lookup. + if (hash_section_start_ == nullptr) { + *error_msg = StringPrintf("Failed to find hash section in ELF file: '%s'", + file_->GetPath().c_str()); + return false; + } + + // And the hash section should be linking to the dynsym. + if (!CheckSectionsLinked(reinterpret_cast(hash_section_start_), + reinterpret_cast(dynsym_section_start_))) { + *error_msg = StringPrintf("Hash section is not linked to the dynstr in ELF file: '%s'", + file_->GetPath().c_str()); + return false; + } + + return true; +} + bool ElfFile::SetMap(MemMap* map, std::string* error_msg) { if (map == nullptr) { // MemMap::Open should have already set an error. @@ -407,70 +567,68 @@ bool ElfFile::SetMap(MemMap* map, std::string* error_msg) { Elf32_Ehdr& ElfFile::GetHeader() const { - CHECK(header_ != nullptr); + CHECK(header_ != nullptr); // Header has been checked in SetMap. This is a sanity check. return *header_; } byte* ElfFile::GetProgramHeadersStart() const { - CHECK(program_headers_start_ != nullptr); + CHECK(program_headers_start_ != nullptr); // Header has been set in Setup. This is a sanity + // check. return program_headers_start_; } byte* ElfFile::GetSectionHeadersStart() const { - CHECK(section_headers_start_ != nullptr); + CHECK(!program_header_only_); // Only used in "full" mode. + CHECK(section_headers_start_ != nullptr); // Is checked in CheckSectionsExist. Sanity check. return section_headers_start_; } Elf32_Phdr& ElfFile::GetDynamicProgramHeader() const { - CHECK(dynamic_program_header_ != nullptr); + CHECK(dynamic_program_header_ != nullptr); // Is checked in CheckSectionsExist. Sanity check. return *dynamic_program_header_; } Elf32_Dyn* ElfFile::GetDynamicSectionStart() const { - CHECK(dynamic_section_start_ != nullptr); + CHECK(dynamic_section_start_ != nullptr); // Is checked in CheckSectionsExist. Sanity check. return dynamic_section_start_; } +static bool IsSymbolSectionType(Elf32_Word section_type) { + return ((section_type == SHT_SYMTAB) || (section_type == SHT_DYNSYM)); +} + Elf32_Sym* ElfFile::GetSymbolSectionStart(Elf32_Word section_type) const { CHECK(IsSymbolSectionType(section_type)) << file_->GetPath() << " " << section_type; - Elf32_Sym* symbol_section_start; switch (section_type) { case SHT_SYMTAB: { - symbol_section_start = symtab_section_start_; + return symtab_section_start_; break; } case SHT_DYNSYM: { - symbol_section_start = dynsym_section_start_; + return dynsym_section_start_; break; } default: { LOG(FATAL) << section_type; - symbol_section_start = nullptr; + return nullptr; } } - CHECK(symbol_section_start != nullptr); - return symbol_section_start; } const char* ElfFile::GetStringSectionStart(Elf32_Word section_type) const { CHECK(IsSymbolSectionType(section_type)) << file_->GetPath() << " " << section_type; - const char* string_section_start; switch (section_type) { case SHT_SYMTAB: { - string_section_start = strtab_section_start_; - break; + return strtab_section_start_; } case SHT_DYNSYM: { - string_section_start = dynstr_section_start_; - break; + return dynstr_section_start_; } default: { LOG(FATAL) << section_type; - string_section_start = nullptr; + return nullptr; } } - CHECK(string_section_start != nullptr); - return string_section_start; } const char* ElfFile::GetString(Elf32_Word section_type, Elf32_Word i) const { @@ -479,12 +637,16 @@ const char* ElfFile::GetString(Elf32_Word section_type, Elf32_Word i) const { return nullptr; } const char* string_section_start = GetStringSectionStart(section_type); - const char* string = string_section_start + i; - return string; + if (string_section_start == nullptr) { + return nullptr; + } + return string_section_start + i; } +// WARNING: The following methods do not check for an error condition (non-existent hash section). +// It is the caller's job to do this. + Elf32_Word* ElfFile::GetHashSectionStart() const { - CHECK(hash_section_start_ != nullptr); return hash_section_start_; } @@ -496,14 +658,22 @@ Elf32_Word ElfFile::GetHashChainNum() const { return GetHashSectionStart()[1]; } -Elf32_Word ElfFile::GetHashBucket(size_t i) const { - CHECK_LT(i, GetHashBucketNum()); +Elf32_Word ElfFile::GetHashBucket(size_t i, bool* ok) const { + if (i >= GetHashBucketNum()) { + *ok = false; + return 0; + } + *ok = true; // 0 is nbucket, 1 is nchain return GetHashSectionStart()[2 + i]; } -Elf32_Word ElfFile::GetHashChain(size_t i) const { - CHECK_LT(i, GetHashChainNum()); +Elf32_Word ElfFile::GetHashChain(size_t i, bool* ok) const { + if (i >= GetHashBucketNum()) { + *ok = false; + return 0; + } + *ok = true; // 0 is nbucket, 1 is nchain, & chains are after buckets return GetHashSectionStart()[2 + GetHashBucketNum() + i]; } @@ -512,18 +682,20 @@ Elf32_Word ElfFile::GetProgramHeaderNum() const { return GetHeader().e_phnum; } -Elf32_Phdr& ElfFile::GetProgramHeader(Elf32_Word i) const { - CHECK_LT(i, GetProgramHeaderNum()) << file_->GetPath(); +Elf32_Phdr* ElfFile::GetProgramHeader(Elf32_Word i) const { + CHECK_LT(i, GetProgramHeaderNum()) << file_->GetPath(); // Sanity check for caller. byte* program_header = GetProgramHeadersStart() + (i * GetHeader().e_phentsize); - CHECK_LT(program_header, End()) << file_->GetPath(); - return *reinterpret_cast(program_header); + if (program_header >= End()) { + return nullptr; // Failure condition. + } + return reinterpret_cast(program_header); } Elf32_Phdr* ElfFile::FindProgamHeaderByType(Elf32_Word type) const { for (Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) { - Elf32_Phdr& program_header = GetProgramHeader(i); - if (program_header.p_type == type) { - return &program_header; + Elf32_Phdr* program_header = GetProgramHeader(i); + if (program_header->p_type == type) { + return program_header; } } return nullptr; @@ -533,14 +705,18 @@ Elf32_Word ElfFile::GetSectionHeaderNum() const { return GetHeader().e_shnum; } -Elf32_Shdr& ElfFile::GetSectionHeader(Elf32_Word i) const { +Elf32_Shdr* ElfFile::GetSectionHeader(Elf32_Word i) const { // Can only access arbitrary sections when we have the whole file, not just program header. // Even if we Load(), it doesn't bring in all the sections. CHECK(!program_header_only_) << file_->GetPath(); - CHECK_LT(i, GetSectionHeaderNum()) << file_->GetPath(); + if (i >= GetSectionHeaderNum()) { + return nullptr; // Failure condition. + } byte* section_header = GetSectionHeadersStart() + (i * GetHeader().e_shentsize); - CHECK_LT(section_header, End()) << file_->GetPath(); - return *reinterpret_cast(section_header); + if (section_header >= End()) { + return nullptr; // Failure condition. + } + return reinterpret_cast(section_header); } Elf32_Shdr* ElfFile::FindSectionByType(Elf32_Word type) const { @@ -548,9 +724,9 @@ Elf32_Shdr* ElfFile::FindSectionByType(Elf32_Word type) const { // We could change this to switch on known types if they were detected during loading. CHECK(!program_header_only_) << file_->GetPath(); for (Elf32_Word i = 0; i < GetSectionHeaderNum(); i++) { - Elf32_Shdr& section_header = GetSectionHeader(i); - if (section_header.sh_type == type) { - return §ion_header; + Elf32_Shdr* section_header = GetSectionHeader(i); + if (section_header->sh_type == type) { + return section_header; } } return nullptr; @@ -570,11 +746,15 @@ static unsigned elfhash(const char *_name) { return h; } -Elf32_Shdr& ElfFile::GetSectionNameStringSection() const { +Elf32_Shdr* ElfFile::GetSectionNameStringSection() const { return GetSectionHeader(GetHeader().e_shstrndx); } const byte* ElfFile::FindDynamicSymbolAddress(const std::string& symbol_name) const { + // Check that we have a hash section. + if (GetHashSectionStart() == nullptr) { + return nullptr; // Failure condition. + } const Elf32_Sym* sym = FindDynamicSymbol(symbol_name); if (sym != nullptr) { return base_address_ + sym->st_value; @@ -583,6 +763,7 @@ const byte* ElfFile::FindDynamicSymbolAddress(const std::string& symbol_name) co } } +// WARNING: Only called from FindDynamicSymbolAddress. Elides check for hash section. const Elf32_Sym* ElfFile::FindDynamicSymbol(const std::string& symbol_name) const { if (GetHashBucketNum() == 0) { // No dynamic symbols at all. @@ -590,22 +771,28 @@ const Elf32_Sym* ElfFile::FindDynamicSymbol(const std::string& symbol_name) cons } Elf32_Word hash = elfhash(symbol_name.c_str()); Elf32_Word bucket_index = hash % GetHashBucketNum(); - Elf32_Word symbol_and_chain_index = GetHashBucket(bucket_index); + bool ok; + Elf32_Word symbol_and_chain_index = GetHashBucket(bucket_index, &ok); + if (!ok) { + return nullptr; + } while (symbol_and_chain_index != 0 /* STN_UNDEF */) { - Elf32_Sym& symbol = GetSymbol(SHT_DYNSYM, symbol_and_chain_index); - const char* name = GetString(SHT_DYNSYM, symbol.st_name); + Elf32_Sym* symbol = GetSymbol(SHT_DYNSYM, symbol_and_chain_index); + if (symbol == nullptr) { + return nullptr; // Failure condition. + } + const char* name = GetString(SHT_DYNSYM, symbol->st_name); if (symbol_name == name) { - return &symbol; + return symbol; + } + symbol_and_chain_index = GetHashChain(symbol_and_chain_index, &ok); + if (!ok) { + return nullptr; } - symbol_and_chain_index = GetHashChain(symbol_and_chain_index); } return nullptr; } -bool ElfFile::IsSymbolSectionType(Elf32_Word section_type) { - return ((section_type == SHT_SYMTAB) || (section_type == SHT_DYNSYM)); -} - Elf32_Word ElfFile::GetSymbolNum(Elf32_Shdr& section_header) const { CHECK(IsSymbolSectionType(section_header.sh_type)) << file_->GetPath() << " " << section_header.sh_type; @@ -613,9 +800,13 @@ Elf32_Word ElfFile::GetSymbolNum(Elf32_Shdr& section_header) const { return section_header.sh_size / section_header.sh_entsize; } -Elf32_Sym& ElfFile::GetSymbol(Elf32_Word section_type, +Elf32_Sym* ElfFile::GetSymbol(Elf32_Word section_type, Elf32_Word i) const { - return *(GetSymbolSectionStart(section_type) + i); + Elf32_Sym* sym_start = GetSymbolSectionStart(section_type); + if (sym_start == nullptr) { + return nullptr; + } + return sym_start + i; } ElfFile::SymbolTable** ElfFile::GetSymbolTable(Elf32_Word section_type) { @@ -646,27 +837,37 @@ Elf32_Sym* ElfFile::FindSymbolByName(Elf32_Word section_type, DCHECK(build_map); *symbol_table = new SymbolTable; Elf32_Shdr* symbol_section = FindSectionByType(section_type); - CHECK(symbol_section != nullptr) << file_->GetPath(); - Elf32_Shdr& string_section = GetSectionHeader(symbol_section->sh_link); + if (symbol_section == nullptr) { + return nullptr; // Failure condition. + } + Elf32_Shdr* string_section = GetSectionHeader(symbol_section->sh_link); + if (string_section == nullptr) { + return nullptr; // Failure condition. + } for (uint32_t i = 0; i < GetSymbolNum(*symbol_section); i++) { - Elf32_Sym& symbol = GetSymbol(section_type, i); - unsigned char type = ELF32_ST_TYPE(symbol.st_info); + Elf32_Sym* symbol = GetSymbol(section_type, i); + if (symbol == nullptr) { + return nullptr; // Failure condition. + } + unsigned char type = ELF32_ST_TYPE(symbol->st_info); if (type == STT_NOTYPE) { continue; } - const char* name = GetString(string_section, symbol.st_name); + const char* name = GetString(*string_section, symbol->st_name); if (name == nullptr) { continue; } std::pair result = - (*symbol_table)->insert(std::make_pair(name, &symbol)); + (*symbol_table)->insert(std::make_pair(name, symbol)); if (!result.second) { // If a duplicate, make sure it has the same logical value. Seen on x86. - CHECK_EQ(symbol.st_value, result.first->second->st_value); - CHECK_EQ(symbol.st_size, result.first->second->st_size); - CHECK_EQ(symbol.st_info, result.first->second->st_info); - CHECK_EQ(symbol.st_other, result.first->second->st_other); - CHECK_EQ(symbol.st_shndx, result.first->second->st_shndx); + if ((symbol->st_value != result.first->second->st_value) || + (symbol->st_size != result.first->second->st_size) || + (symbol->st_info != result.first->second->st_info) || + (symbol->st_other != result.first->second->st_other) || + (symbol->st_shndx != result.first->second->st_shndx)) { + return nullptr; // Failure condition. + } } } } @@ -680,16 +881,24 @@ Elf32_Sym* ElfFile::FindSymbolByName(Elf32_Word section_type, // Fall back to linear search Elf32_Shdr* symbol_section = FindSectionByType(section_type); - CHECK(symbol_section != nullptr) << file_->GetPath(); - Elf32_Shdr& string_section = GetSectionHeader(symbol_section->sh_link); + if (symbol_section == nullptr) { + return nullptr; + } + Elf32_Shdr* string_section = GetSectionHeader(symbol_section->sh_link); + if (string_section == nullptr) { + return nullptr; + } for (uint32_t i = 0; i < GetSymbolNum(*symbol_section); i++) { - Elf32_Sym& symbol = GetSymbol(section_type, i); - const char* name = GetString(string_section, symbol.st_name); + Elf32_Sym* symbol = GetSymbol(section_type, i); + if (symbol == nullptr) { + return nullptr; // Failure condition. + } + const char* name = GetString(*string_section, symbol->st_name); if (name == nullptr) { continue; } if (symbol_name == name) { - return &symbol; + return symbol; } } return nullptr; @@ -708,14 +917,20 @@ Elf32_Addr ElfFile::FindSymbolAddress(Elf32_Word section_type, const char* ElfFile::GetString(Elf32_Shdr& string_section, Elf32_Word i) const { CHECK(!program_header_only_) << file_->GetPath(); // TODO: remove this static_cast from enum when using -std=gnu++0x - CHECK_EQ(static_cast(SHT_STRTAB), string_section.sh_type) << file_->GetPath(); - CHECK_LT(i, string_section.sh_size) << file_->GetPath(); + if (static_cast(SHT_STRTAB) != string_section.sh_type) { + return nullptr; // Failure condition. + } + if (i >= string_section.sh_size) { + return nullptr; + } if (i == 0) { return nullptr; } byte* strings = Begin() + string_section.sh_offset; byte* string = strings + i; - CHECK_LT(string, End()) << file_->GetPath(); + if (string >= End()) { + return nullptr; + } return reinterpret_cast(string); } @@ -785,15 +1000,15 @@ size_t ElfFile::GetLoadedSize() const { Elf32_Addr min_vaddr = 0xFFFFFFFFu; Elf32_Addr max_vaddr = 0x00000000u; for (Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) { - Elf32_Phdr& program_header = GetProgramHeader(i); - if (program_header.p_type != PT_LOAD) { + Elf32_Phdr* program_header = GetProgramHeader(i); + if (program_header->p_type != PT_LOAD) { continue; } - Elf32_Addr begin_vaddr = program_header.p_vaddr; + Elf32_Addr begin_vaddr = program_header->p_vaddr; if (begin_vaddr < min_vaddr) { min_vaddr = begin_vaddr; } - Elf32_Addr end_vaddr = program_header.p_vaddr + program_header.p_memsz; + Elf32_Addr end_vaddr = program_header->p_vaddr + program_header->p_memsz; if (end_vaddr > max_vaddr) { max_vaddr = end_vaddr; } @@ -843,16 +1058,21 @@ bool ElfFile::Load(bool executable, std::string* error_msg) { bool reserved = false; for (Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) { - Elf32_Phdr& program_header = GetProgramHeader(i); + Elf32_Phdr* program_header = GetProgramHeader(i); + if (program_header == nullptr) { + *error_msg = StringPrintf("No program header for entry %d in ELF file %s.", + i, file_->GetPath().c_str()); + return false; + } // Record .dynamic header information for later use - if (program_header.p_type == PT_DYNAMIC) { - dynamic_program_header_ = &program_header; + if (program_header->p_type == PT_DYNAMIC) { + dynamic_program_header_ = program_header; continue; } // Not something to load, move on. - if (program_header.p_type != PT_LOAD) { + if (program_header->p_type != PT_LOAD) { continue; } @@ -874,8 +1094,8 @@ bool ElfFile::Load(bool executable, std::string* error_msg) { } size_t file_length = static_cast(temp_file_length); if (!reserved) { - byte* reserve_base = ((program_header.p_vaddr != 0) ? - reinterpret_cast(program_header.p_vaddr) : nullptr); + byte* reserve_base = ((program_header->p_vaddr != 0) ? + reinterpret_cast(program_header->p_vaddr) : nullptr); std::string reservation_name("ElfFile reservation for "); reservation_name += file_->GetPath(); std::unique_ptr reserve(MemMap::MapAnonymous(reservation_name.c_str(), @@ -894,18 +1114,18 @@ bool ElfFile::Load(bool executable, std::string* error_msg) { segments_.push_back(reserve.release()); } // empty segment, nothing to map - if (program_header.p_memsz == 0) { + if (program_header->p_memsz == 0) { continue; } - byte* p_vaddr = base_address_ + program_header.p_vaddr; + byte* p_vaddr = base_address_ + program_header->p_vaddr; int prot = 0; - if (executable && ((program_header.p_flags & PF_X) != 0)) { + if (executable && ((program_header->p_flags & PF_X) != 0)) { prot |= PROT_EXEC; } - if ((program_header.p_flags & PF_W) != 0) { + if ((program_header->p_flags & PF_W) != 0) { prot |= PROT_WRITE; } - if ((program_header.p_flags & PF_R) != 0) { + if ((program_header->p_flags & PF_R) != 0) { prot |= PROT_READ; } int flags = 0; @@ -915,17 +1135,17 @@ bool ElfFile::Load(bool executable, std::string* error_msg) { } else { flags |= MAP_PRIVATE; } - if (file_length < (program_header.p_offset + program_header.p_memsz)) { + if (file_length < (program_header->p_offset + program_header->p_memsz)) { *error_msg = StringPrintf("File size of %zd bytes not large enough to contain ELF segment " "%d of %d bytes: '%s'", file_length, i, - program_header.p_offset + program_header.p_memsz, + program_header->p_offset + program_header->p_memsz, file_->GetPath().c_str()); return false; } std::unique_ptr segment(MemMap::MapFileAtAddress(p_vaddr, - program_header.p_memsz, + program_header->p_memsz, prot, flags, file_->Fd(), - program_header.p_offset, + program_header->p_offset, true, // implies MAP_FIXED file_->GetPath().c_str(), error_msg)); @@ -944,8 +1164,14 @@ bool ElfFile::Load(bool executable, std::string* error_msg) { } // Now that we are done loading, .dynamic should be in memory to find .dynstr, .dynsym, .hash - dynamic_section_start_ - = reinterpret_cast(base_address_ + GetDynamicProgramHeader().p_vaddr); + byte* dsptr = base_address_ + GetDynamicProgramHeader().p_vaddr; + if ((dsptr < Begin() || dsptr >= End()) && !ValidPointer(dsptr)) { + *error_msg = StringPrintf("dynamic section address invalid in ELF file %s", + file_->GetPath().c_str()); + return false; + } + dynamic_section_start_ = reinterpret_cast(dsptr); + for (Elf32_Word i = 0; i < GetDynamicNum(); i++) { Elf32_Dyn& elf_dyn = GetDynamic(i); byte* d_ptr = base_address_ + elf_dyn.d_un.d_ptr; @@ -989,6 +1215,11 @@ bool ElfFile::Load(bool executable, std::string* error_msg) { } } + // Check for the existence of some sections. + if (!CheckSectionsExist(error_msg)) { + return false; + } + // Use GDB JIT support to do stack backtrace, etc. if (executable) { GdbJITSupport(); @@ -1010,15 +1241,21 @@ bool ElfFile::ValidPointer(const byte* start) const { Elf32_Shdr* ElfFile::FindSectionByName(const std::string& name) const { CHECK(!program_header_only_); - Elf32_Shdr& shstrtab_sec = GetSectionNameStringSection(); + Elf32_Shdr* shstrtab_sec = GetSectionNameStringSection(); + if (shstrtab_sec == nullptr) { + return nullptr; + } for (uint32_t i = 0; i < GetSectionHeaderNum(); i++) { - Elf32_Shdr& shdr = GetSectionHeader(i); - const char* sec_name = GetString(shstrtab_sec, shdr.sh_name); + Elf32_Shdr* shdr = GetSectionHeader(i); + if (shdr == nullptr) { + return nullptr; + } + const char* sec_name = GetString(*shstrtab_sec, shdr->sh_name); if (sec_name == nullptr) { continue; } if (name == sec_name) { - return &shdr; + return shdr; } } return nullptr; @@ -1176,7 +1413,7 @@ class DebugTag { } private: - explicit DebugTag(uint32_t index) : index_(index) {} + explicit DebugTag(uint32_t index) : index_(index), size_(0), tag_(0), has_child_(false) {} void AddAttribute(uint32_t type, uint32_t attr_size) { off_map_.insert(std::pair(type, size_)); size_map_.insert(std::pair(type, attr_size)); diff --git a/runtime/elf_file.h b/runtime/elf_file.h index a966bd963..985be7611 100644 --- a/runtime/elf_file.h +++ b/runtime/elf_file.h @@ -67,35 +67,20 @@ class ElfFile { Elf32_Ehdr& GetHeader() const; Elf32_Word GetProgramHeaderNum() const; - Elf32_Phdr& GetProgramHeader(Elf32_Word) const; - Elf32_Phdr* FindProgamHeaderByType(Elf32_Word type) const; + Elf32_Phdr* GetProgramHeader(Elf32_Word) const; Elf32_Word GetSectionHeaderNum() const; - Elf32_Shdr& GetSectionHeader(Elf32_Word) const; + Elf32_Shdr* GetSectionHeader(Elf32_Word) const; Elf32_Shdr* FindSectionByType(Elf32_Word type) const; Elf32_Shdr* FindSectionByName(const std::string& name) const; - Elf32_Shdr& GetSectionNameStringSection() const; + Elf32_Shdr* GetSectionNameStringSection() const; // Find .dynsym using .hash for more efficient lookup than FindSymbolAddress. const byte* FindDynamicSymbolAddress(const std::string& symbol_name) const; - const Elf32_Sym* FindDynamicSymbol(const std::string& symbol_name) const; - static bool IsSymbolSectionType(Elf32_Word section_type); Elf32_Word GetSymbolNum(Elf32_Shdr&) const; - Elf32_Sym& GetSymbol(Elf32_Word section_type, Elf32_Word i) const; - - // Find symbol in specified table, returning nullptr if it is not found. - // - // If build_map is true, builds a map to speed repeated access. The - // map does not included untyped symbol values (aka STT_NOTYPE) - // since they can contain duplicates. If build_map is false, the map - // will be used if it was already created. Typically build_map - // should be set unless only a small number of symbols will be - // looked up. - Elf32_Sym* FindSymbolByName(Elf32_Word section_type, - const std::string& symbol_name, - bool build_map); + Elf32_Sym* GetSymbol(Elf32_Word section_type, Elf32_Word i) const; // Find address of symbol in specified table, returning 0 if it is // not found. See FindSymbolByName for an explanation of build_map. @@ -107,13 +92,8 @@ class ElfFile { // special 0 offset. const char* GetString(Elf32_Shdr&, Elf32_Word) const; - // Lookup a string by section type. Returns nullptr for special 0 offset. - const char* GetString(Elf32_Word section_type, Elf32_Word) const; - Elf32_Word GetDynamicNum() const; Elf32_Dyn& GetDynamic(Elf32_Word) const; - Elf32_Dyn* FindDynamicByType(Elf32_Sword type) const; - Elf32_Word FindDynamicValueByType(Elf32_Sword type) const; Elf32_Word GetRelNum(Elf32_Shdr&) const; Elf32_Rel& GetRel(Elf32_Shdr&, Elf32_Word) const; @@ -146,14 +126,45 @@ class ElfFile { Elf32_Word* GetHashSectionStart() const; Elf32_Word GetHashBucketNum() const; Elf32_Word GetHashChainNum() const; - Elf32_Word GetHashBucket(size_t i) const; - Elf32_Word GetHashChain(size_t i) const; + Elf32_Word GetHashBucket(size_t i, bool* ok) const; + Elf32_Word GetHashChain(size_t i, bool* ok) const; typedef std::map SymbolTable; SymbolTable** GetSymbolTable(Elf32_Word section_type); bool ValidPointer(const byte* start) const; + const Elf32_Sym* FindDynamicSymbol(const std::string& symbol_name) const; + + // Check that certain sections and their dependencies exist. + bool CheckSectionsExist(std::string* error_msg) const; + + // Check that the link of the first section links to the second section. + bool CheckSectionsLinked(const byte* source, const byte* target) const; + + // Check whether the offset is in range, and set to target to Begin() + offset if OK. + bool CheckAndSet(Elf32_Off offset, const char* label, byte** target, std::string* error_msg); + + // Find symbol in specified table, returning nullptr if it is not found. + // + // If build_map is true, builds a map to speed repeated access. The + // map does not included untyped symbol values (aka STT_NOTYPE) + // since they can contain duplicates. If build_map is false, the map + // will be used if it was already created. Typically build_map + // should be set unless only a small number of symbols will be + // looked up. + Elf32_Sym* FindSymbolByName(Elf32_Word section_type, + const std::string& symbol_name, + bool build_map); + + Elf32_Phdr* FindProgamHeaderByType(Elf32_Word type) const; + + Elf32_Dyn* FindDynamicByType(Elf32_Sword type) const; + Elf32_Word FindDynamicValueByType(Elf32_Sword type) const; + + // Lookup a string by section type. Returns nullptr for special 0 offset. + const char* GetString(Elf32_Word section_type, Elf32_Word) const; + const File* const file_; const bool writable_; const bool program_header_only_; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index d5142a718..1ee53b864 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include "base/bit_vector.h" #include "base/stl_util.h" @@ -91,6 +92,14 @@ OatFile* OatFile::Open(const std::string& filename, return nullptr; } ret.reset(OpenElfFile(file.get(), location, requested_base, false, executable, error_msg)); + + // Opening the file failed. Try to delete it and maybe we have more luck after it gets + // regenerated. + if (ret.get() == nullptr) { + LOG(WARNING) << "Attempting to unlink oat file " << filename << " that could not be opened. " + << "Error was: " << error_msg; + unlink(file->GetPath().c_str()); // Try to remove the file. + } } return ret.release(); } -- 2.11.0