blob: 01b2e912bf71ed22a8684097f7da80ed5beed8ec [file] [log] [blame]
//===--- StaticBinaryELF.cpp ----------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Parse a static ELF binary to implement lookupSymbol() address lookup.
//
//===----------------------------------------------------------------------===//
#if defined(__ELF__) && defined(__linux__)
#include "ImageInspection.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "swift/Basic/Lazy.h"
#include <cassert>
#include <string>
#include <vector>
#include <elf.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <linux/limits.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
using namespace std;
using namespace llvm;
#if __POINTER_WIDTH__ == 64
#define ELFCLASS ELFCLASS64
typedef Elf64_Ehdr Elf_Ehdr;
typedef Elf64_Shdr Elf_Shdr;
typedef Elf64_Phdr Elf_Phdr;
typedef Elf64_Addr Elf_Addr;
typedef Elf64_Word Elf_Word;
typedef Elf64_Sym Elf_Sym;
typedef Elf64_Section Elf_Section;
#define ELF_ST_TYPE(x) ELF64_ST_TYPE(x)
#else
#define ELFCLASS ELFCLASS32
typedef Elf32_Ehdr Elf_Ehdr;
typedef Elf32_Shdr Elf_Shdr;
typedef Elf32_Phdr Elf_Phdr;
typedef Elf32_Addr Elf_Addr;
typedef Elf32_Word Elf_Word;
typedef Elf32_Sym Elf_Sym;
typedef Elf32_Section Elf_Section;
#define ELF_ST_TYPE(x) ELF32_ST_TYPE(x)
#endif
extern const Elf_Ehdr elfHeader asm("__ehdr_start");
class StaticBinaryELF {
private:
// mmap a section of a file that might not be page aligned.
class Mapping {
public:
void *mapping;
size_t mapLength;
off_t diff;
Mapping(int fd, size_t fileSize, off_t offset, size_t length) {
if (fd < 0 || offset + length > fileSize) {
mapping = nullptr;
mapLength = 0;
diff = 0;
return;
}
long pageSize = sysconf(_SC_PAGESIZE);
long pageMask = ~(pageSize - 1);
off_t alignedOffset = offset & pageMask;
diff = (offset - alignedOffset);
mapLength = diff + length;
mapping = mmap(nullptr, mapLength, PROT_READ, MAP_PRIVATE, fd,
alignedOffset);
if (mapping == MAP_FAILED) {
mapping = nullptr;
mapLength = 0;
diff = 0;
}
}
template<typename T>
ArrayRef<T> data() {
size_t elements = (mapLength - diff) / sizeof(T);
const T *data = reinterpret_cast<T *>(reinterpret_cast<char *>(mapping)
+ diff);
return ArrayRef<T>(data, elements);
}
~Mapping() {
if (mapping && mapLength > 0) {
munmap(mapping, mapLength);
}
}
};
string fullPathName;
const Elf_Phdr *programHeaders = nullptr;
Optional<Mapping> symbolTable;
Optional<Mapping> stringTable;
public:
StaticBinaryELF() {
getExecutablePathName();
programHeaders = reinterpret_cast<const Elf_Phdr *>(getauxval(AT_PHDR));
if (programHeaders == nullptr) {
return;
}
// If an interpreter is set in the program headers then this is a
// dynamic executable and therefore not valid.
for (size_t idx = 0; idx < elfHeader.e_phnum; idx++) {
if (programHeaders[idx].p_type == PT_INTERP) {
programHeaders = nullptr;
return;
}
}
if (!fullPathName.empty()) {
mmapExecutable();
}
}
const char *getPathName() {
return fullPathName.empty() ? nullptr : fullPathName.c_str();
}
void *getSectionLoadAddress(const void *addr) {
if (programHeaders) {
auto searchAddr = reinterpret_cast<Elf_Addr>(addr);
for (size_t idx = 0; idx < elfHeader.e_phnum; idx++) {
auto header = &programHeaders[idx];
if (header->p_type == PT_LOAD && searchAddr >= header->p_vaddr
&& searchAddr <= (header->p_vaddr + header->p_memsz)) {
return reinterpret_cast<void *>(header->p_vaddr);
}
}
}
return nullptr;
}
// Lookup a function symbol by address.
const Elf_Sym *findSymbol(const void *addr) {
if (symbolTable.hasValue()) {
auto searchAddr = reinterpret_cast<Elf_Addr>(addr);
auto symbols = symbolTable->data<Elf_Sym>();
for (size_t idx = 0; idx < symbols.size(); idx++) {
auto symbol = &symbols[idx];
if (ELF_ST_TYPE(symbol->st_info) == STT_FUNC
&& searchAddr >= symbol->st_value
&& searchAddr < (symbol->st_value + symbol->st_size)) {
return symbol;
}
}
}
return nullptr;
}
const char *symbolName(const Elf_Sym *symbol) {
if (stringTable.hasValue()) {
auto strings = stringTable->data<char>();
if (symbol->st_name < strings.size()) {
return &strings[symbol->st_name];
}
}
return nullptr;
}
private:
// This is Linux specific - find the full path of the executable
// by looking in /proc/self/maps for a mapping holding the current
// address space. For a static binary it should only be mapping one
// file anyway. Don't use /proc/self/exe as the symlink will be removed
// if the main thread terminates - see proc(5).
void getExecutablePathName() {
uintptr_t address = (uintptr_t)&elfHeader;
FILE *fp = fopen("/proc/self/maps", "r");
if (!fp) {
perror("Unable to open /proc/self/maps");
} else {
char *line = nullptr;
size_t size = 0;
// Format is: addrLo-addrHi perms offset dev inode pathname.
// If the executable has been deleted the last column will be '(deleted)'.
StringRef deleted = StringRef("(deleted)");
while (getdelim(&line, &size, '\n', fp) != -1) {
StringRef entry = StringRef(line).rsplit('\n').first;
auto addrRange = entry.split(' ').first.split('-');
unsigned long long low = strtoull(addrRange.first.str().c_str(),
nullptr, 16);
if (low == 0 || low > UINTPTR_MAX || address < (uintptr_t)low) {
continue;
}
unsigned long long high = strtoull(addrRange.second.str().c_str(),
nullptr, 16);
if (high == 0 || high > UINTPTR_MAX || address > (uintptr_t)high) {
continue;
}
auto fname = entry.split('/').second;
if (fname.empty() || fname.endswith(deleted)) {
continue;
}
fullPathName = "/" + fname.str();
break;
}
if (line) {
free(line);
}
fclose(fp);
}
}
// Parse the ELF binary using mmap for the section headers, symbol table and
// string table.
void mmapExecutable() {
struct stat buf;
int fd = open(fullPathName.c_str(), O_RDONLY);
if (fd < 0) {
return;
}
if (fstat(fd, &buf) != 0) {
close(fd);
return;
}
// Map in the section headers.
size_t sectionHeadersSize = (elfHeader.e_shentsize * elfHeader.e_shnum);
Mapping sectionHeaders = Mapping(fd, buf.st_size, elfHeader.e_shoff,
sectionHeadersSize);
if (sectionHeaders.mapping) {
auto headers = sectionHeaders.data<Elf_Shdr>();
auto section = findSectionHeader(headers, SHT_SYMTAB);
if (section) {
assert(section->sh_entsize == sizeof(Elf_Sym));
symbolTable.emplace(fd, buf.st_size, section->sh_offset,
section->sh_size);
if (symbolTable->mapping == nullptr) {
symbolTable.reset();
}
}
section = findSectionHeader(headers, SHT_STRTAB);
if (section) {
stringTable.emplace(fd, buf.st_size, section->sh_offset,
section->sh_size);
if (stringTable->mapping == nullptr) {
stringTable.reset();
}
}
}
close(fd);
return;
}
// Find the section header of a specified type in the section headers table.
const Elf_Shdr *findSectionHeader(ArrayRef<Elf_Shdr> headers,
Elf_Word sectionType) {
assert(elfHeader.e_shnum == headers.size());
for (size_t idx = 0; idx < headers.size(); idx++) {
if (idx == elfHeader.e_shstrndx) {
continue;
}
auto header = &headers[idx];
if (header->sh_type == sectionType) {
if (header->sh_entsize > 0 && header->sh_size % header->sh_entsize) {
fprintf(stderr,
"section size is not a multiple of entrysize (%ld/%ld)\n",
header->sh_size, header->sh_entsize);
return nullptr;
}
return header;
}
}
return nullptr;
}
};
static swift::Lazy<StaticBinaryELF> TheBinary;
int
swift::lookupSymbol(const void *address, SymbolInfo *info) {
// The pointers returned point into the mmap()'d binary so keep the
// object once instantiated.
auto &binary = TheBinary.get();
info->fileName = binary.getPathName();
info->baseAddress = binary.getSectionLoadAddress(address);
auto symbol = binary.findSymbol(address);
if (symbol != nullptr) {
info->symbolAddress = reinterpret_cast<void *>(symbol->st_value);
info->symbolName = binary.symbolName(symbol);
} else {
info->symbolAddress = nullptr;
info->symbolName = nullptr;
}
return 1;
}
#endif // defined(__ELF__) && defined(__linux__)