Support stack traces on Windows (#624)
Use GCC's unwind and load COFF symbols on mingw target.
This commit is contained in:
committed by
GitHub
parent
a5fe494ae3
commit
f2f82ab843
+4
-4
@@ -100,11 +100,11 @@ void setupCompilationFlags() {
|
||||
ext.targetArgs <<
|
||||
[(ext.host):
|
||||
["--sysroot=${gccToolchainDir}/${gnuTriplet}/sysroot",
|
||||
"-DUSE_GCC_UNWIND=1", "-DELFSIZE=64"],
|
||||
"-DUSE_GCC_UNWIND=1", "-DUSE_ELF_SYMBOLS=1", "-DELFSIZE=64"],
|
||||
"raspberrypi":
|
||||
["-target", getTargetArg("raspberrypi"),
|
||||
"-mfpu=vfp", "-mfloat-abi=hard",
|
||||
"-DUSE_GCC_UNWIND=1", "-DELFSIZE=32",
|
||||
"-DUSE_GCC_UNWIND=1", "-DUSE_ELF_SYMBOLS=1", "-DELFSIZE=32",
|
||||
"--sysroot=$raspberrypiSysrootDir",
|
||||
// TODO: those two are hacks.
|
||||
"-I$raspberrypiSysrootDir/usr/include/c++/4.8.3",
|
||||
@@ -115,7 +115,7 @@ void setupCompilationFlags() {
|
||||
ext.targetArgs <<
|
||||
[(ext.host):
|
||||
["-target", "x86_64-w64-mingw32", "--sysroot=${mingwWithLlvmDir}",
|
||||
"-DOMIT_BACKTRACE", "-DKONAN_WINDOWS=1"]]
|
||||
"-DUSE_GCC_UNWIND=1", "-DUSE_PE_COFF_SYMBOLS=1", "-DKONAN_WINDOWS=1"]]
|
||||
} else {
|
||||
ext.host = "macbook"
|
||||
ext.targetArgs <<
|
||||
@@ -135,7 +135,7 @@ void setupCompilationFlags() {
|
||||
"android_arm32":
|
||||
["-target", getTargetArg("android_arm32"),
|
||||
"--sysroot=$android_arm32SysrootDir",
|
||||
"-D__ANDROID__", "-DUSE_GCC_UNWIND=1", "-DELFSIZE=32",
|
||||
"-D__ANDROID__", "-DUSE_GCC_UNWIND=1", "-DUSE_ELF_SYMBOLS=1", "-DELFSIZE=32",
|
||||
// HACKS!
|
||||
"-I$android_arm32SysrootDir/usr/include/c++/4.9.x",
|
||||
"-I$android_arm32SysrootDir/usr/include/c++/4.9.x/arm-linux-androideabi"]
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#ifndef OMIT_BACKTRACE
|
||||
#if USE_GCC_UNWIND
|
||||
// GCC unwinder for backtrace.
|
||||
@@ -95,11 +96,22 @@ _Unwind_Reason_Code unwindCallback(
|
||||
backtrace->skipCount--;
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
unsigned long ip = _Unwind_GetIP(context);
|
||||
const char* symbol = AddressToSymbol(ip);
|
||||
|
||||
#if (__MINGW32__ || __MINGW64__)
|
||||
_Unwind_Ptr address = _Unwind_GetRegionStart(context);
|
||||
#else
|
||||
_Unwind_Ptr address = _Unwind_GetIP(context);
|
||||
#endif
|
||||
|
||||
char symbol[512];
|
||||
if (!AddressToSymbol((const void*)address, symbol, sizeof(symbol))) {
|
||||
// Make empty string:
|
||||
symbol[0] = '\0';
|
||||
}
|
||||
|
||||
char line[512];
|
||||
snprintf(line, sizeof(line) - 1, "%s (0x%lx)",
|
||||
symbol != nullptr ? symbol : "", ip);
|
||||
snprintf(line, sizeof(line) - 1, "%s (%p)",
|
||||
symbol, (void*)(intptr_t)address);
|
||||
backtrace->setNextElement(line);
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if USE_GCC_UNWIND
|
||||
#include "ExecFormat.h"
|
||||
|
||||
#if USE_ELF_SYMBOLS
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <elf.h>
|
||||
@@ -93,14 +95,12 @@ void initSymbols() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" const char* AddressToSymbol(unsigned long address) {
|
||||
if (address == 0) return nullptr;
|
||||
const char* addressToSymbol(const void* address) {
|
||||
if (address == nullptr) return nullptr;
|
||||
|
||||
// First, look up in dynamically loaded symbols.
|
||||
Dl_info info;
|
||||
if (dladdr((const void*)address, &info) != 0 && info.dli_sname != nullptr) {
|
||||
if (dladdr(address, &info) != 0 && info.dli_sname != nullptr) {
|
||||
return info.dli_sname;
|
||||
}
|
||||
|
||||
@@ -109,12 +109,14 @@ extern "C" const char* AddressToSymbol(unsigned long address) {
|
||||
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 (address >= begin->st_value && address < begin->st_value + begin->st_size) {
|
||||
if (addressValue >= begin->st_value && addressValue < begin->st_value + begin->st_size) {
|
||||
return &record.strtab[begin->st_name];
|
||||
}
|
||||
begin++;
|
||||
@@ -123,10 +125,198 @@ extern "C" const char* AddressToSymbol(unsigned long address) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#else
|
||||
} // namespace
|
||||
|
||||
extern "C" const char* AddressToSymbol(unsigned long address) {
|
||||
return nullptr;
|
||||
extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) {
|
||||
const char* result = addressToSymbol(address);
|
||||
if (result == nullptr) {
|
||||
return false;
|
||||
} else {
|
||||
strncpy(resultBuffer, result, resultBufferSize);
|
||||
resultBuffer[resultBufferSize - 1] = '\0';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_GCC_UNWIND
|
||||
#elif USE_PE_COFF_SYMBOLS
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Assert.h"
|
||||
|
||||
namespace {
|
||||
|
||||
static void* mapModuleFile(HMODULE hModule) {
|
||||
int bufferLength = 64;
|
||||
wchar_t* buffer = nullptr;
|
||||
for (;;) {
|
||||
buffer = (wchar_t*)realloc(buffer, sizeof(wchar_t) * bufferLength);
|
||||
RuntimeAssert(buffer != nullptr, "Out of memory");
|
||||
|
||||
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.
|
||||
free(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
|
||||
);
|
||||
free(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 = §ionHeaders[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) {
|
||||
IMAGE_SYMBOL* symbol = findFunctionSymbol(address);
|
||||
if (symbol == nullptr) {
|
||||
return false;
|
||||
} else {
|
||||
getSymbolName(symbol, resultBuffer, resultBufferSize);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
SymbolTable* theExeSymbolTable = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) {
|
||||
if (theExeSymbolTable == nullptr) {
|
||||
// Note: do not protecting the lazy initialization by critical sections for simplicity;
|
||||
// this doesn't have any serious consequences.
|
||||
theExeSymbolTable = new SymbolTable(GetModuleHandle(nullptr));
|
||||
}
|
||||
return theExeSymbolTable->functionAddressToSymbol(address, resultBuffer, resultBufferSize);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
extern "C" bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // USE_ELF_SYMBOLS
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
#ifndef RUNTIME_EXECFORMAT_H
|
||||
#define RUNTIME_EXECFORMAT_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
const char* AddressToSymbol(unsigned long address);
|
||||
bool AddressToSymbol(const void* address, char* resultBuffer, size_t resultBufferSize);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user