/* * Copyright (C) 2015 The Android Open Source Project * * 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 "read_elf.h" #include "read_apk.h" #include #include #include #include #include #include #include #include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" #include #include #include #include #pragma clang diagnostic pop #include "utils.h" #define ELF_NOTE_GNU "GNU" #define NT_GNU_BUILD_ID 3 bool IsValidElfFile(int fd) { static const char elf_magic[] = {0x7f, 'E', 'L', 'F'}; char buf[4]; return android::base::ReadFully(fd, buf, 4) && memcmp(buf, elf_magic, 4) == 0; } bool IsValidElfPath(const std::string& filename) { if (!IsRegularFile(filename)) { return false; } std::string mode = std::string("rb") + CLOSE_ON_EXEC_MODE; FILE* fp = fopen(filename.c_str(), mode.c_str()); if (fp == nullptr) { return false; } bool result = IsValidElfFile(fileno(fp)); fclose(fp); return result; } static bool GetBuildIdFromNoteSection(const char* section, size_t section_size, BuildId* build_id) { const char* p = section; const char* end = p + section_size; while (p < end) { CHECK_LE(p + 12, end); size_t namesz = *reinterpret_cast(p); p += 4; size_t descsz = *reinterpret_cast(p); p += 4; uint32_t type = *reinterpret_cast(p); p += 4; namesz = ALIGN(namesz, 4); descsz = ALIGN(descsz, 4); CHECK_LE(p + namesz + descsz, end); if ((type == NT_GNU_BUILD_ID) && (strcmp(p, ELF_NOTE_GNU) == 0)) { *build_id = BuildId(p + namesz, descsz); return true; } p += namesz + descsz; } return false; } bool GetBuildIdFromNoteFile(const std::string& filename, BuildId* build_id) { std::string content; if (!android::base::ReadFileToString(filename, &content)) { LOG(DEBUG) << "can't read note file " << filename; return false; } if (GetBuildIdFromNoteSection(content.c_str(), content.size(), build_id) == false) { LOG(DEBUG) << "can't read build_id from note file " << filename; return false; } return true; } template bool GetBuildIdFromELFFile(const llvm::object::ELFFile* elf, BuildId* build_id) { for (auto section_iterator = elf->begin_sections(); section_iterator != elf->end_sections(); ++section_iterator) { if (section_iterator->sh_type == llvm::ELF::SHT_NOTE) { auto contents = elf->getSectionContents(&*section_iterator); if (contents.getError()) { LOG(DEBUG) << "read note section error"; continue; } if (GetBuildIdFromNoteSection(reinterpret_cast(contents->data()), contents->size(), build_id)) { return true; } } } return false; } static bool GetBuildIdFromObjectFile(llvm::object::ObjectFile* obj, BuildId* build_id) { bool result = false; if (auto elf = llvm::dyn_cast(obj)) { result = GetBuildIdFromELFFile(elf->getELFFile(), build_id); } else if (auto elf = llvm::dyn_cast(obj)) { result = GetBuildIdFromELFFile(elf->getELFFile(), build_id); } else { LOG(ERROR) << "unknown elf format in file " << obj->getFileName().data(); return false; } if (!result) { LOG(DEBUG) << "no build id present in file " << obj->getFileName().data(); } return result; } struct BinaryRet { llvm::object::OwningBinary binary; llvm::object::ObjectFile* obj; BinaryRet() : obj(nullptr) { } }; static BinaryRet OpenObjectFile(const std::string& filename, uint64_t file_offset = 0, uint64_t file_size = 0) { BinaryRet ret; FileHelper fhelper(filename); if (!fhelper) { PLOG(DEBUG) << "failed to open " << filename; return ret; } if (file_size == 0) { file_size = GetFileSize(filename); if (file_size == 0) { PLOG(ERROR) << "failed to get size of file " << filename; return ret; } } auto buffer_or_err = llvm::MemoryBuffer::getOpenFileSlice(fhelper.fd(), filename, file_size, file_offset); if (!buffer_or_err) { LOG(ERROR) << "failed to read " << filename << " [" << file_offset << "-" << (file_offset + file_size) << "]: " << buffer_or_err.getError().message(); return ret; } auto binary_or_err = llvm::object::createBinary(buffer_or_err.get()->getMemBufferRef()); if (!binary_or_err) { LOG(ERROR) << filename << " [" << file_offset << "-" << (file_offset + file_size) << "] is not a binary file: " << binary_or_err.getError().message(); return ret; } ret.binary = llvm::object::OwningBinary(std::move(binary_or_err.get()), std::move(buffer_or_err.get())); ret.obj = llvm::dyn_cast(ret.binary.getBinary()); if (ret.obj == nullptr) { LOG(ERROR) << filename << " [" << file_offset << "-" << (file_offset + file_size) << "] is not an object file"; } return ret; } bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id) { if (!IsValidElfPath(filename)) { return false; } return GetBuildIdFromEmbeddedElfFile(filename, 0, 0, build_id); } bool GetBuildIdFromEmbeddedElfFile(const std::string& filename, uint64_t file_offset, uint32_t file_size, BuildId* build_id) { BinaryRet ret = OpenObjectFile(filename, file_offset, file_size); if (ret.obj == nullptr) { return false; } return GetBuildIdFromObjectFile(ret.obj, build_id); } bool IsArmMappingSymbol(const char* name) { // Mapping symbols in arm, which are described in "ELF for ARM Architecture" and // "ELF for ARM 64-bit Architecture". The regular expression to match mapping symbol // is ^\$(a|d|t|x)(\..*)?$ return name[0] == '$' && strchr("adtx", name[1]) != nullptr && (name[2] == '\0' || name[2] == '.'); } template void ParseSymbolsFromELFFile(const llvm::object::ELFFile* elf, std::function callback) { bool is_arm = (elf->getHeader()->e_machine == llvm::ELF::EM_ARM || elf->getHeader()->e_machine == llvm::ELF::EM_AARCH64); auto begin = elf->begin_symbols(); auto end = elf->end_symbols(); if (begin == end) { begin = elf->begin_dynamic_symbols(); end = elf->end_dynamic_symbols(); } for (; begin != end; ++begin) { auto& elf_symbol = *begin; ElfFileSymbol symbol; auto shdr = elf->getSection(&elf_symbol); if (shdr == nullptr) { continue; } auto section_name = elf->getSectionName(shdr); if (section_name.getError() || section_name.get().empty()) { continue; } if (section_name.get() == ".text") { symbol.is_in_text_section = true; } auto symbol_name = elf->getSymbolName(begin); if (symbol_name.getError()) { continue; } symbol.name = symbol_name.get(); if (symbol.name.empty()) { continue; } symbol.vaddr = elf_symbol.st_value; if ((symbol.vaddr & 1) != 0 && is_arm) { // Arm sets bit 0 to mark it as thumb code, remove the flag. symbol.vaddr &= ~1; } symbol.len = elf_symbol.st_size; int type = elf_symbol.getType(); if (type == llvm::ELF::STT_FUNC) { symbol.is_func = true; } else if (type == llvm::ELF::STT_NOTYPE) { if (symbol.is_in_text_section) { symbol.is_label = true; if (is_arm) { // Remove mapping symbols in arm. const char* p = (symbol.name.compare(0, linker_prefix.size(), linker_prefix) == 0) ? symbol.name.c_str() + linker_prefix.size() : symbol.name.c_str(); if (IsArmMappingSymbol(p)) { symbol.is_label = false; } } } } callback(symbol); } } bool MatchBuildId(llvm::object::ObjectFile* obj, const BuildId& expected_build_id, const std::string& debug_filename) { if (expected_build_id.IsEmpty()) { return true; } BuildId real_build_id; if (!GetBuildIdFromObjectFile(obj, &real_build_id)) { return false; } if (expected_build_id != real_build_id) { LOG(DEBUG) << "build id for " << debug_filename << " mismatch: " << "expected " << expected_build_id.ToString() << ", real " << real_build_id.ToString(); return false; } return true; } bool ParseSymbolsFromElfFile(const std::string& filename, const BuildId& expected_build_id, std::function callback) { if (!IsValidElfPath(filename)) { return false; } return ParseSymbolsFromEmbeddedElfFile(filename, 0, 0, expected_build_id, callback); } bool ParseSymbolsFromEmbeddedElfFile(const std::string& filename, uint64_t file_offset, uint32_t file_size, const BuildId& expected_build_id, std::function callback) { BinaryRet ret = OpenObjectFile(filename, file_offset, file_size); if (ret.obj == nullptr || !MatchBuildId(ret.obj, expected_build_id, filename)) { return false; } if (auto elf = llvm::dyn_cast(ret.obj)) { ParseSymbolsFromELFFile(elf->getELFFile(), callback); } else if (auto elf = llvm::dyn_cast(ret.obj)) { ParseSymbolsFromELFFile(elf->getELFFile(), callback); } else { LOG(ERROR) << "unknown elf format in file " << filename; return false; } return true; } template bool ReadMinExecutableVirtualAddress(const llvm::object::ELFFile* elf, uint64_t* p_vaddr) { bool has_vaddr = false; uint64_t min_addr = std::numeric_limits::max(); for (auto it = elf->begin_program_headers(); it != elf->end_program_headers(); ++it) { if ((it->p_type == llvm::ELF::PT_LOAD) && (it->p_flags & llvm::ELF::PF_X)) { if (it->p_vaddr < min_addr) { min_addr = it->p_vaddr; has_vaddr = true; } } } if (has_vaddr) { *p_vaddr = min_addr; } return has_vaddr; } bool ReadMinExecutableVirtualAddressFromElfFile(const std::string& filename, const BuildId& expected_build_id, uint64_t* min_vaddr) { if (!IsValidElfPath(filename)) { return false; } BinaryRet ret = OpenObjectFile(filename); if (ret.obj == nullptr || !MatchBuildId(ret.obj, expected_build_id, filename)) { return false; } bool result = false; if (auto elf = llvm::dyn_cast(ret.obj)) { result = ReadMinExecutableVirtualAddress(elf->getELFFile(), min_vaddr); } else if (auto elf = llvm::dyn_cast(ret.obj)) { result = ReadMinExecutableVirtualAddress(elf->getELFFile(), min_vaddr); } else { LOG(ERROR) << "unknown elf format in file" << filename; return false; } if (!result) { LOG(ERROR) << "no program header in file " << filename; } return result; }