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, ¬e_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), ¬e_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