diff options
author | Milian Wolff <milian.wolff@kdab.com> | 2017-07-04 19:33:51 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-05-02 14:21:44 +0000 |
commit | f528071fa3b392e4e29f500746f799771939b96d (patch) | |
tree | 181cb507f3e8b61497b1939a2ed3558547b74314 | |
parent | 0d25ac0c07494ca4b2cbb0540e987f73959f864a (diff) |
Add manual symbol resolution for addresses in the plt section
This is a workaround until we get this functionality into elfutils
upstream. The symbols are looked up manually, which requires quite
some jumping around the ELF file:
- first map the address into the module space
- check whether the address lies in the .plt section (by name)
- if so, find the index of the .plt entry we just found
- then find the dynamic section and dynamic string table
- in the dynamic section, find the address of the GOT via DT_PLTGOT
- find the section containing the GOT address, this is the GOT
- find the GOT entry corresponding to the .plt index, offset by 2
- find the REL/RELA section containing the entry matching the address
of the GOT entry
- find the (dynamic) symbol for the REL/RELA entry
- find the string for that symbol, demangle it, and append @plt
Change-Id: I67d05f1c728b943317853bb98fa96dba48b58a3c
Reviewed-by: Milian Wolff <milian.wolff@kdab.com>
-rw-r--r-- | app/perfsymboltable.cpp | 153 |
1 files changed, 146 insertions, 7 deletions
diff --git a/app/perfsymboltable.cpp b/app/perfsymboltable.cpp index 6b9dda3..2d0420f 100644 --- a/app/perfsymboltable.cpp +++ b/app/perfsymboltable.cpp @@ -559,6 +559,136 @@ PerfElfMap::ElfInfo PerfSymbolTable::findElf(quint64 ip) const return m_elfs.findElf(ip); } +int symbolIndex(const Elf64_Rel &rel) +{ + return ELF64_R_SYM(rel.r_info); +} + +int symbolIndex(const Elf64_Rela &rel) +{ + return ELF64_R_SYM(rel.r_info); +} + +int symbolIndex(const Elf32_Rel &rel) +{ + return ELF32_R_SYM(rel.r_info); +} + +int symbolIndex(const Elf32_Rela &rel) +{ + return ELF32_R_SYM(rel.r_info); +} + +template<typename elf_relocation_t, typename elf_shdr_t> +int findPltSymbolIndex(Elf_Scn *section, const elf_shdr_t *shdr, Dwarf_Addr addr) +{ + if (shdr->sh_entsize != sizeof(elf_relocation_t)) { + qWarning() << "size mismatch:" << shdr->sh_entsize << sizeof(elf_relocation_t); + return -1; + } + const size_t numEntries = shdr->sh_size / shdr->sh_entsize; + const auto *data = elf_getdata(section, nullptr); + const auto *entries = reinterpret_cast<const elf_relocation_t *>(data->d_buf); + const auto *entriesEnd = entries + numEntries; + auto it = std::lower_bound(entries, entriesEnd, addr, + [](const elf_relocation_t &lhs, Dwarf_Addr addr) { + return lhs.r_offset < addr; + }); + if (it == entriesEnd || it->r_offset != addr) + return -1; + return symbolIndex(*it); +} + +template<typename elf_dyn_t, typename elf_shdr_t> +Elf64_Addr findPltGotAddr(Elf_Scn *section, elf_shdr_t* shdr) +{ + const auto *data = elf_getdata(section, nullptr); + const size_t numEntries = shdr->sh_size / shdr->sh_entsize; + const auto *entries = reinterpret_cast<const elf_dyn_t *>(data->d_buf); + for (size_t i = 0; i < numEntries; ++i) { + if (entries[i].d_tag == DT_PLTGOT) { + return entries[i].d_un.d_ptr; + } + } + return 0; +} + +const char *findPltSymbol(Elf *elf, int index) +{ + if (!index) // first plt entry is special, skip it + return nullptr; + + size_t numSections = 0; + if (elf_getshdrnum(elf, &numSections) != 0) + return nullptr; + + Elf64_Addr pltGotAddr = 0; + Elf_Scn *symtab = nullptr; + for (size_t i = 0; (!pltGotAddr || !symtab) && i < numSections; ++i) { + auto *section = elf_getscn(elf, i); + if (const auto *shdr = elf64_getshdr(section)) { + if (shdr->sh_type == SHT_DYNAMIC) + pltGotAddr = findPltGotAddr<Elf64_Dyn>(section, shdr); + else if (shdr->sh_type == SHT_DYNSYM) + symtab = section; + } else if (const auto *shdr = elf32_getshdr(section)) { + if (shdr->sh_type == SHT_DYNAMIC) + pltGotAddr = findPltGotAddr<Elf32_Dyn>(section, shdr); + else if (shdr->sh_type == SHT_DYNSYM) + symtab = section; + } + } + + if (!pltGotAddr || !symtab) + return nullptr; + + Elf64_Addr indexAddr = 0; + for (size_t i = 0; !indexAddr && i < numSections; ++i) { + auto *section = elf_getscn(elf, i); + if (const auto *shdr = elf64_getshdr(section)) { + if (shdr->sh_addr <= pltGotAddr && pltGotAddr < shdr->sh_addr + shdr->sh_size) + indexAddr = shdr->sh_addr + (index + 2) * sizeof(Elf64_Addr); + } else if (const auto *shdr = elf32_getshdr(section)) { + if (shdr->sh_addr <= pltGotAddr && pltGotAddr < shdr->sh_addr + shdr->sh_size) + indexAddr = shdr->sh_addr + (index + 2) * sizeof(Elf32_Addr); + } + } + + if (!indexAddr) + return nullptr; + + int symbolIndex = -1; + for (size_t i = 0; symbolIndex == -1 && i < numSections; ++i) { + auto section = elf_getscn(elf, i); + if (const auto *shdr = elf64_getshdr(section)) { + if (shdr->sh_type == SHT_REL) + symbolIndex = findPltSymbolIndex<Elf64_Rel>(section, shdr, indexAddr); + else if (shdr->sh_type == SHT_RELA) + symbolIndex = findPltSymbolIndex<Elf64_Rela>(section, shdr, indexAddr); + } else if (const auto *shdr = elf32_getshdr(section)) { + if (shdr->sh_type == SHT_REL) + symbolIndex = findPltSymbolIndex<Elf32_Rel>(section, shdr, indexAddr); + else if (shdr->sh_type == SHT_RELA) + symbolIndex = findPltSymbolIndex<Elf32_Rela>(section, shdr, indexAddr); + } + } + + if (symbolIndex == -1) + return nullptr; + + const auto *symtabData = elf_getdata(symtab, nullptr)->d_buf; + if (const auto *shdr = elf64_getshdr(symtab)) { + const auto *symbols = reinterpret_cast<const Elf64_Sym *>(symtabData); + if (symbolIndex >= 0 && uint(symbolIndex) < (shdr->sh_size / shdr->sh_entsize)) + return elf_strptr(elf, shdr->sh_link, symbols[symbolIndex].st_name); + } else if (const auto *shdr = elf32_getshdr(symtab)) { + const auto *symbols = reinterpret_cast<const Elf32_Sym *>(symtabData); + if (symbolIndex >= 0 && uint(symbolIndex) < (shdr->sh_size / shdr->sh_entsize)) + return elf_strptr(elf, shdr->sh_link, symbols[symbolIndex].st_name); + } + return nullptr; +} + static QByteArray fakeSymbolFromSection(Dwfl_Module *mod, Dwarf_Addr addr) { Dwarf_Addr bias = 0; @@ -572,18 +702,27 @@ static QByteArray fakeSymbolFromSection(Dwfl_Module *mod, Dwarf_Addr addr) if (elf_getshdrstrndx(elf, &textSectionIndex) != 0) return {}; - size_t offset = 0; - if (auto shdr = elf64_getshdr(section)) { - offset = shdr->sh_name; - } else if (auto shdr = elf32_getshdr(section)) { - offset = shdr->sh_name; + size_t nameOffset = 0; + size_t entsize = 0; + if (const auto *shdr = elf64_getshdr(section)) { + nameOffset = shdr->sh_name; + entsize = shdr->sh_entsize; + } else if (const auto *shdr = elf32_getshdr(section)) { + nameOffset = shdr->sh_name; + entsize = shdr->sh_entsize; } - auto str = elf_strptr(elf, textSectionIndex, offset); + auto str = elf_strptr(elf, textSectionIndex, nameOffset); if (!str || str == QLatin1String(".text")) return {}; - // mark .plt entries etc. by section name, see also: + if (str == QLatin1String(".plt")) { + const auto *pltSymbol = findPltSymbol(elf, addr / entsize); + if (pltSymbol) + return demangle(pltSymbol) + "@plt"; + } + + // mark other entries by section name, see also: // http://www.mail-archive.com/elfutils-devel@sourceware.org/msg00019.html QByteArray sym = str; sym.prepend('<'); |