mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
325 lines
9.4 KiB
C++
325 lines
9.4 KiB
C++
//===--- 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;
|
|
|
|
#ifdef __LP64__
|
|
#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__)
|