Skip to content

Instantly share code, notes, and snippets.

@303248153
Last active December 9, 2017 06:26
Show Gist options
  • Save 303248153/f792ed53a4245b46900d4ce3926888e6 to your computer and use it in GitHub Desktop.
Save 303248153/f792ed53a4245b46900d4ce3926888e6 to your computer and use it in GitHub Desktop.
/* g++ -Wall -Wextra --std=c++14 -g abc.cpp -lbfd && valgrind ./a.out */
#include <bfd.h>
#include <elf.h>
#include <cassert>
#include <cstring>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <array>
#include <algorithm>
struct LoadEntry {
std::size_t fileOffset;
std::size_t virtualAddressStart;
std::size_t virtualAddressEnd;
};
template <class EHdr, class Phdr>
void loadLoadEntriesByElfClass(std::ifstream& file, std::vector<LoadEntry>& entries) {
EHdr header;
file.seekg(0);
if (!file.read((char*)&header, sizeof(header))) {
std::cout << "read header error" << std::endl;
return;
}
std::cout << "program headers: " << header.e_phnum << " at " << header.e_phoff << std::endl;
std::vector<Phdr> programHeaders;
programHeaders.resize(header.e_phnum);
file.seekg(header.e_phoff);
if (!file.read((char*)programHeaders.data(), sizeof(Phdr) * programHeaders.size())) {
std::cout << "read program header error" << std::endl;
return;
}
for (auto& programHeader : programHeaders) {
if (programHeader.p_type == PT_LOAD) {
std::cout << "found load entry" << std::endl;
entries.emplace_back(LoadEntry({
programHeader.p_offset,
programHeader.p_vaddr,
programHeader.p_vaddr + programHeader.p_memsz
}));
}
}
}
void loadLoadEntries(const char* path, std::vector<LoadEntry>& entries) {
std::ifstream file(path);
std::array<char, EI_NIDENT> ident;
if (!file.read(ident.data(), ident.size())) {
std::cout << "read ident error" << std::endl;
return;
}
if (std::memcmp(ident.data(), ELFMAG, SELFMAG) != 0) {
std::cout << "magic not matched" << std::endl;
return;
}
std::size_t elfClass = ident.at(EI_CLASS);
if (elfClass == ELFCLASS32) {
loadLoadEntriesByElfClass<Elf32_Ehdr, Elf32_Phdr>(file, entries);
} else if (elfClass == ELFCLASS64) {
std::cout << "parse 64" << std::endl;
loadLoadEntriesByElfClass<Elf64_Ehdr, Elf64_Phdr>(file, entries);
} else {
std::cout << "unsupported elf class" << std::endl;
return;
}
}
int printFile(const char* path) {
std::vector<LoadEntry> entries;
loadLoadEntries(path, entries);
std::cout << "load entires: " << entries.size() << std::endl;
bfd* file = bfd_openr(path, nullptr);
if (file == nullptr) {
perror("bfd_openr");
return -1;
}
char** matching = nullptr;
if (!bfd_check_format_matches(file, bfd_object, &matching)) {
std::cout << "not object file" << std::endl;
return -1;
}
void* minisyms = nullptr;
unsigned int size = 0;
long count = bfd_read_minisymbols(file, true, &minisyms, &size);
std::cout << "count: " << count << std::endl;
asymbol* store = bfd_make_empty_symbol(file);
auto from = (bfd_byte*)minisyms;
auto to = from + count * size;
std::vector<asymbol*> syms;
for (; from < to; from += size) {
asymbol* sym = bfd_minisymbol_to_symbol(file, true, from, store);
if (sym == nullptr) {
perror("bfd_minisymbol_to_symbol");
return -1;
}
syms.emplace_back(sym);
}
std::sort(syms.begin(), syms.end(), [](const auto& a, const auto& b) {
// sort by symbol value, then by section vma
// see size_forward1 in nm.c in binutils
auto valueA = bfd_asymbol_value(a);
auto valueB = bfd_asymbol_value(b);
if (valueA != valueB) {
return valueA < valueB;
}
auto baseA = bfd_asymbol_base(a); // section->vma
auto baseB = bfd_asymbol_base(b);
if (baseA != baseB) {
return baseA < baseB;
}
return false;
});
std::vector<std::size_t> sizes;
char* target = bfd_get_target(file);
bool isElf32 = std::strncmp(target, "elf32", 5) == 0;
bool isElf64 = std::strncmp(target, "elf64", 5) == 0;
std::cout << "isElf32: " << isElf32 << " isElf64: " << isElf64 << std::endl;
for (auto it = syms.begin(); it < syms.end(); ++it) {
asymbol* sym = *it;
std::size_t size = 0;
if ((sym->flags & (BSF_SECTION_SYM | BSF_SYNTHETIC)) != 0) {
// don't have a full type set of data available, see nm.c for full explanation
} else if (isElf32) {
size = *(std::uint32_t*)(((char*)sym)+sizeof(asymbol)+8);
} else if (isElf64) {
size = *(std::uint64_t*)(((char*)sym)+sizeof(asymbol)+8);
}
std::size_t guestSize = 0;
asymbol* next = (it + 1 < syms.end()) ? *(it + 1) : nullptr;
if (next != nullptr && bfd_asymbol_base(sym) == bfd_asymbol_base(next)) {
guestSize = bfd_asymbol_value(next) - bfd_asymbol_value(sym);
} else {
guestSize = bfd_asymbol_base(sym) +
bfd_section_size(file, bfd_get_section(sym)) -
bfd_asymbol_value(sym);
}
// guestSize may be 0 if two symbol have same address
// guestSize may less than size if it's a variable and file only contains it's first part
// calculate the actual file offset with LOAD entry
std::size_t offset = bfd_asymbol_value(sym);
std::size_t offsetOriginal = offset;
for (auto& entry : entries) {
if (offset >= entry.virtualAddressStart && offset < entry.virtualAddressEnd) {
offset = offset - entry.virtualAddressStart + entry.fileOffset;
break;
}
}
std::cout <<
std::hex << offset << "(" << offsetOriginal << ")" << std::dec << "\t" <<
std::hex << size << std::dec << "\t" <<
std::hex << guestSize << std::dec << "\t" <<
bfd_asymbol_name(sym) << std::endl;
}
free(minisyms);
bfd_close(file);
return 0;
}
int main() {
printFile("/home/ubuntu/Downloads/live-profiler/tests/Build/LiveProfilerTest");
//printFile("/lib/i386-linux-gnu/libpthread.so.0");
//printFile("/lib/x86_64-linux-gnu/libc-2.26.so");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment