Home | History | Annotate | Download | only in quipper
      1 // Copyright 2016 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "dso.h"
      6 
      7 #include <elf.h>
      8 #include <fcntl.h>
      9 #include <gelf.h>
     10 #include <libelf.h>
     11 #include <string.h>
     12 #include <sys/stat.h>
     13 #include <sys/sysmacros.h>
     14 #include <unistd.h>
     15 
     16 #include <algorithm>
     17 #include <vector>
     18 
     19 #include "base/logging.h"
     20 #include "compat/string.h"
     21 #include "file_reader.h"
     22 
     23 namespace quipper {
     24 
     25 namespace {
     26 
     27 // Find a section with a name matching one in |names|. Prefer sections matching
     28 // names earlier in the vector.
     29 Elf_Scn *FindElfSection(Elf *elf, const std::vector<string> &names) {
     30   size_t shstrndx;  // section index of the section names string table.
     31   if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
     32     LOG(ERROR) << "elf_getshdrstrndx" << elf_errmsg(-1);
     33     return nullptr;
     34   }
     35   // Ensure the section header string table is available
     36   if (!elf_rawdata(elf_getscn(elf, shstrndx), nullptr)) return nullptr;
     37 
     38   auto best_match = names.end();
     39   Elf_Scn *best_sec = nullptr;
     40   Elf_Scn *sec = nullptr;
     41   while ((sec = elf_nextscn(elf, sec)) != nullptr) {
     42     GElf_Shdr shdr;
     43     gelf_getshdr(sec, &shdr);
     44     char *n = elf_strptr(elf, shstrndx, shdr.sh_name);
     45     if (!n) {
     46       LOG(ERROR) << "Couldn't get string: " << shdr.sh_name << " " << shstrndx;
     47       return nullptr;
     48     }
     49     const string name(n);
     50     auto found = std::find(names.begin(), names.end(), name);
     51     if (found < best_match) {
     52       if (found == names.begin()) return sec;
     53       best_sec = sec;
     54       best_match = found;
     55     }
     56   }
     57 
     58   return best_sec;
     59 }
     60 
     61 bool GetBuildID(Elf *elf, string *buildid) {
     62   Elf_Kind kind = elf_kind(elf);
     63   if (kind != ELF_K_ELF) {
     64     DLOG(ERROR) << "Not an ELF file: " << elf_errmsg(-1);
     65     return false;
     66   }
     67 
     68   static const std::vector<string> kNoteSectionNames{".note.gnu.build-id",
     69                                                      ".notes", ".note"};
     70   Elf_Scn *section = FindElfSection(elf, kNoteSectionNames);
     71   if (!section) {
     72     DLOG(ERROR) << "No note section found";
     73     return false;
     74   }
     75 
     76   Elf_Data *data = elf_getdata(section, nullptr);
     77   if (data == nullptr) return false;
     78 
     79   char *buf = reinterpret_cast<char *>(data->d_buf);
     80   GElf_Nhdr note_header;
     81   size_t name_off;
     82   size_t desc_off;
     83   for (size_t off = 0, next = 0;
     84        (next = gelf_getnote(data, off, &note_header, &name_off, &desc_off)) > 0;
     85        off = next) {
     86     // name is null-padded to a 4-byte boundary.
     87     string name(buf + name_off, strnlen(buf + name_off, note_header.n_namesz));
     88     string desc(buf + desc_off, note_header.n_descsz);
     89     if (note_header.n_type == NT_GNU_BUILD_ID && name == ELF_NOTE_GNU) {
     90       *buildid = desc;
     91       return true;
     92     }
     93   }
     94 
     95   return false;
     96 }
     97 
     98 }  // namespace
     99 
    100 void InitializeLibelf() {
    101   const unsigned int kElfVersionNone = EV_NONE;  // correctly typed.
    102   CHECK_NE(kElfVersionNone, elf_version(EV_CURRENT)) << elf_errmsg(-1);
    103 }
    104 
    105 bool ReadElfBuildId(const string &filename, string *buildid) {
    106   int fd = open(filename.c_str(), O_RDONLY);
    107   if (fd < 0) {
    108     if (errno != ENOENT) LOG(ERROR) << "Failed to open ELF file: " << filename;
    109     return false;
    110   }
    111   bool ret = ReadElfBuildId(fd, buildid);
    112   close(fd);
    113   return ret;
    114 }
    115 
    116 bool ReadElfBuildId(int fd, string *buildid) {
    117   InitializeLibelf();
    118 
    119   Elf *elf = elf_begin(fd, ELF_C_READ_MMAP, nullptr);
    120   if (elf == nullptr) {
    121     LOG(ERROR) << "Could not read ELF file.";
    122     close(fd);
    123     return false;
    124   }
    125 
    126   bool err = GetBuildID(elf, buildid);
    127 
    128   elf_end(elf);
    129 
    130   return err;
    131 }
    132 
    133 // read /sys/module/<module_name>/notes/.note.gnu.build-id
    134 bool ReadModuleBuildId(const string &module_name, string *buildid) {
    135   string note_filename =
    136       "/sys/module/" + module_name + "/notes/.note.gnu.build-id";
    137 
    138   FileReader file(note_filename);
    139   if (!file.IsOpen()) return false;
    140 
    141   return ReadBuildIdNote(&file, buildid);
    142 }
    143 
    144 bool ReadBuildIdNote(DataReader *data, string *buildid) {
    145   GElf_Nhdr note_header;
    146 
    147   while (data->ReadData(sizeof(note_header), &note_header)) {
    148     size_t name_size = Align<4>(note_header.n_namesz);
    149     size_t desc_size = Align<4>(note_header.n_descsz);
    150 
    151     string name;
    152     if (!data->ReadString(name_size, &name)) return false;
    153     string desc;
    154     if (!data->ReadDataString(desc_size, &desc)) return false;
    155     if (note_header.n_type == NT_GNU_BUILD_ID && name == ELF_NOTE_GNU) {
    156       *buildid = desc;
    157       return true;
    158     }
    159   }
    160   return false;
    161 }
    162 
    163 bool IsKernelNonModuleName(string name) {
    164   // List from kernel: tools/perf/util/dso.c : __kmod_path__parse()
    165   static const std::vector<string> kKernelNonModuleNames{
    166       "[kernel.kallsyms]",
    167       "[guest.kernel.kallsyms",
    168       "[vdso]",
    169       "[vsyscall]",
    170   };
    171 
    172   for (const auto &n : kKernelNonModuleNames) {
    173     if (name.compare(0, n.size(), n) == 0) return true;
    174   }
    175   return false;
    176 }
    177 
    178 // Do the |DSOInfo| and |struct stat| refer to the same inode?
    179 bool SameInode(const DSOInfo &dso, const struct stat *s) {
    180   return dso.maj == major(s->st_dev) && dso.min == minor(s->st_dev) &&
    181          dso.ino == s->st_ino;
    182 }
    183 
    184 }  // namespace quipper
    185