this handler sometimes doesnt work

This commit is contained in:
altalk23 2023-10-05 15:09:42 +03:00
parent a77c33974b
commit 66e36b3a38
3 changed files with 618 additions and 4 deletions
loader/src/platform/android

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2019 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#pragma once
#include <unistd.h>
class ScopedFd final {
public:
explicit ScopedFd(int fd) : fd_(fd) {
}
ScopedFd() : fd_(-1) {
}
~ScopedFd() {
reset(-1);
}
void reset(int fd = -1) {
fd_ = fd;
}
int get() const {
return fd_;
}
private:
int fd_;
};

View file

@ -0,0 +1,191 @@
/*
* Copyright (C) 2012 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <dlfcn.h>
#include <execinfo.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <unwind.h>
#include <sys/syscall.h>
#include <linux/memfd.h>
#include "ScopedFd.hpp"
struct StackState {
void** frames;
int frame_count;
int cur_frame = 0;
StackState(void** frames, int frame_count) : frames(frames), frame_count(frame_count) {}
};
static _Unwind_Reason_Code TraceFunction(_Unwind_Context* context, void* arg) {
// The instruction pointer is pointing at the instruction after the return
// call on all architectures.
// Modify the pc to point at the real function.
uintptr_t ip = _Unwind_GetIP(context);
if (ip != 0) {
#if defined(__arm__)
// If the ip is suspiciously low, do nothing to avoid a segfault trying
// to access this memory.
if (ip >= 4096) {
// Check bits [15:11] of the first halfword assuming the instruction
// is 32 bits long. If the bits are any of these values, then our
// assumption was correct:
// b11101
// b11110
// b11111
// Otherwise, this is a 16 bit instruction.
uint16_t value = (*reinterpret_cast<uint16_t*>(ip - 2)) >> 11;
if (value == 0x1f || value == 0x1e || value == 0x1d) {
ip -= 4;
} else {
ip -= 2;
}
}
#elif defined(__aarch64__)
// All instructions are 4 bytes long, skip back one instruction.
ip -= 4;
#elif defined(__i386__) || defined(__x86_64__)
// It's difficult to decode exactly where the previous instruction is,
// so subtract 1 to estimate where the instruction lives.
ip--;
#endif
}
StackState* state = static_cast<StackState*>(arg);
state->frames[state->cur_frame++] = reinterpret_cast<void*>(ip);
return (state->cur_frame >= state->frame_count) ? _URC_END_OF_STACK : _URC_NO_REASON;
}
int backtrace(void** buffer, int size) {
if (size <= 0) {
return 0;
}
StackState state(buffer, size);
_Unwind_Backtrace(TraceFunction, &state);
return state.cur_frame;
}
void backtrace_symbols_fd(void* const* buffer, int size, int fd);
char** backtrace_symbols(void* const* buffer, int size) {
if (size <= 0) {
return nullptr;
}
// Do this calculation first in case the user passes in a bad value.
size_t ptr_size;
if (__builtin_mul_overflow(sizeof(char*), size, &ptr_size)) {
return nullptr;
}
ScopedFd fd(syscall(SYS_memfd_create, "backtrace_symbols_fd", MFD_CLOEXEC));
// ScopedFd fd(memfd_create("backtrace_symbols_fd", MFD_CLOEXEC));
if (fd.get() == -1) {
return nullptr;
}
backtrace_symbols_fd(buffer, size, fd.get());
// Get the size of the file.
off_t file_size = lseek(fd.get(), 0, SEEK_END);
if (file_size <= 0) {
return nullptr;
}
// The interface for backtrace_symbols indicates that only the single
// returned pointer must be freed by the caller. Therefore, allocate a
// buffer that includes the memory for the strings and all of the pointers.
// Add one byte at the end just in case the file didn't end with a '\n'.
size_t symbol_data_size;
if (__builtin_add_overflow(ptr_size, file_size, &symbol_data_size) ||
__builtin_add_overflow(symbol_data_size, 1, &symbol_data_size)) {
return nullptr;
}
uint8_t* symbol_data = reinterpret_cast<uint8_t*>(malloc(symbol_data_size));
if (symbol_data == nullptr) {
return nullptr;
}
// Copy the string data into the buffer.
char* cur_string = reinterpret_cast<char*>(&symbol_data[ptr_size]);
// If this fails, the read won't read back the correct number of bytes.
lseek(fd.get(), 0, SEEK_SET);
ssize_t num_read = read(fd.get(), cur_string, file_size);
fd.reset(-1);
if (num_read != file_size) {
free(symbol_data);
return nullptr;
}
// Make sure the last character in the file is '\n'.
if (cur_string[file_size] != '\n') {
cur_string[file_size++] = '\n';
}
for (int i = 0; i < size; i++) {
(reinterpret_cast<char**>(symbol_data))[i] = cur_string;
cur_string = strchr(cur_string, '\n');
if (cur_string == nullptr) {
free(symbol_data);
return nullptr;
}
cur_string[0] = '\0';
cur_string++;
}
return reinterpret_cast<char**>(symbol_data);
}
// This function should do no allocations if possible.
void backtrace_symbols_fd(void* const* buffer, int size, int fd) {
if (size <= 0 || fd < 0) {
return;
}
for (int frame_num = 0; frame_num < size; frame_num++) {
void* address = buffer[frame_num];
Dl_info info;
if (dladdr(address, &info) != 0) {
if (info.dli_fname != nullptr) {
write(fd, info.dli_fname, strlen(info.dli_fname));
}
if (info.dli_sname != nullptr) {
dprintf(fd, "(%s+0x%" PRIxPTR ") ", info.dli_sname,
reinterpret_cast<uintptr_t>(address) - reinterpret_cast<uintptr_t>(info.dli_saddr));
} else {
dprintf(fd, "(+%p) ", info.dli_saddr);
}
}
dprintf(fd, "[%p]\n", address);
}
}

View file

@ -2,16 +2,384 @@
#ifdef GEODE_IS_ANDROID
ghc::filesystem::path crashlog::getCrashLogDirectory() {
return geode::dirs::getSaveDir();
using namespace geode::prelude;
#include <Geode/utils/string.hpp>
#include <array>
#include <thread>
#include <ghc/fs_fwd.hpp>
#include <execinfo.h>
#include <dlfcn.h>
#include <cxxabi.h>
#include <algorithm>
#include <link.h>
#include <unwind.h>
#include "backtrace/execinfo.hpp"
static constexpr size_t FRAME_SIZE = 64;
static std::mutex s_mutex;
static std::condition_variable s_cv;
static int s_signal = 0;
static siginfo_t* s_siginfo = nullptr;
static ucontext_t* s_context = nullptr;
static size_t s_backtraceSize = 0;
static std::array<void*, FRAME_SIZE> s_backtrace;
static std::string_view getSignalCodeString() {
switch(s_signal) {
case SIGSEGV: return "SIGSEGV: Segmentation Fault";
case SIGINT: return "SIGINT: Interactive attention signal, (usually ctrl+c)";
case SIGFPE:
switch(s_siginfo->si_code) {
case FPE_INTDIV: return "SIGFPE: (integer divide by zero)";
case FPE_INTOVF: return "SIGFPE: (integer overflow)";
case FPE_FLTDIV: return "SIGFPE: (floating-point divide by zero)";
case FPE_FLTOVF: return "SIGFPE: (floating-point overflow)";
case FPE_FLTUND: return "SIGFPE: (floating-point underflow)";
case FPE_FLTRES: return "SIGFPE: (floating-point inexact result)";
case FPE_FLTINV: return "SIGFPE: (floating-point invalid operation)";
case FPE_FLTSUB: return "SIGFPE: (subscript out of range)";
default: return "SIGFPE: Arithmetic Exception";
}
case SIGILL:
switch(s_siginfo->si_code) {
case ILL_ILLOPC: return "SIGILL: (illegal opcode)";
case ILL_ILLOPN: return "SIGILL: (illegal operand)";
case ILL_ILLADR: return "SIGILL: (illegal addressing mode)";
case ILL_ILLTRP: return "SIGILL: (illegal trap)";
case ILL_PRVOPC: return "SIGILL: (privileged opcode)";
case ILL_PRVREG: return "SIGILL: (privileged register)";
case ILL_COPROC: return "SIGILL: (coprocessor error)";
case ILL_BADSTK: return "SIGILL: (internal stack error)";
default: return "SIGILL: Illegal Instruction";
}
case SIGTERM: return "SIGTERM: a termination request was sent to the program";
case SIGABRT: return "SIGABRT: usually caused by an abort() or assert()";
case SIGBUS: return "SIGBUS: Bus error (bad memory access)";
default: return "Unknown signal code";
}
}
static std::string getImageName(Elf32_Phdr const* image) {
if (image == nullptr) {
return "<Unknown>";
}
std::string imageName;// = image->imageFilePath;
if (imageName.empty()) {
imageName = "<Unknown>";
}
return imageName;
}
// static std::vector<struct dyld_image_info const*> getAllImages() {
// std::vector<struct dyld_image_info const*> images;
// struct task_dyld_info dyldInfo;
// mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
// if (task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&dyldInfo, &count) == KERN_SUCCESS) {
// struct dyld_all_image_infos* imageInfos = (struct dyld_all_image_infos*)dyldInfo.all_image_info_addr;
// for (size_t i = 0; i < imageInfos->infoArrayCount; ++i) {
// images.push_back(&imageInfos->infoArray[i]);
// }
// }
// return images;
// }
static Elf32_Phdr const* imageFromAddress(void const* addr) {
if (addr == nullptr) {
return nullptr;
}
// auto loadedImages = getAllImages();
// std::sort(loadedImages.begin(), loadedImages.end(), [](auto const a, auto const b) {
// return (uintptr_t)a->imageLoadAddress < (uintptr_t)b->imageLoadAddress;
// });
// auto iter = std::upper_bound(loadedImages.begin(), loadedImages.end(), addr, [](auto const addr, auto const image) {
// return (uintptr_t)addr < (uintptr_t)image->imageLoadAddress;
// });
// if (iter == loadedImages.begin()) {
// return nullptr;
// }
// --iter;
// auto image = *iter;
// // auto imageSize = getImageSize((struct mach_header_64 const*)image->imageLoadAddress);
// auto imageAddress = (uintptr_t)image->imageLoadAddress;
// if ((uintptr_t)addr >= imageAddress/* && (uintptr_t)addr < imageAddress + imageSize*/) {
// return image;
// }
return nullptr;
}
static Mod* modFromAddress(void const* addr) {
if (addr == nullptr) {
return nullptr;
}
// auto image = imageFromAddress(addr);
// if (image == nullptr) {
// return nullptr;
// }
// ghc::filesystem::path imagePath = getImageName(image);
// if (!ghc::filesystem::exists(imagePath)) {
// return nullptr;
// }
// auto geodePath = dirs::getGameDir() / "Frameworks" / "Geode.dylib";
// if (ghc::filesystem::equivalent(imagePath, geodePath)) {
// return Mod::get();
// }
// for (auto& mod : Loader::get()->getAllMods()) {
// if (!mod->isEnabled() || !ghc::filesystem::exists(mod->getBinaryPath())) {
// continue;
// }
// if (ghc::filesystem::equivalent(imagePath, mod->getBinaryPath())) {
// return mod;
// }
// }
return nullptr;
}
static std::string getInfo(void* address, Mod* faultyMod) {
std::stringstream stream;
stream << "Faulty Lib: " << getImageName(imageFromAddress(address)) << "\n";
stream << "Faulty Mod: " << (faultyMod ? faultyMod->getID() : "<Unknown>") << "\n";
stream << "Instruction Address: " << address << "\n";
stream << "Signal Code: " << std::hex << s_signal << " (" << getSignalCodeString() << ")" << std::dec << "\n";
return stream.str();
}
static struct sigaction oldActionSEGV;
static struct sigaction oldActionBUS;
static struct sigaction oldActionILL;
extern "C" void signalHandler(int signal, siginfo_t* signalInfo, void* vcontext) {
auto context = reinterpret_cast<ucontext_t*>(vcontext);
s_backtraceSize = 0;
// s_backtraceSize = backtrace(s_backtrace.data(), FRAME_SIZE);
// for some reason this is needed, dont ask me why
// s_backtrace[1] = reinterpret_cast<void*>(context->uc_mcontext.fault_address);
// if (s_backtraceSize < FRAME_SIZE) {
// s_backtrace[s_backtraceSize] = nullptr;
// }
{
std::unique_lock<std::mutex> lock(s_mutex);
s_signal = signal;
s_siginfo = signalInfo;
s_context = context;
}
s_cv.notify_all();
std::unique_lock<std::mutex> lock(s_mutex);
s_cv.wait(lock, [] { return s_signal == 0; });
// std::_Exit(EXIT_FAILURE);
switch (signal) {
case SIGSEGV:
sigaction(signal, &oldActionSEGV, nullptr);
oldActionSEGV.sa_sigaction(signal, signalInfo, vcontext);
break;
case SIGBUS:
sigaction(signal, &oldActionBUS, nullptr);
oldActionBUS.sa_sigaction(signal, signalInfo, vcontext);
break;
case SIGILL:
sigaction(signal, &oldActionILL, nullptr);
oldActionILL.sa_sigaction(signal, signalInfo, vcontext);
break;
}
}
static std::string getStacktrace() {
std::stringstream stacktrace;
if (s_backtraceSize == 0) {
return stacktrace.str();
}
auto messages = backtrace_symbols(s_backtrace.data(), s_backtraceSize);
if (s_backtraceSize < FRAME_SIZE) {
messages[s_backtraceSize] = nullptr;
}
for (int i = 1; i < s_backtraceSize; ++i) {
auto message = std::string(messages[i]);
// TODO: parse the message
stacktrace << message << "\n";
// auto stream = std::stringstream(message);
// int index;
// std::string binary;
// uintptr_t address;
// std::string function;
// uintptr_t offset;
// std::string line;
// stream >> index;
// if (!lines.eof()) {
// std::getline(lines, line);
// }
// std::getline(stream, binary);
// auto cutoff = binary.find("0x");
// stream = std::stringstream(binary.substr(cutoff));
// binary = geode::utils::string::trim(binary.substr(0, cutoff));
// stream >> std::hex >> address >> std::dec;
// if (!line.empty()) {
// // log::debug("address: {}", address);
// auto image = imageFromAddress(reinterpret_cast<void*>(address));
// // log::debug("image: {}", image);
// stacktrace << " - " << std::showbase << std::hex;
// if (image) {
// auto baseAddress = image->imageLoadAddress;
// auto imageName = getImageName(image);
// stacktrace << imageName << " + " << (address - (uintptr_t)baseAddress);
// }
// else {
// stacktrace << address;
// }
// stacktrace << std::dec;
// stacktrace << ": " << line << "\n";
// }
// else {
// std::getline(stream, function);
// cutoff = function.find("+");
// stream = std::stringstream(function.substr(cutoff));
// stream >> offset;
// function = geode::utils::string::trim(function.substr(0, cutoff));
// {
// int status;
// auto demangle = abi::__cxa_demangle(function.c_str(), 0, 0, &status);
// if (status == 0) {
// function = demangle;
// }
// free(demangle);
// }
// stacktrace << "- " << binary;
// stacktrace << " @ " << std::showbase << std::hex << address << std::dec;
// stacktrace << " (" << function << " + " << offset << ")\n";
// }
}
free(messages);
return stacktrace.str();
}
static std::string getRegisters() {
std::stringstream registers;
auto context = s_context;
auto& ctx = context->uc_mcontext;
// geez
registers << std::showbase << std::hex /*<< std::setfill('0') << std::setw(16) */;
registers << "r0: " << ctx.arm_r0 << "\n";
registers << "r1: " << ctx.arm_r1 << "\n";
registers << "r2: " << ctx.arm_r2 << "\n";
registers << "r3: " << ctx.arm_r3 << "\n";
registers << "r4: " << ctx.arm_r4 << "\n";
registers << "r5: " << ctx.arm_r5 << "\n";
registers << "r6: " << ctx.arm_r6 << "\n";
registers << "r7: " << ctx.arm_r7 << "\n";
registers << "r8: " << ctx.arm_r8 << "\n";
registers << "r9: " << ctx.arm_r9 << "\n";
registers << "r10: " << ctx.arm_r10 << "\n";
registers << "r11: " << ctx.arm_fp << "\n";
registers << "r12: " << ctx.arm_ip << "\n";
registers << "sp: " << ctx.arm_sp << "\n";
registers << "lr: " << ctx.arm_lr << "\n";
registers << "pc: " << ctx.arm_pc << "\n";
registers << "cpsr: " << ctx.arm_cpsr << "\n";
return registers.str();
}
static void handlerThread() {
std::unique_lock<std::mutex> lock(s_mutex);
s_cv.wait(lock, [] { return s_signal != 0; });
auto signalAddress = reinterpret_cast<void*>(s_context->uc_mcontext.fault_address);
// Mod* faultyMod = nullptr;
// for (int i = 1; i < s_backtraceSize; ++i) {
// auto mod = modFromAddress(s_backtrace[i]);
// if (mod != nullptr) {
// faultyMod = mod;
// break;
// }
// }
Mod* faultyMod = modFromAddress(signalAddress);
auto text = crashlog::writeCrashlog(faultyMod, getInfo(signalAddress, faultyMod), getStacktrace(), getRegisters());
log::error("Geode crashed!\n{}", text);
s_signal = 0;
s_cv.notify_all();
log::debug("Notified");
}
static bool s_lastLaunchCrashed;
bool crashlog::setupPlatformHandler() {
return false;
struct sigaction action;
action.sa_sigaction = &signalHandler;
action.sa_flags = SA_SIGINFO;
sigemptyset(&action.sa_mask);
sigaction(SIGSEGV, &action, &oldActionSEGV);
// I'd rather not track interrupt lol
// sigaction(SIGINT, &action, nullptr);
// sigaction(SIGFPE, &action, nullptr);
sigaction(SIGILL, &action, &oldActionILL);
// sigaction(SIGTERM, &action, nullptr);
// sigaction(SIGABRT, &action, nullptr);
sigaction(SIGBUS, &action, &oldActionBUS);
std::thread(&handlerThread).detach();
auto lastCrashedFile = crashlog::getCrashLogDirectory() / "last-crashed";
if (ghc::filesystem::exists(lastCrashedFile)) {
s_lastLaunchCrashed = true;
try {
ghc::filesystem::remove(lastCrashedFile);
}
catch (...) {
}
}
return true;
}
bool crashlog::didLastLaunchCrash() {
return false;
return s_lastLaunchCrashed;
}
ghc::filesystem::path crashlog::getCrashLogDirectory() {
return dirs::getGeodeDir() / "crashlogs";
}
// ghc::filesystem::path crashlog::getCrashLogDirectory() {
// return geode::dirs::getSaveDir();
// }
// bool crashlog::setupPlatformHandler() {
// return false;
// }
// bool crashlog::didLastLaunchCrash() {
// return false;
// }
#endif