| #if KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG |
| |
| #include <dlfcn.h> |
| #include <inttypes.h> |
| #include <mach-o/loader.h> |
| #include <CoreFoundation/CoreFoundation.h> |
| #include "Natives.h" |
| #include "ObjCExceptions.h" |
| #include "Types.h" |
| |
| extern "C" OBJ_GETTER(Kotlin_Throwable_getStackTrace, KRef throwable); |
| |
| // TODO: Consider refactoring to `FormatToSpan`. |
| static void writeStackTraceToBuffer(KRef throwable, char* buffer, unsigned long bufferSize) { |
| if (bufferSize < 2) return; |
| |
| ObjHolder stackTraceHolder; |
| ArrayHeader* stackTrace = Kotlin_Throwable_getStackTrace(throwable, stackTraceHolder.slot())->array(); |
| |
| char* bufferPointer = buffer; |
| unsigned long remainingBytes = bufferSize; |
| |
| *(bufferPointer++) = '('; |
| --remainingBytes; |
| |
| for (uint32_t index = 0; index < stackTrace->count_; ++index) { |
| KNativePtr ptr = *PrimitiveArrayAddressOfElementAt<KNativePtr>(stackTrace, index); |
| int bytes = snprintf(bufferPointer, remainingBytes, "0x%" PRIxPTR " ", reinterpret_cast<uintptr_t>(ptr)); |
| |
| if (bytes < 0 || static_cast<unsigned long>(bytes) >= remainingBytes) { |
| break; |
| } |
| |
| bufferPointer += bytes; |
| remainingBytes -= bytes; |
| } |
| |
| *(bufferPointer - 1) = ')'; // Replace last space. |
| *bufferPointer = '\0'; |
| } |
| |
| #if !defined(MACHSIZE) |
| #error "Define MACHSIZE to 32 or 64" |
| #endif |
| |
| #if MACHSIZE == 32 |
| |
| typedef struct mach_header mach_header_target; |
| typedef struct segment_command segment_command_target; |
| typedef struct section section_target; |
| static const uint32_t MH_MAGIC_TARGET = MH_MAGIC; |
| static const uint32_t LC_SEGMENT_TARGET = LC_SEGMENT; |
| |
| #elif MACHSIZE == 64 |
| |
| typedef struct mach_header_64 mach_header_target; |
| typedef struct segment_command_64 segment_command_target; |
| typedef struct section_64 section_target; |
| static const uint32_t MH_MAGIC_TARGET = MH_MAGIC_64; |
| static const uint32_t LC_SEGMENT_TARGET = LC_SEGMENT_64; |
| |
| #else |
| |
| #error "Impossible MACHSIZE" |
| |
| #endif |
| |
| static mach_header_target* findCoreFoundationMachHeader() { |
| Dl_info info; |
| if (dladdr(reinterpret_cast<void*>(&CFRunLoopRun), &info) == 0) return nullptr; |
| |
| return reinterpret_cast<mach_header_target*>(info.dli_fbase); |
| } |
| |
| template<int n> |
| bool bufferEqualsString(const char (&buffer)[n], const char* str) { |
| return strncmp(buffer, str, n) == 0; |
| } |
| |
| static char* findExceptionBacktraceSection(unsigned long *size) { |
| mach_header_target* header = findCoreFoundationMachHeader(); |
| if (header == nullptr) return nullptr; |
| if (header->magic != MH_MAGIC_TARGET) return nullptr; |
| |
| uintptr_t textVmaddr = 0; |
| |
| load_command* loadCommand = reinterpret_cast<load_command*>(header + 1); |
| for (uint32_t loadCommandIndex = 0; loadCommandIndex < header->ncmds; ++loadCommandIndex) { |
| if (loadCommand->cmd == LC_SEGMENT_TARGET) { |
| segment_command_target* segmentCommand = reinterpret_cast<segment_command_target*>(loadCommand); |
| if (bufferEqualsString(segmentCommand->segname, "__TEXT")) { |
| textVmaddr = segmentCommand->vmaddr; |
| } |
| |
| if (bufferEqualsString(segmentCommand->segname, "__DATA")) { |
| section_target* sections = reinterpret_cast<section_target*>(segmentCommand + 1); |
| for (uint32_t sectionIndex = 0; sectionIndex < segmentCommand->nsects; ++sectionIndex) { |
| section_target* section = §ions[sectionIndex]; |
| |
| if (bufferEqualsString(section->sectname, "__cf_except_bt") && bufferEqualsString(section->segname, "__DATA")) { |
| *size = section->size; |
| return reinterpret_cast<char*>(reinterpret_cast<uintptr_t>(header) + section->addr - textVmaddr); |
| } |
| } |
| } |
| } |
| |
| loadCommand = reinterpret_cast<load_command*>(reinterpret_cast<uintptr_t>(loadCommand) + loadCommand->cmdsize); |
| } |
| |
| return nullptr; |
| } |
| |
| void ReportBacktraceToIosCrashLog(KRef throwable) { |
| unsigned long bufferSize = 0; |
| char* buffer = findExceptionBacktraceSection(&bufferSize); |
| if (buffer == nullptr) return; |
| |
| // Note: access to this buffer is protected by a lock, but it is not easily accessible. |
| // Instead assume that typically this buffer is accessed only during termination, and |
| // rely on caller guaranteeing this code to be executed only before system termination handlers. |
| |
| writeStackTraceToBuffer(throwable, buffer, bufferSize); |
| } |
| |
| #endif // KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG |