Files
kotlin-fork/kotlin-native/runtime/src/main/cpp/ExecFormat.cpp
T
Alexander Shabalin 663e843544 [K/N] Refactor allocators ^KT-52130
Merge-request: KT-MR-6135
Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
2022-04-27 14:00:27 +00:00

371 lines
12 KiB
C++

/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ExecFormat.h"
#include "Porting.h"
#include "Types.h"
#if USE_ELF_SYMBOLS
#include <dlfcn.h>
#include <elf.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <vector>
#include "KAssert.h"
namespace {
#if !defined(ELFSIZE)
#error "Define ELFSIZE to 32 or 64"
#endif
#if ELFSIZE == 32
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#elif ELFSIZE == 64
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#else
#error "Impossible ELFSIZE"
#endif
struct SymRecord {
Elf_Sym* symtabBegin;
Elf_Sym* symtabEnd;
char* strtab;
};
typedef KStdVector<SymRecord> SymRecordList;
SymRecordList* symbols = nullptr;
// Unfortunately, symbol tables are stored in ELF sections not mapped
// during regular execution, so we have to map binary ourselves.
Elf_Ehdr* findElfHeader() {
int fd = open("/proc/self/exe", O_RDONLY);
if (fd < 0) return nullptr;
struct stat fd_stat;
if (fstat(fd, &fd_stat) < 0) return nullptr;
void* result = mmap(nullptr, fd_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (result == MAP_FAILED) return nullptr;
return (Elf_Ehdr*)result;
}
void initSymbols() {
RuntimeAssert(symbols == nullptr, "Init twice");
symbols = konanConstructInstance<SymRecordList>();
Elf_Ehdr* ehdr = findElfHeader();
if (ehdr == nullptr) return;
RuntimeAssert(strncmp((const char*)ehdr->e_ident, ELFMAG, SELFMAG) == 0, "Must be an ELF");
char* mapAddress = (char*)ehdr;
Elf_Shdr* shdr = (Elf_Shdr*)(mapAddress + ehdr->e_shoff);
for (int i = 0; i < ehdr->e_shnum; i++) {
if (shdr[i].sh_type == SHT_SYMTAB) { // Static symbol table.
SymRecord record;
record.symtabBegin = (Elf_Sym*)(mapAddress + shdr[i].sh_offset);
record.symtabEnd = (Elf_Sym*)((char*)record.symtabBegin + shdr[i].sh_size);
record.strtab = (char *)(mapAddress + shdr[shdr[i].sh_link].sh_offset);
symbols->push_back(record);
}
if (shdr[i].sh_type == SHT_DYNSYM) { // Dynamic symbol table.
SymRecord record;
record.symtabBegin = (Elf_Sym*)(mapAddress + shdr[i].sh_offset);
record.symtabEnd = (Elf_Sym*)((char*)record.symtabBegin + shdr[i].sh_size);
record.strtab = (char*)(mapAddress + shdr[shdr[i].sh_link].sh_offset);
symbols->push_back(record);
}
}
}
const char* addressToSymbol(const void* address, ptrdiff_t& resultOffset) {
if (address == nullptr) return nullptr;
// First, look up in dynamically loaded symbols.
Dl_info info;
if (dladdr(address, &info) != 0 && info.dli_sname != nullptr) {
resultOffset = reinterpret_cast<ptrdiff_t>(address) - reinterpret_cast<ptrdiff_t>(info.dli_saddr);
return info.dli_sname;
}
// Otherwise, consult symbol table of the file.
if (symbols == nullptr) {
initSymbols();
}
unsigned long addressValue = (unsigned long)address;
for (auto record : *symbols) {
auto begin = record.symtabBegin;
auto end = record.symtabEnd;
while (begin < end) {
// st_value is load address adjusted.
if (addressValue >= begin->st_value && addressValue < begin->st_value + begin->st_size) {
resultOffset = addressValue - begin->st_value;
return &record.strtab[begin->st_name];
}
begin++;
}
}
return nullptr;
}
} // namespace
extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize, ptrdiff_t &resultOffset) {
const char* result = addressToSymbol(address, resultOffset);
if (result == nullptr) {
return false;
} else {
strncpy(resultBuffer, result, resultBufferSize);
resultBuffer[resultBufferSize - 1] = '\0';
return true;
}
}
#elif USE_PE_COFF_SYMBOLS
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include "KAssert.h"
namespace {
static void* mapModuleFile(HMODULE hModule) {
DWORD bufferLength = 64;
wchar_t* buffer = nullptr;
for (;;) {
auto newBuffer = (wchar_t*)konanAllocMemory(sizeof(wchar_t) * bufferLength);
RuntimeAssert(newBuffer != nullptr, "Out of memory");
if (buffer != nullptr) {
konanFreeMemory(buffer);
}
buffer = newBuffer;
DWORD res = GetModuleFileNameW(hModule, buffer, bufferLength);
if (res != 0 && res < bufferLength) {
break;
}
const int MAX_BUFFER_SIZE = 32768; // Max path length + 1.
if (res == bufferLength && bufferLength < MAX_BUFFER_SIZE) {
// Buffer is too small, continue:
bufferLength *= 2;
continue;
}
// Invalid result.
konanFreeMemory(buffer);
return nullptr;
}
HANDLE hFile = CreateFileW(
/* lpFileName = */ buffer,
/* dwDesiredAccess = */ GENERIC_READ,
/* dwShareMode = */ FILE_SHARE_READ,
/* lpSecurityAttributes = */ nullptr,
/* dwCreationDisposition = */ OPEN_EXISTING,
/* dwFlagsAndAttributes = */ FILE_ATTRIBUTE_NORMAL,
/* hTemplateFile = */ nullptr
);
konanFreeMemory(buffer);
if (hFile == INVALID_HANDLE_VALUE) {
// Can't open module file.
return nullptr;
}
HANDLE hFileMappingObject = CreateFileMapping(
hFile,
/* lpAttributes = */ nullptr,
/* flProtect = */ PAGE_READONLY,
/* dwMaximumSizeHigh = */ 0,
/* dwMaximumSizeLow = */ 0,
/* lpName = */ nullptr
);
if (hFileMappingObject == nullptr) {
// Can't create file mapping.
CloseHandle(hFile);
return nullptr;
}
LPVOID mapAddress = MapViewOfFile(
hFileMappingObject,
/* dwDesiredAccess = */ FILE_MAP_READ,
/* dwFileOffsetHigh = */ 0,
/* dwFileOffsetLow = */ 0,
/* dwNumberOfBytesToMap = */ 0
);
if (mapAddress == nullptr) {
// Failed to create map view.
CloseHandle(hFileMappingObject);
CloseHandle(hFile);
return nullptr;
}
return mapAddress;
}
class SymbolTable {
private:
char* imageBase = nullptr;
IMAGE_SECTION_HEADER* sectionHeaders = nullptr;
IMAGE_SYMBOL* symbols = nullptr;
DWORD numberOfSymbols = 0;
// Note: it doesn't free resources yet.
~SymbolTable() {}
static const int SYMBOL_SHORT_NAME_LENGTH = 8;
void getSymbolName(IMAGE_SYMBOL* sym, char* resultBuffer, size_t resultBufferSize) {
if (sym->N.Name.Short != 0) {
// ShortName is not zero-terminated if its length exactly equals SYMBOL_SHORT_NAME_LENGTH.
// Copy it to the buffer and zero-terminate explicitly:
size_t bytesToCopy = SYMBOL_SHORT_NAME_LENGTH;
if (bytesToCopy > resultBufferSize - 1) bytesToCopy = resultBufferSize - 1;
memcpy(resultBuffer, sym->N.ShortName, bytesToCopy);
resultBuffer[bytesToCopy] = '\0';
} else {
const char* strTable = (const char*)(symbols + numberOfSymbols);
const char* result = strTable + sym->N.Name.Long;
strncpy(resultBuffer, result, resultBufferSize);
resultBuffer[resultBufferSize - 1] = '\0';
}
}
const void* getSymbolAddress(IMAGE_SYMBOL* symbol) {
IMAGE_SECTION_HEADER* sectionHeader = &sectionHeaders[symbol->SectionNumber - 1];
return (const void*)(imageBase + sectionHeader->VirtualAddress + symbol->Value);
}
IMAGE_SYMBOL* findFunctionSymbol(const void* address) {
for (DWORD i = 0; i < numberOfSymbols; ++i) {
IMAGE_SYMBOL* symbol = &symbols[i];
if (symbol->Type == 0x20 && address == getSymbolAddress(symbol)) {
return symbol;
}
}
return nullptr;
}
public:
explicit SymbolTable(HMODULE hModule) {
imageBase = (char*)hModule;
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)imageBase;
RuntimeAssert(dosHeader->e_magic == IMAGE_DOS_SIGNATURE, "PE executable e_magic mismatch");
IMAGE_NT_HEADERS* ntHeaders = (IMAGE_NT_HEADERS*)(imageBase + dosHeader->e_lfanew);
RuntimeAssert(ntHeaders->Signature == IMAGE_NT_SIGNATURE, "PE executable NT signature mismatch");
IMAGE_FILE_HEADER* fileHeader = &ntHeaders->FileHeader;
sectionHeaders = (IMAGE_SECTION_HEADER*)(((char*)(fileHeader + 1)) + fileHeader->SizeOfOptionalHeader);
if (fileHeader->PointerToSymbolTable == 0 || fileHeader->NumberOfSymbols == 0) {
// No symbols.
return;
}
// Symbol table doesn't get mapped to the memory, so we have to load it ourselves:
char* mappedModuleFile = (char*)mapModuleFile(hModule);
if (mappedModuleFile != nullptr) {
symbols = (IMAGE_SYMBOL*)(mappedModuleFile + fileHeader->PointerToSymbolTable);
numberOfSymbols = fileHeader->NumberOfSymbols;
}
}
bool functionAddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize, ptrdiff_t &resultOffset) {
IMAGE_SYMBOL* symbol = findFunctionSymbol(address);
if (symbol == nullptr) {
return false;
} else {
resultOffset = reinterpret_cast<ptrdiff_t>(address) - reinterpret_cast<ptrdiff_t>(getSymbolAddress(symbol));
getSymbolName(symbol, resultBuffer, resultBufferSize);
return true;
}
}
};
SymbolTable* theExeSymbolTable = nullptr;
} // namespace
extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize, ptrdiff_t &resultOffset) {
if (theExeSymbolTable == nullptr) {
// Note: do not protecting the lazy initialization by critical sections for simplicity;
// this doesn't have any serious consequences.
HMODULE hModule = nullptr;
int rv = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&AddressToSymbol), &hModule);
RuntimeAssert(rv != 0, "GetModuleHandleExW fails");
theExeSymbolTable = konanConstructInstance<SymbolTable>(hModule);
}
return theExeSymbolTable->functionAddressToSymbol(address, resultBuffer, resultBufferSize, resultOffset);
}
#elif __has_include("dlfcn.h")
#include <dlfcn.h>
extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize, ptrdiff_t &resultOffset) {
if (address == nullptr) return false;
Dl_info info;
if (dladdr(address, &info) == 0) return false;
const char *result = nullptr;
char symbuf[20];
if (info.dli_sname) {
result = info.dli_sname;
resultOffset = reinterpret_cast<ptrdiff_t>(address) - reinterpret_cast<ptrdiff_t>(info.dli_saddr);
} else if (info.dli_fname) {
result = info.dli_fname;
resultOffset = reinterpret_cast<ptrdiff_t>(address) - reinterpret_cast<ptrdiff_t>(info.dli_fbase);
} else if (0 < konan::snprintf(symbuf, sizeof(symbuf), "%p", info.dli_saddr)) {
result = symbuf;
resultOffset = reinterpret_cast<ptrdiff_t>(address) - reinterpret_cast<ptrdiff_t>(info.dli_saddr);
} else {
resultOffset = reinterpret_cast<ptrdiff_t>(address);
}
if (result == nullptr) {
return false;
} else {
strncpy(resultBuffer, result, resultBufferSize);
resultBuffer[resultBufferSize - 1] = '\0';
return true;
}
}
#else
extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize, ptrdiff_t &resultOffset) {
return false;
}
#endif // USE_ELF_SYMBOLS