Home | History | Annotate | Download | only in simpleperf
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "read_elf.h"
     18 #include "read_apk.h"
     19 
     20 #include <stdio.h>
     21 #include <string.h>
     22 #include <sys/stat.h>
     23 #include <sys/types.h>
     24 
     25 #include <algorithm>
     26 #include <limits>
     27 
     28 #include <android-base/file.h>
     29 #include <android-base/logging.h>
     30 
     31 #pragma clang diagnostic push
     32 #pragma clang diagnostic ignored "-Wunused-parameter"
     33 
     34 #include <llvm/ADT/StringRef.h>
     35 #include <llvm/Object/Binary.h>
     36 #include <llvm/Object/ELFObjectFile.h>
     37 #include <llvm/Object/ObjectFile.h>
     38 
     39 #pragma clang diagnostic pop
     40 
     41 #include "utils.h"
     42 
     43 #define ELF_NOTE_GNU "GNU"
     44 #define NT_GNU_BUILD_ID 3
     45 
     46 
     47 bool IsValidElfFile(int fd) {
     48   static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
     49   char buf[4];
     50   return android::base::ReadFully(fd, buf, 4) && memcmp(buf, elf_magic, 4) == 0;
     51 }
     52 
     53 bool IsValidElfPath(const std::string& filename) {
     54   if (!IsRegularFile(filename)) {
     55     return false;
     56   }
     57   std::string mode = std::string("rb") + CLOSE_ON_EXEC_MODE;
     58   FILE* fp = fopen(filename.c_str(), mode.c_str());
     59   if (fp == nullptr) {
     60     return false;
     61   }
     62   bool result = IsValidElfFile(fileno(fp));
     63   fclose(fp);
     64   return result;
     65 }
     66 
     67 static bool GetBuildIdFromNoteSection(const char* section, size_t section_size, BuildId* build_id) {
     68   const char* p = section;
     69   const char* end = p + section_size;
     70   while (p < end) {
     71     CHECK_LE(p + 12, end);
     72     size_t namesz = *reinterpret_cast<const uint32_t*>(p);
     73     p += 4;
     74     size_t descsz = *reinterpret_cast<const uint32_t*>(p);
     75     p += 4;
     76     uint32_t type = *reinterpret_cast<const uint32_t*>(p);
     77     p += 4;
     78     namesz = ALIGN(namesz, 4);
     79     descsz = ALIGN(descsz, 4);
     80     CHECK_LE(p + namesz + descsz, end);
     81     if ((type == NT_GNU_BUILD_ID) && (strcmp(p, ELF_NOTE_GNU) == 0)) {
     82       *build_id = BuildId(p + namesz, descsz);
     83       return true;
     84     }
     85     p += namesz + descsz;
     86   }
     87   return false;
     88 }
     89 
     90 bool GetBuildIdFromNoteFile(const std::string& filename, BuildId* build_id) {
     91   std::string content;
     92   if (!android::base::ReadFileToString(filename, &content)) {
     93     LOG(DEBUG) << "can't read note file " << filename;
     94     return false;
     95   }
     96   if (GetBuildIdFromNoteSection(content.c_str(), content.size(), build_id) == false) {
     97     LOG(DEBUG) << "can't read build_id from note file " << filename;
     98     return false;
     99   }
    100   return true;
    101 }
    102 
    103 template <class ELFT>
    104 bool GetBuildIdFromELFFile(const llvm::object::ELFFile<ELFT>* elf, BuildId* build_id) {
    105   for (auto section_iterator = elf->section_begin(); section_iterator != elf->section_end();
    106        ++section_iterator) {
    107     if (section_iterator->sh_type == llvm::ELF::SHT_NOTE) {
    108       auto contents = elf->getSectionContents(&*section_iterator);
    109       if (contents.getError()) {
    110         LOG(DEBUG) << "read note section error";
    111         continue;
    112       }
    113       if (GetBuildIdFromNoteSection(reinterpret_cast<const char*>(contents->data()),
    114                                     contents->size(), build_id)) {
    115         return true;
    116       }
    117     }
    118   }
    119   return false;
    120 }
    121 
    122 static bool GetBuildIdFromObjectFile(llvm::object::ObjectFile* obj, BuildId* build_id) {
    123   bool result = false;
    124   if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
    125     result = GetBuildIdFromELFFile(elf->getELFFile(), build_id);
    126   } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
    127     result = GetBuildIdFromELFFile(elf->getELFFile(), build_id);
    128   } else {
    129     LOG(ERROR) << "unknown elf format in file " << obj->getFileName().data();
    130     return false;
    131   }
    132   if (!result) {
    133     LOG(DEBUG) << "no build id present in file " << obj->getFileName().data();
    134   }
    135   return result;
    136 }
    137 
    138 struct BinaryRet {
    139   llvm::object::OwningBinary<llvm::object::Binary> binary;
    140   llvm::object::ObjectFile* obj;
    141 
    142   BinaryRet() : obj(nullptr) {
    143   }
    144 };
    145 
    146 static BinaryRet OpenObjectFile(const std::string& filename, uint64_t file_offset = 0,
    147                                 uint64_t file_size = 0) {
    148   BinaryRet ret;
    149   FileHelper fhelper = FileHelper::OpenReadOnly(filename);
    150   if (!fhelper) {
    151     PLOG(DEBUG) << "failed to open " << filename;
    152     return ret;
    153   }
    154   if (file_size == 0) {
    155     file_size = GetFileSize(filename);
    156     if (file_size == 0) {
    157       PLOG(ERROR) << "failed to get size of file " << filename;
    158       return ret;
    159     }
    160   }
    161   auto buffer_or_err = llvm::MemoryBuffer::getOpenFileSlice(fhelper.fd(), filename, file_size, file_offset);
    162   if (!buffer_or_err) {
    163     LOG(ERROR) << "failed to read " << filename << " [" << file_offset << "-" << (file_offset + file_size)
    164         << "]: " << buffer_or_err.getError().message();
    165     return ret;
    166   }
    167   auto binary_or_err = llvm::object::createBinary(buffer_or_err.get()->getMemBufferRef());
    168   if (!binary_or_err) {
    169     LOG(ERROR) << filename << " [" << file_offset << "-" << (file_offset + file_size)
    170         << "] is not a binary file: " << binary_or_err.getError().message();
    171     return ret;
    172   }
    173   ret.binary = llvm::object::OwningBinary<llvm::object::Binary>(std::move(binary_or_err.get()),
    174                                                                 std::move(buffer_or_err.get()));
    175   ret.obj = llvm::dyn_cast<llvm::object::ObjectFile>(ret.binary.getBinary());
    176   if (ret.obj == nullptr) {
    177     LOG(ERROR) << filename << " [" << file_offset << "-" << (file_offset + file_size)
    178         << "] is not an object file";
    179   }
    180   return ret;
    181 }
    182 
    183 bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id) {
    184   if (!IsValidElfPath(filename)) {
    185     return false;
    186   }
    187   bool result = GetBuildIdFromEmbeddedElfFile(filename, 0, 0, build_id);
    188   LOG(VERBOSE) << "GetBuildIdFromElfFile(" << filename << ") => " << build_id->ToString();
    189   return result;
    190 }
    191 
    192 bool GetBuildIdFromEmbeddedElfFile(const std::string& filename, uint64_t file_offset,
    193                                    uint32_t file_size, BuildId* build_id) {
    194   BinaryRet ret = OpenObjectFile(filename, file_offset, file_size);
    195   if (ret.obj == nullptr) {
    196     return false;
    197   }
    198   return GetBuildIdFromObjectFile(ret.obj, build_id);
    199 }
    200 
    201 bool IsArmMappingSymbol(const char* name) {
    202   // Mapping symbols in arm, which are described in "ELF for ARM Architecture" and
    203   // "ELF for ARM 64-bit Architecture". The regular expression to match mapping symbol
    204   // is ^\$(a|d|t|x)(\..*)?$
    205   return name[0] == '$' && strchr("adtx", name[1]) != nullptr && (name[2] == '\0' || name[2] == '.');
    206 }
    207 
    208 template <class ELFT>
    209 void ParseSymbolsFromELFFile(const llvm::object::ELFObjectFile<ELFT>* elf_obj,
    210                              std::function<void(const ElfFileSymbol&)> callback) {
    211   auto elf = elf_obj->getELFFile();
    212   bool is_arm = (elf->getHeader()->e_machine == llvm::ELF::EM_ARM ||
    213                  elf->getHeader()->e_machine == llvm::ELF::EM_AARCH64);
    214   auto begin = elf_obj->symbol_begin();
    215   auto end = elf_obj->symbol_end();
    216   if (begin == end) {
    217     begin = elf_obj->dynamic_symbol_begin();
    218     end = elf_obj->dynamic_symbol_end();
    219   }
    220   for (; begin != end; ++begin) {
    221     ElfFileSymbol symbol;
    222     auto elf_symbol = static_cast<const llvm::object::ELFSymbolRef*>(&*begin);
    223     auto section_it = elf_symbol->getSection();
    224     if (!section_it) {
    225       continue;
    226     }
    227     llvm::StringRef section_name;
    228     if (section_it.get()->getName(section_name) || section_name.empty()) {
    229       continue;
    230     }
    231     if (section_name.str() == ".text") {
    232       symbol.is_in_text_section = true;
    233     }
    234 
    235     auto symbol_name = elf_symbol->getName();
    236     if (!symbol_name || symbol_name.get().empty()) {
    237       continue;
    238     }
    239     symbol.name = symbol_name.get();
    240     symbol.vaddr = elf_symbol->getValue();
    241     if ((symbol.vaddr & 1) != 0 && is_arm) {
    242       // Arm sets bit 0 to mark it as thumb code, remove the flag.
    243       symbol.vaddr &= ~1;
    244     }
    245     symbol.len = elf_symbol->getSize();
    246     int type = elf_symbol->getELFType();
    247     if (type == llvm::ELF::STT_FUNC) {
    248       symbol.is_func = true;
    249     } else if (type == llvm::ELF::STT_NOTYPE) {
    250       if (symbol.is_in_text_section) {
    251         symbol.is_label = true;
    252         if (is_arm) {
    253           // Remove mapping symbols in arm.
    254           const char* p = (symbol.name.compare(0, linker_prefix.size(), linker_prefix) == 0)
    255                               ? symbol.name.c_str() + linker_prefix.size()
    256                               : symbol.name.c_str();
    257           if (IsArmMappingSymbol(p)) {
    258             symbol.is_label = false;
    259           }
    260         }
    261       }
    262     }
    263 
    264     callback(symbol);
    265   }
    266 }
    267 
    268 bool MatchBuildId(llvm::object::ObjectFile* obj, const BuildId& expected_build_id,
    269                   const std::string& debug_filename) {
    270   if (expected_build_id.IsEmpty()) {
    271     return true;
    272   }
    273   BuildId real_build_id;
    274   if (!GetBuildIdFromObjectFile(obj, &real_build_id)) {
    275     return false;
    276   }
    277   if (expected_build_id != real_build_id) {
    278     LOG(DEBUG) << "build id for " << debug_filename << " mismatch: "
    279                << "expected " << expected_build_id.ToString()
    280                << ", real " << real_build_id.ToString();
    281     return false;
    282   }
    283   return true;
    284 }
    285 
    286 bool ParseSymbolsFromElfFile(const std::string& filename, const BuildId& expected_build_id,
    287                              std::function<void(const ElfFileSymbol&)> callback) {
    288   if (!IsValidElfPath(filename)) {
    289     return false;
    290   }
    291   return ParseSymbolsFromEmbeddedElfFile(filename, 0, 0, expected_build_id, callback);
    292 }
    293 
    294 bool ParseSymbolsFromEmbeddedElfFile(const std::string& filename, uint64_t file_offset,
    295                                      uint32_t file_size, const BuildId& expected_build_id,
    296                                      std::function<void(const ElfFileSymbol&)> callback) {
    297   BinaryRet ret = OpenObjectFile(filename, file_offset, file_size);
    298   if (ret.obj == nullptr || !MatchBuildId(ret.obj, expected_build_id, filename)) {
    299     return false;
    300   }
    301   if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(ret.obj)) {
    302     ParseSymbolsFromELFFile(elf, callback);
    303   } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(ret.obj)) {
    304     ParseSymbolsFromELFFile(elf, callback);
    305   } else {
    306     LOG(ERROR) << "unknown elf format in file " << filename;
    307     return false;
    308   }
    309   return true;
    310 }
    311 
    312 template <class ELFT>
    313 bool ReadMinExecutableVirtualAddress(const llvm::object::ELFFile<ELFT>* elf, uint64_t* p_vaddr) {
    314   bool has_vaddr = false;
    315   uint64_t min_addr = std::numeric_limits<uint64_t>::max();
    316   for (auto it = elf->program_header_begin(); it != elf->program_header_end(); ++it) {
    317     if ((it->p_type == llvm::ELF::PT_LOAD) && (it->p_flags & llvm::ELF::PF_X)) {
    318       if (it->p_vaddr < min_addr) {
    319         min_addr = it->p_vaddr;
    320         has_vaddr = true;
    321       }
    322     }
    323   }
    324   if (has_vaddr) {
    325     *p_vaddr = min_addr;
    326   }
    327   return has_vaddr;
    328 }
    329 
    330 bool ReadMinExecutableVirtualAddressFromElfFile(const std::string& filename,
    331                                                 const BuildId& expected_build_id,
    332                                                 uint64_t* min_vaddr) {
    333   if (!IsValidElfPath(filename)) {
    334     return false;
    335   }
    336   BinaryRet ret = OpenObjectFile(filename);
    337   if (ret.obj == nullptr || !MatchBuildId(ret.obj, expected_build_id, filename)) {
    338     return false;
    339   }
    340 
    341   bool result = false;
    342   if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(ret.obj)) {
    343     result = ReadMinExecutableVirtualAddress(elf->getELFFile(), min_vaddr);
    344   } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(ret.obj)) {
    345     result = ReadMinExecutableVirtualAddress(elf->getELFFile(), min_vaddr);
    346   } else {
    347     LOG(ERROR) << "unknown elf format in file" << filename;
    348     return false;
    349   }
    350 
    351   if (!result) {
    352     LOG(ERROR) << "no program header in file " << filename;
    353   }
    354   return result;
    355 }
    356 
    357 template <class ELFT>
    358 bool ReadSectionFromELFFile(const llvm::object::ELFFile<ELFT>* elf, const std::string& section_name,
    359                             std::string* content) {
    360   for (auto it = elf->section_begin(); it != elf->section_end(); ++it) {
    361     auto name_or_err = elf->getSectionName(&*it);
    362     if (name_or_err && *name_or_err == section_name) {
    363       auto data_or_err = elf->getSectionContents(&*it);
    364       if (!data_or_err) {
    365         LOG(ERROR) << "failed to read section " << section_name;
    366         return false;
    367       }
    368       content->append(data_or_err->begin(), data_or_err->end());
    369       return true;
    370     }
    371   }
    372   LOG(ERROR) << "can't find section " << section_name;
    373   return false;
    374 }
    375 
    376 bool ReadSectionFromElfFile(const std::string& filename, const std::string& section_name,
    377                             std::string* content) {
    378   if (!IsValidElfPath(filename)) {
    379     return false;
    380   }
    381   BinaryRet ret = OpenObjectFile(filename);
    382   if (ret.obj == nullptr) {
    383     return false;
    384   }
    385   bool result = false;
    386   if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(ret.obj)) {
    387     result = ReadSectionFromELFFile(elf->getELFFile(), section_name, content);
    388   } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(ret.obj)) {
    389     result = ReadSectionFromELFFile(elf->getELFFile(), section_name, content);
    390   } else {
    391     LOG(ERROR) << "unknown elf format in file" << filename;
    392     return false;
    393   }
    394   return result;
    395 }
    396