// Copyright 2016 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "dso.h" #include <elf.h> #include <fcntl.h> #include <gelf.h> #include <libelf.h> #include <string.h> #include <sys/stat.h> #include <sys/sysmacros.h> #include <unistd.h> #include <algorithm> #include <vector> #include "base/logging.h" #include "compat/string.h" #include "file_reader.h" namespace quipper { namespace { // Find a section with a name matching one in |names|. Prefer sections matching // names earlier in the vector. Elf_Scn *FindElfSection(Elf *elf, const std::vector<string> &names) { size_t shstrndx; // section index of the section names string table. if (elf_getshdrstrndx(elf, &shstrndx) != 0) { LOG(ERROR) << "elf_getshdrstrndx" << elf_errmsg(-1); return nullptr; } // Ensure the section header string table is available if (!elf_rawdata(elf_getscn(elf, shstrndx), nullptr)) return nullptr; auto best_match = names.end(); Elf_Scn *best_sec = nullptr; Elf_Scn *sec = nullptr; while ((sec = elf_nextscn(elf, sec)) != nullptr) { GElf_Shdr shdr; gelf_getshdr(sec, &shdr); char *n = elf_strptr(elf, shstrndx, shdr.sh_name); if (!n) { LOG(ERROR) << "Couldn't get string: " << shdr.sh_name << " " << shstrndx; return nullptr; } const string name(n); auto found = std::find(names.begin(), names.end(), name); if (found < best_match) { if (found == names.begin()) return sec; best_sec = sec; best_match = found; } } return best_sec; } bool GetBuildID(Elf *elf, string *buildid) { Elf_Kind kind = elf_kind(elf); if (kind != ELF_K_ELF) { DLOG(ERROR) << "Not an ELF file: " << elf_errmsg(-1); return false; } static const std::vector<string> kNoteSectionNames{".note.gnu.build-id", ".notes", ".note"}; Elf_Scn *section = FindElfSection(elf, kNoteSectionNames); if (!section) { DLOG(ERROR) << "No note section found"; return false; } Elf_Data *data = elf_getdata(section, nullptr); if (data == nullptr) return false; char *buf = reinterpret_cast<char *>(data->d_buf); GElf_Nhdr note_header; size_t name_off; size_t desc_off; for (size_t off = 0, next = 0; (next = gelf_getnote(data, off, ¬e_header, &name_off, &desc_off)) > 0; off = next) { // name is null-padded to a 4-byte boundary. string name(buf + name_off, strnlen(buf + name_off, note_header.n_namesz)); string desc(buf + desc_off, note_header.n_descsz); if (note_header.n_type == NT_GNU_BUILD_ID && name == ELF_NOTE_GNU) { *buildid = desc; return true; } } return false; } } // namespace void InitializeLibelf() { const unsigned int kElfVersionNone = EV_NONE; // correctly typed. CHECK_NE(kElfVersionNone, elf_version(EV_CURRENT)) << elf_errmsg(-1); } bool ReadElfBuildId(const string &filename, string *buildid) { int fd = open(filename.c_str(), O_RDONLY); if (fd < 0) { if (errno != ENOENT) LOG(ERROR) << "Failed to open ELF file: " << filename; return false; } bool ret = ReadElfBuildId(fd, buildid); close(fd); return ret; } bool ReadElfBuildId(int fd, string *buildid) { InitializeLibelf(); Elf *elf = elf_begin(fd, ELF_C_READ_MMAP, nullptr); if (elf == nullptr) { LOG(ERROR) << "Could not read ELF file."; close(fd); return false; } bool err = GetBuildID(elf, buildid); elf_end(elf); return err; } // read /sys/module/<module_name>/notes/.note.gnu.build-id bool ReadModuleBuildId(const string &module_name, string *buildid) { string note_filename = "/sys/module/" + module_name + "/notes/.note.gnu.build-id"; FileReader file(note_filename); if (!file.IsOpen()) return false; return ReadBuildIdNote(&file, buildid); } bool ReadBuildIdNote(DataReader *data, string *buildid) { GElf_Nhdr note_header; while (data->ReadData(sizeof(note_header), ¬e_header)) { size_t name_size = Align<4>(note_header.n_namesz); size_t desc_size = Align<4>(note_header.n_descsz); string name; if (!data->ReadString(name_size, &name)) return false; string desc; if (!data->ReadDataString(desc_size, &desc)) return false; if (note_header.n_type == NT_GNU_BUILD_ID && name == ELF_NOTE_GNU) { *buildid = desc; return true; } } return false; } bool IsKernelNonModuleName(string name) { // List from kernel: tools/perf/util/dso.c : __kmod_path__parse() static const std::vector<string> kKernelNonModuleNames{ "[kernel.kallsyms]", "[guest.kernel.kallsyms", "[vdso]", "[vsyscall]", }; for (const auto &n : kKernelNonModuleNames) { if (name.compare(0, n.size(), n) == 0) return true; } return false; } // Do the |DSOInfo| and |struct stat| refer to the same inode? bool SameInode(const DSOInfo &dso, const struct stat *s) { return dso.maj == major(s->st_dev) && dso.min == minor(s->st_dev) && dso.ino == s->st_ino; } } // namespace quipper