From bfe8087358333ac1ab8fb5fc5c83ea59711aa69b Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 5 Jun 2023 15:35:20 +0100 Subject: [PATCH 01/17] [Backtracing][Linux] Add Linux crash handler to the runtime. This also adds a function to demangle a symbol, and a way for the backtracing code to report warning messages to the same place as the main runtime. I'd like to rename the _swift_isThunkFunction() SPI also, but we can't do that until we've made the changes to the _Backtracing library, so we'll do that there instead. rdar://110261430 --- include/swift/Runtime/Backtrace.h | 19 +- include/swift/Runtime/Debug.h | 3 + stdlib/public/runtime/Backtrace.cpp | 165 ++++- stdlib/public/runtime/CMakeLists.txt | 3 +- stdlib/public/runtime/CrashHandlerLinux.cpp | 770 ++++++++++++++++++++ stdlib/public/runtime/CrashHandlerMacOS.cpp | 2 +- stdlib/public/runtime/Errors.cpp | 6 + 7 files changed, 959 insertions(+), 9 deletions(-) create mode 100644 stdlib/public/runtime/CrashHandlerLinux.cpp diff --git a/include/swift/Runtime/Backtrace.h b/include/swift/Runtime/Backtrace.h index fcd67e8f5ce73..5a82d974d206b 100644 --- a/include/swift/Runtime/Backtrace.h +++ b/include/swift/Runtime/Backtrace.h @@ -17,10 +17,17 @@ #ifndef SWIFT_RUNTIME_BACKTRACE_H #define SWIFT_RUNTIME_BACKTRACE_H +#ifdef __linux__ +#include +#include + +#include +#endif // defined(__linux__) + #include "swift/Runtime/Config.h" +#include "swift/Runtime/CrashInfo.h" #include "swift/shims/Visibility.h" -#include "swift/shims/CrashInfo.h" #include @@ -50,7 +57,11 @@ typedef int ErrorCode; SWIFT_RUNTIME_STDLIB_INTERNAL ErrorCode _swift_installCrashHandler(); +#ifdef __linux__ +SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd); +#else SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv); +#endif enum class UnwindAlgorithm { Auto = 0, @@ -125,6 +136,12 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings; SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool _swift_isThunkFunction(const char *mangledName); +SWIFT_RUNTIME_STDLIB_SPI +char *_swift_backtrace_demangle(const char *mangledName, + size_t mangledNameLength, + char *outputBuffer, + size_t *outputBufferSize, + int *status); #ifdef __cplusplus } // namespace backtrace } // namespace runtime diff --git a/include/swift/Runtime/Debug.h b/include/swift/Runtime/Debug.h index 1559ca666e813..5d67923b603c2 100644 --- a/include/swift/Runtime/Debug.h +++ b/include/swift/Runtime/Debug.h @@ -124,6 +124,9 @@ swift_dynamicCastFailure(const void *sourceType, const char *sourceName, SWIFT_RUNTIME_EXPORT void swift_reportError(uint32_t flags, const char *message); +SWIFT_RUNTIME_EXPORT +void swift_reportWarning(uint32_t flags, const char *message); + // Halt due to an overflow in swift_retain(). SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_RUNTIME_ATTRIBUTE_NOINLINE void swift_abortRetainOverflow(); diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index c5eb53813dcf8..61387a2d2be8a 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -52,6 +52,12 @@ #include #include +#ifdef _WIN32 +// We'll probably want dbghelp.h here +#else +#include +#endif + #define DEBUG_BACKTRACING_SETTINGS 0 #ifndef lengthof @@ -70,7 +76,7 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { // enabled #if TARGET_OS_OSX OnOffTty::TTY, -#elif 0 // defined(__linux__) || defined(_WIN32) +#elif defined(__linux__) // || defined(_WIN32) OnOffTty::On, #else OnOffTty::Off, @@ -80,7 +86,7 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { true, // interactive -#if TARGET_OS_OSX // || defined(__linux__) || defined(_WIN32) +#if TARGET_OS_OSX || defined(__linux__) // || defined(_WIN32) OnOffTty::TTY, #else OnOffTty::Off, @@ -773,6 +779,54 @@ _swift_backtraceSetupEnvironment() *penv = 0; } +#ifdef __linux__ +struct spawn_info { + const char *path; + char * const *argv; + char * const *envp; + int memserver; +}; + +uint8_t spawn_stack[4096]; + +int +do_spawn(void *ptr) { + struct spawn_info *pinfo = (struct spawn_info *)ptr; + + /* Ensure that the memory server is always on fd 4 */ + if (pinfo->memserver != 4) { + dup2(pinfo->memserver, 4); + close(pinfo->memserver); + } + + /* Clear the signal mask */ + sigset_t mask; + sigfillset(&mask); + sigprocmask(SIG_UNBLOCK, &mask, NULL); + + return execvpe(pinfo->path, pinfo->argv, pinfo->envp); +} + +int +safe_spawn(pid_t *ppid, const char *path, int memserver, + char * const argv[], char * const envp[]) +{ + struct spawn_info info = { path, argv, envp, memserver }; + + /* The CLONE_VFORK is *required* because info is on the stack; we don't + want to return until *after* the subprocess has called execvpe(). */ + int ret = clone(do_spawn, spawn_stack + sizeof(spawn_stack), + CLONE_VFORK|CLONE_VM, &info); + if (ret < 0) + return ret; + + close(memserver); + + *ppid = ret; + return 0; +} +#endif // defined(__linux__) + #endif // SWIFT_BACKTRACE_ON_CRASH_SUPPORTED } // namespace @@ -796,13 +850,109 @@ _swift_isThunkFunction(const char *mangledName) { return ctx.isThunkSymbol(mangledName); } +/// Try to demangle a symbol. +/// +/// Unlike other entry points that do this, we try both Swift and C++ here. +/// +/// @param mangledName is the symbol name to be demangled. +/// @param mangledNameLength is the length of this name. +/// @param outputBuffer is a pointer to a buffer in which to place the result. +/// @param outputBufferSize points to a variable that will be filled in with +/// the size of the allocated buffer (NOT the length of the result). +/// @param status returns the status codes defined in the C++ ABI. +/// +/// If outputBuffer and outputBufferSize are both nullptr, this function will +/// allocate memory for the result using malloc(). +/// +/// If outputBuffer is nullptr but outputBufferSize is not, the function will +/// fill outputBufferSize with the required buffer size and return nullptr. +/// +/// Otherwise, the result will be written into the output buffer, and the +/// size of the result will be written into outputBufferSize. If the buffer +/// is too small, the result will be truncated, but outputBufferSize will +/// still be set to the number of bytes that would have been required to +/// copy out the full result (including a trailing NUL). +/// +/// @returns `true` if demangling was successful. +SWIFT_RUNTIME_STDLIB_SPI char * +_swift_backtrace_demangle(const char *mangledName, + size_t mangledNameLength, + char *outputBuffer, + size_t *outputBufferSize, + int *status) { + llvm::StringRef name = llvm::StringRef(mangledName, mangledNameLength); + + if (Demangle::isSwiftSymbol(name)) { + // This is a Swift mangling + auto options = DemangleOptions::SimplifiedUIDemangleOptions(); + auto result = Demangle::demangleSymbolAsString(name, options); + size_t bufferSize; + + if (outputBufferSize) { + bufferSize = *outputBufferSize; + *outputBufferSize = result.length() + 1; + } + + if (outputBuffer == nullptr) { + outputBuffer = (char *)::malloc(result.length() + 1); + bufferSize = result.length() + 1; + } + + size_t toCopy = std::min(bufferSize - 1, result.length()); + ::memcpy(outputBuffer, result.data(), toCopy); + outputBuffer[toCopy] = '\0'; + + *status = 0; + return outputBuffer; +#ifndef _WIN32 + } else if (name.startswith("_Z")) { + // Try C++ + size_t resultLen; + char *result = abi::__cxa_demangle(mangledName, nullptr, &resultLen, status); + + if (result) { + size_t bufferSize; + + if (outputBufferSize) { + bufferSize = *outputBufferSize; + *outputBufferSize = resultLen; + } + + if (outputBuffer == nullptr) { + return result; + } + + size_t toCopy = std::min(bufferSize - 1, resultLen - 1); + ::memcpy(outputBuffer, result, toCopy); + outputBuffer[toCopy] = '\0'; + + *status = 0; + return outputBuffer; + } +#else + // On Windows, the mangling is different. + // ###TODO: Call __unDName() +#endif + } else { + *status = -2; + } + + return nullptr; +} + // N.B. THIS FUNCTION MUST BE SAFE TO USE FROM A CRASH HANDLER. On Linux // and macOS, that means it must be async-signal-safe. On Windows, there // isn't an equivalent notion but a similar restriction applies. SWIFT_RUNTIME_STDLIB_INTERNAL bool +#ifdef __linux__ +_swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd) +#else _swift_spawnBacktracer(const ArgChar * const *argv) +#endif { -#if TARGET_OS_OSX || TARGET_OS_MACCATALYST +#if !SWIFT_BACKTRACE_ON_CRASH_SUPPORTED + return false; +#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST || defined(__linux__) pid_t child; const char *env[BACKTRACE_MAX_ENV_VARS + 1]; @@ -817,10 +967,16 @@ _swift_spawnBacktracer(const ArgChar * const *argv) // SUSv3 says argv and envp are "completely constant" and that the reason // posix_spawn() et al use char * const * is for compatibility. +#ifdef __linux__ + int ret = safe_spawn(&child, swiftBacktracePath, memserver_fd, + const_cast(argv), + const_cast(env)); +#else int ret = posix_spawn(&child, swiftBacktracePath, &backtraceFileActions, &backtraceSpawnAttrs, const_cast(argv), const_cast(env)); +#endif if (ret < 0) return false; @@ -835,10 +991,7 @@ _swift_spawnBacktracer(const ArgChar * const *argv) return false; - // ###TODO: Linux // ###TODO: Windows -#else - return false; #endif } diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index e5adea1fa1a8a..379f72b010a72 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -81,7 +81,8 @@ set(swift_runtime_sources set(swift_runtime_backtracing_sources Backtrace.cpp - CrashHandlerMacOS.cpp) + CrashHandlerMacOS.cpp + CrashHandlerLinux.cpp) # Acknowledge that the following sources are known. set(LLVM_OPTIONAL_SOURCES diff --git a/stdlib/public/runtime/CrashHandlerLinux.cpp b/stdlib/public/runtime/CrashHandlerLinux.cpp new file mode 100644 index 0000000000000..e8fff2a66e08f --- /dev/null +++ b/stdlib/public/runtime/CrashHandlerLinux.cpp @@ -0,0 +1,770 @@ +//===--- CrashHandlerLinux.cpp - Swift crash handler for Linux ----------- ===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// The Linux crash handler implementation. +// +//===----------------------------------------------------------------------===// + +#ifdef __linux__ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "swift/Runtime/Backtrace.h" + +#include + +// Run the memserver in a thread (0) or separate process (1) +#define MEMSERVER_USE_PROCESS 0 + +#ifndef lengthof +#define lengthof(x) (sizeof(x) / sizeof(x[0])) +#endif + +using namespace swift::runtime::backtrace; + +namespace { + +void handle_fatal_signal(int signum, siginfo_t *pinfo, void *uctx); +void suspend_other_threads(struct thread *self); +void resume_other_threads(); +void take_thread_lock(); +void release_thread_lock(); +void notify_paused(); +void wait_paused(uint32_t expected, const struct timespec *timeout); +int memserver_start(); +int memserver_entry(void *); +bool run_backtracer(int fd); + +int safe_read(int fd, void *buf, size_t len) { + int ret; + do { + ret = read(fd, buf, len); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +int safe_write(int fd, const void *buf, size_t len) { + int ret; + do { + ret = write(fd, buf, len); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +CrashInfo crashInfo; + +const int signalsToHandle[] = { + SIGQUIT, + SIGABRT, + SIGBUS, + SIGFPE, + SIGILL, + SIGSEGV, + SIGTRAP +}; + +} // namespace + +namespace swift { +namespace runtime { +namespace backtrace { + +SWIFT_RUNTIME_STDLIB_INTERNAL int +_swift_installCrashHandler() +{ + stack_t ss; + + // See if an alternate signal stack already exists + if (sigaltstack(NULL, &ss) < 0) + return errno; + + if (ss.ss_sp == 0) { + /* No, so set one up; note that if we end up having to do a PLT lookup + for a function we call from the signal handler, we need additional + stack space for the dynamic linker, or we'll just explode. That's + what the extra 16KB is for here. */ + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ + 16384; + ss.ss_sp = mmap(0, ss.ss_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ss.ss_sp == MAP_FAILED) + return errno; + + if (sigaltstack(&ss, NULL) < 0) + return errno; + } + + // Now register signal handlers + struct sigaction sa; + sigfillset(&sa.sa_mask); + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) { + sigdelset(&sa.sa_mask, signalsToHandle[n]); + } + + sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_NODEFER; + sa.sa_sigaction = handle_fatal_signal; + + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) { + struct sigaction osa; + + // See if a signal handler for this signal is already installed + if (sigaction(signalsToHandle[n], NULL, &osa) < 0) + return errno; + + if (osa.sa_handler == SIG_DFL) { + // No, so install ours + if (sigaction(signalsToHandle[n], &sa, NULL) < 0) + return errno; + } + } + + return 0; +} + +} // namespace backtrace +} // namespace runtime +} // namespace swift + +namespace { + +void +reset_signal(int signum) +{ + struct sigaction sa; + sa.sa_handler = SIG_DFL; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + + sigaction(signum, &sa, NULL); +} + +void +handle_fatal_signal(int signum, + siginfo_t *pinfo, + void *uctx) +{ + int old_err = errno; + struct thread self = { 0, (int64_t)gettid(), (uint64_t)uctx }; + + // Prevent this from exploding if more than one thread gets here at once + suspend_other_threads(&self); + + // Remove our signal handlers; crashes should kill us here + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) + reset_signal(signalsToHandle[n]); + + // Fill in crash info + crashInfo.crashing_thread = self.tid; + crashInfo.signal = signum; + crashInfo.fault_address = (uint64_t)pinfo->si_addr; + + // Start the memory server + int fd = memserver_start(); + + /* Start the backtracer; this will suspend the process, so there's no need + to try to suspend other threads from here. */ + run_backtracer(fd); + +#if !MEMSERVER_USE_PROCESS + /* If the memserver is in-process, it may have set signal handlers, + so reset SIGSEGV and SIGBUS again */ + reset_signal(SIGSEGV); + reset_signal(SIGBUS); +#endif + + // Restart the other threads + resume_other_threads(); + + // Restore errno and exit (to crash) + errno = old_err; +} + +// .. Thread handling .......................................................... + +void +reset_threads(struct thread *first) { + __atomic_store_n(&crashInfo.thread_list, (uint64_t)first, __ATOMIC_RELEASE); +} + +void +add_thread(struct thread *thread) { + uint64_t next = __atomic_load_n(&crashInfo.thread_list, __ATOMIC_ACQUIRE); + do { + thread->next = next; + } while (!__atomic_compare_exchange_n(&crashInfo.thread_list, &next, + (uint64_t)thread, + false, + __ATOMIC_RELEASE, __ATOMIC_ACQUIRE)); +} + +bool +seen_thread(pid_t tid) { + uint64_t next = __atomic_load_n(&crashInfo.thread_list, __ATOMIC_ACQUIRE); + while (next) { + struct thread *pthread = (struct thread *)next; + if (pthread->tid == tid) + return true; + next = pthread->next; + } + return false; +} + +void +pause_thread(int signum __attribute__((unused)), + siginfo_t *pinfo __attribute__((unused)), + void *uctx) +{ + int old_err = errno; + struct thread self = { 0, (int64_t)gettid(), (uint64_t)uctx }; + + add_thread(&self); + + notify_paused(); + + take_thread_lock(); + release_thread_lock(); + + errno = old_err; +} + +struct linux_dirent64 { + ino64_t d_ino; + off64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +int +getdents(int fd, void *buf, size_t bufsiz) +{ + return syscall(SYS_getdents64, fd, buf, bufsiz); +} + +/* Stop all other threads in this process; we do this by establishing a + signal handler for SIGUSR1, then iterating through the threads sending + SIGUSR1. + + Finding the other threads is a pain, because Linux has no actual API + for that; instead, you have to read /proc. Unfortunately, opendir() + and readdir() are not async signal safe, so we get to do this with + the getdents system call instead. + + The SIGUSR1 signals also serve to build the thread list. */ +void +suspend_other_threads(struct thread *self) +{ + struct sigaction sa, sa_old; + + // Take the lock + take_thread_lock(); + + // Start the thread list with this thread + reset_threads(self); + + // Swap out the SIGPROF signal handler first + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_NODEFER; + sa.sa_handler = NULL; + sa.sa_sigaction = pause_thread; + + sigaction(SIGPROF, &sa, &sa_old); + + /* Now scan /proc/self/task to get the tids of the threads in this + process. We need to ignore our own thread. */ + int fd = open("/proc/self/task", + O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC); + int our_pid = getpid(); + char buffer[4096]; + size_t offset = 0; + size_t count = 0; + + uint32_t thread_count = 0; + uint32_t old_thread_count; + + do { + old_thread_count = thread_count; + lseek(fd, 0, SEEK_SET); + + for (;;) { + if (offset >= count) { + ssize_t bytes = getdents(fd, buffer, sizeof(buffer)); + if (bytes <= 0) + break; + count = (size_t)bytes; + offset = 0; + } + + struct linux_dirent64 *dp = (struct linux_dirent64 *)&buffer[offset]; + offset += dp->d_reclen; + + if (strcmp(dp->d_name, ".") == 0 + || strcmp(dp->d_name, "..") == 0) + continue; + + int tid = atoi(dp->d_name); + + if ((int64_t)tid != self->tid && !seen_thread(tid)) { + tgkill(our_pid, tid, SIGPROF); + ++thread_count; + } + } + + // Wait up to 5 seconds for the threads to pause + struct timespec timeout = { 5, 0 }; + wait_paused(thread_count, &timeout); + } while (old_thread_count != thread_count); + + // Close the directory + close(fd); + + // Finally, reset the signal handler + sigaction(SIGPROF, &sa_old, NULL); +} + +void +resume_other_threads() +{ + // All we need to do here is release the lock. + release_thread_lock(); +} + +// .. Locking .................................................................. + +/* We use a futex to block the threads; we also use one to let us work out + when all the threads we've asked to pause have actually paused. */ +int +futex(uint32_t *uaddr, int futex_op, uint32_t val, + const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3) +{ + return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3); +} + +uint32_t thread_lock = 0; + +void +take_thread_lock() +{ + do { + uint32_t zero = 0; + if (__atomic_compare_exchange_n(&thread_lock, + &zero, + 1, + true, + __ATOMIC_ACQUIRE, + __ATOMIC_RELAXED)) + return; + } while (!futex(&thread_lock, FUTEX_WAIT, 1, NULL, NULL, 0) + || errno == EAGAIN); +} + +void +release_thread_lock() +{ + __atomic_store_n(&thread_lock, 0, __ATOMIC_RELEASE); + futex(&thread_lock, FUTEX_WAKE, 1, NULL, NULL, 0); +} + +uint32_t threads_paused = 0; + +void +notify_paused() +{ + __atomic_fetch_add(&threads_paused, 1, __ATOMIC_RELEASE); + futex(&threads_paused, FUTEX_WAKE, 1, NULL, NULL, 0); +} + +void +wait_paused(uint32_t expected, const struct timespec *timeout) +{ + uint32_t current; + do { + current = __atomic_load_n(&threads_paused, __ATOMIC_ACQUIRE); + if (current == expected) + return; + } while (!futex(&threads_paused, FUTEX_WAIT, current, timeout, NULL, 0) + || errno == EAGAIN); +} + +// .. Memory server ............................................................ + +/* The memory server exists so that we can gain access to the crashing + process's memory space from the backtracer without having to use ptrace() + or process_vm_readv(), both of which need CAP_SYS_PTRACE. + + We don't want to require CAP_SYS_PTRACE because we're potentially being + used inside of a Docker container, which won't have that enabled. */ + +char memserver_stack[4096]; +char memserver_buffer[4096]; +int memserver_fd; +bool memserver_has_ptrace; +sigjmp_buf memserver_fault_buf; +pid_t memserver_pid; + +int +memserver_start() +{ + int ret; + int fds[2]; + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + if (ret < 0) + return ret; + + memserver_fd = fds[0]; + ret = clone(memserver_entry, memserver_stack + sizeof(memserver_stack), +#if MEMSERVER_USE_PROCESS + 0, +#else + CLONE_THREAD | CLONE_VM | CLONE_FILES + | CLONE_FS | CLONE_IO | CLONE_SIGHAND, +#endif + NULL); + if (ret < 0) + return ret; + +#if MEMSERVER_USE_PROCESS + memserver_pid = ret; + + /* Tell the Yama LSM module, if it's running, that it's OK for + the memserver to read process memory */ + prctl(PR_SET_PTRACER, ret); + + close(fds[0]); +#else + memserver_pid = getpid(); +#endif + + return fds[1]; +} + +void +memserver_fault(int sig) { + (void)sig; + siglongjmp(memserver_fault_buf, -1); +} + +ssize_t __attribute__((noinline)) +memserver_read(void *to, const void *from, size_t len) { + if (memserver_has_ptrace) { + struct iovec local = { to, len }; + struct iovec remote = { const_cast(from), len }; + return process_vm_readv(memserver_pid, &local, 1, &remote, 1, 0); + } else { + if (!sigsetjmp(memserver_fault_buf, 1)) { + memcpy(to, from, len); + return len; + } else { + return 1; + } + } +} + +int +memserver_entry(void *dummy __attribute__((unused))) { + int fd = memserver_fd; + int result = 1; + +#if MEMSERVER_USE_PROCESS + prctl(PR_SET_NAME, "[backtrace]"); +#endif + + memserver_has_ptrace = !!prctl(PR_CAPBSET_READ, CAP_SYS_PTRACE); + + if (!memserver_has_ptrace) { + struct sigaction sa; + sigfillset(&sa.sa_mask); + sa.sa_handler = memserver_fault; + sa.sa_flags = SA_NODEFER; + sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGBUS, &sa, NULL); + } + + for (;;) { + struct memserver_req req; + ssize_t ret; + + ret = safe_read(fd, &req, sizeof(req)); + if (ret != sizeof(req)) + break; + + uint64_t addr = req.addr; + uint64_t bytes = req.len; + + while (bytes) { + uint64_t todo = (bytes < sizeof(memserver_buffer) + ? bytes : sizeof(memserver_buffer)); + + ret = memserver_read(memserver_buffer, (void *)addr, (size_t)todo); + + struct memserver_resp resp; + + resp.addr = addr; + resp.len = ret; + + ret = safe_write(fd, &resp, sizeof(resp)); + if (ret != sizeof(resp)) + goto fail; + + if (resp.len < 0) + break; + + ret = safe_write(fd, memserver_buffer, resp.len); + if (ret != resp.len) + goto fail; + + addr += resp.len; + bytes -= resp.len; + } + } + + result = 0; + + fail: + close(fd); + return result; +} + +// .. Starting the backtracer .................................................. + +char addr_buf[18]; +char timeout_buf[22]; +char limit_buf[22]; +char top_buf[22]; +const char *backtracer_argv[] = { + "swift-backtrace", // 0 + "--unwind", // 1 + "precise", // 2 + "--demangle", // 3 + "true", // 4 + "--interactive", // 5 + "true", // 6 + "--color", // 7 + "true", // 8 + "--timeout", // 9 + timeout_buf, // 10 + "--preset", // 11 + "friendly", // 12 + "--crashinfo", // 13 + addr_buf, // 14 + "--threads", // 15 + "preset", // 16 + "--registers", // 17 + "preset", // 18 + "--images", // 19 + "preset", // 20 + "--limit", // 21 + limit_buf, // 22 + "--top", // 23 + top_buf, // 24 + "--sanitize", // 25 + "preset", // 26 + "--cache", // 27 + "true", // 28 + NULL +}; + +// We can't call sprintf() here because we're in a signal handler, +// so we need to be async-signal-safe. +void +format_address(uintptr_t addr, char buffer[18]) +{ + char *ptr = buffer + 18; + *--ptr = '\0'; + while (ptr > buffer) { + char digit = '0' + (addr & 0xf); + if (digit > '9') + digit += 'a' - '0' - 10; + *--ptr = digit; + addr >>= 4; + if (!addr) + break; + } + + // Left-justify in the buffer + if (ptr > buffer) { + char *pt2 = buffer; + while (*ptr) + *pt2++ = *ptr++; + *pt2++ = '\0'; + } +} +void +format_address(const void *ptr, char buffer[18]) +{ + format_address(reinterpret_cast(ptr), buffer); +} + +// See above; we can't use sprintf() here. +void +format_unsigned(unsigned u, char buffer[22]) +{ + char *ptr = buffer + 22; + *--ptr = '\0'; + while (ptr > buffer) { + char digit = '0' + (u % 10); + *--ptr = digit; + u /= 10; + if (!u) + break; + } + + // Left-justify in the buffer + if (ptr > buffer) { + char *pt2 = buffer; + while (*ptr) + *pt2++ = *ptr++; + *pt2++ = '\0'; + } +} + +const char * +trueOrFalse(bool b) { + return b ? "true" : "false"; +} + +const char * +trueOrFalse(OnOffTty oot) { + return trueOrFalse(oot == OnOffTty::On); +} + +bool +run_backtracer(int memserver_fd) +{ + // Set-up the backtracer's command line arguments + switch (_swift_backtraceSettings.algorithm) { + case UnwindAlgorithm::Fast: + backtracer_argv[2] = "fast"; + break; + default: + backtracer_argv[2] = "precise"; + break; + } + + // (The TTY option has already been handled at this point, so these are + // all either "On" or "Off".) + backtracer_argv[4] = trueOrFalse(_swift_backtraceSettings.demangle); + backtracer_argv[6] = trueOrFalse(_swift_backtraceSettings.interactive); + backtracer_argv[8] = trueOrFalse(_swift_backtraceSettings.color); + + switch (_swift_backtraceSettings.threads) { + case ThreadsToShow::Preset: + backtracer_argv[16] = "preset"; + break; + case ThreadsToShow::All: + backtracer_argv[16] = "all"; + break; + case ThreadsToShow::Crashed: + backtracer_argv[16] = "crashed"; + break; + } + + switch (_swift_backtraceSettings.registers) { + case RegistersToShow::Preset: + backtracer_argv[18] = "preset"; + break; + case RegistersToShow::None: + backtracer_argv[18] = "none"; + break; + case RegistersToShow::All: + backtracer_argv[18] = "all"; + break; + case RegistersToShow::Crashed: + backtracer_argv[18] = "crashed"; + break; + } + + switch (_swift_backtraceSettings.images) { + case ImagesToShow::Preset: + backtracer_argv[20] = "preset"; + break; + case ImagesToShow::None: + backtracer_argv[20] = "none"; + break; + case ImagesToShow::All: + backtracer_argv[20] = "all"; + break; + case ImagesToShow::Mentioned: + backtracer_argv[20] = "mentioned"; + break; + } + + switch (_swift_backtraceSettings.preset) { + case Preset::Friendly: + backtracer_argv[12] = "friendly"; + break; + case Preset::Medium: + backtracer_argv[12] = "medium"; + break; + default: + backtracer_argv[12] = "full"; + break; + } + + switch (_swift_backtraceSettings.sanitize) { + case SanitizePaths::Preset: + backtracer_argv[26] = "preset"; + break; + case SanitizePaths::Off: + backtracer_argv[26] = "false"; + break; + case SanitizePaths::On: + backtracer_argv[26] = "true"; + break; + } + + backtracer_argv[28] = trueOrFalse(_swift_backtraceSettings.cache); + + format_unsigned(_swift_backtraceSettings.timeout, timeout_buf); + + if (_swift_backtraceSettings.limit < 0) + std::strcpy(limit_buf, "none"); + else + format_unsigned(_swift_backtraceSettings.limit, limit_buf); + + format_unsigned(_swift_backtraceSettings.top, top_buf); + format_address(&crashInfo, addr_buf); + + // Actually execute it + return _swift_spawnBacktracer(backtracer_argv, memserver_fd); +} + +} // namespace + +#endif // __linux__ + diff --git a/stdlib/public/runtime/CrashHandlerMacOS.cpp b/stdlib/public/runtime/CrashHandlerMacOS.cpp index 81a6c072c8be0..505a7ba2c553b 100644 --- a/stdlib/public/runtime/CrashHandlerMacOS.cpp +++ b/stdlib/public/runtime/CrashHandlerMacOS.cpp @@ -57,7 +57,7 @@ void suspend_other_threads(); void resume_other_threads(); bool run_backtracer(void); -swift::CrashInfo crashInfo; +CrashInfo crashInfo; os_unfair_lock crashLock = OS_UNFAIR_LOCK_INIT; diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp index b126245d14bbe..040153e406f22 100644 --- a/stdlib/public/runtime/Errors.cpp +++ b/stdlib/public/runtime/Errors.cpp @@ -404,6 +404,12 @@ swift::warning(uint32_t flags, const char *format, ...) warningv(flags, format, args); } +/// Report a warning to the system console and stderr. This is exported, +/// unlike the swift::warning() function above. +void swift::swift_reportWarning(uint32_t flags, const char *message) { + warning(flags, "%s", message); +} + // Crash when a deleted method is called by accident. SWIFT_RUNTIME_EXPORT SWIFT_NORETURN void swift_deletedMethodError() { swift::fatalError(/* flags = */ 0, From dc05590747dbaabbe6c119fb54eaf3671c9a19de Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 5 Jun 2023 16:59:14 +0100 Subject: [PATCH 02/17] [Backtracing][Linux] Add missing CrashInfo.h header. This was added to a later PR, but not to this one, though we need it here. rdar://110261430 --- include/swift/Runtime/CrashInfo.h | 70 +++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 include/swift/Runtime/CrashInfo.h diff --git a/include/swift/Runtime/CrashInfo.h b/include/swift/Runtime/CrashInfo.h new file mode 100644 index 0000000000000..085529e3f7574 --- /dev/null +++ b/include/swift/Runtime/CrashInfo.h @@ -0,0 +1,70 @@ +//===--- CrashInfo.h - Swift Backtracing Crash Information ------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the CrashInfo type that holds information about why the program +// crashed. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CRASHINFO_H +#define SWIFT_CRASHINFO_H + +#include + +#ifdef __cplusplus +namespace swift { +namespace runtime { +namespace backtrace { +extern "C" { +#endif + +struct CrashInfo { + uint64_t crashing_thread; + uint64_t signal; + uint64_t fault_address; + +#ifdef __APPLE__ + uint64_t mctx; +#elif defined(__linux__) + uint64_t thread_list; +#endif +}; + +#ifdef __linux__ + +struct memserver_req { + uint64_t addr; + uint64_t len; +}; + +struct memserver_resp { + uint64_t addr; + int64_t len; + /* Then len bytes of data */ +}; + +struct thread { + uint64_t next; + int64_t tid; + uint64_t uctx; +}; + +#endif + +#ifdef __cplusplus +} // extern "C" +} // namespace backtrace +} // namespace runtime +} // namespace swift +#endif + +#endif // SWIFT_CRASHINFO_H From 546c93de8766b11972e53c25255b0c77f375cd79 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 5 Jun 2023 22:28:53 +0100 Subject: [PATCH 03/17] [Backtracing][Linux] Disable backtrace-on-crash until enabling PR. This should have been disabled until #66338. rdar://110261430 --- stdlib/public/runtime/Backtrace.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index 61387a2d2be8a..84fb8c827f661 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -76,7 +76,7 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { // enabled #if TARGET_OS_OSX OnOffTty::TTY, -#elif defined(__linux__) // || defined(_WIN32) +#elif 0 // defined(__linux__) || defined(_WIN32) OnOffTty::On, #else OnOffTty::Off, @@ -86,7 +86,7 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { true, // interactive -#if TARGET_OS_OSX || defined(__linux__) // || defined(_WIN32) +#if TARGET_OS_OSX // || defined(__linux__) || defined(_WIN32) OnOffTty::TTY, #else OnOffTty::Off, From fe6238a95bb4e88a6003fa2a5976890466f74625 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 6 Jun 2023 15:56:57 +0100 Subject: [PATCH 04/17] [Backtracing][Linux] Tidy a few things up after review. Mike and Max made various helpful suggestions, so I've added and updated various comments and amended the code to cope with partial reads and writes. rdar://110261430 --- include/swift/Runtime/CrashInfo.h | 30 ++++++++++- stdlib/public/runtime/Backtrace.cpp | 26 +++++++--- stdlib/public/runtime/CrashHandlerLinux.cpp | 55 ++++++++++++++------- 3 files changed, 85 insertions(+), 26 deletions(-) diff --git a/include/swift/Runtime/CrashInfo.h b/include/swift/Runtime/CrashInfo.h index 085529e3f7574..737888abbd25e 100644 --- a/include/swift/Runtime/CrashInfo.h +++ b/include/swift/Runtime/CrashInfo.h @@ -27,34 +27,62 @@ namespace backtrace { extern "C" { #endif +// Note: The "pointers" below are pointers in a different process's address +// space, which might not even share our bitness. That is why they are +// `uint64_t`s, rather than pointers or `uintptr_t`s. + +// The address of this structure in memory is passed to swift-backtrace. struct CrashInfo { + // The thread ID for the crashing thread. uint64_t crashing_thread; + + // The signal number corresponding to this crash. uint64_t signal; + + // The fault address. uint64_t fault_address; #ifdef __APPLE__ + // Points to the mcontext_t structure for the crashing thread; other + // threads' contexts can be recovered using Mach APIs later. uint64_t mctx; #elif defined(__linux__) + // The head of the thread list; points at a "struct thread" (see below). uint64_t thread_list; #endif }; #ifdef __linux__ +// A memory server request packet. struct memserver_req { + // Address to read. uint64_t addr; + + // Number of bytes to read. uint64_t len; }; +// A memory server response packet. struct memserver_resp { + // Address that was read from. uint64_t addr; + + // Number of bytes, *or* negative to indicate an error. int64_t len; - /* Then len bytes of data */ + + // Followed by len bytes of data if len > 0 }; +// Holds details of a running thread. struct thread { + // Points at the next thread. uint64_t next; + + // The thread ID for this thread. int64_t tid; + + // Points to the Linux ucontext_t structure. uint64_t uctx; }; diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index 84fb8c827f661..be20e4fcb8705 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -857,15 +857,15 @@ _swift_isThunkFunction(const char *mangledName) { /// @param mangledName is the symbol name to be demangled. /// @param mangledNameLength is the length of this name. /// @param outputBuffer is a pointer to a buffer in which to place the result. -/// @param outputBufferSize points to a variable that will be filled in with -/// the size of the allocated buffer (NOT the length of the result). +/// @param outputBufferSize points to a variable that contains the size of the +/// output buffer. /// @param status returns the status codes defined in the C++ ABI. /// -/// If outputBuffer and outputBufferSize are both nullptr, this function will -/// allocate memory for the result using malloc(). -/// -/// If outputBuffer is nullptr but outputBufferSize is not, the function will -/// fill outputBufferSize with the required buffer size and return nullptr. +/// If outputBuffer is nullptr, the function will allocate memory for the +/// result using malloc(). In this case, outputBufferSize may be nullptr; +/// if it is *not* nullptr, it will be set to the size of buffer that was +/// allocated. This is not necessarily the length of the string (it may be +/// somewhat higher). /// /// Otherwise, the result will be written into the output buffer, and the /// size of the result will be written into outputBufferSize. If the buffer @@ -873,7 +873,10 @@ _swift_isThunkFunction(const char *mangledName) { /// still be set to the number of bytes that would have been required to /// copy out the full result (including a trailing NUL). /// -/// @returns `true` if demangling was successful. +/// The unusual behaviour here is a consequence of the way the C++ ABI's +/// demangling function works. +/// +/// @returns a pointer to the output if demangling was successful. SWIFT_RUNTIME_STDLIB_SPI char * _swift_backtrace_demangle(const char *mangledName, size_t mangledNameLength, @@ -882,6 +885,11 @@ _swift_backtrace_demangle(const char *mangledName, int *status) { llvm::StringRef name = llvm::StringRef(mangledName, mangledNameLength); + // You must provide buffer size if you're providing your own output buffer + if (outputBuffer && !outputBufferSize) { + return nullptr; + } + if (Demangle::isSwiftSymbol(name)) { // This is a Swift mangling auto options = DemangleOptions::SimplifiedUIDemangleOptions(); @@ -926,6 +934,8 @@ _swift_backtrace_demangle(const char *mangledName, ::memcpy(outputBuffer, result, toCopy); outputBuffer[toCopy] = '\0'; + free(result); + *status = 0; return outputBuffer; } diff --git a/stdlib/public/runtime/CrashHandlerLinux.cpp b/stdlib/public/runtime/CrashHandlerLinux.cpp index e8fff2a66e08f..2a7d6515e159b 100644 --- a/stdlib/public/runtime/CrashHandlerLinux.cpp +++ b/stdlib/public/runtime/CrashHandlerLinux.cpp @@ -67,22 +67,44 @@ int memserver_start(); int memserver_entry(void *); bool run_backtracer(int fd); -int safe_read(int fd, void *buf, size_t len) { - int ret; - do { - ret = read(fd, buf, len); - } while (ret < 0 && errno == EINTR); +ssize_t safe_read(int fd, void *buf, size_t len) { + uint8_t *ptr = (uint8_t *)buf; + uint8_t *end = ptr + len; + ssize_t total = 0; + + while (ptr < end) { + ssize_t ret; + do { + ret = read(fd, buf, len); + } while (ret < 0 && errno == EINTR); + if (ret < 0) + return ret; + total += ret; + ptr += ret; + len -= ret; + } - return ret; + return total; } -int safe_write(int fd, const void *buf, size_t len) { - int ret; - do { - ret = write(fd, buf, len); - } while (ret < 0 && errno == EINTR); +ssize_t safe_write(int fd, const void *buf, size_t len) { + const uint8_t *ptr = (uint8_t *)buf; + const uint8_t *end = ptr + len; + ssize_t total = 0; + + while (ptr < end) { + ssize_t ret; + do { + ret = write(fd, buf, len); + } while (ret < 0 && errno == EINTR); + if (ret < 0) + return ret; + total += ret; + ptr += ret; + len -= ret; + } - return ret; + return total; } CrashInfo crashInfo; @@ -195,8 +217,7 @@ handle_fatal_signal(int signum, // Start the memory server int fd = memserver_start(); - /* Start the backtracer; this will suspend the process, so there's no need - to try to suspend other threads from here. */ + // Actually start the backtracer run_backtracer(fd); #if !MEMSERVER_USE_PROCESS @@ -276,15 +297,15 @@ getdents(int fd, void *buf, size_t bufsiz) } /* Stop all other threads in this process; we do this by establishing a - signal handler for SIGUSR1, then iterating through the threads sending - SIGUSR1. + signal handler for SIGPROF, then iterating through the threads sending + SIGPROF. Finding the other threads is a pain, because Linux has no actual API for that; instead, you have to read /proc. Unfortunately, opendir() and readdir() are not async signal safe, so we get to do this with the getdents system call instead. - The SIGUSR1 signals also serve to build the thread list. */ + The SIGPROF signals also serve to build the thread list. */ void suspend_other_threads(struct thread *self) { From 8ee9b8b938d287cc133ff457e57fd9916cdab2b4 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 6 Jun 2023 16:12:47 +0100 Subject: [PATCH 05/17] [Backtracing][Linux] Move doc comment to header. Moved the comment for `_swift_backtrace_demangle` into the header file instead of it being in the implementation. rdar://110261430 --- include/swift/Runtime/Backtrace.h | 27 +++++++++++++++++++++++++++ stdlib/public/runtime/Backtrace.cpp | 28 +--------------------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/include/swift/Runtime/Backtrace.h b/include/swift/Runtime/Backtrace.h index 5a82d974d206b..f6472405b8761 100644 --- a/include/swift/Runtime/Backtrace.h +++ b/include/swift/Runtime/Backtrace.h @@ -136,6 +136,33 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings; SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool _swift_isThunkFunction(const char *mangledName); +/// Try to demangle a symbol. +/// +/// Unlike other entry points that do this, we try both Swift and C++ here. +/// +/// @param mangledName is the symbol name to be demangled. +/// @param mangledNameLength is the length of this name. +/// @param outputBuffer is a pointer to a buffer in which to place the result. +/// @param outputBufferSize points to a variable that contains the size of the +/// output buffer. +/// @param status returns the status codes defined in the C++ ABI. +/// +/// If outputBuffer is nullptr, the function will allocate memory for the +/// result using malloc(). In this case, outputBufferSize may be nullptr; +/// if it is *not* nullptr, it will be set to the size of buffer that was +/// allocated. This is not necessarily the length of the string (it may be +/// somewhat higher). +/// +/// Otherwise, the result will be written into the output buffer, and the +/// size of the result will be written into outputBufferSize. If the buffer +/// is too small, the result will be truncated, but outputBufferSize will +/// still be set to the number of bytes that would have been required to +/// copy out the full result (including a trailing NUL). +/// +/// The unusual behaviour here is a consequence of the way the C++ ABI's +/// demangling function works. +/// +/// @returns a pointer to the output if demangling was successful. SWIFT_RUNTIME_STDLIB_SPI char *_swift_backtrace_demangle(const char *mangledName, size_t mangledNameLength, diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index be20e4fcb8705..104aa3be99237 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -850,33 +850,7 @@ _swift_isThunkFunction(const char *mangledName) { return ctx.isThunkSymbol(mangledName); } -/// Try to demangle a symbol. -/// -/// Unlike other entry points that do this, we try both Swift and C++ here. -/// -/// @param mangledName is the symbol name to be demangled. -/// @param mangledNameLength is the length of this name. -/// @param outputBuffer is a pointer to a buffer in which to place the result. -/// @param outputBufferSize points to a variable that contains the size of the -/// output buffer. -/// @param status returns the status codes defined in the C++ ABI. -/// -/// If outputBuffer is nullptr, the function will allocate memory for the -/// result using malloc(). In this case, outputBufferSize may be nullptr; -/// if it is *not* nullptr, it will be set to the size of buffer that was -/// allocated. This is not necessarily the length of the string (it may be -/// somewhat higher). -/// -/// Otherwise, the result will be written into the output buffer, and the -/// size of the result will be written into outputBufferSize. If the buffer -/// is too small, the result will be truncated, but outputBufferSize will -/// still be set to the number of bytes that would have been required to -/// copy out the full result (including a trailing NUL). -/// -/// The unusual behaviour here is a consequence of the way the C++ ABI's -/// demangling function works. -/// -/// @returns a pointer to the output if demangling was successful. +// Try to demangle a symbol. SWIFT_RUNTIME_STDLIB_SPI char * _swift_backtrace_demangle(const char *mangledName, size_t mangledNameLength, From 49ac8c38bb2251fee1a4305b8a945fef1dc24d35 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 5 Jun 2023 15:18:22 +0100 Subject: [PATCH 06/17] [Backtracing][IRGen] Add a semantic attribute to force frame pointer. The Swift backtracer's frame pointer unwinder cannot work on Linux without this change, because the compiler omits the frame pointer from the function in libSwift_Backtracing that actually captures the stack. rdar://110260855 --- include/swift/AST/SemanticAttrs.def | 3 +++ lib/IRGen/IRGenSIL.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/include/swift/AST/SemanticAttrs.def b/include/swift/AST/SemanticAttrs.def index 2faf8887c1555..a763eaeae767b 100644 --- a/include/swift/AST/SemanticAttrs.def +++ b/include/swift/AST/SemanticAttrs.def @@ -144,5 +144,8 @@ SEMANTICS_ATTR(NO_PERFORMANCE_ANALYSIS, "no_performance_analysis") // that may cause the user to think there is a bug in the compiler. SEMANTICS_ATTR(NO_MOVEONLY_DIAGNOSTICS, "sil.optimizer.moveonly.diagnostic.ignore") +// Force the use of the frame pointer for the specified function +SEMANTICS_ATTR(USE_FRAME_POINTER, "use_frame_pointer") + #undef SEMANTICS_ATTR diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index df01588d89b0f..80d202bc6ec6c 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -21,6 +21,7 @@ #include "swift/AST/IRGenOptions.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Pattern.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/Types.h" #include "swift/Basic/ExternalUnion.h" @@ -1854,6 +1855,11 @@ IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, SILFunction *f) } } + // If we have @_semantics("use_frame_pointer"), force the use of a + // frame pointer for this function. + if (f->hasSemanticsAttr(semantics::USE_FRAME_POINTER)) + CurFn->addFnAttr("frame-pointer", "all"); + // Disable inlining of coroutine functions until we split. if (f->getLoweredFunctionType()->isCoroutine()) { CurFn->addFnAttr(llvm::Attribute::NoInline); From 33e1af113245af5b857d03ec110456da4ef27f86 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 5 Jun 2023 15:53:07 +0100 Subject: [PATCH 07/17] [Backtracing][Linux] Reorganise modules for _Backtracing. Using `SwiftShims` is undesirable - it creates all kinds of build issues, and means shipping the `_SwiftBacktracing.h` header in the SDK, which is not necessary. While we're doing this, add the necessary definitions for reading ELF and DWARF information. rdar://110261712 --- stdlib/public/Backtracing/CMakeLists.txt | 20 +- stdlib/public/Backtracing/Shims.cpp | 45 - .../public/Backtracing/modules/Compression.h | 168 ++++ .../public/Backtracing/modules/FixedLayout.h | 60 ++ .../modules/ImageFormats/Dwarf/dwarf.h | 737 ++++++++++++++++ .../modules/ImageFormats/Dwarf/eh_frame_hdr.h | 61 ++ .../modules/ImageFormats/Elf/elf.h | 787 ++++++++++++++++++ .../ImageFormats/ImageFormats.modulemap | 12 + stdlib/public/Backtracing/modules/OS/Darwin.h | 253 ++++++ stdlib/public/Backtracing/modules/OS/Libc.h | 128 +++ stdlib/public/Backtracing/modules/OS/Linux.h | 42 + .../Backtracing/modules/OS/OS.modulemap | 17 + .../modules/OS/Windows.h} | 25 +- .../Backtracing/modules/Runtime/Runtime.h | 38 + .../Backtracing/modules/module.modulemap | 14 + .../SwiftShims/swift/shims/CMakeLists.txt | 2 - .../swift/shims/_SwiftBacktracing.h | 396 --------- .../SwiftShims/swift/shims/module.modulemap | 4 - 18 files changed, 2343 insertions(+), 466 deletions(-) delete mode 100644 stdlib/public/Backtracing/Shims.cpp create mode 100644 stdlib/public/Backtracing/modules/Compression.h create mode 100644 stdlib/public/Backtracing/modules/FixedLayout.h create mode 100644 stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h create mode 100644 stdlib/public/Backtracing/modules/ImageFormats/Dwarf/eh_frame_hdr.h create mode 100644 stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h create mode 100644 stdlib/public/Backtracing/modules/ImageFormats/ImageFormats.modulemap create mode 100644 stdlib/public/Backtracing/modules/OS/Darwin.h create mode 100644 stdlib/public/Backtracing/modules/OS/Libc.h create mode 100644 stdlib/public/Backtracing/modules/OS/Linux.h create mode 100644 stdlib/public/Backtracing/modules/OS/OS.modulemap rename stdlib/public/{SwiftShims/swift/shims/CrashInfo.h => Backtracing/modules/OS/Windows.h} (58%) create mode 100644 stdlib/public/Backtracing/modules/Runtime/Runtime.h create mode 100644 stdlib/public/Backtracing/modules/module.modulemap delete mode 100644 stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h diff --git a/stdlib/public/Backtracing/CMakeLists.txt b/stdlib/public/Backtracing/CMakeLists.txt index 9f7c7927b2ba2..f6be46d683007 100644 --- a/stdlib/public/Backtracing/CMakeLists.txt +++ b/stdlib/public/Backtracing/CMakeLists.txt @@ -30,12 +30,26 @@ set(BACKTRACING_SOURCES Registers.swift SymbolicatedBacktrace.swift Utils.swift - Shims.cpp + Win32Extras.cpp get-cpu-context.${SWIFT_ASM_EXT} ) -set(BACKTRACING_COMPILE_FLAGS) +set(BACKTRACING_COMPILE_FLAGS + "-Xfrontend;-experimental-spi-only-imports" + "-Xcc;-I${SWIFT_SOURCE_DIR}/include" + "-Xcc;-I${CMAKE_BINARY_DIR}/include" + "-Xcc;-fno-implicit-module-maps" + "-Xcc;-fbuiltin-module-map" + "-Xcc;-fmodule-map-file=${SWIFT_STDLIB_SOURCE_DIR}/public/SwiftShims/swift/shims/module.modulemap" + "-Xcc;-fmodule-map-file=${SWIFT_STDLIB_SOURCE_DIR}/public/Backtracing/modules/module.modulemap") + +###TODO: Add these when we add static linking support +# +#list(APPEND BACKTRACING_COMPILE_FLAGS +# "-Xcc;-I${SWIFT_PATH_TO_ZLIB_SOURCE}" +# "-Xcc;-I${SWIFT_PATH_TO_ZSTD_SOURCE}/lib" +# "-Xcc;-I${SWIFT_PATH_TO_LIBLZMA_SOURCE}/src/liblzma/api") if(SWIFT_ASM_AVAILABLE) list(APPEND BACKTRACING_SOURCES get-cpu-context.${SWIFT_ASM_EXT}) @@ -52,7 +66,7 @@ set(LLVM_OPTIONAL_SOURCES add_swift_target_library(swift_Backtracing ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB ${BACKTRACING_SOURCES} - SWIFT_MODULE_DEPENDS ${concurrency} + SWIFT_MODULE_DEPENDS ${concurrency} _StringProcessing LINK_LIBRARIES ${swift_backtracing_link_libraries} diff --git a/stdlib/public/Backtracing/Shims.cpp b/stdlib/public/Backtracing/Shims.cpp deleted file mode 100644 index 751581687a133..0000000000000 --- a/stdlib/public/Backtracing/Shims.cpp +++ /dev/null @@ -1,45 +0,0 @@ -//===--- Shims.cpp - Operating system shims ---------------------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2023 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 -// -//===----------------------------------------------------------------------===// -// -// Defines operating system shims. -// -//===----------------------------------------------------------------------===// - -// We do this here because we can safely include dlfcn.h from this file -// without worrying about it trying to drag in the Darwin module. -// -// If we did this in _SwiftBacktracing.h as an inline, we'd need to know -// what RTLD_LAZY was for every platform we support. - -#if __has_include() -#include - -#ifdef __cplusplus -namespace swift { -extern "C" { -#endif - -void *_swift_backtrace_dlopen_lazy(const char *path) { - return dlopen(path, RTLD_LAZY); -} - -void *_swift_backtrace_dlsym(void *handle, const char *name) { - return dlsym(handle, name); -} - -#ifdef __cplusplus -} // extern "C" -} // namespace swift -#endif - -#endif - diff --git a/stdlib/public/Backtracing/modules/Compression.h b/stdlib/public/Backtracing/modules/Compression.h new file mode 100644 index 0000000000000..872d8701af26f --- /dev/null +++ b/stdlib/public/Backtracing/modules/Compression.h @@ -0,0 +1,168 @@ +//===--- Compression.h - C decls for compression libraries ------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Includes and definitions to allow us to use the compression libraries +// (zlib, zstd and liblzma) in the backtracing module. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_COMPRESSION_H +#define SWIFT_BACKTRACING_COMPRESSION_H + +#include +#include + +// Right now, we're soft linking to zlib/zstd/liblzma, so that users don't +// need it installed (but if they try to do something that requires it, +// they'll see an error message). +// +// As a result, we've grabbed copies of the relevant definitions here so +// that we don't need to install the -dev packages in order to build Swift. + +#if SWIFT_BACKTRACE_STATIC_ZLIB +#include "zlib.h" +#else +// This is the version we took the z_stream structure from +#define ZLIB_VERSION "1.2.11" + +#define Z_OK 0 +#define Z_STREAM_END 1 + +#define Z_NO_FLUSH 0 + +typedef struct z_stream_s { + uint8_t *next_in; + unsigned avail_in; + unsigned long total_in; + + uint8_t *next_out; + unsigned avail_out; + unsigned long total_out; + + const char *msg; + struct internal_state *state; + + void (*zalloc)(void *, unsigned, unsigned); + void (*zfree)(void *, void *); + void *opaque; + + int data_type; + + unsigned long adler; + unsigned long reserved; +} z_stream; + +typedef z_stream *z_streamp; +#endif + +#if SWIFT_BACKTRACE_STATIC_ZSTD +#include "zstd.h" +#else +typedef struct ZSTD_inBuffer_s { + const void *src; + size_t size; + size_t pos; +} ZSTD_inBuffer; + +typedef struct ZSTD_outBuffer_s { + void *dst; + size_t size; + size_t pos; +} ZSTD_outBuffer; +#endif + +#if SWIFT_BACKTRACE_STATIC_LIBLZMA +#include "lzma.h" +#else +typedef enum { + LZMA_OK = 0, + LZMA_STREAM_END = 1, + LZMA_NO_CHECK = 2, + LZMA_UNSUPPORTED_CHECK = 3, + LZMA_GET_CHECK = 4, + LZMA_MEM_ERROR = 5, + LZMA_MEMLIMIT_ERROR = 6, + LZMA_FORMAT_ERROR = 7, + LZMA_OPTIONS_ERROR = 8, + LZMA_DATA_ERROR = 9, + LZMA_BUF_ERROR = 10, + LZMA_PROG_ERROR = 11, +} lzma_ret; + +typedef enum { + LZMA_RUN = 0, + LZMA_SYNC_FLUSH = 1, + LZMA_FULL_FLUSH = 2, + LZMA_FULL_BARRIER = 4, + LZMA_FINISH = 3 +} lzma_action; + +typedef enum { + LZMA_RESERVED_ENUM = 0, +} lzma_reserved_enum; + +typedef struct { + void *(*alloc)(void *, size_t, size_t); + void (*free)(void *, void *); + void *opaque; +} lzma_allocator; + +typedef struct lzma_internal_s lzma_internal; + +typedef struct { + const uint8_t *next_in; + size_t avail_in; + uint64_t total_in; + + uint8_t *next_out; + size_t avail_out; + uint64_t total_out; + + const lzma_allocator *allocator; + + lzma_internal *internal; + + void *reserved_ptr1; + void *reserved_ptr2; + void *reserved_ptr3; + void *reserved_ptr4; + uint64_t reserved_int1; + uint64_t reserved_int2; + size_t reserved_int3; + size_t reserved_int4; + lzma_reserved_enum reserved_enum1; + lzma_reserved_enum reserved_enum2; +} lzma_stream; + +#define LZMA_STREAM_INIT {0} +#endif + +#ifdef __cplusplus +namespace swift { +extern "C" { +#endif + +// The Swift importer can't cope with complex macros; it will do inline +// functions, however. +static inline lzma_stream lzma_stream_init() { + return (lzma_stream)LZMA_STREAM_INIT; +} +static inline z_stream zlib_stream_init() { + return (z_stream){ 0 }; +} + +#ifdef __cplusplus +} // extern "C" +} // namespace swift +#endif + +#endif // SWIFT_BACKTRACING_COMPRESSION_H diff --git a/stdlib/public/Backtracing/modules/FixedLayout.h b/stdlib/public/Backtracing/modules/FixedLayout.h new file mode 100644 index 0000000000000..e35022f9aca98 --- /dev/null +++ b/stdlib/public/Backtracing/modules/FixedLayout.h @@ -0,0 +1,60 @@ +//===--- FixedLayout.h - Types whose layout must be fixed -------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines types whose in-memory layout must be fixed, which therefore have +// to be defined using C code. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_FIXED_LAYOUT_H +#define SWIFT_BACKTRACING_FIXED_LAYOUT_H + +#ifdef __cplusplus +namespace swift { +extern "C" { +#endif + +#include + +struct x86_64_gprs { + uint64_t _r[16]; + uint64_t rflags; + uint16_t cs, fs, gs, _pad0; + uint64_t rip; + uint64_t valid; +}; + +struct i386_gprs { + uint32_t _r[8]; + uint32_t eflags; + uint16_t segreg[6]; + uint32_t eip; + uint32_t valid; +}; + +struct arm64_gprs { + uint64_t _x[32]; + uint64_t pc; + uint64_t valid; +}; + +struct arm_gprs { + uint32_t _r[16]; + uint32_t valid; +}; + +#ifdef __cpluspus +} // extern "C" +} // namespace swift +#endif + +#endif // SWIFT_BACKTRACING_FIXED_LAYOUT_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h new file mode 100644 index 0000000000000..015f1a66034c9 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h @@ -0,0 +1,737 @@ +//===--- dwarf.h - Definitions of DWARF structures for import into Swift --===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Definitions of DWARF structures for import into Swift code +// +// The types here are taken from the DWARF 5 specification, from +// +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DWARF_H +#define SWIFT_DWARF_H + +#include + +#pragma pack(push, 1) + +/* .. Useful macros ......................................................... */ + +#define DWARF_EXTENSIBLE_ENUM __attribute__((enum_extensibility(open))) +#define DWARF_ENUM(t,n) \ + enum DWARF_EXTENSIBLE_ENUM n: t +#define DWARF_BYTECODE \ + DWARF_EXTENSIBLE_ENUM : Dwarf_Byte + +/* .. Data Representation ................................................... */ + +// Common sizes (these don't change between 32-bit and 64-bit) +typedef uint8_t Dwarf_Byte; +typedef uint16_t Dwarf_Half; +typedef uint32_t Dwarf_Word; +typedef uint64_t Dwarf_Xword; +typedef int8_t Dwarf_Sbyte; +typedef int32_t Dwarf_Sword; +typedef int64_t Dwarf_Sxword; + +// 32-bit sizes +typedef uint32_t Dwarf32_Offset; +typedef uint32_t Dwarf32_Size; +typedef uint32_t Dwarf32_Length; // 0xfffffff0 and above are reserved + +// 64-bit sizes +typedef uint64_t Dwarf64_Offset; +typedef uint64_t Dwarf64_Size; +typedef struct { + uint32_t magic; // 0xffffffff means we're using 64-bit + uint64_t length; +} Dwarf64_Length; + +/* .. Unit Types ............................................................ */ + +// Table 7.2 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_UnitType) { + DW_UT_unknown = 0x00, + DW_UT_compile = 0x01, + DW_UT_type = 0x02, + DW_UT_partial = 0x03, + DW_UT_skeleton = 0x04, + DW_UT_split_compile = 0x05, + DW_UT_split_type = 0x06, + DW_UT_lo_user = 0x80, + DW_UT_hi_user = 0xff +} Dwarf_UnitType; + +/* .. Tags .................................................................. */ + +// Table 7.3 +typedef DWARF_ENUM(Dwarf_Xword, Dwarf_Tag) { + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parmeter = 0x05, + // Reserved = 0x06, + // Reserved = 0x07, + DW_TAG_imported_declaration = 0x08, + // Reserved = 0x09, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + // Reserved = 0x0c, + DW_TAG_member = 0x0d, + // Reserved = 0x0e, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + // Reserved = 0x14, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_parameter = 0x2f, + DW_TAG_template_value_parameter = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + // Reserved = 0x3e, + DW_TAG_condition = 0x3f, + DW_TAG_shared_type = 0x40, + DW_TAG_type_unit = 0x41, + DW_TAG_rvalue_reference_type = 0x42, + DW_TAG_template_alias = 0x43, + DW_TAG_coarray_type = 0x44, + DW_TAG_generic_subrange = 0x45, + DW_TAG_dynamic_type = 0x46, + DW_TAG_atomic_type = 0x47, + DW_TAG_call_site = 0x48, + DW_TAG_call_site_parameter = 0x49, + DW_TAG_skeleton_unit = 0x4a, + DW_TAG_immutable_type = 0x4b, + DW_TAG_lo_user = 0x4080, + DW_TAG_hi_user = 0xffff, +} Dwarf_Tag; + +/* .. Child Determination Encodings ......................................... */ + +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_ChildDetermination) { + DW_CHILDREN_no = 0x00, + DW_CHILDREN_yes = 0x01, +} Dwarf_ChildDetermination; + +/* .. Attribute Encodings ................................................... */ + +// Table 7.5 +typedef enum DWARF_EXTENSIBLE_ENUM Dwarf_Attribute { + DW_AT_sibling = 0x01, // reference + DW_AT_location = 0x02, // exprloc, loclist + DW_AT_name = 0x03, // string + // Reserved = 0x04, // not applicable + // Reserved = 0x05, // not applicable + // Reserved = 0x06, // not applicable + // Reserved = 0x07, // not applicable + // Reserved = 0x08, // not applicable + DW_AT_ordering = 0x09, // constant + // Reserved = 0x0a, // not applicable + DW_AT_byte_size = 0x0b, // constant, exprloc, reference + // Reserved = 0x0c2, // constant, exprloc, reference + DW_AT_bit_size = 0x0d, // constant, exprloc, reference + // Reserved = 0x0e, // not applicable + // Reserved = 0x0f, // not applicable + DW_AT_stmt_list = 0x10, // lineptr + DW_AT_low_pc = 0x11, // address + DW_AT_high_pc = 0x12, // address, constant + DW_AT_language = 0x13, // constant + // Reserved = 0x14, // not applicable + DW_AT_discr = 0x15, // reference + DW_AT_discr_value = 0x16, // constant + DW_AT_visibility = 0x17, // constant + DW_AT_import = 0x18, // reference + DW_AT_string_length = 0x19, // exprloc, loclist, reference + DW_AT_common_reference = 0x1a, // reference + DW_AT_comp_dir = 0x1b, // string + DW_AT_const_value = 0x1c, // block, constant, string + DW_AT_containing_type = 0x1d, // reference + DW_AT_default_value = 0x1e, // constant, reference, flag + // Reserved = 0x1f, // not applicable + DW_AT_inline = 0x20, // constant + DW_AT_is_optional = 0x21, // flag + DW_AT_lower_bound = 0x22, // constant, exprloc, reference + // Reserved = 0x23, // not applicable + // Reserved = 0x24, // not applicable + DW_AT_producer = 0x25, // string + // Reserved = 0x26, // not applicable + DW_AT_prototyped = 0x27, // flag + // Reserved = 0x28, // not applicable + // Reserved = 0x29, // not applicable + DW_AT_return_addr = 0x2a, // exprloc, loclist + // Reserved = 0x2b, // not applicable + DW_AT_start_scope = 0x2c, // constant, rnglist + // Reserved = 0x2d, // not applicable + DW_AT_bit_stride = 0x2e, // constant, exprloc, reference + DW_AT_upper_bound = 0x2f, // constant, exprloc, reference + // Reserved = 0x30, // not applicable + DW_AT_abstract_origin = 0x31, // reference + DW_AT_accessibility = 0x32, // constant + DW_AT_address_class = 0x33, // constant + DW_AT_artificial = 0x34, // flag + DW_AT_base_types = 0x35, // reference + DW_AT_calling_convention = 0x36, // constant + DW_AT_count = 0x37, // constant, exprloc, reference + DW_AT_data_member_location = 0x38, // constant, exprloc, loclist + DW_AT_decl_column = 0x39, // constant + DW_AT_decl_file = 0x3a, // constant + DW_AT_decl_line = 0x3b, // constant + DW_AT_declaration = 0x3c, // flag + DW_AT_discr_list = 0x3d, // block + DW_AT_encoding = 0x3e, // constant + DW_AT_external = 0x3f, // flag + DW_AT_frame_base = 0x40, // exprloc, loclist + DW_AT_friend = 0x41, // reference + DW_AT_identifier_case = 0x42, // constant + // Reserved = 0x43, // macptr + DW_AT_namelist_item = 0x44, // reference + DW_AT_priority = 0x45, // reference + DW_AT_segment = 0x46, // exprloc, loclist + DW_AT_specification = 0x47, // reference + DW_AT_static_link = 0x48, // exprloc, loclist + DW_AT_type = 0x49, // reference + DW_AT_use_location = 0x4a, // exprloc, loclist + DW_AT_variable_parameter = 0x4b, // flag + DW_AT_virtuality = 0x4c, // constant + DW_AT_vtable_elem_location = 0x4d, // exprloc, loclist + DW_AT_allocated = 0x4e, // constant, exprloc, reference + DW_AT_associated = 0x4f, // constant, exprloc, reference + DW_AT_data_location = 0x50, // exprloc + DW_AT_byte_stride = 0x51, // constant, exprloc, reference + DW_AT_entry_pc = 0x52, // address, constant + DW_AT_use_UTF8 = 0x53, // flag + DW_AT_extension = 0x54, // reference + DW_AT_ranges = 0x55, // rnglist + DW_AT_trampoline = 0x56, // address, flag, reference, string + DW_AT_call_column = 0x57, // constant + DW_AT_call_file = 0x58, // constant + DW_AT_call_line = 0x59, // constant + DW_AT_description = 0x5a, // string + DW_AT_binary_scale = 0x5b, // constant + DW_AT_decimal_scale = 0x5c, // constant + DW_AT_small = 0x5d, // reference + DW_AT_decimal_sign = 0x5e, // constant + DW_AT_digit_count = 0x5f, // constant + DW_AT_picture_string = 0x60, // string + DW_AT_mutable = 0x61, // flag + DW_AT_threads_scaled = 0x62, // flag + DW_AT_explicit = 0x63, // flag + DW_AT_object_pointer = 0x64, // reference + DW_AT_endianity = 0x65, // constant + DW_AT_elemental = 0x66, // flag + DW_AT_pure = 0x67, // flag + DW_AT_recursive = 0x68, // flag + DW_AT_signature = 0x69, // reference + DW_AT_main_subprogram = 0x6a, // flag + DW_AT_data_bit_offset = 0x6b, // constant + DW_AT_const_expr = 0x6c, // flag + DW_AT_enum_class = 0x6d, // flag + DW_AT_linkage_name = 0x6e, // string + DW_AT_string_length_bit_size = 0x6f, // constant + DW_AT_string_length_byte_size = 0x70, // constant + DW_AT_rank = 0x71, // constant, exprloc + DW_AT_str_offsets_base = 0x72, // stroffsetsptr + DW_AT_addr_base = 0x73, // addrptr + DW_AT_rnglists_base = 0x74, // rnglistsptr + // Reserved = 0x75, // Unused + DW_AT_dwo_name = 0x76, // string + DW_AT_reference = 0x77, // flag + DW_AT_rvalue_reference = 0x78, // flag + DW_AT_macros = 0x79, // macptr + DW_AT_call_all_calls = 0x7a, // flag + DW_AT_call_all_source_calls = 0x7b, // flag + DW_AT_call_all_tail_calls = 0x7c, // flag + DW_AT_call_return_pc = 0x7d, // address + DW_AT_call_value = 0x7e, // exprloc + DW_AT_call_origin = 0x7f, // exprloc + DW_AT_call_parameter = 0x80, // reference + DW_AT_call_pc = 0x81, // address + DW_AT_call_tail_call = 0x82, // flag + DW_AT_call_target = 0x83, // exprloc + DW_AT_call_target_clobbered = 0x84, // exprloc + DW_AT_call_data_location = 0x85, // exprloc + DW_AT_call_data_value = 0x86, // exprloc + DW_AT_noreturn = 0x87, // flag + DW_AT_alignment = 0x88, // constant + DW_AT_export_symbols = 0x89, // flag + DW_AT_deleted = 0x8a, // flag + DW_AT_defaulted = 0x8b, // constant + DW_AT_loclists_base = 0x8c, // loclistsptr + DW_AT_lo_user = 0x2000, // — + DW_AT_hi_user = 0x3fff, // — +} Dwarf_Attribute; + +/* .. Form Encodings ........................................................ */ + +// Table 7.6 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_Form) { + DW_FORM_addr = 0x01, + // Reserved = 0x02, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_strx = 0x1a, + DW_FORM_addrx = 0x1b, + DW_FORM_ref_sup4 = 0x1c, + DW_FORM_strp_sup = 0x1d, + DW_FORM_data16 = 0x1e, + DW_FORM_line_strp = 0x1f, + DW_FORM_ref_sig8 = 0x20, + DW_FORM_implicit_const = 0x21, + DW_FORM_loclistx = 0x22, + DW_FORM_rnglistx = 0x23, + DW_FORM_ref_sup8 = 0x24, + DW_FORM_strx1 = 0x25, + DW_FORM_strx2 = 0x26, + DW_FORM_strx3 = 0x27, + DW_FORM_strx4 = 0x28, + DW_FORM_addrx1 = 0x29, + DW_FORM_addrx2 = 0x2a, + DW_FORM_addrx3 = 0x2b, + DW_FORM_addrx4 = 0x2c, +} Dwarf_Form; + +/* .. DWARF Expressions ..................................................... */ + +// Table 7.9 +enum DWARF_BYTECODE { + // Reserved = 0x01, + // Reserved = 0x02, + DW_OP_addr = 0x03, + // Reserved = 0x04, + // Reserved = 0x05, + DW_OP_deref = 0x06, + // Reserved = 0x07, + DW_OP_const1u = 0x08, + DW_OP_const1s = 0x09, + DW_OP_const2u = 0x0a, + DW_OP_const2s = 0x0b, + DW_OP_const4u = 0x0c, + DW_OP_const4s = 0x0d, + DW_OP_const8u = 0x0e, + DW_OP_const8s = 0x0f, + DW_OP_constu = 0x10, + DW_OP_consts = 0x11, + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1a, + DW_OP_div = 0x1b, + DW_OP_minus = 0x1c, + DW_OP_mod = 0x1d, + DW_OP_mul = 0x1e, + DW_OP_neg = 0x1f, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_bra = 0x28, + DW_OP_eq = 0x29, + DW_OP_ge = 0x2a, + DW_OP_gt = 0x2b, + DW_OP_le = 0x2c, + DW_OP_lt = 0x2d, + DW_OP_ne = 0x2e, + DW_OP_skip = 0x2f, + + DW_OP_lit0 = 0x30, + DW_OP_lit1 = 0x31, + DW_OP_lit2 = 0x32, + DW_OP_lit3 = 0x33, + DW_OP_lit4 = 0x34, + DW_OP_lit5 = 0x35, + DW_OP_lit6 = 0x36, + DW_OP_lit7 = 0x37, + DW_OP_lit8 = 0x38, + DW_OP_lit9 = 0x39, + DW_OP_lit10 = 0x3a, + DW_OP_lit11 = 0x3b, + DW_OP_lit12 = 0x3c, + DW_OP_lit13 = 0x3d, + DW_OP_lit14 = 0x3e, + DW_OP_lit15 = 0x3f, + DW_OP_lit16 = 0x40, + DW_OP_lit17 = 0x41, + DW_OP_lit18 = 0x42, + DW_OP_lit19 = 0x43, + DW_OP_lit20 = 0x44, + DW_OP_lit21 = 0x45, + DW_OP_lit22 = 0x46, + DW_OP_lit23 = 0x47, + DW_OP_lit24 = 0x48, + DW_OP_lit25 = 0x49, + DW_OP_lit26 = 0x4a, + DW_OP_lit27 = 0x4b, + DW_OP_lit28 = 0x4c, + DW_OP_lit29 = 0x4d, + DW_OP_lit30 = 0x4e, + DW_OP_lit31 = 0x4f, + + DW_OP_reg0 = 0x50, + DW_OP_reg1 = 0x51, + DW_OP_reg2 = 0x52, + DW_OP_reg3 = 0x53, + DW_OP_reg4 = 0x54, + DW_OP_reg5 = 0x55, + DW_OP_reg6 = 0x56, + DW_OP_reg7 = 0x57, + DW_OP_reg8 = 0x58, + DW_OP_reg9 = 0x59, + DW_OP_reg10 = 0x5a, + DW_OP_reg11 = 0x5b, + DW_OP_reg12 = 0x5c, + DW_OP_reg13 = 0x5d, + DW_OP_reg14 = 0x5e, + DW_OP_reg15 = 0x5f, + DW_OP_reg16 = 0x60, + DW_OP_reg17 = 0x61, + DW_OP_reg18 = 0x62, + DW_OP_reg19 = 0x63, + DW_OP_reg20 = 0x64, + DW_OP_reg21 = 0x65, + DW_OP_reg22 = 0x66, + DW_OP_reg23 = 0x67, + DW_OP_reg24 = 0x68, + DW_OP_reg25 = 0x69, + DW_OP_reg26 = 0x6a, + DW_OP_reg27 = 0x6b, + DW_OP_reg28 = 0x6c, + DW_OP_reg29 = 0x6d, + DW_OP_reg30 = 0x6e, + DW_OP_reg31 = 0x6f, + + DW_OP_breg0 = 0x70, + DW_OP_breg1 = 0x71, + DW_OP_breg2 = 0x72, + DW_OP_breg3 = 0x73, + DW_OP_breg4 = 0x74, + DW_OP_breg5 = 0x75, + DW_OP_breg6 = 0x76, + DW_OP_breg7 = 0x77, + DW_OP_breg8 = 0x78, + DW_OP_breg9 = 0x79, + DW_OP_breg10 = 0x7a, + DW_OP_breg11 = 0x7b, + DW_OP_breg12 = 0x7c, + DW_OP_breg13 = 0x7d, + DW_OP_breg14 = 0x7e, + DW_OP_breg15 = 0x7f, + DW_OP_breg16 = 0x80, + DW_OP_breg17 = 0x81, + DW_OP_breg18 = 0x82, + DW_OP_breg19 = 0x83, + DW_OP_breg20 = 0x84, + DW_OP_breg21 = 0x85, + DW_OP_breg22 = 0x86, + DW_OP_breg23 = 0x87, + DW_OP_breg24 = 0x88, + DW_OP_breg25 = 0x89, + DW_OP_breg26 = 0x8a, + DW_OP_breg27 = 0x8b, + DW_OP_breg28 = 0x8c, + DW_OP_breg29 = 0x8d, + DW_OP_breg30 = 0x8e, + DW_OP_breg31 = 0x8f, + + DW_OP_regx = 0x90, + DW_OP_fbreg = 0x91, + DW_OP_bregx = 0x92, + + DW_OP_piece = 0x93, + DW_OP_deref_size = 0x94, + DW_OP_xderef_size = 0x95, + DW_OP_nop = 0x96, + DW_OP_push_object_address = 0x97, + DW_OP_call2 = 0x98, + DW_OP_call4 = 0x99, + DW_OP_call_ref = 0x9a, + DW_OP_form_tls_address = 0x9b, + DW_OP_call_frame_cfa = 0x9c, + DW_OP_bit_piece = 0x9d, + DW_OP_implicit_value = 0x9e, + DW_OP_stack_value = 0x9f, + DW_OP_implicit_pointer = 0xa0, + DW_OP_addrx = 0xa1, + DW_OP_constx = 0xa2, + DW_OP_entry_value = 0xa3, + DW_OP_const_type = 0xa4, + DW_OP_regval_type = 0xa5, + DW_OP_deref_type = 0xa6, + DW_OP_xderef_type = 0xa7, + DW_OP_convert = 0xa8, + DW_OP_reinterpret = 0xa9, + DW_OP_lo_user = 0xe0, + DW_OP_hi_user = 0xff, +}; + +/* .. Line Number Information ............................................... */ + +// Table 7.25 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_LNS_Opcode) { + DW_LNS_extended = 0x00, + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c, +} Dwarf_LNS_Opcode; + +// Table 7.26 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_LNE_Opcode) { + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_define_file = 0x03, + DW_LNE_set_discriminator = 0x04, + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff, +} Dwarf_LNE_Opcode; + +// Table 7.27 +typedef DWARF_ENUM(Dwarf_Half, Dwarf_Lhdr_Format) { + DW_LNCT_path = 0x0001, + DW_LNCT_directory_index = 0x0002, + DW_LNCT_timestamp = 0x0003, + DW_LNCT_size = 0x0004, + DW_LNCT_MD5 = 0x0005, + DW_LNCT_lo_user = 0x2000, + DW_LNCT_hi_user = 0x3fff, +} Dwarf_Lhdr_Format; + +// 6.2.4 The Line Number Program Header +typedef struct { + Dwarf32_Length length; + Dwarf_Half version; + Dwarf_Byte address_size; + Dwarf_Byte segment_selector_size; + Dwarf32_Size header_length; + Dwarf_Byte minimum_instruction_length; + Dwarf_Byte maximum_operations_per_instruction; + Dwarf_Byte default_is_stmt; + Dwarf_Sbyte line_base; + Dwarf_Byte line_range; + Dwarf_Byte opcode_base; + + // Variable length fields: + // Dwarf_Byte standard_opcode_lengths[]; + // Dwarf_Byte directory_entry_format_count; + // Dwarf_ULEB128 directory_entry_format[]; + // Dwarf_ULEB128 directories_count; + // encoded directories[]; + // Dwarf_Byte file_name_entry_format_count; + // Dwarf_ULEB128 file_name_entry_format; + // Dwarf_ULEB128 file_names_count; + // encoded file_names[]; +} DWARF32_Lhdr; + +typedef struct { + Dwarf64_Length length; + Dwarf_Half version; + Dwarf_Byte address_size; + Dwarf_Byte segment_selector_size; + Dwarf64_Size header_length; + Dwarf_Byte minimum_instruction_length; + Dwarf_Byte maximum_operations_per_instruction; + Dwarf_Byte default_is_stmt; + Dwarf_Sbyte line_base; + Dwarf_Byte line_range; + Dwarf_Byte opcode_base; + + // Variable length fields: + // Dwarf_Byte standard_opcode_lengths[]; + // Dwarf_Byte directory_entry_format_count; + // Dwarf_ULEB128 directory_entry_format[]; + // Dwarf_ULEB128 directories_count; + // encoded directories[]; + // Dwarf_Byte file_name_entry_format_count; + // Dwarf_ULEB128 file_name_entry_format; + // Dwarf_ULEB128 file_names_count; + // encoded file_names[]; +} DWARF64_Lhdr; + +/* .. Call frame instruction encodings ...................................... */ + +// Table 7.29 +enum DWARF_BYTECODE { + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f +}; + +/* .. Range list encodings .................................................. */ + +// Table 7.30 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_RLE_Entry) { + DW_RLE_end_of_list = 0x00, + DW_RLE_base_addressx = 0x01, + DW_RLE_startx_endx = 0x02, + DW_RLE_startx_length = 0x03, + DW_RLE_offset_pair = 0x04, + DW_RLE_base_address = 0x05, + DW_RLE_start_end = 0x06, + DW_RLE_start_length = 0x07 +} Dwarf_RLE_Entry; + +/* .. Common Information Entry .............................................. */ + +// Table 4.5: Common Information Entry (CIE) +typedef struct { + Dwarf32_Length length; + Dwarf32_Offset CIE_id; + Dwarf_Byte version; + + // Followed by variable length fields as follows: + // + // Dwarf_Byte augmentation[]; // NUL terminated string + // Dwarf_Byte address_size; + // Dwarf_Byte segment_selector_size; + // Dwarf_ULEB128 code_alignment_factor; + // Dwarf_SLEB128 data_alignment_factor; + // Dwarf_ULEB128 return_address_register; + // Dwarf_Byte initial_instructions[]; + // Dwarf_Byte padding[] +} Dwarf32_CIEHdr; + +typedef struct { + Dwarf64_Length length; + Dwarf64_Offset CIE_id; + Dwarf_Byte version; + + // Followed by variable length fields as follows: + // + // Dwarf_Byte augmentation[]; // NUL terminated string + // Dwarf_Byte address_size; + // Dwarf_Byte segment_selector_size; + // Dwarf_ULEB128 code_alignment_factor; + // Dwarf_SLEB128 data_alignment_factor; + // Dwarf_ULEB128 return_address_register; + // Dwarf_Byte initial_instructions[]; + // Dwarf_Byte padding[] +} Dwarf64_CIEHdr; + +/* .. Frame Descriptor Entry ................................................ */ + +// Table 4.7: Frame Descriptor Entry (FDE) +typedef struct { + Dwarf32_Length length; + Dwarf32_Offset CIE_pointer; // Offset into the section that points at CIE + + // Followed by variable fields as follows: + // + // Dwarf_FarAddr initial_location; // May include segment selector and address + // Dwarf_Addr address_range; // Number of bytes of instructions + // Dwarf_Byte instructions[]; + // Dwarf_Byte padding[]; +} Dwarf32_FDEHdr; + +#pragma pack(pop) + +#endif // SWIFT_DWARF_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/eh_frame_hdr.h b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/eh_frame_hdr.h new file mode 100644 index 0000000000000..87988bea54781 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/eh_frame_hdr.h @@ -0,0 +1,61 @@ +//===--- eh_frame_hdr.h - DWARF EH frame header definitions ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the format of the .eh_frame_hdr section, which isn't part of the +// DWARF specification (it's a GNU extension). +// +// https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_EH_FRAME_HDR_H +#define SWIFT_EH_FRAME_HDR_H + +#include + +/* .. Useful macros ......................................................... */ + +#define EH_FRAME_OPTIONS(t,n) \ + t n; \ + enum __attribute__((flag_enum,enum_extensibility(open))): t + +/* .. .eh_frame_hdr header .................................................. */ + +struct EHFrameHdr { + uint8_t version; + uint8_t eh_frame_ptr_enc; + uint8_t fde_count_enc; + uint8_t table_enc; + // encoded eh_frame_ptr; + // encoded fde_count; + // encoded binary_search_table[fde_count]; +}; + +/* .. Constants ............................................................. */ + +typedef EH_FRAME_OPTIONS(uint8_t, EHFrameEncoding) { + DW_EH_PE_omit = 0xff, // No value is present + DW_EH_PE_uleb128 = 0x01, // Unsigned value encoded using LEB128 + DW_EH_PE_udata2 = 0x02, // A 2-byte unsigned value + DW_EH_PE_udata4 = 0x03, // A 4-byte unsigned value + DW_EH_PE_udata8 = 0x04, // An 8-byte unsigned value + DW_EH_PE_sleb128 = 0x09, // Signed value encoded using LEB128 + DW_EH_PE_sdata2 = 0x0a, // A 2-byte signed value + DW_EH_PE_sdata4 = 0x0b, // A 4-byte signed value + DW_EH_PE_sdata8 = 0x0c, // An 8-byte signed value + + DW_EH_PE_absptr = 0x00, // Absolute, used with no modification + DW_EH_PE_pcrel = 0x10, // Relative to the current program counter + DW_EH_PE_datarel = 0x30, // Relative to the beginning of the .eh_frame_hdr +}; + +#endif // SWIFT_EH_FRAME_HDR_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h b/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h new file mode 100644 index 0000000000000..ed129dac2b350 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h @@ -0,0 +1,787 @@ +//===--- elf.h - Definitions of ELF structures for import into Swift ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Definitions of ELF structures for import into Swift code +// +// The types here are taken from the System V ABI update, here: +// +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_ELF_H +#define SWIFT_ELF_H + +#include + +/* .. Useful macros ......................................................... */ + +#define ELF_ENUM(t,n) \ + enum __attribute__((enum_extensibility(open))) n: t +#define ELF_OPTIONS(t,n) \ + t n; \ + enum __attribute__((flag_enum,enum_extensibility(open))): t + +/* .. Data Representation ................................................... */ + +// Common sizes (these don't change between 32-bit and 64-bit) +typedef uint8_t Elf_Byte; +typedef uint16_t Elf_Half; +typedef uint32_t Elf_Word; +typedef uint64_t Elf_Xword; +typedef int32_t Elf_Sword; +typedef int64_t Elf_Sxword; + +// 32-bit sizes (includes some aliases of the above, for compatibility) +typedef Elf_Byte Elf32_Byte; +typedef uint32_t Elf32_Addr; +typedef Elf_Half Elf32_Half; +typedef uint32_t Elf32_Off; +typedef Elf_Sword Elf32_Sword; +typedef Elf_Word Elf32_Word; + +// 64-bit sizes (includes some aliases of the above, for compatibility) +typedef Elf_Byte Elf64_Byte; +typedef uint64_t Elf64_Addr; +typedef uint64_t Elf64_Off; +typedef Elf_Half Elf64_Half; +typedef Elf_Word Elf64_Word; +typedef Elf_Sword Elf64_Sword; +typedef Elf_Xword Elf64_Xword; +typedef Elf_Sxword Elf64_Sxword; + +/* .. Constants ............................................................. */ + +// e_type values +typedef ELF_ENUM(Elf_Half, Elf_Ehdr_Type) { + ET_NONE = 0, // No file type + ET_REL = 1, // Relocatable file + ET_EXEC = 2, // Executable file + ET_DYN = 3, // Shared object file + ET_CORE = 4, // Core file + ET_LOOS = 0xfe00, // Operating system specific + ET_HIOS = 0xfeff, // Operating system specific + ET_LOPROC = 0xff00, // Processor specific + ET_HIPROC = 0xffff, // Processor specific +} Elf_Ehdr_Type; + +// e_machine values +typedef ELF_ENUM(Elf_Half, Elf_Ehdr_Machine) { + EM_NONE = 0, // No machine + EM_M32 = 1, // AT&T WE 32100 + EM_SPARC = 2, // SPARC + EM_386 = 3, // Intel 80386 + EM_68K = 4, // Motorola 68000 + EM_88K = 5, // Motorola 88000 + + EM_860 = 7, // Intel 80860 + EM_MIPS = 8, // MIPS I Architecture + EM_S370 = 9, // IBM System/370 Processor + EM_MIPS_RS3_LE = 10, // MIPS RS3000 Little-endian + + EM_PARISC = 15, // Hewlett-Packard PA-RISC + + EM_VPP500 = 17, // Fujitsu VPP500 + EM_SPARC32PLUS = 18, // Enhanced instruction set SPARC + EM_960 = 19, // Intel 80960 + EM_PPC = 20, // PowerPC + EM_PPC64 = 21, // 64-bit PowerPC + EM_S390 = 22, // IBM System/390 Processor + EM_SPU = 23, // IBM SPU/SPC + + EM_V800 = 36, // NEC V800 + EM_FR20 = 37, // Fujitsu FR20 + EM_RH32 = 38, // TRW RH-32 + EM_RCE = 39, // Motorola RCE + EM_ARM = 40, // ARM 32-bit architecture (AARCH32) + EM_ALPHA = 41, // Digital Alpha + EM_SH = 42, // Hitachi SH + EM_SPARCV9 = 43, // SPARC Version 9 + EM_TRICORE = 44, // Siemens TriCore embedded processor + EM_ARC = 45, // Argonaut RISC Core, Argonaut Technologies Inc. + EM_H8_300 = 46, // Hitachi H8/300 + EM_H8_300H = 47, // Hitachi H8/300H + EM_H8S = 48, // Hitachi H8S + EM_H8_500 = 49, // Hitachi H8/500 + EM_IA_64 = 50, // Intel IA-64 processor architecture + EM_MIPS_X = 51, // Stanford MIPS-X + EM_COLDFIRE = 52, // Motorola ColdFire + EM_68HC12 = 53, // Motorola M68HC12 + EM_MMA = 54, // Fujitsu MMA Multimedia Accelerator + EM_PCP = 55, // Siemens PCP + EM_NCPU = 56, // Sony nCPU embedded RISC processor + EM_NDR1 = 57, // Denso NDR1 microprocessor + EM_STARCORE = 58, // Motorola Star*Core processor + EM_ME16 = 59, // Toyota ME16 processor + EM_ST100 = 60, // STMicroelectronics ST100 processor + EM_TINYJ = 61, // Advanced Logic Corp. TinyJ embedded processor family + EM_X86_64 = 62, // AMD x86-64 architecture + EM_PDSP = 63, // Sony DSP Processor + EM_PDP10 = 64, // Digital Equipment Corp. PDP-10 + EM_PDP11 = 65, // Digital Equipment Corp. PDP-11 + EM_FX66 = 66, // Siemens FX66 microcontroller + EM_ST9PLUS = 67, // STMicroelectronics ST9+ 8/16 bit microcontroller + EM_ST7 = 68, // STMicroelectronics ST7 8-bit microcontroller + EM_68HC16 = 69, // Motorola MC68HC16 Microcontroller + EM_68HC11 = 70, // Motorola MC68HC11 Microcontroller + EM_68HC08 = 71, // Motorola MC68HC08 Microcontroller + EM_68HC05 = 72, // Motorola MC68HC05 Microcontroller + EM_SVX = 73, // Silicon Graphics SVx + EM_ST19 = 74, // STMicroelectronics ST19 8-bit microcontroller + EM_VAX = 75, // Digital VAX + EM_CRIS = 76, // Axis Communications 32-bit embedded processor + EM_JAVELIN = 77, // Infineon Technologies 32-bit embedded processor + EM_FIREPATH = 78, // Element 14 64-bit DSP Processor + EM_ZSP = 79, // LSI Logic 16-bit DSP Processor + EM_MMIX = 80, // Donald Knuth's educational 64-bit processor + EM_HUANY = 81, // Harvard University machine-independent object files + EM_PRISM = 82, // SiTera Prism + EM_AVR = 83, // Atmel AVR 8-bit microcontroller + EM_FR30 = 84, // Fujitsu FR30 + EM_D10V = 85, // Mitsubishi D10V + EM_D30V = 86, // Mitsubishi D30V + EM_V850 = 87, // NEC v850 + EM_M32R = 88, // Mitsubishi M32R + EM_MN10300 = 89, // Matsushita MN10300 + EM_MN10200 = 90, // Matsushita MN10200 + EM_PJ = 91, // picoJava + EM_OPENRISC = 92, // OpenRISC 32-bit embedded processor + EM_ARC_COMPACT = 93, // ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5) + EM_XTENSA = 94, // Tensilica Xtensa Architecture + EM_VIDEOCORE = 95, // Alphamosaic VideoCore processor + EM_TMM_GPP = 96, // Thompson Multimedia General Purpose Processor + EM_NS32K = 97, // National Semiconductor 32000 series + EM_TPC = 98, // Tenor Network TPC processor + EM_SNP1K = 99, // Trebia SNP 1000 processor + EM_ST200 = 100, // STMicroelectronics (www.st.com) ST200 microcontroller + EM_IP2K = 101, // Ubicom IP2xxx microcontroller family + EM_MAX = 102, // MAX Processor + EM_CR = 103, // National Semiconductor CompactRISC microprocessor + EM_F2MC16 = 104, // Fujitsu F2MC16 + EM_MSP430 = 105, // Texas Instruments embedded microcontroller msp430 + EM_BLACKFIN = 106, // Analog Devices Blackfin (DSP) processor + EM_SE_C33 = 107, // S1C33 Family of Seiko Epson processors + EM_SEP = 108, // Sharp embedded microprocessor + EM_ARCA = 109, // Arca RISC Microprocessor + EM_UNICORE = 110, // Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University + EM_EXCESS = 111, // eXcess: 16/32/64-bit configurable embedded CPU + EM_DXP = 112, // Icera Semiconductor Inc. Deep Execution Processor + EM_ALTERA_NIOS2 = 113, // Altera Nios II soft-core processor + EM_CRX = 114, // National Semiconductor CompactRISC CRX microprocessor + EM_XGATE = 115, // Motorola XGATE embedded processor + EM_C166 = 116, // Infineon C16x/XC16x processor + EM_M16C = 117, // Renesas M16C series microprocessors + EM_DSPIC30F = 118, // Microchip Technology dsPIC30F Digital Signal Controller + EM_CE = 119, // Freescale Communication Engine RISC core + EM_M32C = 120, // Renesas M32C series microprocessors + + EM_TSK3000 = 131, // Altium TSK3000 core + EM_RS08 = 132, // Freescale RS08 embedded processor + EM_SHARC = 133, // Analog Devices SHARC family of 32-bit DSP processors + EM_ECOG2 = 134, // Cyan Technology eCOG2 microprocessor + EM_SCORE7 = 135, // Sunplus S+core7 RISC processor + EM_DSP24 = 136, // New Japan Radio (NJR) 24-bit DSP Processor + EM_VIDEOCORE3 = 137, // Broadcom VideoCore III processor + EM_LATTICEMICO32 = 138, // RISC processor for Lattice FPGA architecture + EM_SE_C17 = 139, // Seiko Epson C17 family + EM_TI_C6000 = 140, // The Texas Instruments TMS320C6000 DSP family + EM_TI_C2000 = 141, // The Texas Instruments TMS320C2000 DSP family + EM_TI_C5500 = 142, // The Texas Instruments TMS320C55x DSP family + + EM_MMDSP_PLUS = 160, // STMicroelectronics 64bit VLIW Data Signal Processor + EM_CYPRESS_M8C = 161, // Cypress M8C microprocessor + EM_R32C = 162, // Renesas R32C series microprocessors + EM_TRIMEDIA = 163, // NXP Semiconductors TriMedia architecture family + EM_QDSP6 = 164, // QUALCOMM DSP6 Processor + EM_8051 = 165, // Intel 8051 and variants + EM_STXP7X = 166, // STMicroelectronics STxP7x family of configurable and extensible RISC processors + EM_NDS32 = 167, // Andes Technology compact code size embedded RISC processor family + EM_ECOG1 = 168, // Cyan Technology eCOG1X family + EM_ECOG1X = 168, // Cyan Technology eCOG1X family + EM_MAXQ30 = 169, // Dallas Semiconductor MAXQ30 Core Micro-controllers + EM_XIMO16 = 170, // New Japan Radio (NJR) 16-bit DSP Processor + EM_MANIK = 171, // M2000 Reconfigurable RISC Microprocessor + EM_CRAYNV2 = 172, // Cray Inc. NV2 vector architecture + EM_RX = 173, // Renesas RX family + EM_METAG = 174, // Imagination Technologies META processor architecture + EM_MCST_ELBRUS = 175, // MCST Elbrus general purpose hardware architecture + EM_ECOG16 = 176, // Cyan Technology eCOG16 family + EM_CR16 = 177, // National Semiconductor CompactRISC CR16 16-bit microprocessor + EM_ETPU = 178, // Freescale Extended Time Processing Unit + EM_SLE9X = 179, // Infineon Technologies SLE9X core + EM_L10M = 180, // Intel L10M + EM_K10M = 181, // Intel K10M + + EM_AARCH64 = 183, // ARM 64-bit architecture (AARCH64) + + EM_AVR32 = 185, // Atmel Corporation 32-bit microprocessor family + EM_STM8 = 186, // STMicroeletronics STM8 8-bit microcontroller + EM_TILE64 = 187, // Tilera TILE64 multicore architecture family + EM_TILEPRO = 188, // Tilera TILEPro multicore architecture family + EM_MICROBLAZE = 189, // Xilinx MicroBlaze 32-bit RISC soft processor core + EM_CUDA = 190, // NVIDIA CUDA architecture + EM_TILEGX = 191, // Tilera TILE-Gx multicore architecture family + EM_CLOUDSHIELD = 192, // CloudShield architecture family + EM_COREA_1ST = 193, // KIPO-KAIST Core-A 1st generation processor family + EM_COREA_2ND = 194, // KIPO-KAIST Core-A 2nd generation processor family + EM_ARC_COMPACT2 = 195, // Synopsys ARCompact V2 + EM_OPEN8 = 196, // Open8 8-bit RISC soft processor core + EM_RL78 = 197, // Renesas RL78 family + EM_VIDEOCORE5 = 198, // Broadcom VideoCore V processor + EM_78KOR = 199, // Renesas 78KOR family + EM_56800EX = 200, // Freescale 56800EX Digital Signal Controller (DSC) + EM_BA1 = 201, // Beyond BA1 CPU architecture + EM_BA2 = 202, // Beyond BA2 CPU architecture + EM_XCORE = 203, // XMOS xCORE processor family + EM_MCHP_PIC = 204, // Microchip 8-bit PIC(r) family +} Elf_Ehdr_Machine; + +// e_version values +typedef ELF_ENUM(Elf_Word, Elf_Ehdr_Version) { + EV_NONE = 0, // Invalid version + EV_CURRENT = 1, // Current version +} Elf_Ehdr_Version; + +// e_ident[] identification indices +enum { + EI_MAG0 = 0, // File identification = 0x7f + EI_MAG1 = 1, // File identification = 'E' 0x45 + EI_MAG2 = 2, // File identification = 'L' 0x4c + EI_MAG3 = 3, // File identification = 'F' 0x46 + EI_CLASS = 4, // File class + EI_DATA = 5, // Data encoding + EI_VERSION = 6, // File version + EI_OSABI = 7, // Operating system/ABI identification + EI_ABIVERSION = 8, // ABI version + EI_PAD = 9, // Start of padding bytes +}; + +// Magic number +enum : uint8_t { + ELFMAG0 = 0x7f, + ELFMAG1 = 'E', + ELFMAG2 = 'L', + ELFMAG3 = 'F', +}; + +// File class +typedef ELF_ENUM(Elf_Byte, Elf_Ehdr_Class) { + ELFCLASSNONE = 0, // Invalid class + ELFCLASS32 = 1, // 32-bit objects + ELFCLASS64 = 2, // 64-bit objects +} Elf_Ehdr_Class; + +// Data encoding +typedef ELF_ENUM(Elf_Byte, Elf_Ehdr_Data) { + ELFDATANONE = 0, // Invalid data encoding + ELFDATA2LSB = 1, // 2's complement Little Endian + ELFDATA2MSB = 2, // 2's complement Big Endian +} Elk_Ehdr_Data; + +// OS/ABI identification +typedef ELF_ENUM(Elf_Byte, Elf_Ehdr_OsAbi) { + ELFOSABI_NONE = 0, // No extensions or unspecified + ELFOSABI_HPUX = 1, // Hewlett-Packard HP-UX + ELFOSABI_NETBSD = 2, // NetBSD + ELFOSABI_GNU = 3, // GNU + ELFOSABI_LINUX = 3, // Linux (historical - alias for ELFOSABI_GNU) + ELFOSABI_SOLARIS = 6, // Sun Solaris + ELFOSABI_AIX = 7, // AIX + ELFOSABI_IRIX = 8, // IRIX + ELFOSABI_FREEBSD = 9, // FreeBSD + ELFOSABI_TRU64 = 10, // Compaq TRU64 UNIX + ELFOSABI_MODESTO = 11, // Novell Modesto + ELFOSABI_OPENBSD = 12, // Open BSD + ELFOSABI_OPENVMS = 13, // Open VMS + ELFOSABI_NSK = 14, // Hewlett-Packard Non-Stop Kernel + ELFOSABI_AROS = 15, // Amiga Research OS + ELFOSABI_FENIXOS = 16, // The FenixOS highly scalable multi-core OS +} Elf_Ehdr_OsAbi; + +// Special Section Indices +enum { + SHN_UNDEF = 0, // Undefined, missing, irrelevant or meaningless + + SHN_LORESERVE = 0xff00, // Lower bound of reserved indices + + SHN_LOPROC = 0xff00, // Processor specific + SHN_HIPROC = 0xff1f, + + SHN_LOOS = 0xff20, // OS specific + SHN_HIOS = 0xff3f, + + SHN_ABS = 0xfff1, // Absolute (symbols are not relocated) + SHN_COMMON = 0xfff2, // Common + SHN_XINDEX = 0xffff, // Indicates section header index is elsewhere + + SHN_HIRESERVE = 0xffff, +}; + +// Section types +typedef ELF_ENUM(Elf_Word, Elf_Shdr_Type) { + SHT_NULL = 0, // Inactive + SHT_PROGBITS = 1, // Program-defined information + SHT_SYMTAB = 2, // Symbol table + SHT_STRTAB = 3, // String table + SHT_RELA = 4, // Relocation entries with explicit addends + SHT_HASH = 5, // Symbol hash table + SHT_DYNAMIC = 6, // Information for dynamic linking + SHT_NOTE = 7, // Notes + SHT_NOBITS = 8, // Program-defined empty space (bss) + SHT_REL = 9, // Relocation entries without explicit addents + SHT_SHLIB = 10, // Reserved + SHT_DYNSYM = 11, + SHT_INIT_ARRAY = 14, // Pointers to initialization functions + SHT_FINI_ARRAY = 15, // Pointers to termination functions + SHT_PREINIT_ARRAY = 16, // Pointers to pre-initialization functions + SHT_GROUP = 17, // Defines a section group + SHT_SYMTAB_SHNDX = 18, // Section header indices for symtab + + SHT_LOOS = 0x60000000, // OS specific + SHT_GNU_ATTRIBUTES = 0x6ffffff5, // Object attributes + SHT_GNU_HASH = 0x6ffffff6, // GNU-style hash table + SHT_GNU_LIBLIST = 0x6ffffff7, // Prelink library list + SHT_CHECKSUM = 0x6ffffff8, // Checksum for DSK content + + SHT_LOSUNW = 0x6ffffffa, // Sun-specific + SHT_SUNW_move = 0x6ffffffa, + SHT_SUNW_COMDAT = 0x6ffffffb, + SHT_SUNW_syminfo = 0x6ffffffc, + + SHT_GNU_verdef = 0x6ffffffd, + SHT_GNU_verneed = 0x6ffffffe, + SHT_GNU_versym = 0x6fffffff, + + SHT_HISUNW = 0x6fffffff, + SHT_HIOS = 0x6fffffff, + + SHT_LOPROC = 0x70000000, // Processor specific + SHT_HIPROC = 0x7fffffff, + + SHT_LOUSER = 0x80000000, // Application specific + SHT_HIUSER = 0xffffffff, +} Elf_Shdr_Type; + +// Section attribute flags (we can't have a type for these because the +// 64-bit section header defines them as 64-bit) +enum { + SHF_WRITE = 0x1, // Writable + SHF_ALLOC = 0x2, // Mapped + SHF_EXECINSTR = 0x4, // Executable instructions + SHF_MERGE = 0x10, // Mergeable elements + SHF_STRINGS = 0x20, // NUL-terminated strings + SHF_INFO_LINK = 0x40, // Section header table index + SHF_LINK_ORDER = 0x80, // Special ordering requirement + SHF_OS_NONCONFORMING = 0x100, // OS-specific processing + SHF_GROUP = 0x200, // Section group member + SHF_TLS = 0x400, // Thread Local Storage + SHF_COMPRESSED = 0x800, // Compressed + SHF_MASKOS = 0x0ff00000, // Operating system specific flags + SHF_MASKPROC = 0xf0000000, // Processor specific flags +}; + +// Section group flags +enum : Elf_Word { + GRP_COMDAT = 0x1, // COMDAT group + GRP_MASKOS = 0x0ff00000, // Operating system specific flags + GRP_MASKPROC = 0xf0000000, // Processof specific flags +}; + +// Compression type +typedef ELF_ENUM(Elf_Word, Elf_Chdr_Type) { + ELFCOMPRESS_ZLIB = 1, // DEFLATE algorithm + ELFCOMPRESS_ZSTD = 2, // zstd algorithm + + ELFCOMPRESS_LOOS = 0x60000000, // Operating system specific + ELFCOMPRESS_HIOS = 0x6fffffff, + + ELFCOMPRESS_LOPROC = 0x70000000, // Processor specific + ELFCOMPRESS_HIPROC = 0x7fffffff +} Elf_Chdr_Type; + +// Symbol table entry +enum : Elf_Word { + STN_UNDEF = 0 +}; + +typedef ELF_ENUM(Elf_Byte, Elf_Sym_Binding) { + STB_LOCAL = 0, + STB_GLOBAL = 1, + STB_WEAK = 2, + + STB_LOOS = 10, // Operating system specific + STB_HIOS = 12, + + STB_LOPROC = 13, // Processor specific + STB_HIPROC = 15 +} Elf_Sym_Binding; + +typedef ELF_ENUM(Elf_Byte, Elf_Sym_Type) { + STT_NOTYPE = 0, // Unspecified + STT_OBJECT = 1, // Data object (variable, array, &c) + STT_FUNC = 2, // Function or other executable code + STT_SECTION = 3, // A section + STT_FILE = 4, // Source file name + STT_COMMON = 5, // Uninitialized common block + STT_TLS = 6, // Thread Local Storage + + STT_LOOS = 10, // Operating system specific + STT_HIOS = 12, + + STT_LOPROC = 13, // Processor specific + STT_HIPROC = 15, +} Elf_Sym_Type; + +typedef ELF_ENUM(Elf_Byte, Elf_Sym_Visibility) { + STV_DEFAULT = 0, + STV_INTERNAL = 1, // Processor specific but like hidden + STV_HIDDEN = 2, // Not visible from other components + STV_PROTECTED = 3, // Visible but cannot be preempted +} Elf_Sym_Visibility; + +// Program header types +typedef ELF_ENUM(Elf_Word, Elf_Phdr_Type) { + PT_NULL = 0, // Element unused + PT_LOAD = 1, // Loadable segment + PT_DYNAMIC = 2, // Dynamic linking information + PT_INTERP = 3, // Interpreter + PT_NOTE = 4, // Auxiliary information + PT_SHLIB = 5, // Reserved + PT_PHDR = 6, // Program header table + PT_TLS = 7, // Thread Local Storage + + PT_LOOS = 0x60000000, // Operating system specific + PT_GNU_EH_FRAME = 0x6474e550, // GNU .eh_frame_hdr segment + PT_GNU_STACK = 0x6474e551, // Indicates stack executability + PT_GNU_RELRO = 0x6474e552, // Read-only after relocation + + PT_LOSUNW = 0x6ffffffa, + PT_SUNWBSS = 0x6ffffffa, + PT_SUNWSTACK = 0x6ffffffb, + PT_HISUNW = 0x6fffffff, + PT_HIOS = 0x6fffffff, + + PT_LOPROC = 0x70000000, // Processor specific + PT_HIPROC = 0x7fffffff, +} Elf_Phdr_Type; + +// Program header flags +typedef ELF_OPTIONS(Elf_Word, Elf_Phdr_Flags) { + PF_X = 0x1, // Execute + PF_W = 0x2, // Write + PF_R = 0x4, // Read, + + PF_MASKOS = 0x0ff00000, // Operating system specific + PF_MASKPROC = 0xf0000000, // Processor specific +}; + +// Dynamic linking tags +enum { + DT_NULL = 0, // Marks the end of the _DYNAMIC array + DT_NEEDED = 1, // String table offset of name of needed library + DT_PLTRELSZ = 2, // Total size of relocation entries for PLT + DT_PLTGOT = 3, // Address of PLT/GOT + DT_HASH = 4, // Address of symbol hash table + DT_STRTAB = 5, // Address of string table + DT_SYMTAB = 6, // Address of symbol table + DT_RELA = 7, // Address of DT_RELA relocation table + DT_RELASZ = 8, // Size of DT_RELA table + DT_RELAENT = 9, // Size of DT_RELA entry + DT_STRSZ = 10, // Size of string table + DT_SYMENT = 11, // Size of symbol table entry + DT_INIT = 12, // Address of initialization function + DT_FINI = 13, // Address of termination function + DT_SONAME = 14, // String table offset of name of shared object + DT_RPATH = 15, // String table offset of search path + DT_SYMBOLIC = 16, // Means to search from shared object first + DT_REL = 17, // Address of DT_REL relocation table + DT_RELSZ = 18, // Size of DT_REL table + DT_RELENT = 19, // Size of DT_REL entry + DT_PLTREL = 20, // Type of PLT relocation entry (DT_REL/DT_RELA) + DT_DEBUG = 21, // Used for debugging + DT_TEXTREL = 22, // Means relocations might write to read-only segment + DT_JMPREL = 23, // Address of relocation entries for PLT + DT_BIND_NOW = 24, // Means linker should not lazily bind + DT_INIT_ARRAY = 25, // Address of pointers to initialization functions + DT_FINI_ARRAY = 26, // Address of pointers to termination functions + DT_INIT_ARRAYSZ = 27, // Size in bytes of initialization function array + DT_FINI_ARRAYSZ = 28, // Size in bytes of termination function array + DT_RUNPATH = 29, // String table offset of search path + DT_FLAGS = 30, // Flags + + DT_ENCODING = 32, // Tags equal to or above this follow encoding rules + + DT_PREINIT_ARRAY = 32, // Address of pre-initialization function array + DT_PREINIT_ARRAYSZ = 33, // Size in bytes of pre-initialization fn array + + DT_LOOS = 0x6000000D, // Operating system specific + DT_HIOS = 0x6ffff000, + + DT_LOPROC = 0x70000000, // Processor specific + DT_HIPROC = 0x7fffffff, +}; + +// Dynamic linking flags +enum { + DF_ORIGIN = 0x1, // Uses $ORIGIN substitution string + DF_SYMBOLIC = 0x2, // Search shared object first before usual search + DF_TEXTREL = 0x4, // Relocations may modify read-only segments + DF_BIND_NOW = 0x8, // Linker should not lazily bind + DF_STATIC_TLS = 0x10, // Uses static TLS - must not be dynamically loaded +}; + +// GNU note types +enum { + NT_GNU_ABI_TAG = 1, // ABI information + NT_GNU_HWCAP = 2, // Synthetic hwcap information + NT_GNU_BUILD_ID = 3, // Build ID + NT_GNU_GOLD_VERSION = 4, // Generated by GNU gold + NT_GNU_PROPERTY_TYPE_0 = 5, // Program property +}; + +/* .. ELF Header ............................................................ */ + +#define EI_NIDENT 16 + +typedef struct { + Elf32_Byte e_ident[EI_NIDENT]; + Elf_Ehdr_Type e_type; + Elf_Ehdr_Machine e_machine; + Elf_Ehdr_Version e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +typedef struct { + Elf64_Byte e_ident[EI_NIDENT]; + Elf_Ehdr_Type e_type; + Elf_Ehdr_Machine e_machine; + Elf_Ehdr_Version e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +/* .. Section Header ........................................................ */ + +typedef struct { + Elf32_Word sh_name; + Elf_Shdr_Type sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct { + Elf64_Word sh_name; + Elf_Shdr_Type sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + +/* .. Compression Header .................................................... */ + +typedef struct { + Elf_Chdr_Type ch_type; + Elf32_Word ch_size; + Elf32_Word ch_addralign; +} Elf32_Chdr; + +typedef struct { + Elf_Chdr_Type ch_type; + Elf64_Word ch_reserved; + Elf64_Xword ch_size; + Elf64_Xword ch_addralign; +} Elf64_Chdr; + +/* .. Symbol Table .......................................................... */ + +typedef struct { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + Elf32_Byte st_info; + Elf32_Byte st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +typedef struct { + Elf64_Word st_name; + Elf64_Byte st_info; + Elf64_Byte st_other; + Elf64_Half st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +static inline Elf_Sym_Binding ELF32_ST_BIND(Elf_Byte i) { return i >> 4; } +static inline Elf_Sym_Type ELF32_ST_TYPE(Elf_Byte i) { return i & 0xf; } +static inline Elf_Byte ELF32_ST_INFO(Elf_Sym_Binding b, Elf_Sym_Type t) { + return (b << 4) | (t & 0xf); +} + +static inline Elf_Sym_Binding ELF64_ST_BIND(Elf_Byte i) { return i >> 4; } +static inline Elf_Sym_Type ELF64_ST_TYPE(Elf_Byte i) { return i & 0xf; } +static inline Elf_Byte ELF64_ST_INFO(Elf_Sym_Binding b, Elf_Sym_Type t) { + return (b << 4) | (t & 0xf); +} + +static inline Elf_Sym_Visibility ELF32_ST_VISIBILITY(Elf_Byte o) { + return o & 3; +} +static inline Elf_Sym_Visibility ELF64_ST_VISIBILITY(Elf_Byte o) { + return o & 3; +} + +/* .. Relocation ............................................................ */ + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; +} Elf64_Rel; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_Rela; + +static inline Elf32_Byte ELF32_R_SYM(Elf32_Word i) { return i >> 8; } +static inline Elf32_Byte ELF32_R_TYPE(Elf32_Word i) { return i & 0xff; } +static inline Elf32_Word ELF32_R_INFO(Elf32_Byte s, Elf32_Byte t) { + return (s << 8) | t; +} + +static inline Elf64_Word ELF64_R_SYM(Elf64_Xword i) { return i >> 32; } +static inline Elf64_Word ELF64_R_TYPE(Elf64_Xword i) { return i & 0xffffffff; } +static inline Elf64_Xword ELF64_R_INFO(Elf64_Word s, Elf64_Word t) { + return (((Elf64_Xword)s) << 32) | t; +} + +/* .. Program Header ........................................................ */ + +typedef struct { + Elf_Phdr_Type p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf_Phdr_Flags p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +typedef struct { + Elf_Phdr_Type p_type; + Elf_Phdr_Flags p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + +/* .. Note Header ........................................................... */ + +typedef struct { + Elf32_Word n_namesz; + Elf32_Word n_descsz; + Elf32_Word n_type; +} Elf32_Nhdr; + +typedef struct { + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; +} Elf64_Nhdr; + +/* .. Dynamic Linking ....................................................... */ + +typedef struct { + Elf32_Sword d_tag; + union { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +typedef struct { + Elf64_Sxword d_tag; + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + +/* .. Hash Table ............................................................ */ + +typedef struct { + Elf32_Word h_nbucket; + Elf32_Word h_nchain; +} Elf32_Hash; + +typedef struct { + Elf64_Word h_nbucket; + Elf64_Word h_nchain; +} Elf64_Hash; + +static inline unsigned long +elf_hash(const unsigned char *name) +{ + unsigned long h = 0, g; + while (*name) { + h = (h << 4) + *name++; + if ((g = h & 0xf0000000)) + h ^= g >> 24; + h &= ~g; + } + return h; +} + +#endif // ELF_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/ImageFormats.modulemap b/stdlib/public/Backtracing/modules/ImageFormats/ImageFormats.modulemap new file mode 100644 index 0000000000000..94ebbd2651c64 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/ImageFormats.modulemap @@ -0,0 +1,12 @@ +module ImageFormats { + module Elf { + header "Elf/elf.h" + export * + } + module Dwarf { + header "Dwarf/dwarf.h" + header "Dwarf/eh_frame_hdr.h" + export * + } + export * +} diff --git a/stdlib/public/Backtracing/modules/OS/Darwin.h b/stdlib/public/Backtracing/modules/OS/Darwin.h new file mode 100644 index 0000000000000..7d6a4183930da --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Darwin.h @@ -0,0 +1,253 @@ +//===--- Darwin.h - Darwin specifics ----------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Darwin specifics. +// +// WARNING: Some of the things in this file are SPI. If you use them in +// your own code, we will mercilessly break your program for you and hand +// you the pieces :-) +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_DARWIN_H +#define SWIFT_BACKTRACING_DARWIN_H +#ifdef __APPLE__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include + +#include + +// .. Mach fixes ............................................................... + +// Use an inline function for mach_task_self() or it won't import into Swift +#undef mach_task_self +static inline task_t mach_task_self() { return mach_task_self_; } + +// .. Thread states ............................................................ + +/* We can't import these from the system header, because it uses all kinds of + macros and the Swift importer can't cope with that. So declare them here + in a form it can understand. */ +#define ARM_THREAD_STATE64 6 +struct darwin_arm64_thread_state { + uint64_t _x[29]; + uint64_t fp; + uint64_t lr; + uint64_t sp; + uint64_t pc; + uint32_t cpsr; + uint32_t __pad; +}; + +struct darwin_arm64_exception_state { + uint64_t far; + uint32_t esr; + uint32_t exception; +}; + +struct darwin_arm64_mcontext { + struct darwin_arm64_exception_state es; + struct darwin_arm64_thread_state ss; + // followed by NEON state (which we don't care about) +}; + +#define X86_THREAD_STATE64 4 +struct darwin_x86_64_thread_state { + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint64_t cs; + uint64_t fs; + uint64_t gs; +}; + +struct darwin_x86_64_exception_state { + uint16_t trapno; + uint16_t cpu; + uint32_t err; + uint64_t faultvaddr; +}; + +struct darwin_x86_64_mcontext { + struct darwin_x86_64_exception_state es; + struct darwin_x86_64_thread_state ss; + // followed by FP/AVX/AVX512 state (which we don't care about) +}; + +// .. libproc SPI .............................................................. + +int proc_name(int pid, void * buffer, uint32_t buffersize); + +// .. Mach SPI ................................................................. + +extern kern_return_t task_read_for_pid(task_t task, int pid, task_t *ptask); + +// .. dyld SPI ................................................................. + +struct dyld_process_cache_info { + uuid_t cacheUUID; + uint64_t cacheBaseAddress; + bool noCache; + bool privateCache; +}; +typedef struct dyld_process_cache_info dyld_process_cache_info; +typedef const struct dyld_process_info_base* dyld_process_info; + +extern dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kernelError); +extern void _dyld_process_info_release(dyld_process_info info); +extern void _dyld_process_info_retain(dyld_process_info info); +extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo); +extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)); +extern void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)); + +// .. Code Signing SPI ......................................................... + +#define CS_OPS_STATUS 0 +#define CS_PLATFORM_BINARY 0x04000000 +#define CS_PLATFORM_PATH 0x08000000 +extern int csops(int, unsigned int, void *, size_t); + +// .. CoreSymbolication SPI .................................................... + +typedef int32_t cpu_type_t; +typedef int32_t cpu_subtype_t; + +struct _CSArchitecture { + cpu_type_t cpu_type; + cpu_subtype_t cpu_subtype; +}; + +typedef struct _CSArchitecture CSArchitecture; + +static const CSArchitecture kCSArchitectureI386 = { + CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL +}; +static const CSArchitecture kCSArchitectureX86_64 = { + CPU_TYPE_X86_64, CPU_SUBTYPE_I386_ALL +}; +static const CSArchitecture kCSArchitectureArm64 = { + CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL +}; +static const CSArchitecture kCSArchitectureArm64_32 = { + CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_ALL +}; +static const CSArchitecture kCSArchitectureArmV7K = { + CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K +}; + +typedef struct _CSBinaryRelocationInformation { + vm_address_t base; + vm_address_t extent; + char name[17]; +} CSBinaryRelocationInformation; + +typedef struct _CSBinaryImageInformation { + vm_address_t base; + vm_address_t extent; + CFUUIDBytes uuid; + CSArchitecture arch; + const char *path; + CSBinaryRelocationInformation *relocations; + uint32_t relocationCount; + uint32_t flags; +} CSBinaryImageInformation; + +typedef uint64_t CSMachineTime; + +static const CSMachineTime kCSBeginningOfTime = 0; +static const CSMachineTime kCSEndOfTime = (1ull<<63) - 1; +static const CSMachineTime kCSNow = (1ull<<63); +static const CSMachineTime kCSAllTimes = (1ull<<63) + 1; + +struct _CSTypeRef { + uintptr_t _opaque_1; + uintptr_t _opaque_2; +}; + +typedef struct _CSTypeRef CSTypeRef; + +typedef CSTypeRef CSNullRef; +typedef CSTypeRef CSSymbolicatorRef; +typedef CSTypeRef CSSymbolOwnerRef; +typedef CSTypeRef CSSymbolRef; +typedef CSTypeRef CSSourceInfoRef; + +static const CSNullRef kCSNull = { 0, 0 }; + +typedef void (^CSSymbolOwnerIterator)(CSSymbolOwnerRef owner); +typedef void (^CSStackFrameIterator)(CSSymbolRef symbol, CSSourceInfoRef info); + +typedef struct _CSNotificationData { + CSSymbolicatorRef symbolicator; + union { + struct Ping { + uint32_t value; + } ping; + + struct DyldLoad { + CSSymbolOwnerRef symbolOwner; + } dyldLoad; + + struct DyldUnload { + CSSymbolOwnerRef symbolOwner; + } dyldUnload; + } u; +} CSNotificationData; + +typedef void (^CSNotificationBlock)(uint32_t type, CSNotificationData data); + +struct _CSRange { + vm_address_t location; + vm_size_t length; +}; + +typedef struct _CSRange CSRange; + +enum { + kCRSanitizePathGlobLocalHomeDirectories = 1, + kCRSanitizePathGlobLocalVolumes = 2, + kCRSanitizePathGlobAllTypes = 0xff, + + kCRSanitizePathNormalize = 0x100 << 0, + kCRSanitizePathKeepFile = 0x100 << 1, +}; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __APPLE__ +#endif // SWIFT_BACKTRACING_DARWIN_H + diff --git a/stdlib/public/Backtracing/modules/OS/Libc.h b/stdlib/public/Backtracing/modules/OS/Libc.h new file mode 100644 index 0000000000000..17893a89c9da1 --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Libc.h @@ -0,0 +1,128 @@ +//===--- Libc.h - Imports from the C library --------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Imported functions from the C library. We can't use Darwin, Glibc or +// MSVCRT from here because that would create dependency issues. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_LIBC_H +#define SWIFT_BACKTRACING_LIBC_H + +#include +#include + +#if __has_include() +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#endif + +#ifdef _WIN32 +#include "swift/Runtime/Win32.h" + +#include +#include + +// Work around the fact that MSVCRT renamed all the POSIX functions and +// doesn't actually implement all of them anyway. +#ifdef __cplusplus +extern "C" { +#endif + +typedef __int64 off_t; +typedef int ssize_t; + +#define O_APPEND _O_APPEND +#define O_BINARY _O_BINARY +#define O_CREAT _O_CREAT +#define O_EXCL _O_EXCL +#define O_RDONLY _O_RDONLY +#define O_RDWR _O_RDWR +#define O_TEXT _O_TEXT +#define O_TRUNC _O_TRUNC +#define O_WRONLY _O_WRONLY + +static inline int open(const char *filename, int oflag, ...) { + wchar_t *wide = _swift_win32_copyWideFromUTF8(path); + int pmode = 0; + if (oflag & O_CREAT) { + va_list val; + va_start(val, oflag); + pmode = va_arg(val, int); + va_end(val); + } + int fd = _wopen(wpath, oflag, pmode); + free(wide); + return fd; +} + +static inline int close(int fd) { + return _close(fd); +} + +static inline off_t lseek(int fd, off_t offset, int whence) { + return _lseeki64(fd, offset, whence); +} + +static inline ssize_t read(int fd, void *buf, size_t nbyte) { + return _read(fd, buf, nbyte); +} + +static inline ssize_t write(int fd, void *buf, size_t nbyte) { + return _write(fd, buf, nbyte); +} + +ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset); +ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset); + +#ifdef __cplusplus +} +#endif + +#else +#include +#endif + +// .. Swift affordances ........................................................ + +#ifdef __cplusplus +extern "C" { +#endif + +/* open() is usually declared as a variadic function; these don't import into + Swift. */ +static inline int _swift_open(const char *filename, int oflag, int mode) { + return open(filename, oflag, mode); +} + +/* errno is typically not going to be easily accessible (it's often a macro), + so add a get_errno() function to do that. */ +static inline int _swift_get_errno() { return errno; } +static void _swift_set_errno(int err) { errno = err; } + +#ifdef __cplusplus +} +#endif + +#endif // SWIFT_BACKTRACING_LIBC_H diff --git a/stdlib/public/Backtracing/modules/OS/Linux.h b/stdlib/public/Backtracing/modules/OS/Linux.h new file mode 100644 index 0000000000000..716bc9635fafd --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Linux.h @@ -0,0 +1,42 @@ +//===--- Linux.h - Linux specifics ------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Linux specific includes and declarations. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_LINUX_H +#define SWIFT_BACKTRACING_LINUX_H +#ifdef __linux__ + +#define _GNU_SOURCE +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +ssize_t process_vm_readv(pid_t pid, + const struct iovec *local_iov, + unsigned long liovcnt, + const struct iovec *remote_iov, + unsigned long riovcnt, + unsigned long flags); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __linux__ +#endif // SWIFT_BACKTRACING_LINUX_H + diff --git a/stdlib/public/Backtracing/modules/OS/OS.modulemap b/stdlib/public/Backtracing/modules/OS/OS.modulemap new file mode 100644 index 0000000000000..6d27426e635fd --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/OS.modulemap @@ -0,0 +1,17 @@ +module OS { + module Libc { + header "Libc.h" + } + + module Darwin { + header "Darwin.h" + } + + module Linux { + header "Linux.h" + } + + module Windows { + header "Windows.h" + } +} diff --git a/stdlib/public/SwiftShims/swift/shims/CrashInfo.h b/stdlib/public/Backtracing/modules/OS/Windows.h similarity index 58% rename from stdlib/public/SwiftShims/swift/shims/CrashInfo.h rename to stdlib/public/Backtracing/modules/OS/Windows.h index 21feb458c88b5..e67f4e0c55d71 100644 --- a/stdlib/public/SwiftShims/swift/shims/CrashInfo.h +++ b/stdlib/public/Backtracing/modules/OS/Windows.h @@ -1,4 +1,4 @@ -//===--- CrashInfo.h - Swift Backtracing Crash Information ------*- C++ -*-===// +//===--- Windows.h - Windows specifics --------------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -10,31 +10,24 @@ // //===----------------------------------------------------------------------===// // -// Defines the CrashInfo type that holds information about why the program -// crashed. +// Windows specific includes and declarations. // //===----------------------------------------------------------------------===// -#ifndef SWIFT_CRASHINFO_H -#define SWIFT_CRASHINFO_H - -#include "SwiftStdint.h" +#ifndef SWIFT_BACKTRACING_WINDOWS_H +#define SWIFT_BACKTRACING_WINDOWS_H +#ifdef _WIN32 #ifdef __cplusplus -namespace swift { extern "C" { #endif -struct CrashInfo { - __swift_uint64_t crashing_thread; - __swift_uint64_t signal; - __swift_uint64_t fault_address; - __swift_uint64_t mctx; -}; + // Nothing yet #ifdef __cplusplus } // extern "C" -} // namespace swift #endif -#endif // SWIFT_CRASHINFO_H +#endif // _WIN32 +#endif // SWIFT_BACKTRACING_WINDOWS_H + diff --git a/stdlib/public/Backtracing/modules/Runtime/Runtime.h b/stdlib/public/Backtracing/modules/Runtime/Runtime.h new file mode 100644 index 0000000000000..78537b2497b35 --- /dev/null +++ b/stdlib/public/Backtracing/modules/Runtime/Runtime.h @@ -0,0 +1,38 @@ +//===--- Runtime.h - Swift runtime imports ----------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Things to drag in from the Swift runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_RUNTIME_H +#define SWIFT_BACKTRACING_RUNTIME_H + +#include +#include + +#include "swift/Runtime/CrashInfo.h" + +// Can't import swift/Runtime/Debug.h because it assumes C++ +void swift_reportWarning(uint32_t flags, const char *message); + +// Returns true if the given function is a thunk function +bool _swift_backtrace_isThunkFunction(const char *rawName); + +// Demangle the given raw name (supports Swift and C++) +char *_swift_backtrace_demangle(const char *rawName, + size_t rawNameLength, + char *outputBuffer, + size_t *outputBufferSize, + int *status); + +#endif // SWIFT_BACKTRACING_RUNTIME_H diff --git a/stdlib/public/Backtracing/modules/module.modulemap b/stdlib/public/Backtracing/modules/module.modulemap new file mode 100644 index 0000000000000..4c9121ec6f49a --- /dev/null +++ b/stdlib/public/Backtracing/modules/module.modulemap @@ -0,0 +1,14 @@ +extern module ImageFormats "ImageFormats/ImageFormats.modulemap" +extern module OS "OS/OS.modulemap" + +module Runtime { + header "Runtime/Runtime.h" +} + +module FixedLayout { + header "FixedLayout.h" +} + +module CompressionLibs { + header "Compression.h" +} diff --git a/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt b/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt index 92c481d05b701..c54b2cf84b272 100644 --- a/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt +++ b/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt @@ -1,7 +1,6 @@ set(sources AssertionReporting.h CoreFoundationShims.h - CrashInfo.h FoundationShims.h GlobalObjects.h HeapObject.h @@ -22,7 +21,6 @@ set(sources ThreadLocalStorage.h UnicodeData.h Visibility.h - _SwiftBacktracing.h _SwiftConcurrency.h _SwiftDistributed.h diff --git a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h deleted file mode 100644 index b2d86f199363d..0000000000000 --- a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h +++ /dev/null @@ -1,396 +0,0 @@ -//===--- _SwiftBacktracing.h - Swift Backtracing Support --------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2023 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 -// -//===----------------------------------------------------------------------===// -// -// Defines types and support functions for the Swift backtracing code. -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_BACKTRACING_H -#define SWIFT_BACKTRACING_H - -#include "SwiftStdbool.h" -#include "SwiftStddef.h" -#include "SwiftStdint.h" -#include "Target.h" - -#include "CrashInfo.h" - -#ifdef __cplusplus -namespace swift { -extern "C" { -#endif - -// .. C library ................................................................ - -static inline __swift_size_t _swift_backtrace_strlen(const char *s) { - extern __swift_size_t strlen(const char *); - return strlen(s); -} - -void *_swift_backtrace_dlopen_lazy(const char *path); -void *_swift_backtrace_dlsym(void *handle, const char *name); - -typedef struct __swift_backtrace_FILE __swift_backtrace_FILE; - -static inline __swift_backtrace_FILE *_swift_backtrace_fopen(const char *fname, - const char *mode) { - extern __swift_backtrace_FILE *fopen(const char *, const char *); - return fopen(fname, mode); -} -static inline int _swift_backtrace_fclose(__swift_backtrace_FILE *fp) { - extern int fclose(__swift_backtrace_FILE *); - return fclose(fp); -} -static inline int _swift_backtrace_feof(__swift_backtrace_FILE *fp) { - extern int feof(__swift_backtrace_FILE *); - return feof(fp); -} -static inline int _swift_backtrace_ferror(__swift_backtrace_FILE *fp) { - extern int ferror(__swift_backtrace_FILE *); - return ferror(fp); -} -static inline char *_swift_backtrace_fgets(char *buffer, int size, - __swift_backtrace_FILE *fp) { - extern char *fgets(char *, int, __swift_backtrace_FILE *); - return fgets(buffer, size, fp); -} - -// .. Core Foundation .......................................................... - -#if SWIFT_TARGET_OS_DARWIN - -#if __LLP64__ -typedef long long __swift_backtrace_CFIndex; -#else -typedef long __swift_backtrace_CFIndex; -#endif - -struct __swift_backtrace_CFRange { - __swift_backtrace_CFIndex location; - __swift_backtrace_CFIndex length; - }; - -typedef struct { - __swift_uint8_t byte0; - __swift_uint8_t byte1; - __swift_uint8_t byte2; - __swift_uint8_t byte3; - __swift_uint8_t byte4; - __swift_uint8_t byte5; - __swift_uint8_t byte6; - __swift_uint8_t byte7; - __swift_uint8_t byte8; - __swift_uint8_t byte9; - __swift_uint8_t byte10; - __swift_uint8_t byte11; - __swift_uint8_t byte12; - __swift_uint8_t byte13; - __swift_uint8_t byte14; - __swift_uint8_t byte15; -} __swift_backtrace_CFUUIDBytes; - -#endif // SWIFT_TARGET_OS_DARWIN - -// .. Processor specifics ...................................................... - -struct x86_64_gprs { - __swift_uint64_t _r[16]; - __swift_uint64_t rflags; - __swift_uint16_t cs, fs, gs, _pad0; - __swift_uint64_t rip; - __swift_uint64_t valid; -}; - -struct i386_gprs { - __swift_uint32_t _r[8]; - __swift_uint32_t eflags; - __swift_uint16_t segreg[6]; - __swift_uint32_t eip; - __swift_uint32_t valid; -}; - -struct arm64_gprs { - __swift_uint64_t _x[32]; - __swift_uint64_t pc; - __swift_uint64_t valid; -}; - -struct arm_gprs { - __swift_uint32_t _r[16]; - __swift_uint32_t valid; -}; - -// .. Darwin specifics ......................................................... - -#if SWIFT_TARGET_OS_DARWIN - -// From libproc -int proc_name(int pid, void * buffer, __swift_uint32_t buffersize); - -/* Darwin thread states. We can't import these from the system header because - it uses all kinds of macros and the Swift importer can't cope with that. - So declare them here in a form it can understand. */ -#define ARM_THREAD_STATE64 6 -struct darwin_arm64_thread_state { - __swift_uint64_t _x[29]; - __swift_uint64_t fp; - __swift_uint64_t lr; - __swift_uint64_t sp; - __swift_uint64_t pc; - __swift_uint32_t cpsr; - __swift_uint32_t __pad; -}; - -struct darwin_arm64_exception_state { - __swift_uint64_t far; - __swift_uint32_t esr; - __swift_uint32_t exception; -}; - -struct darwin_arm64_mcontext { - struct darwin_arm64_exception_state es; - struct darwin_arm64_thread_state ss; - // followed by NEON state (which we don't care about) -}; - -#define x86_THREAD_STATE64 4 -struct darwin_x86_64_thread_state { - __swift_uint64_t rax; - __swift_uint64_t rbx; - __swift_uint64_t rcx; - __swift_uint64_t rdx; - __swift_uint64_t rdi; - __swift_uint64_t rsi; - __swift_uint64_t rbp; - __swift_uint64_t rsp; - __swift_uint64_t r8; - __swift_uint64_t r9; - __swift_uint64_t r10; - __swift_uint64_t r11; - __swift_uint64_t r12; - __swift_uint64_t r13; - __swift_uint64_t r14; - __swift_uint64_t r15; - __swift_uint64_t rip; - __swift_uint64_t rflags; - __swift_uint64_t cs; - __swift_uint64_t fs; - __swift_uint64_t gs; -}; - -struct darwin_x86_64_exception_state { - __swift_uint16_t trapno; - __swift_uint16_t cpu; - __swift_uint32_t err; - __swift_uint64_t faultvaddr; -}; - -struct darwin_x86_64_mcontext { - struct darwin_x86_64_exception_state es; - struct darwin_x86_64_thread_state ss; - // followed by FP/AVX/AVX512 state (which we don't care about) -}; - -typedef unsigned int __swift_task_t; -typedef unsigned int __swift_thread_t; -typedef unsigned int __swift_kern_return_t; -typedef unsigned char __swift_uuid_t[16]; -typedef __swift_uint64_t __swift_vm_address_t; -typedef __swift_uint64_t __swift_vm_size_t; -typedef int __swift_thread_state_flavor_t; -typedef unsigned int __swift_natural_t; -typedef __swift_natural_t __swift_msg_type_number_t; -typedef __swift_natural_t *__swift_thread_state_t; - -#define _SWIFT_KERN_SUCCESS 0 - -static inline __swift_task_t -_swift_backtrace_task_self() { - extern __swift_task_t mach_task_self_; - return mach_task_self_; -} - -static inline __swift_kern_return_t -_swift_backtrace_vm_read(__swift_task_t task, - __swift_vm_address_t address, - __swift_vm_size_t size, - void *buffer, - __swift_vm_size_t *length) { - extern __swift_kern_return_t mach_vm_read_overwrite(__swift_task_t, - __swift_vm_address_t, - __swift_vm_size_t, - __swift_vm_address_t, - __swift_vm_size_t *); - return mach_vm_read_overwrite(task, - address, - size, - (__swift_vm_address_t)buffer, - (__swift_vm_size_t *)length); -} - -#define _SWIFT_X86_THREAD_STATE64 6 -#define _SWIFT_ARM_THREAD_STATE64 6 - -static inline __swift_kern_return_t -_swift_backtrace_thread_get_state(__swift_thread_t target_act, - __swift_thread_state_flavor_t flavor, - __swift_thread_state_t old_state, - __swift_msg_type_number_t *old_stateCnt) { - extern __swift_kern_return_t thread_get_state(__swift_thread_t, - __swift_thread_state_flavor_t, - __swift_thread_state_t, - __swift_msg_type_number_t *); - return thread_get_state(target_act, flavor, old_state, old_stateCnt); -} - -extern __swift_kern_return_t task_read_for_pid(__swift_task_t task, int pid, __swift_task_t *ptask); - -/* DANGER! These are SPI. They may change (or vanish) at short notice, may - not work how you expect, and are generally dangerous to use. */ -struct dyld_process_cache_info { - __swift_uuid_t cacheUUID; - __swift_uint64_t cacheBaseAddress; - __swift_bool noCache; - __swift_bool privateCache; -}; -typedef struct dyld_process_cache_info dyld_process_cache_info; -typedef const struct dyld_process_info_base* dyld_process_info; - -extern dyld_process_info _dyld_process_info_create(__swift_task_t task, __swift_uint64_t timestamp, __swift_kern_return_t* kernelError); -extern void _dyld_process_info_release(dyld_process_info info); -extern void _dyld_process_info_retain(dyld_process_info info); -extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo); -extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(__swift_uint64_t machHeaderAddress, const __swift_uuid_t uuid, const char* path)); -extern void _dyld_process_info_for_each_segment(dyld_process_info info, __swift_uint64_t machHeaderAddress, void (^callback)(__swift_uint64_t segmentAddress, __swift_uint64_t segmentSize, const char* segmentName)); - -#define CS_OPS_STATUS 0 -#define CS_PLATFORM_BINARY 0x04000000 -#define CS_PLATFORM_PATH 0x08000000 -extern int csops(int, unsigned int, void *, __swift_size_t); - -/* DANGER! CoreSymbolication is a private framework. This is all SPI. */ -typedef __swift_int32_t cpu_type_t; -typedef __swift_int32_t cpu_subtype_t; - -struct _CSArchitecture { - cpu_type_t cpu_type; - cpu_subtype_t cpu_subtype; -}; - -typedef struct _CSArchitecture CSArchitecture; - -#define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */ -#define CPU_ARCH_ABI64_32 0x02000000 /* ABI for 64-bit hardware with 32-bit types; LP32 */ - -#define CPU_TYPE_X86 ((cpu_type_t) 7) -#define CPU_TYPE_I386 CPU_TYPE_X86 /* compatibility */ - -#define CPU_SUBTYPE_INTEL(f, m) ((cpu_subtype_t) (f) + ((m) << 4)) -#define CPU_SUBTYPE_I386_ALL CPU_SUBTYPE_INTEL(3, 0) - -#define CPU_TYPE_ARM ((cpu_type_t) 12) - -#define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) -#define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) - -static const CSArchitecture kCSArchitectureI386 = { CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL }; -static const CSArchitecture kCSArchitectureX86_64 = { CPU_TYPE_I386|CPU_ARCH_ABI64, CPU_SUBTYPE_I386_ALL }; -static const CSArchitecture kCSArchitectureArm64 = { CPU_TYPE_ARM | CPU_ARCH_ABI64, CPU_SUBTYPE_ARM64_ALL }; -static const CSArchitecture kCSArchitectureArm64_32 = { CPU_TYPE_ARM | CPU_ARCH_ABI64_32, CPU_SUBTYPE_ARM64_ALL }; -static const CSArchitecture kCSArchitectureArmV7K = { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K }; - -typedef struct _CSBinaryRelocationInformation { - __swift_vm_address_t base; - __swift_vm_address_t extent; - char name[17]; -} CSBinaryRelocationInformation; - -typedef struct _CSBinaryImageInformation { - __swift_vm_address_t base; - __swift_vm_address_t extent; - __swift_backtrace_CFUUIDBytes uuid; - CSArchitecture arch; - const char *path; - CSBinaryRelocationInformation *relocations; - __swift_uint32_t relocationCount; - __swift_uint32_t flags; -} CSBinaryImageInformation; - -typedef __swift_uint64_t CSMachineTime; - -static const CSMachineTime kCSBeginningOfTime = 0; -static const CSMachineTime kCSEndOfTime = (1ull<<63) - 1; -static const CSMachineTime kCSNow = (1ull<<63); -static const CSMachineTime kCSAllTimes = (1ull<<63) + 1; - -struct _CSTypeRef { - __swift_uintptr_t _opaque_1; - __swift_uintptr_t _opaque_2; -}; - -typedef struct _CSTypeRef CSTypeRef; - -typedef CSTypeRef CSNullRef; -typedef CSTypeRef CSSymbolicatorRef; -typedef CSTypeRef CSSymbolOwnerRef; -typedef CSTypeRef CSSymbolRef; -typedef CSTypeRef CSSourceInfoRef; - -static const CSNullRef kCSNull = { 0, 0 }; - -typedef void (^CSSymbolOwnerIterator)(CSSymbolOwnerRef owner); -typedef void (^CSStackFrameIterator)(CSSymbolRef symbol, CSSourceInfoRef info); - -typedef struct _CSNotificationData { - CSSymbolicatorRef symbolicator; - union { - struct Ping { - __swift_uint32_t value; - } ping; - - struct DyldLoad { - CSSymbolOwnerRef symbolOwner; - } dyldLoad; - - struct DyldUnload { - CSSymbolOwnerRef symbolOwner; - } dyldUnload; - } u; -} CSNotificationData; - -typedef void (^CSNotificationBlock)(__swift_uint32_t type, CSNotificationData data); - -struct _CSRange { - __swift_vm_address_t location; - __swift_vm_size_t length; -}; - -typedef struct _CSRange CSRange; - -/* DANGER! This is also SPI */ -enum { - kCRSanitizePathGlobLocalHomeDirectories = 1, - kCRSanitizePathGlobLocalVolumes = 2, - kCRSanitizePathGlobAllTypes = 0xff, - - kCRSanitizePathNormalize = 0x100 << 0, - kCRSanitizePathKeepFile = 0x100 << 1, -}; - -#endif // SWIFT_TARGET_OS_DARWIN - -#ifdef __cplusplus -} // extern "C" -} // namespace swift -#endif - -#endif // SWIFT_BACKTRACING_H diff --git a/stdlib/public/SwiftShims/swift/shims/module.modulemap b/stdlib/public/SwiftShims/swift/shims/module.modulemap index 5da2bd8f7d94f..36d7dd8be0d2d 100644 --- a/stdlib/public/SwiftShims/swift/shims/module.modulemap +++ b/stdlib/public/SwiftShims/swift/shims/module.modulemap @@ -26,10 +26,6 @@ module _SwiftConcurrencyShims { header "_SwiftConcurrency.h" } -module _SwiftBacktracingShims { - header "_SwiftBacktracing.h" -} - module SwiftOverlayShims { header "LibcOverlayShims.h" export * From efa90a51e2f2361d8aebe03a9c35aeb3e13dc718 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 5 Jun 2023 16:01:33 +0100 Subject: [PATCH 08/17] [Backtracing][Linux] Add Linux support to the _Backtracing module. Use the new module structure rather the old SwiftShims header. This is much cleaner and lets us include operating system headers to get the relevant definitions where possible. Add code to support ELF and DWARF, including decompression using zlib, zstd and liblzma if those turn out to be required and available. rdar://110261712 --- include/swift/Runtime/Backtrace.h | 3 +- .../public/Backtracing/ArrayImageSource.swift | 50 + stdlib/public/Backtracing/Backtrace.swift | 264 ++- .../Backtracing/BacktraceFormatter.swift | 120 +- stdlib/public/Backtracing/ByteSwapping.swift | 95 + stdlib/public/Backtracing/CMakeLists.txt | 11 +- stdlib/public/Backtracing/Compression.swift | 528 +++++ stdlib/public/Backtracing/Context.swift | 155 +- .../Backtracing/CoreSymbolication.swift | 82 +- stdlib/public/Backtracing/Dwarf.swift | 1786 +++++++++++++++++ stdlib/public/Backtracing/Elf.swift | 1769 ++++++++++++++++ .../public/Backtracing/FileImageSource.swift | 70 + .../Backtracing/FramePointerUnwinder.swift | 124 +- stdlib/public/Backtracing/Image.swift | 167 ++ stdlib/public/Backtracing/ImageSource.swift | 153 ++ .../Backtracing/MemoryImageSource.swift | 35 + stdlib/public/Backtracing/MemoryReader.swift | 169 +- .../Backtracing/SymbolicatedBacktrace.swift | 194 +- stdlib/public/Backtracing/Utils.swift | 69 +- stdlib/public/Backtracing/Win32Extras.cpp | 57 + .../libexec/swift-backtrace/CMakeLists.txt | 11 +- .../{Target.swift => TargetMacOS.swift} | 11 +- stdlib/public/runtime/Backtrace.cpp | 4 +- 23 files changed, 5691 insertions(+), 236 deletions(-) create mode 100644 stdlib/public/Backtracing/ArrayImageSource.swift create mode 100644 stdlib/public/Backtracing/ByteSwapping.swift create mode 100644 stdlib/public/Backtracing/Compression.swift create mode 100644 stdlib/public/Backtracing/Dwarf.swift create mode 100644 stdlib/public/Backtracing/Elf.swift create mode 100644 stdlib/public/Backtracing/FileImageSource.swift create mode 100644 stdlib/public/Backtracing/Image.swift create mode 100644 stdlib/public/Backtracing/ImageSource.swift create mode 100644 stdlib/public/Backtracing/MemoryImageSource.swift create mode 100644 stdlib/public/Backtracing/Win32Extras.cpp rename stdlib/public/libexec/swift-backtrace/{Target.swift => TargetMacOS.swift} (96%) diff --git a/include/swift/Runtime/Backtrace.h b/include/swift/Runtime/Backtrace.h index f6472405b8761..4ce219f778eb6 100644 --- a/include/swift/Runtime/Backtrace.h +++ b/include/swift/Runtime/Backtrace.h @@ -134,7 +134,8 @@ struct BacktraceSettings { SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings; -SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool _swift_isThunkFunction(const char *mangledName); +SWIFT_RUNTIME_STDLIB_SPI +bool _swift_backtrace_isThunkFunction(const char *mangledName); /// Try to demangle a symbol. /// diff --git a/stdlib/public/Backtracing/ArrayImageSource.swift b/stdlib/public/Backtracing/ArrayImageSource.swift new file mode 100644 index 0000000000000..db38033e1b789 --- /dev/null +++ b/stdlib/public/Backtracing/ArrayImageSource.swift @@ -0,0 +1,50 @@ +//===--- ArrayImageSource.swift - An image source backed by an Array -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines ArrayImageSource, an image source that is backed by a Swift Array. +// +//===----------------------------------------------------------------------===// + +import Swift + +@_implementationOnly import OS.Libc + +enum ArrayImageSourceError: Error { + case outOfBoundsRead(UInt64, UInt64) +} + +struct ArrayImageSource: ImageSource { + private var array: Array + + public init(array: Array) { + self.array = array + } + + public var isMappedImage: Bool { return false } + public var path: String? { return nil } + public var bounds: Bounds? { + return Bounds(base: 0, size: Size(array.count * MemoryLayout.stride)) + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try array.withUnsafeBytes{ + let size = Size($0.count) + let requested = Size(buffer.count * MemoryLayout.stride) + if addr > size || requested > size - addr { + throw ArrayImageSourceError.outOfBoundsRead(addr, requested) + } + + memcpy(buffer.baseAddress!, $0.baseAddress! + Int(addr), Int(requested)) + } + } +} diff --git a/stdlib/public/Backtracing/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift index 13f49068c198b..a337e855d7778 100644 --- a/stdlib/public/Backtracing/Backtrace.swift +++ b/stdlib/public/Backtracing/Backtrace.swift @@ -16,10 +16,16 @@ import Swift -#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import _StringProcessing -@_implementationOnly import _SwiftBacktracingShims +@_implementationOnly import OS.Libc +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import OS.Darwin +#endif + +#if os(Linux) +@_implementationOnly import ImageFormats.Elf #endif /// Holds a backtrace. @@ -29,7 +35,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// This is intentionally _not_ a pointer, because you shouldn't be /// dereferencing them; they may refer to some other process, for /// example. - public typealias Address = UInt + public typealias Address = UInt64 /// The unwind algorithm to use. public enum UnwindAlgorithm { @@ -127,18 +133,23 @@ public struct Backtrace: CustomStringConvertible, Sendable { } /// A textual description of this frame. - public var description: String { + public func description(width: Int) -> String { switch self { case let .programCounter(addr): - return "\(hex(addr))" + return "\(hex(addr, width: width))" case let .returnAddress(addr): - return "\(hex(addr)) [ra]" + return "\(hex(addr, width: width)) [ra]" case let .asyncResumePoint(addr): - return "\(hex(addr)) [async]" + return "\(hex(addr, width: width)) [async]" case .omittedFrames(_), .truncated: return "..." } } + + /// A textual description of this frame. + public var description: String { + return description(width: MemoryLayout
.size * 2) + } } /// Represents an image loaded in the process's address space @@ -160,15 +171,26 @@ public struct Backtrace: CustomStringConvertible, Sendable { public var endOfText: Backtrace.Address /// Provide a textual description of an Image. - public var description: String { + public func description(width: Int) -> String { if let buildID = self.buildID { - return "\(hex(baseAddress))-\(hex(endOfText)) \(hex(buildID)) \(name) \(path)" + return "\(hex(baseAddress, width: width))-\(hex(endOfText, width: width)) \(hex(buildID)) \(name) \(path)" } else { - return "\(hex(baseAddress))-\(hex(endOfText)) \(name) \(path)" + return "\(hex(baseAddress, width: width))-\(hex(endOfText, width: width)) \(name) \(path)" } } + + /// A textual description of an Image. + public var description: String { + return description(width: MemoryLayout
.size * 2) + } } + /// The architecture of the system that captured this backtrace. + public var architecture: String + + /// The width of an address in this backtrace, in bits. + public var addressWidth: Int + /// A list of captured frame information. public var frames: [Frame] @@ -199,6 +221,17 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// be `nil`. public var sharedCacheInfo: SharedCacheInfo? + /// Format an address according to the addressWidth. + /// + /// @param address The address to format. + /// @param prefix Whether to include a "0x" prefix. + /// + /// @returns A String containing the formatted Address. + public func formatAddress(_ address: Address, + prefix: Bool = true) -> String { + return hex(address, prefix: prefix, width: (addressWidth + 3) / 4) + } + /// Capture a backtrace from the current program location. /// /// The `capture()` method itself will not be included in the backtrace; @@ -219,15 +252,23 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// /// @returns A new `Backtrace` struct. @inline(never) + @_semantics("use_frame_pointer") public static func capture(algorithm: UnwindAlgorithm = .auto, limit: Int? = 64, offset: Int = 0, top: Int = 16) throws -> Backtrace { + #if os(Linux) + let images = captureImages() + #else + let images: [Image]? = nil + #endif + // N.B. We use offset+1 here to skip this frame, rather than inlining // this code into the client. return try HostContext.withCurrentContext { ctx in try capture(from: ctx, using: UnsafeLocalMemoryReader(), + images: images, algorithm: algorithm, limit: limit, offset: offset + 1, @@ -236,23 +277,32 @@ public struct Backtrace: CustomStringConvertible, Sendable { } @_spi(Internal) - public static func capture(from context: some Context, - using memoryReader: some MemoryReader, - algorithm: UnwindAlgorithm = .auto, - limit: Int? = 64, - offset: Int = 0, - top: Int = 16) throws -> Backtrace { + public static func capture( + from context: Ctx, + using memoryReader: Rdr, + images: [Image]?, + algorithm: UnwindAlgorithm = .auto, + limit: Int? = 64, + offset: Int = 0, + top: Int = 16 + ) throws -> Backtrace { + let addressWidth = 8 * MemoryLayout.size + switch algorithm { // All of them, for now, use the frame pointer unwinder. In the long // run, we should be using DWARF EH frame data for .precise. case .auto, .fast, .precise: let unwinder = - FramePointerUnwinder(context: context, memoryReader: memoryReader) + FramePointerUnwinder(context: context, + images: images, + memoryReader: memoryReader) .dropFirst(offset) if let limit = limit { if limit <= 0 { - return Backtrace(frames: [.truncated]) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: [.truncated]) } let realTop = top < limit ? top : limit - 1 @@ -276,7 +326,9 @@ public struct Backtrace: CustomStringConvertible, Sendable { frames[limit - 1] = .truncated } - return Backtrace(frames: frames) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: frames) } else { // If we still have frames at this point, start tracking the @@ -312,10 +364,16 @@ public struct Backtrace: CustomStringConvertible, Sendable { with: topFrames.prefix(secondPart)) } - return Backtrace(frames: frames) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: frames, + images: images) } } else { - return Backtrace(frames: Array(unwinder)) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: Array(unwinder), + images: images) } } } @@ -326,20 +384,20 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// @returns A list of `Image`s. public static func captureImages() -> [Image] { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - return captureImages(for: _swift_backtrace_task_self()) + return captureImages(for: mach_task_self()) #else - return [] + return captureImages(using: UnsafeLocalMemoryReader()) #endif } #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - private static func withDyldProcessInfo(for task: __swift_task_t, + private static func withDyldProcessInfo(for task: task_t, fn: (OpaquePointer?) throws -> T) rethrows -> T { - var kret = __swift_kern_return_t(_SWIFT_KERN_SUCCESS) + var kret = kern_return_t(KERN_SUCCESS) let dyldInfo = _dyld_process_info_create(task, 0, &kret) - if kret != _SWIFT_KERN_SUCCESS { + if kret != KERN_SUCCESS { fatalError("error: cannot create dyld process info") } @@ -351,12 +409,11 @@ public struct Backtrace: CustomStringConvertible, Sendable { } #endif + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) @_spi(Internal) public static func captureImages(for process: Any) -> [Image] { var images: [Image] = [] - - #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - let task = process as! __swift_task_t + let task = process as! task_t withDyldProcessInfo(for: task) { dyldInfo in _dyld_process_info_for_each_image(dyldInfo) { @@ -365,7 +422,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { if let path = path, let uuid = uuid { let pathString = String(cString: path) let theUUID = Array(UnsafeBufferPointer(start: uuid, - count: MemoryLayout<__swift_uuid_t>.size)) + count: MemoryLayout.size)) let name: String if let slashIndex = pathString.lastIndex(of: "/") { name = String(pathString.suffix(from: @@ -393,17 +450,146 @@ public struct Backtrace: CustomStringConvertible, Sendable { } } } - #endif // os(macOS) || os(iOS) || os(watchOS) return images.sorted(by: { $0.baseAddress < $1.baseAddress }) } + #else // !(os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) + private struct AddressRange { + var low: Address = 0 + var high: Address = 0 + } + + @_spi(Internal) + public static func captureImages(using reader: M, + forProcess pid: Int? = nil) -> [Image] { + var images: [Image] = [] + + #if os(Linux) + let path: String + if let pid = pid { + path = "/proc/\(pid)/maps" + } else { + path = "/proc/self/maps" + } + + guard let procMaps = readString(from: path) else { + return [] + } + + let mapRegex = #/ + ^(?[A-Fa-f0-9]+)-(?[A-Fa-f0-9]+)\s+ + (?[-rwxsp]{4})\s+ + (?[A-Fa-f0-9]+)\s+ + (?[A-Fa-f0-9]+):(?[A-Fa-f0-9]+)\s+ + (?\d+)\s+ + (?.*)\s*$ + /# + let lines = procMaps.split(separator: "\n") + + // Find all the mapped files and get high/low ranges + var mappedFiles: [Substring:AddressRange] = [:] + for line in lines { + if let match = try? mapRegex.wholeMatch(in: line) { + let path = stripWhitespace(match.pathname) + if match.inode == "0" || path == "" { + continue + } + guard let start = Address(match.start, radix: 16), + let end = Address(match.end, radix: 16) else { + continue + } + + if let range = mappedFiles[path] { + mappedFiles[path] = AddressRange(low: min(start, range.low), + high: max(end, range.high)) + } else { + mappedFiles[path] = AddressRange(low: start, + high: end) + } + } + } + + // Look for ELF headers in the process' memory + typealias Source = MemoryImageSource + let source = Source(with: reader) + for line in lines { + if let match = try? mapRegex.wholeMatch(in: line) { + let path = stripWhitespace(match.pathname) + if match.inode == "0" || path == "" { + continue + } + + guard let start = Address(match.start, radix: 16), + let end = Address(match.end, radix: 16), + let offset = Address(match.offset, radix: 16) else { + continue + } + + if offset != 0 || end - start < EI_NIDENT { + continue + } + + // Extract the filename from path + let name: Substring + if let slashIndex = path.lastIndex(of: "/") { + name = path.suffix(from: path.index(after: slashIndex)) + } else { + name = path + } + + // Inspect the image and extract the UUID and end of text + let range = mappedFiles[path]! + let subSource = SubImageSource(parent: source, + baseAddress: Source.Address(range.low), + length: Source.Size(range.high + - range.low)) + var theUUID: [UInt8]? = nil + var endOfText: Address = range.low + + if let image = try? Elf32Image(source: subSource) { + theUUID = image.uuid + + for hdr in image.programHeaders { + if hdr.p_type == .PT_LOAD && (hdr.p_flags & PF_X) != 0 { + endOfText = max(endOfText, range.low + Address(hdr.p_vaddr + + hdr.p_memsz)) + } + } + } else if let image = try? Elf64Image(source: subSource) { + theUUID = image.uuid + + for hdr in image.programHeaders { + if hdr.p_type == .PT_LOAD && (hdr.p_flags & PF_X) != 0 { + endOfText = max(endOfText, range.low + Address(hdr.p_vaddr + + hdr.p_memsz)) + } + } + } else { + // Not a valid ELF image + continue + } + + let image = Image(name: String(name), + path: String(path), + buildID: theUUID, + baseAddress: range.low, + endOfText: endOfText) + + images.append(image) + } + } + #endif + + return images.sorted(by: { $0.baseAddress < $1.baseAddress }) + } + #endif /// Capture shared cache information. /// /// @returns A `SharedCacheInfo`. public static func captureSharedCacheInfo() -> SharedCacheInfo? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - return captureSharedCacheInfo(for: _swift_backtrace_task_self()) + return captureSharedCacheInfo(for: mach_task_self()) #else return nil #endif @@ -412,13 +598,13 @@ public struct Backtrace: CustomStringConvertible, Sendable { @_spi(Internal) public static func captureSharedCacheInfo(for t: Any) -> SharedCacheInfo? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - let task = t as! __swift_task_t + let task = t as! task_t return withDyldProcessInfo(for: task) { dyldInfo in var cacheInfo = dyld_process_cache_info() _dyld_process_info_get_cache(dyldInfo, &cacheInfo) let theUUID = withUnsafePointer(to: cacheInfo.cacheUUID) { Array(UnsafeRawBufferPointer(start: $0, - count: MemoryLayout<__swift_uuid_t>.size)) + count: MemoryLayout.size)) } return SharedCacheInfo(uuid: theUUID, baseAddress: Address(cacheInfo.cacheBaseAddress), @@ -462,10 +648,11 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// Provide a textual version of the backtrace. public var description: String { var lines: [String] = [] + let addressChars = (addressWidth + 3) / 4 var n = 0 for frame in frames { - lines.append("\(n)\t\(frame)") + lines.append("\(n)\t\(frame.description(width: addressChars))") switch frame { case let .omittedFrames(count): n += count @@ -479,7 +666,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { lines.append("Images:") lines.append("") for (n, image) in images.enumerated() { - lines.append("\(n)\t\(image)") + lines.append("\(n)\t\(image.description(width: addressChars))") } } @@ -488,8 +675,9 @@ public struct Backtrace: CustomStringConvertible, Sendable { lines.append("") lines.append("Shared Cache:") lines.append("") - lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") - lines.append(" Base: \(hex(sharedCacheInfo.baseAddress))") + lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") + lines.append(" Base: \(hex(sharedCacheInfo.baseAddress, width: addressChars))") + lines.append(" Active: \(!sharedCacheInfo.noCache)") } #endif diff --git a/stdlib/public/Backtracing/BacktraceFormatter.swift b/stdlib/public/Backtracing/BacktraceFormatter.swift index 9ac84850e1e13..7f0abf15f637d 100644 --- a/stdlib/public/Backtracing/BacktraceFormatter.swift +++ b/stdlib/public/Backtracing/BacktraceFormatter.swift @@ -17,7 +17,7 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +@_implementationOnly import OS.Libc /// A backtrace formatting theme. @_spi(Formatting) @@ -517,23 +517,25 @@ public struct BacktraceFormatter { /// Format an individual frame into a list of columns. /// - /// @param frame The frame to format. - /// @param index The frame index, if required. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result An array of strings, one per column. public func formatColumns(frame: Backtrace.Frame, + addressWidth: Int, index: Int? = nil) -> [String] { let pc: String var attrs: [String] = [] switch frame { case let .programCounter(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" case let .returnAddress(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("ra") case let .asyncResumePoint(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("async") case .omittedFrames(_), .truncated: pc = "..." @@ -555,37 +557,48 @@ public struct BacktraceFormatter { /// Format a frame into a list of rows. /// - /// @param frame The frame to format. - /// @param index The frame index, if required. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result An array of table rows. public func formatRows(frame: Backtrace.Frame, + addressWidth: Int, index: Int? = nil) -> [TableRow] { - return [.columns(formatColumns(frame: frame, index: index))] + return [.columns(formatColumns(frame: frame, + addressWidth: addressWidth, + index: index))] } /// Format just one frame. /// - /// @param frame The frame to format. - /// @param index (Optional) frame index. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result A `String` containing the formatted data. - public func format(frame: Backtrace.Frame, index: Int? = nil) -> String { - let rows = formatRows(frame: frame, index: index) + public func format(frame: Backtrace.Frame, + addressWidth: Int, + index: Int? = nil) -> String { + let rows = formatRows(frame: frame, + addressWidth: addressWidth, + index: index) return BacktraceFormatter.formatTable(rows, alignments: [.right]) } /// Format the frame list from a backtrace. /// - /// @param frames The frames to format. + /// @param frames The frames to format. + /// @param addressWidth The width, in characters, of an address. /// /// @result A `String` containing the formatted data. - public func format(frames: some Sequence) -> String { + public func format(frames: some Sequence, + addressWidth: Int) -> String { var rows: [TableRow] = [] var n = 0 for frame in frames { - rows += formatRows(frame: frame, index: n) + rows += formatRows(frame: frame, addressWidth: addressWidth, index: n) if case let .omittedFrames(count) = frame { n += count @@ -603,7 +616,8 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(backtrace: Backtrace) -> String { - return format(frames: backtrace.frames) + return format(frames: backtrace.frames, + addressWidth: (backtrace.addressWidth + 3) / 4) } /// Grab source lines for a symbolicated backtrace. @@ -613,11 +627,11 @@ public struct BacktraceFormatter { /// with the point at which the program crashed highlighted. private func formattedSourceLines(from sourceLocation: SymbolicatedBacktrace.SourceLocation, indent theIndent: Int = 2) -> String? { - guard let fp = _swift_backtrace_fopen(sourceLocation.path, "rt") else { + guard let fp = fopen(sourceLocation.path, "rt") else { return nil } defer { - _swift_backtrace_fclose(fp) + fclose(fp) } let indent = String(repeating: " ", count: theIndent) @@ -693,8 +707,8 @@ public struct BacktraceFormatter { } } - while _swift_backtrace_feof(fp) == 0 && _swift_backtrace_ferror(fp) == 0 { - guard let result = _swift_backtrace_fgets(buffer.baseAddress, + while feof(fp) == 0 && ferror(fp) == 0 { + guard let result = fgets(buffer.baseAddress, CInt(buffer.count), fp) else { break } @@ -720,18 +734,19 @@ public struct BacktraceFormatter { /// /// @result An array of strings, one per column. public func formatColumns(frame: SymbolicatedBacktrace.Frame, + addressWidth: Int, index: Int? = nil) -> [String] { let pc: String var attrs: [String] = [] switch frame.captured { case let .programCounter(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" case let .returnAddress(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("ra") case let .asyncResumePoint(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("async") case .omittedFrames(_), .truncated: pc = "" @@ -830,14 +845,18 @@ public struct BacktraceFormatter { /// Format a frame into a list of rows. /// - /// @param frame The frame to format. - /// @param index The frame index, if required. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result An array of table rows. public func formatRows(frame: SymbolicatedBacktrace.Frame, + addressWidth: Int, index: Int? = nil, showSource: Bool = true) -> [TableRow] { - let columns = formatColumns(frame: frame, index: index) + let columns = formatColumns(frame: frame, + addressWidth: addressWidth, + index: index) var rows: [TableRow] = [.columns(columns)] if showSource { @@ -855,14 +874,17 @@ public struct BacktraceFormatter { /// Format just one frame. /// - /// @param frame The frame to format. - /// @param index (Optional) frame index. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result A `String` containing the formatted data. public func format(frame: SymbolicatedBacktrace.Frame, + addressWidth: Int, index: Int? = nil, showSource: Bool = true) -> String { - let rows = formatRows(frame: frame, index: index, showSource: showSource) + let rows = formatRows(frame: frame, addressWidth: addressWidth, + index: index, showSource: showSource) return BacktraceFormatter.formatTable(rows, alignments: [.right]) } @@ -875,10 +897,12 @@ public struct BacktraceFormatter { /// Format the frame list from a symbolicated backtrace. /// - /// @param frames The frames to format. + /// @param frames The frames to format. + /// @param addressWidth The width, in characters, of an address. /// /// @result A `String` containing the formatted data. - public func format(frames: some Sequence) -> String { + public func format(frames: some Sequence, + addressWidth: Int) -> String { var rows: [TableRow] = [] var sourceLocationsShown = Set() @@ -898,7 +922,8 @@ public struct BacktraceFormatter { } } - rows += formatRows(frame: frame, index: n, showSource: showSource) + rows += formatRows(frame: frame, addressWidth: addressWidth, + index: n, showSource: showSource) if case let .omittedFrames(count) = frame.captured { n += count @@ -916,14 +941,16 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(backtrace: SymbolicatedBacktrace) -> String { - var result = format(frames: backtrace.frames) + let addressChars = (backtrace.addressWidth + 3) / 4 + var result = format(frames: backtrace.frames, addressWidth: addressChars) switch options._showImages { case .none: break case .all: result += "\n\nImages:\n" - result += format(images: backtrace.images) + result += format(images: backtrace.images, + addressWidth: addressChars) case .mentioned: var mentionedImages = Set() for frame in backtrace.frames { @@ -942,7 +969,7 @@ public struct BacktraceFormatter { } else { result += "\n\nImages (only mentioned):\n" } - result += format(images: images) + result += format(images: images, addressWidth: addressChars) } return result @@ -950,11 +977,13 @@ public struct BacktraceFormatter { /// Format a `Backtrace.Image` into a list of columns. /// - /// @param image The `Image` object to format. + /// @param image The `Image` object to format. + /// @param addressWidth The width of an address, in characters. /// /// @result An array of strings, one per column. - public func formatColumns(image: Backtrace.Image) -> [String] { - let addressRange = "\(hex(image.baseAddress))–\(hex(image.endOfText))" + public func formatColumns(image: Backtrace.Image, + addressWidth: Int) -> [String] { + let addressRange = "\(hex(image.baseAddress, width: addressWidth))–\(hex(image.endOfText, width: addressWidth))" let buildID: String if let bytes = image.buildID { buildID = hex(bytes) @@ -977,11 +1006,18 @@ public struct BacktraceFormatter { /// Format an array of `Backtrace.Image`s. /// - /// @param images The array of `Image` objects to format. + /// @param images The array of `Image` objects to format. + /// @param addressWidth The width of an address, in characters. /// /// @result A string containing the formatted data. - public func format(images: some Sequence) -> String { - let rows = images.map{ TableRow.columns(formatColumns(image: $0)) } + public func format(images: some Sequence, + addressWidth: Int) -> String { + let rows = images.map{ + TableRow.columns( + formatColumns(image: $0, + addressWidth: addressWidth) + ) + } return BacktraceFormatter.formatTable(rows) } diff --git a/stdlib/public/Backtracing/ByteSwapping.swift b/stdlib/public/Backtracing/ByteSwapping.swift new file mode 100644 index 0000000000000..0ab94299f0ebd --- /dev/null +++ b/stdlib/public/Backtracing/ByteSwapping.swift @@ -0,0 +1,95 @@ +//===--- ByteSwapping.swift - Utilities for byte swapping -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines a ByteSwappable protocol that types can implement to indicate that +// they are able to perform byte swap operations. +// +// Mostly the types that implement this should be defined in C. +// +//===----------------------------------------------------------------------===// + +import Swift + +protocol ByteSwappable { + var byteSwapped: Self { get } + var bigEndian: Self { get } + var littleEndian: Self { get } + + init(bigEndian: Self) + init(littleEndian: Self) +} + +extension ByteSwappable { + init(bigEndian value: Self) { +#if _endian(big) + self = value +#else + self = value.byteSwapped +#endif + } + + init(littleEndian value: Self) { +#if _endian(little) + self = value +#else + self = value.byteSwapped +#endif + } + + var littleEndian: Self { +#if _endian(little) + return self +#else + return self.byteSwapped +#endif + } + + var bigEndian: Self { +#if _endian(big) + return self +#else + return self.byteSwapped +#endif + } +} + +extension Array where Self.Element: ByteSwappable { + mutating func swapBytes() { + for n in 0.. UnsafeBufferPointer + typealias OutputSink = (_ used: UInt, _ done: Bool) throws + -> UnsafeMutableBufferPointer? + + func decompress(input: InputSource, output: OutputSink) throws -> UInt +} + +// .. Compression library bindings ............................................. + +private var lzmaHandle = dlopen("liblzma.so.5", RTLD_LAZY) +private var zlibHandle = dlopen("libz.so.1", RTLD_LAZY) +private var zstdHandle = dlopen("libzstd.so.1", RTLD_LAZY) + +private func symbol(_ handle: UnsafeMutableRawPointer?, _ name: String) -> T? { + guard let handle = handle, let result = dlsym(handle, name) else { + return nil + } + return unsafeBitCast(result, to: T.self) +} + +private enum Sym { + static let lzma_stream_decoder: ( + @convention(c) (UnsafeMutablePointer, + UInt64, UInt32) -> lzma_ret)? + = symbol(lzmaHandle, "lzma_stream_decoder") + + static let lzma_code: (@convention(c) (UnsafeMutablePointer, + lzma_action) -> lzma_ret)? + = symbol(lzmaHandle, "lzma_code") + + static let lzma_end: (@convention(c) (UnsafeMutablePointer) -> ())? + = symbol(lzmaHandle, "lzma_end") + + static let inflateInit_: (@convention(c) (z_streamp, + UnsafePointer, CInt) -> CInt)? + = symbol(zlibHandle, "inflateInit_") + + static func inflateInit(_ stream: z_streamp) -> CInt { + return inflateInit_!(stream, ZLIB_VERSION, CInt(MemoryLayout.size)) + } + + static let inflate: (@convention(c) (z_streamp, CInt) -> CInt)? + = symbol(zlibHandle, "inflate") + + static let inflateEnd: (@convention(c) (z_streamp) -> CInt)? + = symbol(zlibHandle, "inflateEnd") + + static let ZSTD_createDStream: ( + @convention(c) () -> UnsafeMutableRawPointer?)? + = symbol(zstdHandle, "ZSTD_createDStream") + + static let ZSTD_freeDStream: ( + @convention(c) (UnsafeMutableRawPointer) -> UInt)? + = symbol(zstdHandle, "ZSTD_freeDStream") + + static let ZSTD_decompressStream: ( + @convention(c) (UnsafeMutableRawPointer, + UnsafeMutablePointer, + UnsafeMutablePointer) -> UInt)? + = symbol(zstdHandle, "ZSTD_decompressStream") + + static let ZSTD_isError: (@convention(c) (UInt) -> UInt)? + = symbol(zstdHandle, "ZSTD_isError") +} + +// .. zlib (deflate) ........................................................... + +enum ZLibError: Error { + case decodeError(CInt) +} + +struct ZLibStream: CompressedStream { + init() {} + + func decompress(input: InputSource, output: OutputSink) throws -> UInt { + + if zlibHandle == nil { + throw CompressedImageSourceError.libraryNotFound("libz") + } + + var stream = zlib_stream_init() + + let ret = Sym.inflateInit(&stream) + if ret != Z_OK { + throw ZLibError.decodeError(ret) + } + defer { + _ = Sym.inflateEnd!(&stream) + } + + var outputBufferSize = UInt(0) + while true { + if stream.avail_in == 0 { + let buffer = try input() + + // Not really mutable; this is just an issue with z_const + stream.next_in = UnsafeMutablePointer(mutating: buffer.baseAddress) + stream.avail_in = CUnsignedInt(buffer.count) + } + + if stream.avail_out == 0 { + guard let buffer = try output(outputBufferSize, false) else { + throw CompressedImageSourceError.outputOverrun + } + + stream.next_out = buffer.baseAddress + stream.avail_out = CUnsignedInt(buffer.count) + outputBufferSize = UInt(buffer.count) + } + + let ret = Sym.inflate!(&stream, Z_NO_FLUSH) + + if ret == Z_STREAM_END { + _ = try output(outputBufferSize - UInt(stream.avail_out), true) + return stream.total_out + } + + if ret != Z_OK { + throw ZLibError.decodeError(ret) + } + } + } +} + +// .. zstd ..................................................................... + +enum ZStdError: Error { + case unableToCreateStream + case decodeError(UInt) +} + +struct ZStdStream: CompressedStream { + init() {} + + func decompress(input: InputSource, output: OutputSink) throws -> UInt { + + if zstdHandle == nil { + throw CompressedImageSourceError.libraryNotFound("libzstd") + } + + guard let stream = Sym.ZSTD_createDStream!() else { + throw ZStdError.unableToCreateStream + } + defer { + _ = Sym.ZSTD_freeDStream!(stream) + } + + var inBuffer = ZSTD_inBuffer(src: nil, size: 0, pos: 0) + var outBuffer = ZSTD_outBuffer(dst: nil, size: 0, pos: 0) + var totalOutput = UInt(0) + + while true { + if inBuffer.size == inBuffer.pos { + let buffer = try input() + + inBuffer.src = UnsafeRawPointer(buffer.baseAddress) + inBuffer.size = buffer.count + inBuffer.pos = 0 + } + + if outBuffer.size == outBuffer.pos { + let byteCount = UInt(outBuffer.pos) + + totalOutput += byteCount + + guard let buffer = try output(byteCount, false) else { + throw CompressedImageSourceError.outputOverrun + } + + outBuffer.dst = UnsafeMutableRawPointer(buffer.baseAddress) + outBuffer.size = buffer.count + outBuffer.pos = 0 + } + + let ret = Sym.ZSTD_decompressStream!(stream, &outBuffer, &inBuffer) + + if ret == 0 { + _ = try output(UInt(outBuffer.pos), true) + return totalOutput + } + + if Sym.ZSTD_isError!(ret) != 0 { + throw ZStdError.decodeError(ret) + } + } + } +} + + +// .. LZMA ..................................................................... + +enum LZMAError: Error { + case decodeError(lzma_ret) +} + +struct LZMAStream: CompressedStream { + private var memlimit: UInt64 + private var flags: UInt32 + + init(memlimit: UInt64 = ~UInt64(0), flags: UInt32 = 0) { + self.memlimit = memlimit + self.flags = flags + } + + func decompress(input: InputSource, output: OutputSink) throws -> UInt { + + if lzmaHandle == nil { + throw CompressedImageSourceError.libraryNotFound("liblzma") + } + + var stream = lzma_stream_init() + + let ret = Sym.lzma_stream_decoder!(&stream, memlimit, flags) + if ret != LZMA_OK { + throw LZMAError.decodeError(ret) + } + defer { + Sym.lzma_end!(&stream) + } + + var outputBufferSize = UInt(0) + while true { + if stream.avail_in == 0 { + let buffer = try input() + stream.next_in = buffer.baseAddress + stream.avail_in = buffer.count + } + + if stream.avail_out == 0 { + guard let buffer = try output(outputBufferSize, false) else { + throw CompressedImageSourceError.outputOverrun + } + + stream.next_out = buffer.baseAddress + stream.avail_out = buffer.count + outputBufferSize = UInt(buffer.count) + } + + let ret = Sym.lzma_code!(&stream, LZMA_RUN) + + if ret == LZMA_STREAM_END { + _ = try output(outputBufferSize - UInt(stream.avail_out), true) + return UInt(stream.total_out) + } + + if ret != LZMA_OK { + throw LZMAError.decodeError(ret) + } + } + } +} + +// .. Image Sources ............................................................ + +fileprivate func decompress( + stream: S, source: I, dataBounds: I.Bounds, uncompressedSize: UInt? = nil) + throws -> [UInt8] { + + var pos = dataBounds.base + var remaining = dataBounds.size + + let bufSize = 65536 + + if let uncompressedSize = uncompressedSize { + // If we know the uncompressed size, we can decompress directly into the + // array. + + let inputBuffer + = UnsafeMutableBufferPointer.allocate(capacity: bufSize) + defer { + inputBuffer.deallocate() + } + + return try [UInt8].init(unsafeUninitializedCapacity: Int(uncompressedSize)) { + (outputBuffer: inout UnsafeMutableBufferPointer, + count: inout Int) in + + count = Int(try stream.decompress( + input: { () throws -> UnsafeBufferPointer in + + let chunkSize = min(Int(remaining), inputBuffer.count) + let slice = inputBuffer[0.. UnsafeMutableBufferPointer? in + + if used == 0 { + return outputBuffer + } else { + return nil + } + } + )) + } + } else { + // Otherwise, we decompress in chunks and append them to the array. + + let buffer + = UnsafeMutableBufferPointer.allocate(capacity: 2 * bufSize) + defer { + buffer.deallocate() + } + + let inputBuffer = UnsafeMutableBufferPointer(rebasing: buffer[0.. UnsafeBufferPointer in + + let chunkSize = min(Int(remaining), inputBuffer.count) + let slice = inputBuffer[0.. UnsafeMutableBufferPointer? in + + data.append(contentsOf: outputBuffer[..: ImageSource { + + private var data: [UInt8] + + var isMappedImage: Bool { return false } + var path: String? { return nil } + var bounds: Bounds? { return Bounds(base: Address(0), size: Size(data.count)) } + + init(source: some ImageSource) throws { + guard let bounds = source.bounds else { + throw CompressedImageSourceError.unboundedImageSource + } + + if bounds.size < MemoryLayout.size { + throw CompressedImageSourceError.badCompressedData + } + + let chdr = try source.fetch(from: bounds.base, + as: Traits.Chdr.self) + let dataBounds = bounds.adjusted(by: MemoryLayout.stride) + let uncompressedSize = UInt(chdr.ch_size) + + switch chdr.ch_type { + case .ELFCOMPRESS_ZLIB: + data = try decompress(stream: ZLibStream(), + source: source, dataBounds: dataBounds, + uncompressedSize: uncompressedSize) + case .ELFCOMPRESS_ZSTD: + data = try decompress(stream: ZStdStream(), + source: source, dataBounds: dataBounds, + uncompressedSize: uncompressedSize) + default: + throw CompressedImageSourceError.unsupportedFormat + } + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { + throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) + } + + buffer.withMemoryRebound(to: UInt8.self) { outBuf in + for n in 0..(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { + throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) + } + + buffer.withMemoryRebound(to: UInt8.self) { outBuf in + for n in 0..(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { + throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) + } + + buffer.withMemoryRebound(to: UInt8.self) { outBuf in + for n in 0.. HostContext? { - return X86_64Context(from: thread as! __swift_thread_t) + return X86_64Context(from: thread as! thread_t) } public static func fromHostMContext(_ mcontext: Any) -> HostContext { return X86_64Context(with: mcontext as! darwin_x86_64_mcontext) } + #elseif os(Linux) && arch(x86_64) + init(with mctx: mcontext_t) { + gprs.setR(X86_64Register.rax.rawValue, to: UInt64(bitPattern: mctx.gregs.13)) + gprs.setR(X86_64Register.rbx.rawValue, to: UInt64(bitPattern: mctx.gregs.12)) + gprs.setR(X86_64Register.rcx.rawValue, to: UInt64(bitPattern: mctx.gregs.14)) + gprs.setR(X86_64Register.rdx.rawValue, to: UInt64(bitPattern: mctx.gregs.11)) + gprs.setR(X86_64Register.rdi.rawValue, to: UInt64(bitPattern: mctx.gregs.9)) + gprs.setR(X86_64Register.rsi.rawValue, to: UInt64(bitPattern: mctx.gregs.8)) + gprs.setR(X86_64Register.rbp.rawValue, to: UInt64(bitPattern: mctx.gregs.10)) + gprs.setR(X86_64Register.rsp.rawValue, to: UInt64(bitPattern: mctx.gregs.15)) + gprs.setR(X86_64Register.r8.rawValue, to: UInt64(bitPattern: mctx.gregs.0)) + gprs.setR(X86_64Register.r9.rawValue, to: UInt64(bitPattern: mctx.gregs.1)) + gprs.setR(X86_64Register.r10.rawValue, to: UInt64(bitPattern: mctx.gregs.2)) + gprs.setR(X86_64Register.r11.rawValue, to: UInt64(bitPattern: mctx.gregs.3)) + gprs.setR(X86_64Register.r12.rawValue, to: UInt64(bitPattern: mctx.gregs.4)) + gprs.setR(X86_64Register.r13.rawValue, to: UInt64(bitPattern: mctx.gregs.5)) + gprs.setR(X86_64Register.r14.rawValue, to: UInt64(bitPattern: mctx.gregs.6)) + gprs.setR(X86_64Register.r15.rawValue, to: UInt64(bitPattern: mctx.gregs.7)) + gprs.rip = UInt64(bitPattern: mctx.gregs.16) + gprs.rflags = UInt64(bitPattern: mctx.gregs.17) + gprs.cs = UInt16(mctx.gregs.18 & 0xffff) + gprs.fs = UInt16((mctx.gregs.18 >> 16) & 0xffff) + gprs.gs = UInt16((mctx.gregs.18 >> 32) & 0xffff) + gprs.valid = 0x1fffff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return X86_64Context(with: mcontext as! mcontext_t) + } #endif #if os(Windows) || !SWIFT_ASM_AVAILABLE @@ -398,6 +438,8 @@ extension arm_gprs { var gprs = i386_gprs() + public var architecture: String { "i386" } + public var programCounter: GPRValue { get { return gprs.eip } set { @@ -423,6 +465,32 @@ extension arm_gprs { public static var registerCount: Int { return 50 } + #if os(Linux) && arch(i386) + init(with mctx: mcontext_t) { + gprs.setR(I386Register.eax.rawValue, to: UInt32(bitPattern: mctx.gregs.11)) + gprs.setR(I386Register.ecx.rawValue, to: UInt32(bitPattern: mctx.gregs.10)) + gprs.setR(I386Register.edx.rawValue, to: UInt32(bitPattern: mctx.gregs.9)) + gprs.setR(I386Register.ebx.rawValue, to: UInt32(bitPattern: mctx.gregs.8)) + gprs.setR(I386Register.esp.rawValue, to: UInt32(bitPattern: mctx.gregs.7)) + gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.6)) + gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.5)) + gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.4)) + gprs.eip = UInt32(bitPattern: mctx.gregs.14) + gprs.eflags = UInt32(bitPattern: mctx.gregs.16) + gprs.segreg.0 = UInt16(bitPattern: mctx.gregs.2 & 0xffff) // es + gprs.segreg.1 = UInt16(bitPattern: mctx.gregs.15 & 0xffff) // cs + gprs.segreg.2 = UInt16(bitPattern: mctx.gregs.18 & 0xffff) // ss + gprs.segreg.3 = UInt16(bitPattern: mctx.gregs.3 & 0xffff) // ds + gprs.segreg.4 = UInt16(bitPattern: mctx.gregs.1 & 0xffff) // fs + gprs.segreg.5 = UInt16(bitPattern: mctx.gregs.0 & 0xffff) // gs + gprs.valid = 0x7fff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return I386Context(with: mcontext as! mcontext_t) + } + #endif + #if os(Windows) || !SWIFT_ASM_AVAILABLE struct NotImplemented: Error {} public static func withCurrentContext(fn: (I386Context) throws -> T) throws -> T { @@ -553,6 +621,8 @@ extension arm_gprs { var gprs = arm64_gprs() + public var architecture: String { "arm64" } + public var programCounter: GPRValue { get { return gprs.pc } set { @@ -583,12 +653,12 @@ extension arm_gprs { public static var registerCount: Int { return 40 } #if os(macOS) && arch(arm64) - init?(from thread: __swift_thread_t) { + init?(from thread: thread_t) { var state = darwin_arm64_thread_state() let kr = thread_get_state(thread, - _SWIFT_ARM_THREAD_STATE64, + ARM_THREAD_STATE64, &state) - if kr != _SWIFT_KERN_SUCCESS { + if kr != KERN_SUCCESS { return nil } @@ -620,13 +690,35 @@ extension arm_gprs { } public static func fromHostThread(_ thread: Any) -> HostContext? { - return ARM64Context(from: thread as! __swift_thread_t) + return ARM64Context(from: thread as! thread_t) } public static func fromHostMContext(_ mcontext: Any) -> HostContext { return ARM64Context(with: mcontext as! darwin_arm64_mcontext) } -#endif + #elseif os(Linux) && arch(arm64) + init(with mctx: mcontext_t) { + withUnsafeMutablePointer(to: &gprs._x) { + $0.withMemoryRebound(to: UInt64.self, capacity: 32){ to in + withUnsafePointer(to: mctx.regs) { + $0.withMemoryRebound(to: UInt64.self, capacity: 31) { from in + for n in 0..<31 { + to[n] = from[n] + } + } + } + + to[31] = mctx.sp + } + } + gprs.pc = mctx.pc + gprs.valid = 0x1ffffffff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return ARM64Context(with: mcontext as! mcontext_t) + } + #endif #if os(Windows) || !SWIFT_ASM_AVAILABLE struct NotImplemented: Error {} @@ -747,6 +839,8 @@ extension arm_gprs { var gprs = arm_gprs() + public var architecture: String { "arm" } + public var programCounter: GPRValue { get { return gprs.getR(ARMRegister.r15.rawValue) } set { gprs.setR(ARMRegister.r15.rawValue, to: newValue) } @@ -769,6 +863,27 @@ extension arm_gprs { public static var registerCount: Int { return 16 } + #if os(Linux) && arch(arm) + init(with mctx: mcontext_t) { + withUnsafeMutablePointer(to: &gprs._r) { + $0.withMemoryRebound(to: UInt32.self, capacity: 16) { + withUnsafePointer(to: &mctx.arm_r0) { + $0.withMemoryRebound(to: UInt32.self, capacity: 16) { + for n in 0..<16 { + to[n] = from[n] + } + } + } + } + } + gprs.valid = 0xffff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return ARMContext(with: mcontext as! mcontext_t) + } + #endif + #if os(Windows) || !SWIFT_ASM_AVAILABLE struct NotImplemented: Error {} public static func withCurrentContext(fn: (ARMContext) throws -> T) throws -> T { @@ -860,20 +975,20 @@ extension arm_gprs { // .. Darwin specifics ......................................................... #if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) -private func thread_get_state(_ thread: __swift_thread_t, +private func thread_get_state(_ thread: thread_t, _ flavor: CInt, - _ result: inout T) -> __swift_kern_return_t { - var count: __swift_msg_type_number_t - = __swift_msg_type_number_t(MemoryLayout.stride - / MemoryLayout<__swift_natural_t>.stride) + _ result: inout T) -> kern_return_t { + var count: mach_msg_type_number_t + = mach_msg_type_number_t(MemoryLayout.stride + / MemoryLayout.stride) return withUnsafeMutablePointer(to: &result) { ptr in - ptr.withMemoryRebound(to: __swift_natural_t.self, + ptr.withMemoryRebound(to: natural_t.self, capacity: Int(count)) { intPtr in - return _swift_backtrace_thread_get_state(thread, - __swift_thread_state_flavor_t(flavor), - intPtr, - &count) + return thread_get_state(thread, + thread_state_flavor_t(flavor), + intPtr, + &count) } } } diff --git a/stdlib/public/Backtracing/CoreSymbolication.swift b/stdlib/public/Backtracing/CoreSymbolication.swift index 43388a5f15727..e5cbbeea03058 100644 --- a/stdlib/public/Backtracing/CoreSymbolication.swift +++ b/stdlib/public/Backtracing/CoreSymbolication.swift @@ -19,29 +19,28 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +@_implementationOnly import OS.Libc +@_implementationOnly import OS.Darwin // .. Dynamic binding .......................................................... private let coreFoundationPath = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" -private let coreFoundationHandle = - _swift_backtrace_dlopen_lazy(coreFoundationPath)! +private let coreFoundationHandle = dlopen(coreFoundationPath, RTLD_LAZY)! private let coreSymbolicationPath = "/System/Library/PrivateFrameworks/CoreSymbolication.framework/CoreSymbolication" -private let coreSymbolicationHandle = - _swift_backtrace_dlopen_lazy(coreSymbolicationPath)! +private let coreSymbolicationHandle = dlopen(coreSymbolicationPath, RTLD_LAZY)! private let crashReporterSupportPath = "/System/Library/PrivateFrameworks/CrashReporterSupport.framework/CrashReporterSupport" private let crashReporterSupportHandle - = _swift_backtrace_dlopen_lazy(crashReporterSupportPath)! + = dlopen(crashReporterSupportPath, RTLD_LAZY)! private func symbol(_ handle: UnsafeMutableRawPointer, _ name: String) -> T { - guard let result = _swift_backtrace_dlsym(handle, name) else { + guard let result = dlsym(handle, name) else { fatalError("Unable to look up \(name) in CoreSymbolication") } return unsafeBitCast(result, to: T.self) @@ -49,7 +48,7 @@ private func symbol(_ handle: UnsafeMutableRawPointer, _ name: String) -> T { private enum Sym { // CRCopySanitizedPath - static let CRCopySanitizedPath: @convention(c) (CFStringRef, CFIndex) -> CFStringRef = + static let CRCopySanitizedPath: @convention(c) (CFString, CFIndex) -> CFString = symbol(crashReporterSupportHandle, "CRCopySanitizedPath") // Base functionality @@ -69,7 +68,7 @@ private enum Sym { symbol(coreSymbolicationHandle, "CSSymbolicatorCreateWithBinaryImageList") static let CSSymbolicatorGetSymbolOwnerWithAddressAtTime: - @convention(c) (CSSymbolicatorRef, __swift_vm_address_t, + @convention(c) (CSSymbolicatorRef, vm_address_t, CSMachineTime) -> CSSymbolOwnerRef = symbol(coreSymbolicationHandle, "CSSymbolicatorGetSymbolOwnerWithAddressAtTime") static let CSSymbolicatorForeachSymbolOwnerAtTime: @@ -81,16 +80,16 @@ private enum Sym { @convention(c) (CSSymbolOwnerRef) -> UnsafePointer? = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetName") static let CSSymbolOwnerGetSymbolWithAddress: - @convention(c) (CSSymbolOwnerRef, __swift_vm_address_t) -> CSSymbolRef = + @convention(c) (CSSymbolOwnerRef, vm_address_t) -> CSSymbolRef = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetSymbolWithAddress") static let CSSymbolOwnerGetSourceInfoWithAddress: - @convention(c) (CSSymbolOwnerRef, __swift_vm_address_t) -> CSSourceInfoRef = + @convention(c) (CSSymbolOwnerRef, vm_address_t) -> CSSourceInfoRef = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetSourceInfoWithAddress") static let CSSymbolOwnerForEachStackFrameAtAddress: - @convention(c) (CSSymbolOwnerRef, __swift_vm_address_t, CSStackFrameIterator) -> UInt = + @convention(c) (CSSymbolOwnerRef, vm_address_t, CSStackFrameIterator) -> UInt = symbol(coreSymbolicationHandle, "CSSymbolOwnerForEachStackFrameAtAddress") static let CSSymbolOwnerGetBaseAddress: - @convention(c) (CSSymbolOwnerRef) -> __swift_vm_address_t = + @convention(c) (CSSymbolOwnerRef) -> vm_address_t = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetBaseAddress") // CSSymbol @@ -117,17 +116,17 @@ private enum Sym { // CFString static let CFStringCreateWithBytes: - @convention(c) (CFAllocatorRef?, UnsafeRawPointer?, CFIndex, - CFStringEncoding, Bool) -> CFStringRef? = + @convention(c) (CFAllocator?, UnsafeRawPointer?, CFIndex, + CFStringEncoding, Bool) -> CFString? = symbol(coreFoundationHandle, "CFStringCreateWithBytes") static let CFStringGetLength: - @convention(c) (CFStringRef) -> CFIndex = + @convention(c) (CFString) -> CFIndex = symbol(coreFoundationHandle, "CFStringGetLength") static let CFStringGetCStringPtr: - @convention(c) (CFStringRef, CFStringEncoding) -> UnsafePointer? = + @convention(c) (CFString, CFStringEncoding) -> UnsafePointer? = symbol(coreFoundationHandle, "CFStringGetCStringPtr") static let CFStringGetBytes: - @convention(c) (CFStringRef, CFRange, CFStringEncoding, UInt8, Bool, + @convention(c) (CFString, CFRange, CFStringEncoding, UInt8, Bool, UnsafeMutableRawPointer?, CFIndex, UnsafeMutablePointer?) -> CFIndex = symbol(coreFoundationHandle, "CFStringGetBytes") @@ -135,29 +134,16 @@ private enum Sym { // .. Core Foundation miscellany ............................................... -internal typealias CFTypeRef = OpaquePointer -internal typealias CFStringRef = CFTypeRef -internal typealias CFAllocatorRef = CFTypeRef -internal typealias CFIndex = __swift_backtrace_CFIndex -internal typealias CFRange = __swift_backtrace_CFRange -internal typealias CFUUIDBytes = __swift_backtrace_CFUUIDBytes -internal typealias CFStringEncoding = UInt32 - -internal enum CFStringBuiltInEncodings: UInt32 { - case ASCII = 0x0600 - case UTF8 = 0x08000100 -} - internal func CFRangeMake(_ location: CFIndex, _ length: CFIndex) -> CFRange { return CFRange(location: location, length: length) } -internal func CFStringCreateWithBytes(_ allocator: CFAllocatorRef?, +internal func CFStringCreateWithBytes(_ allocator: CFAllocator?, _ bytes: UnsafeRawPointer?, _ length: CFIndex, _ encoding: CFStringEncoding, _ isExternalRepresentation: Bool) - -> CFStringRef? { + -> CFString? { return Sym.CFStringCreateWithBytes(allocator, bytes, length, @@ -165,17 +151,17 @@ internal func CFStringCreateWithBytes(_ allocator: CFAllocatorRef?, isExternalRepresentation) } -internal func CFStringGetLength(_ s: CFStringRef) -> CFIndex { +internal func CFStringGetLength(_ s: CFString) -> CFIndex { return Sym.CFStringGetLength(s) } -internal func CFStringGetCStringPtr(_ s: CFStringRef, +internal func CFStringGetCStringPtr(_ s: CFString, _ encoding: CFStringEncoding) -> UnsafePointer? { return Sym.CFStringGetCStringPtr(s, encoding) } -internal func CFStringGetBytes(_ s: CFStringRef, +internal func CFStringGetBytes(_ s: CFString, _ range: CFRange, _ encoding: CFStringEncoding, _ lossByte: UInt8, @@ -194,7 +180,7 @@ internal func CFStringGetBytes(_ s: CFStringRef, // We can't import swiftFoundation here, so there's no automatic bridging for // CFString. As a result, we need to do the dance manually. -private func toCFString(_ s: String) -> CFStringRef! { +private func toCFString(_ s: String) -> CFString! { var s = s return s.withUTF8 { return CFStringCreateWithBytes(nil, @@ -205,7 +191,7 @@ private func toCFString(_ s: String) -> CFStringRef! { } } -private func fromCFString(_ cf: CFStringRef) -> String { +private func fromCFString(_ cf: CFString) -> String { let length = CFStringGetLength(cf) if length == 0 { return "" @@ -267,14 +253,14 @@ func CSIsNull(_ obj: CSTypeRef) -> Bool { let kCSSymbolicatorDisallowDaemonCommunication = UInt32(0x00000800) struct BinaryRelocationInformation { - var base: __swift_vm_address_t - var extent: __swift_vm_address_t + var base: vm_address_t + var extent: vm_address_t var name: String } struct BinaryImageInformation { - var base: __swift_vm_address_t - var extent: __swift_vm_address_t + var base: vm_address_t + var extent: vm_address_t var uuid: CFUUIDBytes var arch: CSArchitecture var path: String @@ -322,7 +308,7 @@ func CSSymbolicatorCreateWithBinaryImageList( imageList[n].relocationCount = UInt32(image.relocations.count) imageList[n].flags = image.flags - pathPtr += _swift_backtrace_strlen(pathPtr) + 1 + pathPtr += strlen(pathPtr) + 1 for relocation in image.relocations { relocationPtr.pointee.base = relocation.base @@ -355,7 +341,7 @@ func CSSymbolicatorCreateWithBinaryImageList( func CSSymbolicatorGetSymbolOwnerWithAddressAtTime( _ symbolicator: CSSymbolicatorRef, - _ addr: __swift_vm_address_t, + _ addr: vm_address_t, _ time: CSMachineTime ) -> CSSymbolOwnerRef { return Sym.CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, @@ -380,21 +366,21 @@ func CSSymbolOwnerGetName(_ sym: CSTypeRef) -> String? { func CSSymbolOwnerGetSymbolWithAddress( _ owner: CSSymbolOwnerRef, - _ address: __swift_vm_address_t + _ address: vm_address_t ) -> CSSymbolRef { return Sym.CSSymbolOwnerGetSymbolWithAddress(owner, address) } func CSSymbolOwnerGetSourceInfoWithAddress( _ owner: CSSymbolOwnerRef, - _ address: __swift_vm_address_t + _ address: vm_address_t ) -> CSSourceInfoRef { return Sym.CSSymbolOwnerGetSourceInfoWithAddress(owner, address) } func CSSymbolOwnerForEachStackFrameAtAddress( _ owner: CSSymbolOwnerRef, - _ address: __swift_vm_address_t, + _ address: vm_address_t, _ iterator: CSStackFrameIterator ) -> UInt { return Sym.CSSymbolOwnerForEachStackFrameAtAddress(owner, address, iterator) @@ -402,7 +388,7 @@ func CSSymbolOwnerForEachStackFrameAtAddress( func CSSymbolOwnerGetBaseAddress( _ owner: CSSymbolOwnerRef -) -> __swift_vm_address_t { +) -> vm_address_t { return Sym.CSSymbolOwnerGetBaseAddress(owner) } diff --git a/stdlib/public/Backtracing/Dwarf.swift b/stdlib/public/Backtracing/Dwarf.swift new file mode 100644 index 0000000000000..7d9fba1e44733 --- /dev/null +++ b/stdlib/public/Backtracing/Dwarf.swift @@ -0,0 +1,1786 @@ +//===--- Dwarf.swift - DWARF support for Swift ----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines various DWARF structures and provides types for working with +// DWARF data on disk and in memory. +// +//===----------------------------------------------------------------------===// + +#if os(Linux) + +import Swift + +@_implementationOnly import ImageFormats.Dwarf +@_implementationOnly import Runtime + +// .. Dwarf specific errors .................................................... + +private enum DwarfError: Error { + case noDebugInformation + case unsupportedVersion(Int) + case unknownEHValueEncoding + case unknownEHOffsetEncoding + case badAttribute(UInt64) + case badForm(UInt64) + case badTag(UInt64) + case badLength(UInt32) + case badAddressSize(Int) + case badLineContentType(UInt64) + case badString + case missingAbbrev(UInt64) + case doubleIndirectForm + case unknownForm(Dwarf_Form) + case missingBaseOffset + case missingAddrSection + case missingStrSection + case missingLineStrSection + case missingStrOffsetsSection + case missingAddrBase + case missingStrOffsetsBase + case missingLocListsBase + case unspecifiedAddressSize +} + +// .. Dwarf utilities for ImageSource .......................................... + +extension ImageSource { + + func fetchULEB128(from a: Address) throws -> (Address, UInt64) { + var addr = a + var shift = 0 + var value: UInt64 = 0 + while true { + let byte = try fetch(from: addr, as: UInt8.self) + addr += 1 + value |= UInt64(byte & 0x7f) << shift + if (byte & 0x80) == 0 { + break + } + shift += 7 + } + + return (addr, value) + } + + func fetchSLEB128(from a: Address) throws -> (Address, Int64) { + var addr = a + var shift = 0 + var sign: UInt8 = 0 + var value: Int64 = 0 + + while true { + let byte = try fetch(from: addr, as: UInt8.self) + addr += 1 + value |= Int64(byte & 0x7f) << shift + shift += 7 + sign = byte & 0x40 + if (byte & 0x80) == 0 { + break + } + } + + if shift < 64 && sign != 0 { + value |= -(1 << shift) + } + + return (addr, value) + } + + func fetchEHValue(from a: Address, with encoding: EHFrameEncoding, + pc: Address = 0, data: Address = 0, shouldSwap: Bool = false) throws + -> (Address, UInt64)? { + + func maybeSwap(_ x: T) -> T { + if shouldSwap { + return x.byteSwapped + } + return x + } + + let valueEnc = EHFrameEncoding(encoding & 0x0f) + var value: UInt64 = 0 + var addr = a + + switch valueEnc { + case DW_EH_PE_omit: + return nil + case DW_EH_PE_uleb128: + (addr, value) = try fetchULEB128(from: addr) + case DW_EH_PE_udata2: + let u2 = maybeSwap(try fetch(from: addr, as: UInt16.self)) + value = UInt64(u2) + addr += 2 + case DW_EH_PE_udata4: + let u4 = maybeSwap(try fetch(from: addr, as: UInt32.self)) + value = UInt64(u4) + addr += 4 + case DW_EH_PE_udata8: + let u8 = maybeSwap(try fetch(from: addr, as: UInt64.self)) + value = u8 + addr += 8 + case DW_EH_PE_sleb128: + let (newAddr, newValue) = try fetchSLEB128(from: addr) + value = UInt64(bitPattern: newValue) + addr = newAddr + case DW_EH_PE_sdata2: + let s2 = maybeSwap(try fetch(from: addr, as: Int16.self)) + value = UInt64(bitPattern: Int64(s2)) + addr += 2 + case DW_EH_PE_sdata4: + let s4 = maybeSwap(try fetch(from: addr, as: Int32.self)) + value = UInt64(bitPattern: Int64(s4)) + addr += 4 + case DW_EH_PE_sdata8: + let s8 = maybeSwap(try fetch(from: addr, as: Int64.self)) + value = UInt64(bitPattern: s8) + addr += 8 + default: + throw DwarfError.unknownEHValueEncoding + } + + let offsetEnc = EHFrameEncoding(encoding & 0xf0) + + switch offsetEnc { + case DW_EH_PE_absptr: + return (addr, value) + case DW_EH_PE_pcrel: + return (addr, UInt64(pc) &+ value) + case DW_EH_PE_datarel: + return (addr, UInt64(data) &+ value) + default: + throw DwarfError.unknownEHOffsetEncoding + } + } + + func fetchDwarfLength(from addr: Address) throws + -> (length: UInt64, isDwarf64: Bool) { + + let len32 = try fetch(from: addr, as: UInt32.self) + if len32 < 0xfffffff0 { + return (length: UInt64(len32), isDwarf64: false) + } else if len32 < 0xffffffff { + throw DwarfError.badLength(len32) + } else { + let len64 = try fetch(from: addr + 4, as: UInt64.self) + return (length: len64, isDwarf64: true) + } + } +} + +// .. Dwarf utilities for ImageSourceCursor ..................................... + +extension ImageSourceCursor { + + mutating func readULEB128() throws -> UInt64 { + let (next, result) = try source.fetchULEB128(from: pos) + pos = next + return result + } + + mutating func readSLEB128() throws -> Int64 { + let (next, result) = try source.fetchSLEB128(from: pos) + pos = next + return result + } + + mutating func readEHValue( + with encoding: EHFrameEncoding, + pc: Address = 0, + data: Address = 0, + shouldSwap: Bool = false + ) throws -> UInt64? { + guard let (next, result) + = try source.fetchEHValue(from: pos, + with: encoding, + pc: pc, + data: data, + shouldSwap: shouldSwap) else { + return nil + } + + pos = next + return result + } + + mutating func readDwarfLength() throws -> (length: UInt64, isDwarf64: Bool) { + let result = try source.fetchDwarfLength(from: pos) + pos += result.isDwarf64 ? 12 : 4 + return result + } + +} + +// .. DwarfReader ............................................................... + +enum DwarfSection { + case debugAbbrev + case debugAddr + case debugARanges + case debugFrame + case debugInfo + case debugLine + case debugLineStr + case debugLoc + case debugLocLists + case debugMacInfo + case debugMacro + case debugNames + case debugPubNames + case debugPubTypes + case debugRanges + case debugRngLists + case debugStr + case debugStrOffsets + case debugSup + case debugTypes + case debugCuIndex + case debugTuIndex +} + +protocol DwarfSource { + + func getDwarfSection(_ section: DwarfSection) -> (any ImageSource)? + +} + +struct DwarfReader { + + typealias Source = S + typealias Address = UInt64 + typealias Size = UInt64 + struct Bounds { + var base: Address + var size: Size + var end: Address { return base + size } + } + + var source: Source + + struct AbbrevInfo { + var tag: Dwarf_Tag + var hasChildren: Bool + var attributes: [(Dwarf_Attribute, Dwarf_Form, Int64?)] + } + + var infoSection: any ImageSource + var abbrevSection: any ImageSource + var lineSection: (any ImageSource)? + var addrSection: (any ImageSource)? + var strSection: (any ImageSource)? + var lineStrSection: (any ImageSource)? + var strOffsetsSection: (any ImageSource)? + var rangesSection: (any ImageSource)? + var shouldSwap: Bool + + typealias DwarfAbbrev = UInt64 + + struct Unit { + var baseOffset: Address + var version: Int + var isDwarf64: Bool + var unitType: Dwarf_UnitType + var addressSize: Int + var abbrevOffset: Address + var dieBounds: Bounds + + var lowPC: Address? + + var lineBase: UInt64? + var addrBase: UInt64? + var strOffsetsBase: UInt64? + var loclistsBase: UInt64? + + var abbrevs: [DwarfAbbrev: AbbrevInfo] + + var tag: Dwarf_Tag + var attributes: [Dwarf_Attribute:DwarfValue] = [:] + } + + struct FileInfo { + var path: String + var directoryIndex: Int? + var timestamp: Int? + var size: UInt64? + var md5sum: [UInt8]? + } + + struct LineNumberState: CustomStringConvertible { + var address: Address + var opIndex: UInt + var file: Int + var path: String + var line: Int + var column: Int + var isStmt: Bool + var basicBlock: Bool + var endSequence: Bool + var prologueEnd: Bool + var epilogueBegin: Bool + var isa: UInt + var discriminator: UInt + + var description: String { + var flags: [String] = [] + if isStmt { + flags.append("is_stmt") + } + if basicBlock { + flags.append("basic_block") + } + if endSequence { + flags.append("end_sequence") + } + if prologueEnd { + flags.append("prologue_end") + } + if epilogueBegin { + flags.append("epilogue_begin") + } + + let flagsString = flags.joined(separator:" ") + + return """ + \(hex(address)) \(pad(line, 6)) \(pad(column, 6)) \(pad(file, 6)) \ + \(pad(isa, 3)) \(pad(discriminator, 13)) \(flagsString) + """ + } + } + + struct LineNumberInfo { + var baseOffset: Address + var version: Int + var addressSize: Int? + var selectorSize: Int? + var headerLength: UInt64 + var minimumInstructionLength: UInt + var maximumOpsPerInstruction: UInt + var defaultIsStmt: Bool + var lineBase: Int8 + var lineRange: UInt8 + var opcodeBase: UInt8 + var standardOpcodeLengths: [UInt64] + var directories: [String] = [] + var files: [FileInfo] = [] + var program: [UInt8] = [] + var shouldSwap: Bool + + /// Compute the full path for a file, given its index in the file table. + func fullPathForFile(index: Int) -> String { + if index >= files.count { + return "" + } + + let info = files[index] + if info.path.hasPrefix("/") { + return info.path + } + + let dirName: String + if let dirIndex = info.directoryIndex, + dirIndex < directories.count { + dirName = directories[dirIndex] + } else { + dirName = "" + } + + return "\(dirName)/\(info.path)" + } + + /// Execute the line number program, calling a closure for every line + /// table entry. + mutating func executeProgram( + line: (LineNumberState, inout Bool) -> () + ) throws { + let source = ArrayImageSource(array: program) + let bounds = source.bounds! + var cursor = ImageSourceCursor(source: source) + + func maybeSwap(_ x: T) -> T { + if shouldSwap { + return x.byteSwapped + } + return x + } + + // Table 6.4: Line number program initial state + let initialState = LineNumberState( + address: 0, + opIndex: 0, + file: 1, + path: fullPathForFile(index: 1), + line: 1, + column: 0, + isStmt: defaultIsStmt, + basicBlock: false, + endSequence: false, + prologueEnd: false, + epilogueBegin: false, + isa: 0, + discriminator: 0 + ) + + var state = initialState + + // Flag to allow fast exit + var done = false + + while !done && cursor.pos < bounds.end { + let opcode = try cursor.read(as: Dwarf_LNS_Opcode.self) + + if opcode.rawValue >= opcodeBase { + // Special opcode + let adjustedOpcode = UInt(opcode.rawValue - opcodeBase) + let advance = adjustedOpcode / UInt(lineRange) + let lineAdvance = adjustedOpcode % UInt(lineRange) + let instrAdvance + = (state.opIndex + advance) / maximumOpsPerInstruction + let newOp = (state.opIndex + advance) % maximumOpsPerInstruction + state.address += Address(instrAdvance) + state.opIndex = newOp + state.line += Int(lineBase) + Int(lineAdvance) + + line(state, &done) + + state.discriminator = 0 + state.basicBlock = false + state.prologueEnd = false + state.epilogueBegin = false + } else if opcode == .DW_LNS_extended { + // Extended opcode + let length = try cursor.readULEB128() + let opcode = try cursor.read(as: Dwarf_LNE_Opcode.self) + + switch opcode { + case .DW_LNE_end_sequence: + state.endSequence = true + line(state, &done) + state = initialState + case .DW_LNE_set_address: + let address: UInt64 + guard let addressSize = addressSize else { + throw DwarfError.unspecifiedAddressSize + } + switch addressSize { + case 4: + address = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + case 8: + address = maybeSwap(try cursor.read(as: UInt64.self)) + default: + throw DwarfError.badAddressSize(addressSize) + } + state.address = Address(address) + case .DW_LNE_define_file: + guard let path = try cursor.readString() else { + throw DwarfError.badString + } + let directoryIndex = try cursor.readULEB128() + let timestamp = try cursor.readULEB128() + let size = try cursor.readULEB128() + files.append(FileInfo( + path: path, + directoryIndex: Int(directoryIndex), + timestamp: timestamp != 0 ? Int(timestamp) : nil, + size: size != 0 ? size : nil, + md5sum: nil + )) + case .DW_LNE_set_discriminator: + let discriminator = try cursor.readULEB128() + state.discriminator = UInt(discriminator) + default: + cursor.pos += length - 1 + } + } else { + // Standard opcode + switch opcode { + case .DW_LNS_copy: + line(state, &done) + state.discriminator = 0 + state.basicBlock = false + state.prologueEnd = false + state.epilogueBegin = false + case .DW_LNS_advance_pc: + let advance = UInt(try cursor.readULEB128()) + let instrAdvance + = (state.opIndex + advance) / maximumOpsPerInstruction + let newOp = (state.opIndex + advance) % maximumOpsPerInstruction + state.address += Address(instrAdvance) + state.opIndex = newOp + case .DW_LNS_advance_line: + let advance = try cursor.readSLEB128() + state.line += Int(advance) + case .DW_LNS_set_file: + let file = Int(try cursor.readULEB128()) + state.file = file + state.path = fullPathForFile(index: state.file) + case .DW_LNS_set_column: + let column = Int(try cursor.readULEB128()) + state.column = column + case .DW_LNS_negate_stmt: + state.isStmt = !state.isStmt + case .DW_LNS_set_basic_block: + state.basicBlock = true + case .DW_LNS_const_add_pc: + let adjustedOpcode = UInt(255 - opcodeBase) + let advance = adjustedOpcode / UInt(lineRange) + let instrAdvance + = (state.opIndex + advance) / maximumOpsPerInstruction + let newOp = (state.opIndex + advance) % maximumOpsPerInstruction + state.address += Address(instrAdvance) + state.opIndex = newOp + case .DW_LNS_fixed_advance_pc: + let advance = try cursor.read(as: Dwarf_Half.self) + state.address += Address(advance) + state.opIndex = 0 + case .DW_LNS_set_prologue_end: + state.prologueEnd = true + case .DW_LNS_set_epilogue_begin: + state.epilogueBegin = true + case .DW_LNS_set_isa: + let isa = UInt(try cursor.readULEB128()) + state.isa = isa + default: + // Skip this unknown opcode + let length = standardOpcodeLengths[Int(opcode.rawValue)] + for _ in 0..= 5 { + continue + } + + for unit in self.units { + if let lineBase = unit.lineBase, + lineNumberInfo[n].baseOffset == lineBase { + var filename = "" + if let nameVal = unit.attributes[.DW_AT_name], + case let .string(theName) = nameVal { + filename = theName + } + var dirname = "." + if let dirVal = unit.attributes[.DW_AT_comp_dir], + case let .string(theDir) = dirVal { + dirname = theDir + } + + lineNumberInfo[n].directories[0] = dirname + lineNumberInfo[n].files[0] = FileInfo( + path: filename, + directoryIndex: 0, + timestamp: nil, + size: nil, + md5sum: nil + ) + lineNumberInfo[n].addressSize = unit.addressSize + break + } + } + } + } + + private func maybeSwap(_ x: T) -> T { + if shouldSwap { + return x.byteSwapped + } else { + return x + } + } + + private func readUnits() throws -> [Unit] { + guard let bounds = infoSection.bounds else { + return [] + } + + var units: [Unit] = [] + var cursor = ImageSourceCursor(source: infoSection) + + while cursor.pos < bounds.end { + // See 7.5.1.1 Full and Partial Compilation Unit Headers + let base = cursor.pos + + // .1 unit_length + let (length, dwarf64) = try cursor.readDwarfLength() + let next = cursor.pos + length + + // .2 version + let version = Int(maybeSwap(try cursor.read(as: Dwarf_Half.self))) + + if version < 3 || version > 5 { + throw DwarfError.unsupportedVersion(version) + } + + var unitType: Dwarf_UnitType = .DW_UT_unknown + let addressSize: Int + let abbrevOffset: Address + let dieBounds: Bounds + + if dwarf64 { + if version >= 3 && version <= 4 { + // .3 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Xword.self))) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + } else if version == 5 { + // .3 unit_type + unitType = try cursor.read(as: Dwarf_UnitType.self) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + + // .5 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Xword.self))) + } else { + throw DwarfError.unsupportedVersion(version) + } + + dieBounds = Bounds(base: cursor.pos, size: next - cursor.pos) + } else { + if version >= 3 && version <= 4 { + // .3 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Word.self))) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + } else if version == 5 { + // .3 unit_type + unitType = try cursor.read(as: Dwarf_UnitType.self) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + + // .5 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Word.self))) + } else { + throw DwarfError.unsupportedVersion(version) + } + + dieBounds = Bounds(base: cursor.pos, size: next - cursor.pos) + } + + if unitType == .DW_UT_skeleton || unitType == .DW_UT_split_compile { + // .6 dwo_id + let _ = try cursor.read(as: UInt64.self) + } else if unitType == .DW_UT_type || unitType == .DW_UT_split_type { + // .6 type_signature + let _ = try cursor.read(as: UInt64.self) + + // .7 type_offset + if dwarf64 { + let _ = try cursor.read(as: UInt64.self) + } else { + let _ = try cursor.read(as: UInt32.self) + } + } + + let abbrevs = try readAbbrevs(at: abbrevOffset) + + let abbrev = try cursor.readULEB128() + + guard let abbrevInfo = abbrevs[abbrev] else { + throw DwarfError.missingAbbrev(abbrev) + } + let tag = abbrevInfo.tag + + var unit = Unit(baseOffset: base, + version: Int(version), + isDwarf64: dwarf64, + unitType: unitType, + addressSize: Int(addressSize), + abbrevOffset: abbrevOffset, + dieBounds: dieBounds, + abbrevs: abbrevs, + tag: tag) + + let attrPos = cursor.pos + let firstPass = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: false + ) + + if let value = firstPass[.DW_AT_addr_base], + case let .sectionOffset(offset) = value { + unit.addrBase = offset + } + if let value = firstPass[.DW_AT_str_offsets_base], + case let .sectionOffset(offset) = value { + unit.strOffsetsBase = offset + } + if let value = firstPass[.DW_AT_loclists_base], + case let .sectionOffset(offset) = value { + unit.loclistsBase = offset + } + if let value = firstPass[.DW_AT_stmt_list], + case let .sectionOffset(offset) = value { + unit.lineBase = offset + } + if let value = firstPass[.DW_AT_low_pc], + case let .address(lowPC) = value { + unit.lowPC = lowPC + } + + // Re-read the attributes, with indirect fetching enabled; + // we can't do this in one step because attributes might be using + // indirections based on the base attributes, and those can come + // after the data needed to decode them. + cursor.pos = attrPos + + let attributes = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: true + ) + + unit.attributes = attributes + + units.append(unit) + + cursor.pos = next + } + + return units + } + + private func readLineNumberInfo() throws -> [LineNumberInfo] { + guard let lineSection = lineSection, + let bounds = lineSection.bounds else { + return [] + } + + var result: [LineNumberInfo] = [] + var cursor = ImageSourceCursor(source: lineSection, offset: 0) + + while cursor.pos < bounds.end { + // 6.2.4 The Line Number Program Header + + // .1 unit_length + let baseOffset = cursor.pos + let (length, dwarf64) = try cursor.readDwarfLength() + if length == 0 { + break + } + + let nextOffset = cursor.pos + length + + // .2 version + let version = Int(maybeSwap(try cursor.read(as: Dwarf_Half.self))) + + if version < 3 || version > 5 { + cursor.pos = nextOffset + continue + } + + var addressSize: Int? = nil + var segmentSelectorSize: Int? = nil + + if version == 5 { + // .3 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + + // .4 segment_selector_size + segmentSelectorSize = Int(try cursor.read(as: Dwarf_Byte.self)) + } + + // .5 header_length + let headerLength: UInt64 + if dwarf64 { + headerLength = maybeSwap(try cursor.read(as: Dwarf_Xword.self)) + } else { + headerLength = UInt64(maybeSwap(try cursor.read(as: Dwarf_Word.self))) + } + + // .6 minimum_instruction_length + let minimumInstructionLength = UInt(try cursor.read(as: Dwarf_Byte.self)) + + // .7 maximum_operations_per_instruction + let maximumOpsPerInstruction = UInt(try cursor.read(as: Dwarf_Byte.self)) + + // .8 default_is_stmt + let defaultIsStmt = try cursor.read(as: Dwarf_Byte.self) != 0 + + // .9 line_base + let lineBase = try cursor.read(as: Dwarf_Sbyte.self) + + // .10 line_range + let lineRange = try cursor.read(as: Dwarf_Byte.self) + + // .11 opcode_base + let opcodeBase = try cursor.read(as: Dwarf_Byte.self) + + // .12 standard_opcode_lengths + var standardOpcodeLengths: [UInt64] = [0] + for _ in 1..", + directoryIndex: 0, + timestamp: nil, + size: nil, + md5sum: nil)) + + while true { + guard let path = try cursor.readString() else { + throw DwarfError.badString + } + + if path == "" { + break + } + + let dirIndex = try cursor.readULEB128() + let timestamp = try cursor.readULEB128() + let size = try cursor.readULEB128() + + fileInfo.append(FileInfo( + path: path, + directoryIndex: Int(dirIndex), + timestamp: timestamp != 0 ? Int(timestamp) : nil, + size: size != 0 ? size : nil, + md5sum: nil)) + } + } else if version == 5 { + // .13/.14 directory_entry_format + var dirEntryFormat: [(Dwarf_Lhdr_Format, Dwarf_Form)] = [] + let dirEntryFormatCount = Int(try cursor.read(as: Dwarf_Byte.self)) + for _ in 0..") + } + } + + // .17/.18 file_name_entry_format + var fileEntryFormat: [(Dwarf_Lhdr_Format, Dwarf_Form)] = [] + let fileEntryFormatCount = Int(try cursor.read(as: Dwarf_Byte.self)) + for _ in 0.. [DwarfAbbrev: AbbrevInfo] { + var abbrevs: [DwarfAbbrev: AbbrevInfo] = [:] + var cursor = ImageSourceCursor(source: abbrevSection, offset: offset) + while true { + let abbrev = try cursor.readULEB128() + + if abbrev == 0 { + break + } + + let rawTag = try cursor.readULEB128() + + guard let tag = Dwarf_Tag(rawValue: rawTag) else { + throw DwarfError.badTag(rawTag) + } + + let children = try cursor.read(as: Dwarf_ChildDetermination.self) + + // Fetch attributes + var attributes: [(Dwarf_Attribute, Dwarf_Form, Int64?)] = [] + while true { + let rawAttr = try cursor.readULEB128() + let rawForm = try cursor.readULEB128() + + if rawAttr == 0 && rawForm == 0 { + break + } + + guard let attr = Dwarf_Attribute(rawValue: UInt32(rawAttr)) else { + throw DwarfError.badAttribute(rawAttr) + } + guard let form = Dwarf_Form(rawValue: Dwarf_Byte(rawForm)) else { + throw DwarfError.badForm(rawForm) + } + + if form == .DW_FORM_implicit_const { + let value = try cursor.readSLEB128() + attributes.append((attr, form, value)) + } else { + attributes.append((attr, form, nil)) + } + } + + abbrevs[abbrev] = AbbrevInfo(tag: tag, + hasChildren: children != .DW_CHILDREN_no, + attributes: attributes) + } + + return abbrevs + } + + enum DwarfValue { + case flag(Bool) + case string(String) + case address(UInt64) + case integer(Int) + case unsignedInt8(UInt8) + case unsignedInt16(UInt16) + case unsignedInt32(UInt32) + case signedInt64(Int64) + case unsignedInt64(UInt64) + case dieOffset(UInt64) + case data([UInt8]) + case expression([UInt8]) + case locationList(UInt64) + case rangeList(UInt64) + case sectionOffset(UInt64) + case reference(UInt64) + case signature([UInt8]) + case supplementaryReference(UInt64) + case supplementaryString(UInt64) + case indirectAddress(UInt64) + case stringFromStrTab(UInt64) + case stringFromLineStrTab(UInt64) + case stringViaStrOffsets(UInt64) + + func uint64Value() -> UInt64? { + switch self { + case let .unsignedInt8(value): return UInt64(value) + case let .unsignedInt16(value): return UInt64(value) + case let .unsignedInt32(value): return UInt64(value) + case let .unsignedInt64(value): return value + default: + return nil + } + } + + func intValue() -> Int? { + switch self { + case let .unsignedInt8(value): return Int(value) + case let .unsignedInt16(value): return Int(value) + case let .unsignedInt32(value): return Int(value) + case let .unsignedInt64(value): return Int(value) + default: + return nil + } + } + } + + private func threeByteToOffset(_ bytes: (UInt8, UInt8, UInt8)) -> UInt64 { + let offset: UInt64 + #if _endian(big) + if shouldSwap { + offset = UInt64(bytes.0) | UInt64(bytes.1) << 8 | UInt64(bytes.2) << 16 + } else { + offset = UInt64(bytes.2) | UInt64(bytes.1) << 8 | UInt64(bytes.0) << 16 + } + #else + if shouldSwap { + offset = UInt64(bytes.2) | UInt64(bytes.1) << 8 | UInt64(bytes.0) << 16 + } else { + offset = UInt64(bytes.0) | UInt64(bytes.1) << 8 | UInt64(bytes.2) << 16 + } + #endif + return offset + } + + private func read(form theForm: Dwarf_Form, + at cursor: inout ImageSourceCursor, + addressSize: Int, isDwarf64: Bool, + unit: Unit?, + shouldFetchIndirect: Bool, + constantValue: Int64? = nil) throws -> DwarfValue { + let form: Dwarf_Form + if theForm == .DW_FORM_indirect { + let rawForm = try cursor.readULEB128() + guard let theForm = Dwarf_Form(rawValue: Dwarf_Byte(rawForm)) else { + throw DwarfError.badForm(rawForm) + } + form = theForm + } else { + form = theForm + } + + switch form { + case .DW_FORM_implicit_const: + return .signedInt64(constantValue!) + + case .DW_FORM_addr: + let address: UInt64 + switch addressSize { + case 4: + address = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + case 8: + address = maybeSwap(try cursor.read(as: UInt64.self)) + default: + throw DwarfError.badAddressSize(addressSize) + } + return .address(address) + case .DW_FORM_addrx, .DW_FORM_addrx1, .DW_FORM_addrx2, + .DW_FORM_addrx3, .DW_FORM_addrx4: + guard let addrSection = addrSection else { + throw DwarfError.missingAddrSection + } + + let ndx: UInt64 + switch form { + case .DW_FORM_addrx: + ndx = try cursor.readULEB128() + case .DW_FORM_addrx1: + ndx = UInt64(try cursor.read(as: UInt8.self)) + case .DW_FORM_addrx2: + ndx = UInt64(maybeSwap(try cursor.read(as: UInt16.self))) + case .DW_FORM_addrx3: + let bytes = try cursor.read(as: (UInt8, UInt8, UInt8).self) + ndx = threeByteToOffset(bytes) + case .DW_FORM_addrx4: + ndx = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + default: + fatalError("unreachable") + } + + if !shouldFetchIndirect { + return .indirectAddress(ndx) + } else { + guard let addrBase = unit?.addrBase else { + throw DwarfError.missingAddrBase + } + + let address: UInt64 + switch addressSize { + case 4: + address = UInt64(maybeSwap( + try addrSection.fetch(from: ndx * 4 + addrBase, + as: UInt32.self))) + case 8: + address = maybeSwap(try addrSection.fetch(from: ndx * 8 + addrBase, + as: UInt64.self)) + default: + throw DwarfError.badAddressSize(addressSize) + } + return .address(address) + } + case .DW_FORM_block: + let length = try cursor.readULEB128() + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + case .DW_FORM_block1: + let length = try cursor.read(as: UInt8.self) + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + case .DW_FORM_block2: + let length = maybeSwap(try cursor.read(as: UInt16.self)) + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + case .DW_FORM_block4: + let length = maybeSwap(try cursor.read(as: UInt32.self)) + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + + case .DW_FORM_sdata: + let data = try cursor.readSLEB128() + return .signedInt64(data) + + case .DW_FORM_udata: + let data = try cursor.readULEB128() + return .unsignedInt64(data) + + case .DW_FORM_data1: + let data = try cursor.read(as: UInt8.self) + return .unsignedInt8(data) + + case .DW_FORM_data2: + let data = maybeSwap(try cursor.read(as: UInt16.self)) + return .unsignedInt16(data) + + case .DW_FORM_data4: + let data = maybeSwap(try cursor.read(as: UInt32.self)) + return .unsignedInt32(data) + + case .DW_FORM_data8: + let data = maybeSwap(try cursor.read(as: UInt64.self)) + return .unsignedInt64(data) + + case .DW_FORM_data16: + let data = try cursor.read(count: 16, as: UInt8.self) + return .data(data) + + case .DW_FORM_exprloc: + let length = try cursor.readULEB128() + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .expression(bytes) + + case .DW_FORM_flag: + let flag = try cursor.read(as: UInt8.self) + return .flag(flag != 0) + + case .DW_FORM_flag_present: + return .flag(true) + + case .DW_FORM_loclistx: + let offset = try cursor.readULEB128() + return .locationList(offset) + + case .DW_FORM_sec_offset: + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + return .sectionOffset(offset) + + case .DW_FORM_rnglistx: + let offset = try cursor.readULEB128() + return .rangeList(offset) + + case .DW_FORM_ref1, .DW_FORM_ref2, .DW_FORM_ref4, .DW_FORM_ref8, + .DW_FORM_ref_udata: + guard let baseOffset = unit?.baseOffset else { + throw DwarfError.missingBaseOffset + } + + let offset: Address + switch form { + case .DW_FORM_ref1: + offset = UInt64(try cursor.read(as: UInt8.self)) + case .DW_FORM_ref2: + offset = UInt64(maybeSwap(try cursor.read(as: UInt16.self))) + case .DW_FORM_ref4: + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + case .DW_FORM_ref8: + offset = maybeSwap(try cursor.read(as: UInt64.self)) + case .DW_FORM_ref_udata: + offset = try cursor.readULEB128() + default: + fatalError("unreachable") + } + return .reference(offset + baseOffset) + + case .DW_FORM_ref_addr: + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + return .reference(offset) + + case .DW_FORM_ref_sig8: + let signature = try cursor.read(count: 8, as: UInt8.self) + return .signature(signature) + + case .DW_FORM_ref_sup4: + let offset = maybeSwap(try cursor.read(as: UInt32.self)) + return .supplementaryReference(Address(offset)) + + case .DW_FORM_ref_sup8: + let offset = maybeSwap(try cursor.read(as: UInt64.self)) + return .supplementaryReference(Address(offset)) + + case .DW_FORM_string: + guard let string = try cursor.readString() else { + throw DwarfError.badString + } + return .string(string) + + case .DW_FORM_strp: + guard let strSection = strSection else { + throw DwarfError.missingStrSection + } + + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + + if !shouldFetchIndirect { + return .stringFromStrTab(offset) + } else { + guard let string = try strSection.fetchString(from: offset) else { + throw DwarfError.badString + } + return .string(string) + } + + case .DW_FORM_strp_sup: + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + return .supplementaryString(offset) + + case .DW_FORM_line_strp: + guard let lineStrSection = lineStrSection else { + throw DwarfError.missingLineStrSection + } + + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + + if !shouldFetchIndirect { + return .stringFromLineStrTab(offset) + } else { + guard let string = try lineStrSection.fetchString(from: offset) else { + throw DwarfError.badString + } + return .string(string) + } + + case .DW_FORM_strx, + .DW_FORM_strx1, .DW_FORM_strx2, .DW_FORM_strx3,.DW_FORM_strx4: + guard let strOffsetsSection = strOffsetsSection else { + throw DwarfError.missingStrOffsetsSection + } + guard let strSection = strSection else { + throw DwarfError.missingStrSection + } + + let offset: UInt64 + switch form { + case .DW_FORM_strx: + offset = try cursor.readULEB128() + case .DW_FORM_strx1: + offset = UInt64(try cursor.read(as: UInt8.self)) + case .DW_FORM_strx2: + offset = UInt64(maybeSwap(try cursor.read(as: UInt16.self))) + case .DW_FORM_strx3: + let bytes = try cursor.read(as: (UInt8, UInt8, UInt8).self) + offset = threeByteToOffset(bytes) + case .DW_FORM_strx4: + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + default: + fatalError("unreachable") + } + + if !shouldFetchIndirect { + return .stringViaStrOffsets(offset) + } else { + guard let strBase = unit?.strOffsetsBase else { + throw DwarfError.missingStrOffsetsBase + } + + let actualOffset: UInt64 + if isDwarf64 { + actualOffset = maybeSwap(try strOffsetsSection.fetch( + from: offset * 8 + strBase, + as: UInt64.self)) + } else { + actualOffset = UInt64(maybeSwap(try strOffsetsSection.fetch( + from: offset * 4 + strBase, + as: UInt32.self))) + } + + guard let string = try strSection.fetchString(from: actualOffset) + else { + throw DwarfError.badString + } + return .string(string) + } + + case .DW_FORM_indirect: + // We should have handled this already + throw DwarfError.doubleIndirectForm + default: + throw DwarfError.unknownForm(theForm) + } + } + + private func readDieAttributes( + at cursor: inout ImageSourceCursor, + unit: Unit, + abbrevInfo: AbbrevInfo, + shouldFetchIndirect: Bool + ) throws -> [Dwarf_Attribute:DwarfValue] { + var attributes: [Dwarf_Attribute:DwarfValue] = [:] + + for (attribute, form, constantValue) in abbrevInfo.attributes { + attributes[attribute] = try read(form: form, + at: &cursor, + addressSize: unit.addressSize, + isDwarf64: unit.isDwarf64, + unit: unit, + shouldFetchIndirect: shouldFetchIndirect, + constantValue: constantValue) + } + + return attributes + } + + struct CallSiteInfo { + var depth: Int + var rawName: String? + var name: String? + var lowPC: Address + var highPC: Address + var filename: String + var line: Int + var column: Int + } + + private func buildCallSiteInfo( + depth: Int, + unit: Unit, + attributes: [Dwarf_Attribute:DwarfValue], + _ fn: (CallSiteInfo) -> () + ) throws { + guard let abstractOriginVal = attributes[.DW_AT_abstract_origin], + let callFile = attributes[.DW_AT_call_file]?.uint64Value(), + let callLine = attributes[.DW_AT_call_line]?.uint64Value(), + let callColumn = attributes[.DW_AT_call_column]?.uint64Value(), + case let .reference(abstractOrigin) = abstractOriginVal else { + return + } + + var cursor = ImageSourceCursor(source: infoSection, + offset: abstractOrigin) + let abbrev = try cursor.readULEB128() + if abbrev == 0 { + return + } + + guard let abbrevInfo = unit.abbrevs[abbrev] else { + throw DwarfError.missingAbbrev(abbrev) + } + + let tag = abbrevInfo.tag + + if tag != .DW_TAG_subprogram { + return + } + + let refAttrs = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: true + ) + + var name: String? = nil + var rawName: String? = nil + + if let nameVal = refAttrs[.DW_AT_name], + case let .string(theName) = nameVal { + name = theName + } + + if let linkageNameVal = refAttrs[.DW_AT_linkage_name], + case let .string(theRawName) = linkageNameVal { + rawName = theRawName + } else { + rawName = name + } + + var filename: String = "" + for info in lineNumberInfo { + if info.baseOffset == unit.lineBase { + filename = info.fullPathForFile(index: Int(callFile)) + break + } + } + + if let lowPCVal = attributes[.DW_AT_low_pc], + let highPCVal = attributes[.DW_AT_high_pc], + case let .address(lowPC) = lowPCVal { + let highPC: Address + if case let .address(highPCAddr) = highPCVal { + highPC = highPCAddr + } else if let highPCOffset = highPCVal.uint64Value() { + highPC = lowPC + highPCOffset + } else { + return + } + + fn(CallSiteInfo( + depth: depth, + rawName: rawName, + name: name, + lowPC: lowPC, + highPC: highPC, + filename: filename, + line: Int(callLine), + column: Int(callColumn))) + } else if let rangeVal = attributes[.DW_AT_ranges], + let rangesSection = rangesSection, + case let .sectionOffset(offset) = rangeVal, + unit.version < 5 { + // We don't support .debug_rnglists at present (which is what we'd + // have if unit.version is 5 or higher). + var rangeCursor = ImageSourceCursor(source: rangesSection, + offset: offset) + var rangeBase: Address = unit.lowPC ?? 0 + + while true { + let beginning: Address + let ending: Address + + switch unit.addressSize { + case 4: + beginning = UInt64(maybeSwap(try rangeCursor.read(as: UInt32.self))) + ending = UInt64(maybeSwap(try rangeCursor.read(as: UInt32.self))) + if beginning == 0xffffffff { + rangeBase = ending + continue + } + case 8: + beginning = maybeSwap(try rangeCursor.read(as: UInt64.self)) + ending = maybeSwap(try rangeCursor.read(as: UInt64.self)) + if beginning == 0xffffffffffffffff { + rangeBase = ending + continue + } + default: + throw DwarfError.badAddressSize(unit.addressSize) + } + + if beginning == 0 && ending == 0 { + break + } + + fn(CallSiteInfo( + depth: depth, + rawName: rawName, + name: name, + lowPC: beginning + rangeBase, + highPC: ending + rangeBase, + filename: filename, + line: Int(callLine), + column: Int(callColumn))) + } + } + } + + lazy var inlineCallSites: [CallSiteInfo] = _buildCallSiteList() + + private func _buildCallSiteList() -> [CallSiteInfo] { + var callSites: [CallSiteInfo] = [] + + for unit in units { + do { + var cursor = ImageSourceCursor(source: infoSection, + offset: unit.dieBounds.base) + var depth = 0 + + while cursor.pos < unit.dieBounds.end { + let abbrev = try cursor.readULEB128() + + if abbrev == 0 { + depth -= 1 + if depth == 0 { + break + } + continue + } + + guard let abbrevInfo = unit.abbrevs[abbrev] else { + throw DwarfError.missingAbbrev(abbrev) + } + + let tag = abbrevInfo.tag + + let attributes = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: tag == .DW_TAG_inlined_subroutine + ) + + if tag == .DW_TAG_inlined_subroutine { + try buildCallSiteInfo(depth: depth, + unit: unit, + attributes: attributes) { + callSites.append($0) + } + } + + if abbrevInfo.hasChildren { + depth += 1 + } + } + } catch { + let name: String + if let value = unit.attributes[.DW_AT_name], + case let .string(theName) = value { + name = theName + } else { + name = "" + } + swift_reportWarning(0, + """ + swift-runtime: warning: unable to fetch inline \ + frame data for DWARF unit \(name): \(error) + """) + } + } + + callSites.sort( + by: { (a, b) in + a.lowPC < b.lowPC || (a.lowPC == b.lowPC) && a.depth > b.depth + }) + + return callSites + } + +} + +// .. Testing .................................................................. + +@_spi(DwarfTest) +public func testDwarfReaderFor(path: String) -> Bool { + guard let source = try? FileImageSource(path: path) else { + print("\(path) was not accessible") + return false + } + + if let elfImage = try? Elf32Image(source: source) { + print("\(path) is a 32-bit ELF image") + + var reader: DwarfReader> + do { + reader = try DwarfReader(source: elfImage) + } catch { + print("Unable to create reader - \(error)") + return false + } + + print("Units:") + print(reader.units) + + print("Call Sites:") + print(reader.inlineCallSites) + return true + } else if let elfImage = try? Elf64Image(source: source) { + print("\(path) is a 64-bit ELF image") + + var reader: DwarfReader> + do { + reader = try DwarfReader(source: elfImage) + } catch { + print("Unable to create reader - \(error)") + return false + } + + print("Units:") + for unit in reader.units { + if let value = unit.attributes[.DW_AT_name], + case let .string(name) = value { + print(" \(name)") + } else { + print(" ") + } + } + + print("Call Sites:") + print(reader.inlineCallSites) + return true + } else { + print("\(path) is not an ELF image") + return false + } +} + +#endif // os(Linux) diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift new file mode 100644 index 0000000000000..40b2d12c9fc06 --- /dev/null +++ b/stdlib/public/Backtracing/Elf.swift @@ -0,0 +1,1769 @@ +//===--- Elf.swift - ELF support for Swift --------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines various ELF structures and provides types for working with ELF +// images on disk and in memory. +// +//===----------------------------------------------------------------------===// + +// ###FIXME: We shouldn't really use String for paths. + +#if os(Linux) + +import Swift + +@_implementationOnly import OS.Libc +@_implementationOnly import ImageFormats.Elf +@_implementationOnly import Runtime + +// .. Utilities ................................................................ + +private func realPath(_ path: String) -> String? { + guard let result = realpath(path, nil) else { + return nil + } + + let s = String(cString: result) + + free(result) + + return s +} + +private func dirname(_ path: String) -> Substring { + guard let lastSlash = path.lastIndex(of: "/") else { + return "" + } + return path.prefix(upTo: lastSlash) +} + +private let crc32Table: [UInt32] = [ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +] + +private func updateCrc(_ crc: UInt32, + _ bytes: UnsafeBufferPointer) -> UInt32 { + var theCrc = ~crc + for byte in bytes { + theCrc = crc32Table[Int(UInt8(truncatingIfNeeded: theCrc) + ^ byte)] ^ (theCrc >> 8) + } + return ~theCrc +} + +// .. Byte swapping ............................................................ + +extension Elf32_Ehdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Ehdr( + e_ident: e_ident, + e_type: Elf_Ehdr_Type(rawValue: e_type.rawValue.byteSwapped)!, + e_machine: Elf_Ehdr_Machine(rawValue: e_machine.rawValue.byteSwapped)!, + e_version: Elf_Ehdr_Version(rawValue: e_version.rawValue.byteSwapped)!, + e_entry: e_entry.byteSwapped, + e_phoff: e_phoff.byteSwapped, + e_shoff: e_shoff.byteSwapped, + e_flags: e_flags.byteSwapped, + e_ehsize: e_ehsize.byteSwapped, + e_phentsize: e_phentsize.byteSwapped, + e_phnum: e_phnum.byteSwapped, + e_shentsize: e_shentsize.byteSwapped, + e_shnum: e_shnum.byteSwapped, + e_shstrndx: e_shstrndx.byteSwapped + ) + } +} + +extension Elf64_Ehdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Ehdr( + e_ident: e_ident, + e_type: Elf_Ehdr_Type(rawValue: e_type.rawValue.byteSwapped)!, + e_machine: Elf_Ehdr_Machine(rawValue: e_machine.rawValue.byteSwapped)!, + e_version: Elf_Ehdr_Version(rawValue: e_version.rawValue.byteSwapped)!, + e_entry: e_entry.byteSwapped, + e_phoff: e_phoff.byteSwapped, + e_shoff: e_shoff.byteSwapped, + e_flags: e_flags.byteSwapped, + e_ehsize: e_ehsize.byteSwapped, + e_phentsize: e_phentsize.byteSwapped, + e_phnum: e_phnum.byteSwapped, + e_shentsize: e_shentsize.byteSwapped, + e_shnum: e_shnum.byteSwapped, + e_shstrndx: e_shstrndx.byteSwapped + ) + } +} + +extension Elf32_Shdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Shdr( + sh_name: sh_name.byteSwapped, + sh_type: Elf_Shdr_Type(rawValue: sh_type.rawValue.byteSwapped)!, + sh_flags: sh_flags.byteSwapped, + sh_addr: sh_addr.byteSwapped, + sh_offset: sh_offset.byteSwapped, + sh_size: sh_size.byteSwapped, + sh_link: sh_link.byteSwapped, + sh_info: sh_info.byteSwapped, + sh_addralign: sh_addralign.byteSwapped, + sh_entsize: sh_entsize.byteSwapped + ) + } +} + +extension Elf64_Shdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Shdr( + sh_name: sh_name.byteSwapped, + sh_type: Elf_Shdr_Type(rawValue: sh_type.rawValue.byteSwapped)!, + sh_flags: sh_flags.byteSwapped, + sh_addr: sh_addr.byteSwapped, + sh_offset: sh_offset.byteSwapped, + sh_size: sh_size.byteSwapped, + sh_link: sh_link.byteSwapped, + sh_info: sh_info.byteSwapped, + sh_addralign: sh_addralign.byteSwapped, + sh_entsize: sh_entsize.byteSwapped + ) + } +} + +protocol Elf_Chdr: ByteSwappable { + associatedtype Size: FixedWidthInteger + + init() + + var ch_type: Elf_Chdr_Type { get set } + var ch_size: Size { get set } + var ch_addralign: Size { get set } +} + +extension Elf32_Chdr: Elf_Chdr { + var byteSwapped: Self { + return Elf32_Chdr( + ch_type: Elf_Chdr_Type(rawValue: ch_type.rawValue.byteSwapped)!, + ch_size: ch_size.byteSwapped, + ch_addralign: ch_addralign.byteSwapped + ) + } +} + +extension Elf64_Chdr: Elf_Chdr { + var byteSwapped: Self { + return Elf64_Chdr( + ch_type: Elf_Chdr_Type(rawValue: ch_type.rawValue.byteSwapped)!, + ch_reserved: ch_reserved.byteSwapped, + ch_size: ch_size.byteSwapped, + ch_addralign: ch_addralign.byteSwapped + ) + } +} + +extension Elf32_Sym: ByteSwappable { + var byteSwapped: Self { + return Elf32_Sym( + st_name: st_name.byteSwapped, + st_value: st_value.byteSwapped, + st_size: st_size.byteSwapped, + st_info: st_info.byteSwapped, + st_other: st_other.byteSwapped, + st_shndx: st_shndx.byteSwapped + ) + } +} + +extension Elf64_Sym: ByteSwappable { + var byteSwapped: Self { + return Elf64_Sym( + st_name: st_name.byteSwapped, + st_info: st_info.byteSwapped, + st_other: st_other.byteSwapped, + st_shndx: st_shndx.byteSwapped, + st_value: st_value.byteSwapped, + st_size: st_size.byteSwapped + ) + } +} + +extension Elf32_Rel: ByteSwappable { + var byteSwapped: Self { + return Elf32_Rel( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped + ) + } +} + +extension Elf32_Rela: ByteSwappable { + var byteSwapped: Self { + return Elf32_Rela( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped, + r_addend: r_addend.byteSwapped + ) + } +} + +extension Elf64_Rel: ByteSwappable { + var byteSwapped: Self { + return Elf64_Rel( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped + ) + } +} + +extension Elf64_Rela: ByteSwappable { + var byteSwapped: Self { + return Elf64_Rela( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped, + r_addend: r_addend.byteSwapped + ) + } +} + +extension Elf32_Phdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Phdr( + p_type: Elf_Phdr_Type(rawValue: p_type.rawValue.byteSwapped)!, + p_offset: p_offset.byteSwapped, + p_vaddr: p_vaddr.byteSwapped, + p_paddr: p_paddr.byteSwapped, + p_filesz: p_filesz.byteSwapped, + p_memsz: p_memsz.byteSwapped, + p_flags: p_flags.byteSwapped, + p_align: p_align.byteSwapped + ) + } +} + +extension Elf64_Phdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Phdr( + p_type: Elf_Phdr_Type(rawValue: p_type.rawValue.byteSwapped)!, + p_flags: p_flags.byteSwapped, + p_offset: p_offset.byteSwapped, + p_vaddr: p_vaddr.byteSwapped, + p_paddr: p_paddr.byteSwapped, + p_filesz: p_filesz.byteSwapped, + p_memsz: p_memsz.byteSwapped, + p_align: p_align.byteSwapped + ) + } +} + +extension Elf32_Nhdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Nhdr( + n_namesz: n_namesz.byteSwapped, + n_descsz: n_descsz.byteSwapped, + n_type: n_type.byteSwapped + ) + } +} + +extension Elf64_Nhdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Nhdr( + n_namesz: n_namesz.byteSwapped, + n_descsz: n_descsz.byteSwapped, + n_type: n_type.byteSwapped + ) + } +} + +extension Elf32_Dyn: ByteSwappable { + var byteSwapped: Self { + return Elf32_Dyn( + d_tag: d_tag.byteSwapped, + d_un: .init(d_val: d_un.d_val.byteSwapped) + ) + } +} + +extension Elf64_Dyn: ByteSwappable { + var byteSwapped: Self { + return Elf64_Dyn( + d_tag: d_tag.byteSwapped, + d_un: .init(d_val: d_un.d_val.byteSwapped) + ) + } +} + +extension Elf32_Hash: ByteSwappable { + var byteSwapped: Self { + return Elf32_Hash( + h_nbucket: h_nbucket.byteSwapped, + h_nchain: h_nchain.byteSwapped + ) + } +} + +extension Elf64_Hash: ByteSwappable { + var byteSwapped: Self { + return Elf64_Hash( + h_nbucket: h_nbucket.byteSwapped, + h_nchain: h_nchain.byteSwapped + ) + } +} + +// .. Protocols ................................................................ + +typealias Elf_Magic = (UInt8, UInt8, UInt8, UInt8) + +typealias Elf_Ident = ( + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8 +) + +let ElfMagic: Elf_Magic = (0x7f, 0x45, 0x4c, 0x46) + +protocol Elf_Ehdr : ByteSwappable { + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + + init() + + var e_ident: Elf_Ident { get set } + var ei_magic: Elf_Magic { get set } + var ei_class: Elf_Ehdr_Class { get set } + var ei_data: Elf_Ehdr_Data { get set } + var ei_version: Elf_Byte { get set } + var ei_osabi: Elf_Ehdr_OsAbi { get set } + var ei_abiversion: Elf_Byte { get set } + + var e_type: Elf_Ehdr_Type { get set } + var e_machine: Elf_Ehdr_Machine { get set } + var e_version: Elf_Ehdr_Version { get set } + var e_entry: Address { get set } + var e_phoff: Offset { get set } + var e_shoff: Offset { get set } + var e_flags: Elf_Word { get set } + var e_ehsize: Elf_Half { get set } + var e_phentsize: Elf_Half { get set } + var e_phnum: Elf_Half { get set } + var e_shentsize: Elf_Half { get set } + var e_shnum: Elf_Half { get set } + var e_shstrndx: Elf_Half { get set } + + var shouldByteSwap: Bool { get } +} + +extension Elf_Ehdr { + var ei_magic: Elf_Magic { + get { + return (e_ident.0, e_ident.1, e_ident.2, e_ident.3) + } + set { + e_ident.0 = newValue.0 + e_ident.1 = newValue.1 + e_ident.2 = newValue.2 + e_ident.3 = newValue.3 + } + } + var ei_class: Elf_Ehdr_Class { + get { + return Elf_Ehdr_Class(rawValue: e_ident.4)! + } + set { + e_ident.4 = newValue.rawValue + } + } + var ei_data: Elf_Ehdr_Data { + get { + return Elf_Ehdr_Data(rawValue: e_ident.5)! + } + set { + e_ident.5 = newValue.rawValue + } + } + var ei_version: UInt8 { + get { + return e_ident.6 + } + set { + e_ident.6 = newValue + } + } + var ei_osabi: Elf_Ehdr_OsAbi { + get { + return Elf_Ehdr_OsAbi(rawValue: e_ident.7)! + } + set { + e_ident.7 = newValue.rawValue + } + } + var ei_abiversion: UInt8 { + get { + return e_ident.8 + } + set { + e_ident.8 = newValue + } + } + var ei_pad: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) { + get { + return (e_ident.9, e_ident.10, e_ident.11, + e_ident.12, e_ident.13, e_ident.14, + e_ident.15) + } + set { + e_ident.9 = newValue.0 + e_ident.10 = newValue.1 + e_ident.11 = newValue.2 + e_ident.12 = newValue.3 + e_ident.13 = newValue.4 + e_ident.14 = newValue.5 + e_ident.15 = newValue.6 + } + } + + var shouldByteSwap: Bool { + #if _endian(big) + return ei_data == .ELFDATA2LSB + #else + return ei_data == .ELFDATA2MSB + #endif + } +} + +extension Elf32_Ehdr : Elf_Ehdr { +} + +extension Elf64_Ehdr : Elf_Ehdr { +} + +protocol Elf_Shdr : ByteSwappable { + associatedtype Flags: FixedWidthInteger + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + init() + + var sh_name: Elf_Word { get set } + var sh_type: Elf_Shdr_Type { get set } + var sh_flags: Flags { get set } + var sh_addr: Address { get set } + var sh_offset: Offset { get set } + var sh_size: Size { get set } + var sh_link: Elf_Word { get set } + var sh_info: Elf_Word { get set } + var sh_addralign: Size { get set } + var sh_entsize: Size { get set } +} + +extension Elf32_Shdr : Elf_Shdr { +} + +extension Elf64_Shdr : Elf_Shdr { +} + +protocol Elf_Phdr : ByteSwappable { + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + init() + + var p_type: Elf_Phdr_Type { get set } + var p_flags: Elf_Phdr_Flags { get set } + var p_offset: Offset { get set } + var p_vaddr: Address { get set } + var p_paddr: Address { get set } + var p_filesz: Size { get set } + var p_memsz: Size { get set } + var p_align: Size { get set } +} + +extension Elf32_Phdr : Elf_Phdr { +} + +extension Elf64_Phdr : Elf_Phdr { +} + +protocol Elf_Nhdr : ByteSwappable { + init() + + var n_namesz: Elf_Word { get set } + var n_descsz: Elf_Word { get set } + var n_type: Elf_Word { get set } +} + +extension Elf32_Nhdr : Elf_Nhdr { +} + +extension Elf64_Nhdr : Elf_Nhdr { +} + +protocol Elf_Sym { + associatedtype Address: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + var st_name: Elf_Word { get set } + var st_value: Address { get set } + var st_size: Size { get set } + var st_info: Elf_Byte { get set } + var st_other: Elf_Byte { get set } + var st_shndx: Elf_Half { get set } + + var st_binding: Elf_Sym_Binding { get set } + var st_type: Elf_Sym_Type { get set } + var st_visibility: Elf_Sym_Visibility { get set } +} + +extension Elf32_Sym: Elf_Sym { + var st_binding: Elf_Sym_Binding { + get { + return ELF32_ST_BIND(st_info) + } + set { + st_info = ELF32_ST_INFO(newValue, ELF32_ST_TYPE(st_info)) + } + } + + var st_type: Elf_Sym_Type { + get { + return ELF32_ST_TYPE(st_info) + } + set { + st_info = ELF32_ST_INFO(ELF32_ST_BIND(st_info), newValue) + } + } + + var st_visibility: Elf_Sym_Visibility { + get { + return ELF32_ST_VISIBILITY(st_other) + } + set { + st_other = (st_other & ~3) | newValue.rawValue + } + } +} + +extension Elf64_Sym: Elf_Sym { + var st_binding: Elf_Sym_Binding { + get { + return ELF64_ST_BIND(st_info) + } + set { + st_info = ELF64_ST_INFO(newValue, ELF64_ST_TYPE(st_info)) + } + } + + var st_type: Elf_Sym_Type { + get { + return ELF64_ST_TYPE(st_info) + } + set { + st_info = ELF64_ST_INFO(ELF64_ST_BIND(st_info), newValue) + } + } + + var st_visibility: Elf_Sym_Visibility { + get { + return ELF64_ST_VISIBILITY(st_other) + } + set { + st_other = (st_other & ~3) | newValue.rawValue + } + } +} + +extension Elf32_Rel { + var r_sym: Elf32_Byte { + get { + return ELF32_R_SYM(r_info) + } + set { + r_info = ELF32_R_INFO(newValue, ELF32_R_TYPE(r_info)) + } + } + + var r_type: Elf32_Byte { + get { + return ELF32_R_TYPE(r_info) + } + set { + r_info = ELF32_R_INFO(ELF32_R_SYM(r_info), newValue) + } + } +} + +extension Elf32_Rela { + var r_sym: Elf32_Byte { + get { + return ELF32_R_SYM(r_info) + } + set { + r_info = ELF32_R_INFO(newValue, ELF32_R_TYPE(r_info)) + } + } + + var r_type: Elf32_Byte { + get { + return ELF32_R_TYPE(r_info) + } + set { + r_info = ELF32_R_INFO(ELF32_R_SYM(r_info), newValue) + } + } +} + +extension Elf64_Rel { + var r_sym: Elf64_Word { + get { + return ELF64_R_SYM(r_info) + } + set { + r_info = ELF64_R_INFO(newValue, ELF64_R_TYPE(r_info)) + } + } + + var r_type: Elf64_Word { + get { + return ELF64_R_TYPE(r_info) + } + set { + r_info = ELF64_R_INFO(ELF64_R_SYM(r_info), newValue) + } + } +} + +extension Elf64_Rela { + var r_sym: Elf64_Word { + get { + return ELF64_R_SYM(r_info) + } + set { + r_info = ELF64_R_INFO(newValue, ELF64_R_TYPE(r_info)) + } + } + + var r_type: Elf64_Word { + get { + return ELF64_R_TYPE(r_info) + } + set { + r_info = ELF64_R_INFO(ELF64_R_SYM(r_info), newValue) + } + } +} + +// .. Traits ................................................................... + +protocol ElfTraits { + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + associatedtype Ehdr: Elf_Ehdr where Ehdr.Address == Address, + Ehdr.Offset == Offset + associatedtype Phdr: Elf_Phdr where Phdr.Address == Address, + Phdr.Offset == Offset, + Phdr.Size == Size + associatedtype Shdr: Elf_Shdr where Shdr.Address == Address, + Shdr.Offset == Offset, + Shdr.Size == Size + associatedtype Nhdr: Elf_Nhdr + associatedtype Chdr: Elf_Chdr where Chdr.Size == Size + associatedtype Sym: Elf_Sym where Sym.Address == Address, Sym.Size == Size + + static var elfClass: Elf_Ehdr_Class { get } +} + +struct Elf32Traits: ElfTraits { + typealias Address = UInt32 + typealias Offset = UInt32 + typealias Size = UInt32 + + typealias Ehdr = Elf32_Ehdr + typealias Phdr = Elf32_Phdr + typealias Shdr = Elf32_Shdr + typealias Nhdr = Elf32_Nhdr + typealias Chdr = Elf32_Chdr + typealias Sym = Elf32_Sym + + static let elfClass: Elf_Ehdr_Class = .ELFCLASS32 +} + +struct Elf64Traits: ElfTraits { + typealias Address = UInt64 + typealias Offset = UInt64 + typealias Size = UInt64 + + typealias Ehdr = Elf64_Ehdr + typealias Phdr = Elf64_Phdr + typealias Shdr = Elf64_Shdr + typealias Nhdr = Elf64_Nhdr + typealias Chdr = Elf64_Chdr + typealias Sym = Elf64_Sym + + static let elfClass: Elf_Ehdr_Class = .ELFCLASS64 +} + +// .. ElfStringSection ......................................................... + +struct ElfStringSection { + let bytes: [UInt8] + + func getStringAt(index: Int) -> String? { + if index < 0 || index >= bytes.count { + return nil + } + + let slice = bytes[index...] + var len: Int = 0 + slice.withUnsafeBufferPointer{ ptr in + len = strnlen(ptr.baseAddress!, ptr.count) + } + return String(decoding: bytes[index.. (any ImageSource)? + func getSection(_ name: String) -> (any ImageSource)? +} + +extension ElfGetSectionProtocol { + func getSection(_ name: String) -> (any ImageSource)? { + return getSection(name, debug: false) + } +} + +protocol ElfSymbolProtocol: Equatable { + associatedtype Address: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + var name: String { get set } + var value: Address { get set } + var size: Size { get set } + var sectionIndex: Int { get set } + var binding: Elf_Sym_Binding { get set } + var type: Elf_Sym_Type { get set } + var visibility: Elf_Sym_Visibility { get set } +} + +protocol ElfSymbolTableProtocol { + associatedtype Traits: ElfTraits + associatedtype Symbol: ElfSymbolProtocol where Symbol.Address == Traits.Address, + Symbol.Size == Traits.Size + + func lookupSymbol(address: Traits.Address) -> Symbol? +} + +protocol ElfImageProtocol: Image, ElfGetSectionProtocol, DwarfSource { + associatedtype Traits: ElfTraits + associatedtype SymbolTable: ElfSymbolTableProtocol + where SymbolTable.Traits == Traits + + var header: Traits.Ehdr { get } + var programHeaders: [Traits.Phdr] { get } + var sectionHeaders: [Traits.Shdr]? { get } + + var imageName: String { get } + var debugImage: (any ElfImageProtocol)? { get } + var debugLinkCRC: UInt32? { get } + + var symbolTable: SymbolTable { get } + + func _getSymbolTable(debug: Bool) -> SymbolTable +} + +struct ElfSymbolTable: ElfSymbolTableProtocol { + typealias Traits = SomeElfTraits + + struct Symbol: ElfSymbolProtocol { + typealias Address = Traits.Address + typealias Size = Traits.Size + + var name: String + var value: Address + var size: Size + var sectionIndex: Int + var binding: Elf_Sym_Binding + var type: Elf_Sym_Type + var visibility: Elf_Sym_Visibility + } + + private var _symbols: [Symbol] = [] + + init() {} + + init?(image: ElfImage) { + guard let strtab = image.getSection(".strtab", debug: false), + let symtab = image.getSection(".symtab", debug: false), + let strings = strtab.fetchAllBytes(), + let symdata = symtab.fetchAllBytes() else { + return nil + } + + let stringSect = ElfStringSection(bytes: strings) + + // Extract all the data + symdata.withUnsafeBufferPointer{ + $0.withMemoryRebound(to: Traits.Sym.self) { symbols in + for symbol in symbols { + // Ignore things that are not functions + if symbol.st_type != .STT_FUNC { + continue + } + + // Ignore anything undefined + if symbol.st_shndx == SHN_UNDEF { + continue + } + + _symbols.append( + Symbol( + name: (stringSect.getStringAt(index: Int(symbol.st_name)) + ?? ""), + value: symbol.st_value, + size: symbol.st_size, + sectionIndex: Int(symbol.st_shndx), + binding: symbol.st_binding, + type: symbol.st_type, + visibility: symbol.st_visibility + ) + ) + } + } + } + + // Now sort by address + _symbols.sort(by: { + $0.value < $1.value || ( + $0.value == $1.value && $0.size < $1.size + ) + }) + } + + private init(sortedSymbols: [Symbol]) { + _symbols = sortedSymbols + } + + public func merged(with other: ElfSymbolTable) -> ElfSymbolTable { + var merged: [Symbol] = [] + + var ourNdx = 0, theirNdx = 0 + + while ourNdx < _symbols.count && theirNdx < other._symbols.count { + let ourSym = _symbols[ourNdx] + let theirSym = other._symbols[theirNdx] + + if ourSym.value < theirSym.value { + merged.append(ourSym) + ourNdx += 1 + } else if ourSym.value > theirSym.value { + merged.append(theirSym) + theirNdx += 1 + } else if ourSym == theirSym { + merged.append(ourSym) + ourNdx += 1 + theirNdx += 1 + } else { + if ourSym.size <= theirSym.size { + merged.append(ourSym) + } + merged.append(theirSym) + if ourSym.size > theirSym.size { + merged.append(theirSym) + } + ourNdx += 1 + theirNdx += 1 + } + } + + if ourNdx < _symbols.count { + merged.append(contentsOf:_symbols[ourNdx...]) + } + if theirNdx < other._symbols.count { + merged.append(contentsOf:other._symbols[theirNdx...]) + } + + return ElfSymbolTable(sortedSymbols: merged) + } + + public func lookupSymbol(address: Traits.Address) -> Symbol? { + var min = 0 + var max = _symbols.count + + while min < max { + let mid = min + (max - min) / 2 + let symbol = _symbols[mid] + let nextValue: Traits.Address + if mid == _symbols.count - 1 { + nextValue = ~Traits.Address(0) + } else { + nextValue = _symbols[mid + 1].value + } + + if symbol.value <= address && nextValue > address { + var ndx = mid + while ndx > 0 && _symbols[ndx - 1].value == address { + ndx -= 1 + } + return _symbols[ndx] + } else if symbol.value < address { + min = mid + 1 + } else if symbol.value > address { + max = mid + } + } + + return nil + } +} + +class ElfImage: ElfImageProtocol { + typealias Traits = SomeElfTraits + typealias Source = SomeImageSource + typealias SymbolTable = ElfSymbolTable + + // This is arbitrary and it isn't in the spec + let maxNoteNameLength = 256 + + var baseAddress: Source.Address + var endAddress: Source.Address + + var source: SomeImageSource + var header: Traits.Ehdr + var programHeaders: [Traits.Phdr] + var sectionHeaders: [Traits.Shdr]? + var shouldByteSwap: Bool { return header.shouldByteSwap } + + required init(source: SomeImageSource, + baseAddress: Source.Address = 0, + endAddress: Source.Address = 0) throws { + self.source = source + self.baseAddress = baseAddress + self.endAddress = endAddress + + header = try source.fetch(from: 0, as: Traits.Ehdr.self) + if header.ei_magic != ElfMagic { + throw ElfImageError.notAnElfImage + } + + if header.ei_class != Traits.elfClass { + throw ElfImageError.wrongClass + } + + if header.shouldByteSwap { + header = header.byteSwapped + } + + let byteSwap = header.shouldByteSwap + func maybeSwap(_ x: T) -> T { + if byteSwap { + return x.byteSwapped + } + return x + } + + var phdrs: [Traits.Phdr] = [] + var phAddr = Source.Address(header.e_phoff) + for _ in 0..= header.e_shnum { + throw ElfImageError.badStringTableSectionIndex + } + } + + struct Note { + let name: String + let type: UInt32 + let desc: [UInt8] + } + + struct Notes: Sequence { + var image: ElfImage + + struct NoteIterator: IteratorProtocol { + var image: ElfImage + + var hdrNdx = -1 + var noteAddr = Source.Address() + var noteEnd = Source.Address() + + init(image: ElfImage) { + self.image = image + } + + mutating func startHeader() { + let ph = image.programHeaders[hdrNdx] + + if image.source.isMappedImage { + noteAddr = Source.Address(ph.p_vaddr) + noteEnd = noteAddr + Source.Address(ph.p_memsz) + } else { + noteAddr = Source.Address(ph.p_offset) + noteEnd = noteAddr + Source.Address(ph.p_filesz) + } + } + + mutating func next() -> Note? { + if hdrNdx >= image.programHeaders.count { + return nil + } + while true { + while noteAddr >= noteEnd { + repeat { + hdrNdx += 1 + if hdrNdx >= image.programHeaders.count { + return nil + } + } while image.programHeaders[hdrNdx].p_type != .PT_NOTE + startHeader() + } + + do { + let nhdr = try image.fetch(from: noteAddr, as: Traits.Nhdr.self) + + noteAddr += Source.Address(MemoryLayout.size) + + if noteEnd - noteAddr < nhdr.n_namesz { + // The segment is probably corrupted + noteAddr = noteEnd + continue + } + + let nameLen = nhdr.n_namesz > 0 ? nhdr.n_namesz - 1 : 0 + let nameBytes = try image.fetch(from: noteAddr, + count: Int(nameLen), + as: UInt8.self) + let name = String(decoding: nameBytes, as: UTF8.self) + + noteAddr += Source.Address(nhdr.n_namesz) + if (noteAddr & 3) != 0 { + noteAddr += 4 - (noteAddr & 3) + } + + if noteEnd - noteAddr < nhdr.n_descsz { + // The segment is probably corrupted + noteAddr = noteEnd + continue + } + + let desc = try image.fetch(from: noteAddr, + count: Int(nhdr.n_descsz), + as: UInt8.self) + + noteAddr += Source.Address(nhdr.n_descsz) + if (noteAddr & 3) != 0 { + noteAddr += 4 - (noteAddr & 3) + } + + return Note(name: name, type: UInt32(nhdr.n_type), desc: desc) + } catch { + hdrNdx = image.programHeaders.count + return nil + } + } + } + } + + func makeIterator() -> NoteIterator { + return NoteIterator(image: image) + } + } + + var notes: Notes { + return Notes(image: self) + } + + private var _uuid: [UInt8]? + var uuid: [UInt8]? { + if let uuid = _uuid { + return uuid + } + + for note in notes { + if note.name == "GNU" && note.type == ImageFormats.NT_GNU_BUILD_ID { + _uuid = note.desc + return _uuid + } + } + + return nil + } + + private var _debugLinkCRC: UInt32? + var debugLinkCRC: UInt32? { + guard let bounds = source.bounds else { + return nil + } + + if let crc = _debugLinkCRC { + return crc + } + + let bufSize = 65536 + let buffer = UnsafeMutableBufferPointer.allocate(capacity: bufSize) + defer { + buffer.deallocate() + } + + var pos = bounds.base + var remaining = bounds.size + var crc: UInt32 = 0 + do { + while remaining > 0 { + let todo = min(bufSize, Int(remaining)) + let slice = buffer[..(rebasing: slice) + + try fetch(from: pos, into: chunk) + + crc = updateCrc(crc, UnsafeBufferPointer(chunk)) + + remaining -= Source.Size(todo) + pos += Source.Address(todo) + } + } catch { + return nil + } + + return crc + } + + struct Range { + var base: Source.Address + var size: Source.Size + } + + struct EHFrameInfo { + var ehFrameSection: Range? + var ehFrameHdrSection: Range? + } + + private var _ehFrameInfo: EHFrameInfo? + var ehFrameInfo: EHFrameInfo? { + if let ehFrameInfo = _ehFrameInfo { + return ehFrameInfo + } + + var ehFrameInfo = EHFrameInfo() + + for phdr in programHeaders { + if phdr.p_type == .PT_GNU_EH_FRAME { + var ehFrameHdrRange: Range + if source.isMappedImage { + ehFrameHdrRange = Range(base: Source.Address(phdr.p_vaddr), + size: Source.Size(phdr.p_memsz)) + } else { + ehFrameHdrRange = Range(base: Source.Address(phdr.p_offset), + size: Source.Size(phdr.p_filesz)) + } + + if (ehFrameHdrRange.size < MemoryLayout.size) { + continue + } + + guard let ehdr = try? fetch(from: Source.Address(ehFrameHdrRange.base), + as: EHFrameHdr.self) else { + continue + } + + if ehdr.version != 1 { + continue + } + + let pc = ehFrameHdrRange.base + Source.Address(MemoryLayout.size) + guard let (_, eh_frame_ptr) = + try? source.fetchEHValue(from: Source.Address(pc), + with: ehdr.eh_frame_ptr_enc, + pc: Source.Address(pc)) else { + continue + } + + ehFrameInfo.ehFrameHdrSection = ehFrameHdrRange + + // The .eh_frame_hdr section doesn't specify the size of the + // .eh_frame section, so we just rely on it being properly + // terminated. This does mean that bulk fetching the entire + // thing isn't a good idea. + ehFrameInfo.ehFrameSection = Range(base: Source.Address(eh_frame_ptr), + size: ~Source.Size(0)) + } + } + + if let sectionHeaders = sectionHeaders { + let stringShdr = sectionHeaders[Int(header.e_shstrndx)] + do { + let bytes = try source.fetch(from: Source.Address(stringShdr.sh_offset), + count: Int(stringShdr.sh_size), + as: UInt8.self) + let stringSect = ElfStringSection(bytes: bytes) + + for shdr in sectionHeaders { + guard let name = stringSect.getStringAt(index: Int(shdr.sh_name)) else { + continue + } + + if name == ".eh_frame" { + ehFrameInfo.ehFrameSection = Range(base: Source.Address(shdr.sh_offset), + size: Source.Size(shdr.sh_size)) + } + } + } catch { + } + } + + return ehFrameInfo + } + + // Image name + private var _imageName: String? + var imageName: String { + if let imageName = _imageName { + return imageName + } + + let name: String + if let path = source.path { + name = path + } else if let uuid = uuid { + name = "image \(hex(uuid))" + } else { + name = "" + } + + _imageName = name + return name + } + + // If we have external debug information, this points at it + private var _checkedDebugImage: Bool? + private var _debugImage: (any ElfImageProtocol)? + var debugImage: (any ElfImageProtocol)? { + if let checked = _checkedDebugImage, checked { + return _debugImage + } + + let tryPath = { [self] (_ path: String) -> (any ElfImageProtocol)? in + do { + let fileSource = try FileImageSource(path: path) + let image = try ElfImage(source: fileSource) + _debugImage = image + return image + } catch { + return nil + } + } + + if let uuid = uuid { + let uuidString = hex(uuid) + let uuidSuffix = uuidString.dropFirst(2) + let uuidPrefix = uuidString.prefix(2) + let path = "/usr/lib/debug/.build-id/\(uuidPrefix)/\(uuidSuffix).debug" + if let image = tryPath(path) { + _debugImage = image + _checkedDebugImage = true + return image + } + } + + if let imagePath = source.path, let realImagePath = realPath(imagePath) { + let imageDir = dirname(realImagePath) + let debugLink = getDebugLink() + let debugAltLink = getDebugAltLink() + + let tryLink = { (_ link: String) -> (any ElfImageProtocol)? in + if let image = tryPath("\(imageDir)/\(link)") { + return image + } + if let image = tryPath("\(imageDir)/.debug/\(link)") { + return image + } + if let image = tryPath("/usr/lib/debug/\(imageDir)/\(link)") { + return image + } + return nil + } + + if let debugAltLink = debugAltLink, let image = tryLink(debugAltLink.link), + image.uuid == debugAltLink.uuid { + _debugImage = image + _checkedDebugImage = true + return image + } + + if let debugLink = debugLink, let image = tryLink(debugLink.link), + image.debugLinkCRC == debugLink.crc { + _debugImage = image + _checkedDebugImage = true + return image + } + } + + if let debugData = getSection(".gnu_debugdata") { + do { + let source = try LZMACompressedImageSource(source: debugData) + _debugImage = try ElfImage(source: source) + _checkedDebugImage = true + return _debugImage + } catch let CompressedImageSourceError.libraryNotFound(library) { + swift_reportWarning(0, + """ + swift-runtime: warning: \(library) not found, \ + unable to decode the .gnu_debugdata section in \ + \(imageName) + """) + } catch { + } + } + + _checkedDebugImage = true + return nil + } + + /// Find the named section and return an ImageSource pointing at it. + /// + /// In general, the section may be compressed or even in a different image; + /// this is particularly the case for debug sections. We will only attempt + /// to look for other images if `debug` is `true`. + func getSection(_ name: String, debug: Bool) -> (any ImageSource)? { + if let sectionHeaders = sectionHeaders { + let zname = ".z" + name.dropFirst() + let stringShdr = sectionHeaders[Int(header.e_shstrndx)] + do { + let bytes = try source.fetch(from: Source.Address(stringShdr.sh_offset), + count: Int(stringShdr.sh_size), + as: UInt8.self) + let stringSect = ElfStringSection(bytes: bytes) + + for shdr in sectionHeaders { + guard let sname + = stringSect.getStringAt(index: Int(shdr.sh_name)) else { + continue + } + + if name == sname { + let subSource = SubImageSource(parent: source, + baseAddress: Source.Address(shdr.sh_offset), + length: Source.Size(shdr.sh_size)) + if (shdr.sh_flags & Traits.Shdr.Flags(SHF_COMPRESSED)) != 0 { + return try ElfCompressedImageSource(source: subSource) + } else { + return subSource + } + } + + if zname == sname { + let subSource = SubImageSource(parent: source, + baseAddress: Source.Address(shdr.sh_offset), + length: Source.Size(shdr.sh_size)) + return try ElfGNUCompressedImageSource(source: subSource) + } + } + } catch let CompressedImageSourceError.libraryNotFound(library) { + swift_reportWarning(0, + """ + swift-runtime: warning: \(library) not found, \ + unable to decode the \(name) section in \ + \(imageName) + """) + } catch { + } + } + + if debug, let image = debugImage { + return image.getSection(name) + } + + return nil + } + + struct DebugLinkInfo { + var link: String + var crc: UInt32 + } + + struct DebugAltLinkInfo { + var link: String + var uuid: [UInt8] + } + + /// Get and decode a .gnu_debuglink section + func getDebugLink() -> DebugLinkInfo? { + guard let section = getSection(".gnu_debuglink") else { + return nil + } + + guard let bytes = section.fetchAllBytes() else { + return nil + } + + guard let nullIndex = bytes.firstIndex(of: UInt8(0)) else { + return nil + } + + let link = String(decoding: bytes[0.. DebugAltLinkInfo? { + guard let section = getSection(".gnu_debugaltlink") else { + return nil + } + + guard let bytes = section.fetchAllBytes() else { + return nil + } + + guard let nullIndex = bytes.firstIndex(of: UInt8(0)) else { + return nil + } + + let link = String(decoding: bytes[0.. String? { + guard let sectionSource = getSection(name) else { + return nil + } + + if let data = sectionSource.fetchAllBytes() { + return String(decoding: data, as: UTF8.self) + } + + return nil + } + + struct ElfSymbol { + var name: String + var value: Traits.Address + var size: Traits.Size + var sectionIndex: Int + var binding: Elf_Sym_Binding + var type: Elf_Sym_Type + var visibility: Elf_Sym_Visibility + } + + var _symbolTable: SymbolTable? = nil + var symbolTable: SymbolTable { return _getSymbolTable(debug: false) } + + func _getSymbolTable(debug: Bool) -> SymbolTable { + if let table = _symbolTable { + return table + } + + let debugTable: SymbolTable? + if !debug, let debugImage = debugImage { + debugTable = debugImage._getSymbolTable(debug: true) + as any ElfSymbolTableProtocol + as? SymbolTable + } else { + debugTable = nil + } + + guard let localTable = SymbolTable(image: self) else { + // If we have no symbol table, try the debug image + let table = debugTable ?? SymbolTable() + _symbolTable = table + return table + } + + // Check if we have a debug image; if we do, get its symbol table and + // merge it with this one. This means that it doesn't matter which + // symbols have been stripped in both images. + if let debugTable = debugTable { + let merged = localTable.merged(with: debugTable) + _symbolTable = merged + return merged + } + + _symbolTable = localTable + return localTable + } + + public func lookupSymbol(address: Source.Address) -> ImageSymbol? { + let relativeAddress = Traits.Address(address - baseAddress) + guard let symbol = symbolTable.lookupSymbol(address: relativeAddress) else { + return nil + } + + return ImageSymbol(name: symbol.name, + offset: Int(relativeAddress - symbol.value)) + } + + func getDwarfSection(_ section: DwarfSection) -> (any ImageSource)? { + switch section { + case .debugAbbrev: return getSection(".debug_abbrev") + case .debugAddr: return getSection(".debug_addr") + case .debugARanges: return getSection(".debug_aranges") + case .debugFrame: return getSection(".debug_frame") + case .debugInfo: return getSection(".debug_info") + case .debugLine: return getSection(".debug_line") + case .debugLineStr: return getSection(".debug_line_str") + case .debugLoc: return getSection(".debug_loc") + case .debugLocLists: return getSection(".debug_loclists") + case .debugMacInfo: return getSection(".debug_macinfo") + case .debugMacro: return getSection(".debug_macro") + case .debugNames: return getSection(".debug_names") + case .debugPubNames: return getSection(".debug_pubnames") + case .debugPubTypes: return getSection(".debug_pubtypes") + case .debugRanges: return getSection(".debug_ranges") + case .debugRngLists: return getSection(".debug_rnglists") + case .debugStr: return getSection(".debug_str") + case .debugStrOffsets: return getSection(".debug_str_offsets") + case .debugSup: return getSection(".debug_sup") + case .debugTypes: return getSection(".debug_types") + case .debugCuIndex: return getSection(".debug_cu_index") + case .debugTuIndex: return getSection(".debug_tu_index") + } + } + + private lazy var dwarfReader + = try? DwarfReader(source: self, shouldSwap: header.shouldByteSwap) + + typealias CallSiteInfo = DwarfReader.CallSiteInfo + + func inlineCallSites(at address: Address) -> ArraySlice { + guard let callSiteInfo = dwarfReader?.inlineCallSites else { + return [][0..<0] + } + + var min = 0 + var max = callSiteInfo.count + + while min < max { + let mid = min + (max - min) / 2 + let callSite = callSiteInfo[mid] + + if callSite.lowPC <= address && callSite.highPC > address { + var first = mid, last = mid + while first > 0 + && callSiteInfo[first - 1].lowPC <= address + && callSiteInfo[first - 1].highPC > address { + first -= 1 + } + while last < callSiteInfo.count - 1 + && callSiteInfo[last + 1].lowPC <= address + && callSiteInfo[last + 1].highPC > address { + last += 1 + } + + return callSiteInfo[first...last] + } else if callSite.highPC <= address { + min = mid + 1 + } else if callSite.lowPC > address { + max = mid + } + } + + return [] + } + + typealias SourceLocation = SymbolicatedBacktrace.SourceLocation + + func sourceLocation(for address: Address) throws -> SourceLocation? { + var result: SourceLocation? = nil + var prevState: DwarfReader.LineNumberState? = nil + guard let dwarfReader = dwarfReader else { + return nil + } + for ndx in 0..= oldState.address && address < state.address { + result = SourceLocation( + path: oldState.path, + line: oldState.line, + column: oldState.column + ) + done = true + } + + prevState = state + } + } + + return result + } +} + +typealias Elf32Image = ElfImage +typealias Elf64Image = ElfImage + +// .. Testing .................................................................. + +@_spi(ElfTest) +public func testElfImageAt(path: String) -> Bool { + guard let source = try? FileImageSource(path: path) else { + print("\(path) was not accessible") + return false + } + + let debugSections: [String] = [ + ".debug_info", + ".debug_line", + ".debug_abbrev", + ".debug_ranges", + ".debug_str", + ".debug_addr", + ".debug_str_offsets", + ".debug_line_str", + ".debug_rnglists" + ] + + if let elfImage = try? Elf32Image(source: source) { + print("\(path) is a 32-bit ELF image") + + if let uuid = elfImage.uuid { + print(" uuid: \(hex(uuid))") + } else { + print(" uuid: ") + } + + if let debugImage = elfImage.debugImage { + print(" debug image: \(debugImage.imageName)") + } else { + print(" debug image: ") + } + + for section in debugSections { + if let _ = elfImage.getSection(section, debug: true) { + print(" \(section): found") + } else { + print(" \(section): not found") + } + } + + return true + } else if let elfImage = try? Elf64Image(source: source) { + print("\(path) is a 64-bit ELF image") + + if let uuid = elfImage.uuid { + print(" uuid: \(hex(uuid))") + } else { + print(" uuid: ") + } + + if let debugImage = elfImage.debugImage { + print(" debug image: \(debugImage.imageName)") + } else { + print(" debug image: ") + } + + for section in debugSections { + if let _ = elfImage.getSection(section, debug: true) { + print(" \(section): found") + } else { + print(" \(section): not found") + } + } + + return true + } else { + print("\(path) is not an ELF image") + return false + } +} + +#endif // os(Linux) diff --git a/stdlib/public/Backtracing/FileImageSource.swift b/stdlib/public/Backtracing/FileImageSource.swift new file mode 100644 index 0000000000000..94ed7b420f94d --- /dev/null +++ b/stdlib/public/Backtracing/FileImageSource.swift @@ -0,0 +1,70 @@ +//===--- FileImageSource.swift - An image source that reads from a file ---===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines FileImageSource, an image source that reads data from a file. +// +//===----------------------------------------------------------------------===// + +import Swift + +@_implementationOnly import OS.Libc + +enum FileImageSourceError: Error { + case posixError(Int32) + case truncatedRead +} + +class FileImageSource: ImageSource { + private var fd: Int32 + + public var isMappedImage: Bool { return false } + + private var _path: String + public var path: String? { return _path } + + public lazy var bounds: Bounds? = { + let size = lseek(fd, 0, SEEK_END) + if size < 0 { + return nil + } + return Bounds(base: 0, size: Size(size)) + }() + + public init(path: String) throws { + _path = path + fd = _swift_open(path, O_RDONLY, 0) + if fd < 0 { + throw FileImageSourceError.posixError(_swift_get_errno()) + } + } + + deinit { + close(fd) + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + while true { + let size = MemoryLayout.stride * buffer.count + let result = pread(fd, buffer.baseAddress, size, off_t(addr)) + + if result < 0 { + throw FileImageSourceError.posixError(_swift_get_errno()) + } + + if result != size { + throw FileImageSourceError.truncatedRead + } + break + } + } +} diff --git a/stdlib/public/Backtracing/FramePointerUnwinder.swift b/stdlib/public/Backtracing/FramePointerUnwinder.swift index d8232e9be58ee..643fc557eb17a 100644 --- a/stdlib/public/Backtracing/FramePointerUnwinder.swift +++ b/stdlib/public/Backtracing/FramePointerUnwinder.swift @@ -28,15 +28,83 @@ public struct FramePointerUnwinder: Sequence, Itera var first: Bool var isAsync: Bool + #if os(Linux) + var elf32Cache: [Int:Elf32Image] = [:] + var elf64Cache: [Int:Elf64Image] = [:] + var images: [Backtrace.Image]? + #endif + var reader: MemoryReader - public init(context: Context, memoryReader: MemoryReader) { + public init(context: Context, + images: [Backtrace.Image]?, + memoryReader: MemoryReader) { + pc = Address(context.programCounter) fp = Address(context.framePointer) first = true isAsync = false asyncContext = 0 reader = memoryReader + + // On Linux, the unwinder needs images in order to spot async frames + #if os(Linux) + self.images = images + #endif + } + + private func isAsyncSymbol(_ mangledName: String) -> Bool { + let mangledUTF8 = mangledName.utf8 + if mangledUTF8.last == UInt8(ascii: "_") { + let withoutUnderscore = mangledUTF8.dropLast(1) + if let beforeIndexNdx = withoutUnderscore.lastIndex( + where: { $0 < UInt8(ascii: "0") || $0 > UInt8(ascii: "9") } + ) { + let beforeIndex = withoutUnderscore[...beforeIndexNdx] + let suffix = beforeIndex.suffix(2) + let awaitResume = "TY".utf8 + let suspendResume = "TQ".utf8 + return suffix.elementsEqual(awaitResume) || + suffix.elementsEqual(suspendResume) + } + } + return false + } + + private mutating func isAsyncPC(_ pc: Address) -> Bool { + // On Linux, we need to examine the PC to see if this is an async frame + #if os(Linux) + let address = FileImageSource.Address(pc) + + if let images = images, + let imageNdx = images.firstIndex( + where: { address >= $0.baseAddress && address < $0.endOfText } + ) { + let relativeAddress = address - FileImageSource.Address(images[imageNdx].baseAddress) + var elf32Image = elf32Cache[imageNdx] + var elf64Image = elf64Cache[imageNdx] + + if elf32Image == nil && elf64Image == nil { + if let source = try? FileImageSource(path: images[imageNdx].path) { + if let elfImage = try? Elf32Image(source: source) { + elf32Image = elfImage + elf32Cache[imageNdx] = elfImage + } else if let elfImage = try? Elf64Image(source: source) { + elf64Image = elfImage + elf64Cache[imageNdx] = elfImage + } + } + } + + if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) { + return isAsyncSymbol(theSymbol.name) + } else if let theSymbol = elf64Image?.lookupSymbol(address: relativeAddress) { + return isAsyncSymbol(theSymbol.name) + } + } + #endif + + return false } private func isAsyncFrame(_ storedFp: Address) -> Bool { @@ -73,32 +141,34 @@ public struct FramePointerUnwinder: Sequence, Itera } if !isAsync { - // Try to read the next fp/pc pair - var next: Address = 0 - let strippedFp = stripPtrAuth(fp) - - if strippedFp == 0 - || !Context.isAlignedForStack(framePointer: - Context.Address(strippedFp)) { - return nil - } - - do { - pc = stripPtrAuth(try reader.fetch(from: - strippedFp + Address(MemoryLayout
.size), - as: Address.self)) - next = try reader.fetch(from: Address(strippedFp), as: Address.self) - } catch { - return nil - } - - if next <= fp { - return nil - } - - if !isAsyncFrame(next) { - fp = next - return .returnAddress(Backtrace.Address(pc)) + if !isAsyncPC(pc) { + // Try to read the next fp/pc pair + var next: Address = 0 + let strippedFp = stripPtrAuth(fp) + + if strippedFp == 0 + || !Context.isAlignedForStack(framePointer: + Context.Address(strippedFp)) { + return nil + } + + do { + pc = stripPtrAuth(try reader.fetch(from: + strippedFp + Address(MemoryLayout
.size), + as: Address.self)) + next = try reader.fetch(from: Address(strippedFp), as: Address.self) + } catch { + return nil + } + + if next <= fp { + return nil + } + + if !isAsyncFrame(next) { + fp = next + return .returnAddress(Backtrace.Address(pc)) + } } isAsync = true diff --git a/stdlib/public/Backtracing/Image.swift b/stdlib/public/Backtracing/Image.swift new file mode 100644 index 0000000000000..7f3d1ef6ebae1 --- /dev/null +++ b/stdlib/public/Backtracing/Image.swift @@ -0,0 +1,167 @@ +//===--- Image.swift - Binary image protocol for Swift --------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Defines a protocol for binary image files that allows us to fetch what +// we need without knowing all of the gory details. +// +//===----------------------------------------------------------------------===// + +import Swift + +struct ImageSymbol { + var name: String + var offset: Int +} + +internal protocol Image { + associatedtype Source: ImageSource + + typealias UUID = [UInt8] + typealias Address = Source.Address + + init(source: Source, baseAddress: Address, endAddress: Address) throws + + var baseAddress: Address { get set } + var endAddress: Address { get set } + + var source: Source { get } + var uuid: UUID? { get } + var shouldByteSwap: Bool { get } + + func swapIfRequired(_ x: T) -> T + func swapIfRequired(_ x: T) -> T + func swapIfRequired(_ x: T) -> T + + func swapIfRequired(array: inout [T]) + func swapIfRequired(array: inout [T]) + func swapIfRequired(array: inout [T]) + + func swapIfRequired(buffer: UnsafeMutableBufferPointer) + func swapIfRequired(buffer: UnsafeMutableBufferPointer) + func swapIfRequired(buffer: UnsafeMutableBufferPointer) + + func swapIfRequired(pointer: UnsafeMutablePointer) + func swapIfRequired(pointer: UnsafeMutablePointer) + func swapIfRequired(pointer: UnsafeMutablePointer) + + func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws + func fetch(from addr: Address, into pointer: UnsafeMutablePointer) throws + func fetch(from addr: Address, count: Int, as: T.Type) throws -> [T] + func fetch(from addr: Address, as type: T.Type) throws -> T + + func fetchUnswapped(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws + func fetchUnswapped(from addr: Address, + into pointer: UnsafeMutablePointer) throws + func fetchUnswapped(from addr: Address, count: Int, as: T.Type) throws -> [T] + func fetchUnswapped(from addr: Address, as type: T.Type) throws -> T + + func lookupSymbol(address: Address) -> ImageSymbol? +} + +extension Image { + public func swapIfRequired(_ x: T) -> T { + if shouldByteSwap { + return x.byteSwapped + } + return x + } + + public func swapIfRequired(_ x: T) -> T { + if shouldByteSwap { + return x.byteSwapped + } + return x + } + + public func swapIfRequired(_ x: T) -> T { + return x + } + + public func swapIfRequired(array: inout [T]) { + if shouldByteSwap { + array.swapBytes() + } + } + public func swapIfRequired(array: inout [T]) { + if shouldByteSwap { + array.swapBytes() + } + } + public func swapIfRequired(array: inout [T]) { + // Nothing to do + } + + public func swapIfRequired(buffer: UnsafeMutableBufferPointer) { + if shouldByteSwap { + buffer.swapBytes() + } + } + public func swapIfRequired(buffer: UnsafeMutableBufferPointer) { + if shouldByteSwap { + buffer.swapBytes() + } + } + public func swapIfRequired(buffer: UnsafeMutableBufferPointer) { + // Nothing to do + } + + public func swapIfRequired(pointer: UnsafeMutablePointer) { + if shouldByteSwap { + pointer.pointee = pointer.pointee.byteSwapped + } + } + public func swapIfRequired(pointer: UnsafeMutablePointer) { + if shouldByteSwap { + pointer.pointee = pointer.pointee.byteSwapped + } + } + public func swapIfRequired(pointer: UnsafeMutablePointer) { + // Nothing to do + } + + + public func fetchUnswapped(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + return try source.fetch(from: addr, into: buffer) + } + public func fetchUnswapped(from addr: Address, + into pointer: UnsafeMutablePointer) throws { + return try source.fetch(from: addr, into: pointer) + } + public func fetchUnswapped(from addr: Address, count: Int, as type: T.Type) throws -> [T] { + return try source.fetch(from: addr, count: count, as: type) + } + public func fetchUnswapped(from addr: Address, as type: T.Type) throws -> T { + return try source.fetch(from: addr, as: type) + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try fetchUnswapped(from: addr, into: buffer) + swapIfRequired(buffer: buffer) + } + public func fetch(from addr: Address, + into pointer: UnsafeMutablePointer) throws { + try fetchUnswapped(from: addr, into: pointer) + swapIfRequired(pointer: pointer) + } + public func fetch(from addr: Address, count: Int, as type: T.Type) throws -> [T]{ + var result = try fetchUnswapped(from: addr, count: count, as: type) + swapIfRequired(array: &result) + return result + } + public func fetch(from addr: Address, as type: T.Type) throws -> T { + return swapIfRequired(try fetchUnswapped(from: addr, as: type)) + } +} diff --git a/stdlib/public/Backtracing/ImageSource.swift b/stdlib/public/Backtracing/ImageSource.swift new file mode 100644 index 0000000000000..2c68a45b8605f --- /dev/null +++ b/stdlib/public/Backtracing/ImageSource.swift @@ -0,0 +1,153 @@ +//===--- ImageSource.swift - A place from which to read image data --------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines ImageSource, which is a protocol that can be implemented to +// provide an image reader with a way to read data from a file, a buffer +// in memory, or wherever we might wish to read an image from. +// +//===----------------------------------------------------------------------===// + +import Swift + +struct ImageBounds { + var base: Address + var size: Size + var end: Address { + return base + Address(size) + } + + func adjusted(by offset: some FixedWidthInteger) -> Self { + return Self(base: base + Address(offset), size: size - Size(offset)) + } +} + +protocol ImageSource: MemoryReader { + typealias Bounds = ImageBounds + + /// Says whether we are looking at a loaded image in memory or not. + /// The layout in memory may differ from the on-disk layout; in particular, + /// some information may not be available when the image is mapped into + /// memory (an example is ELF section headers). + var isMappedImage: Bool { get } + + /// If this ImageSource knows its path, this will be non-nil. + var path: String? { get } + + /// If this ImageSource knows its bounds, this will be non-nil. + var bounds: Bounds? { get } +} + +struct ImageSourceCursor { + typealias Address = UInt64 + typealias Size = UInt64 + typealias Bounds = ImageBounds + + var source: any ImageSource + var pos: Address + + init(source: any ImageSource, offset: Address = 0) { + self.source = source + self.pos = offset + } + + public mutating func read(into buffer: UnsafeMutableBufferPointer) throws { + try source.fetch(from: pos, into: buffer) + pos += UInt64(MemoryLayout.stride * buffer.count) + } + + public mutating func read(into pointer: UnsafeMutablePointer) throws { + try source.fetch(from: pos, into: pointer) + pos += UInt64(MemoryLayout.stride) + } + + public mutating func read(as type: T.Type) throws -> T { + let stride = MemoryLayout.stride + let result = try source.fetch(from: pos, as: type) + pos += UInt64(stride) + return result + } + + public mutating func read(count: Int, as type: T.Type) throws -> [T] { + let stride = MemoryLayout.stride + let result = try source.fetch(from: pos, count: count, as: type) + pos += UInt64(stride * count) + return result + } + + public mutating func readString() throws -> String? { + var bytes: [UInt8] = [] + while true { + let ch = try read(as: UInt8.self) + if ch == 0 { + break + } + bytes.append(ch) + } + + return String(decoding: bytes, as: UTF8.self) + } + +} + +extension ImageSource { + /// Fetch all the data from this image source (which must be bounded) + func fetchAllBytes() -> [UInt8]? { + guard let bounds = self.bounds else { + return nil + } + if let data = try? fetch(from: bounds.base, + count: Int(bounds.size), + as: UInt8.self) { + return data + } + return nil + } +} + +enum SubImageSourceError: Error { + case outOfRangeFetch(UInt64, Int) +} + +struct SubImageSource: ImageSource { + var parent: S + var baseAddress: Address + var length: Size + var path: String? { return parent.path } + + var bounds: Bounds? { + return Bounds(base: 0, size: length) + } + + public init(parent: S, baseAddress: Address, length: Size) { + self.parent = parent + self.baseAddress = baseAddress + self.length = length + } + + public var isMappedImage: Bool { + return parent.isMappedImage + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > length { + throw SubImageSourceError.outOfRangeFetch(UInt64(addr), toFetch) + } + if Address(length) - addr < toFetch { + throw SubImageSourceError.outOfRangeFetch(UInt64(addr), toFetch) + } + + return try parent.fetch(from: baseAddress + addr, into: buffer) + } +} diff --git a/stdlib/public/Backtracing/MemoryImageSource.swift b/stdlib/public/Backtracing/MemoryImageSource.swift new file mode 100644 index 0000000000000..29baa7d163cd8 --- /dev/null +++ b/stdlib/public/Backtracing/MemoryImageSource.swift @@ -0,0 +1,35 @@ +//===--- MemoryImageSource.swift - An image source that reads from a file ---===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines MemoryImageSource, an image source that reads data using a +// MemoryReader. +// +//===----------------------------------------------------------------------===// + +import Swift + +class MemoryImageSource: ImageSource { + private var reader: M + + public var isMappedImage: Bool { return true } + public var path: String? { return nil } + public var bounds: Bounds? { return nil } + + public init(with reader: M) { + self.reader = reader + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try reader.fetch(from: addr, into: buffer) + } +} diff --git a/stdlib/public/Backtracing/MemoryReader.swift b/stdlib/public/Backtracing/MemoryReader.swift index b82853029c067..67adabb300dd2 100644 --- a/stdlib/public/Backtracing/MemoryReader.swift +++ b/stdlib/public/Backtracing/MemoryReader.swift @@ -17,20 +17,36 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +@_implementationOnly import OS.Libc +#if os(macOS) +@_implementationOnly import OS.Darwin +#elseif os(Linux) +@_implementationOnly import OS.Linux +#endif + +@_implementationOnly import Runtime @_spi(MemoryReaders) public protocol MemoryReader { - associatedtype Address: FixedWidthInteger + typealias Address = UInt64 + typealias Size = UInt64 + /// Fill the specified buffer with data from the specified location in + /// the source. func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws + /// Write data from the specified location in the source through a pointer func fetch(from addr: Address, into pointer: UnsafeMutablePointer) throws + /// Fetch an array of Ts from the specified location in the source func fetch(from addr: Address, count: Int, as: T.Type) throws -> [T] + /// Fetch a T from the specified location in the source func fetch(from addr: Address, as: T.Type) throws -> T + + /// Fetch a NUL terminated string from the specified location in the source + func fetchString(from addr: Address) throws -> String? } extension MemoryReader { @@ -60,44 +76,59 @@ extension MemoryReader { } } + public func fetchString(from addr: Address) throws -> String? { + var bytes: [UInt8] = [] + var ptr = addr + while true { + let ch = try fetch(from: ptr, as: UInt8.self) + if ch == 0 { + break + } + bytes.append(ch) + ptr += 1 + } + + return String(decoding: bytes, as: UTF8.self) + } + } @_spi(MemoryReaders) public struct UnsafeLocalMemoryReader: MemoryReader { - public typealias Address = UInt + public init() {} public func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws { - buffer.baseAddress!.update(from: UnsafePointer(bitPattern: address)!, + buffer.baseAddress!.update(from: UnsafePointer(bitPattern: UInt(address))!, count: buffer.count) } } #if os(macOS) @_spi(MemoryReaders) public struct MachError: Error { - var result: __swift_kern_return_t + var result: kern_return_t } @_spi(MemoryReaders) public struct RemoteMemoryReader: MemoryReader { - public typealias Address = UInt64 - - private var task: __swift_task_t + private var task: task_t // Sadly we can't expose the type of this argument public init(task: Any) { - self.task = task as! __swift_task_t + self.task = task as! task_t } public func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws { let size = UInt64(MemoryLayout.stride * buffer.count) var sizeOut = UInt64(0) - let result = _swift_backtrace_vm_read(task, - UInt64(address), - UInt64(size), - buffer.baseAddress, - &sizeOut) - - if result != _SWIFT_KERN_SUCCESS { + let result = mach_vm_read_overwrite(task, + UInt64(address), + UInt64(size), + mach_vm_address_t( + Int(bitPattern: buffer.baseAddress) + ), + &sizeOut) + + if result != KERN_SUCCESS { throw MachError(result: result) } } @@ -105,10 +136,114 @@ extension MemoryReader { @_spi(MemoryReaders) public struct LocalMemoryReader: MemoryReader { public typealias Address = UInt64 + public typealias Size = UInt64 + + public func fetch(from address: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let reader = RemoteMemoryReader(task: mach_task_self()) + return try reader.fetch(from: address, into: buffer) + } +} +#endif + +#if os(Linux) +@_spi(MemoryReaders) public struct POSIXError: Error { + var errno: CInt +} + +@_spi(MemoryReaders) public struct MemserverError: Error { + var message: String +} + +@_spi(MemoryReaders) public struct MemserverMemoryReader: MemoryReader { + private var fd: CInt + + public init(fd: CInt) { + self.fd = fd + } + + private func sendRequest(for bytes: Size, from addr: Address) throws { + var request = memserver_req(addr: addr, len: bytes) + try withUnsafeBytes(of: &request){ ptr in + let ret = write(fd, ptr.baseAddress, ptr.count) + if ret < 0 || ret != ptr.count { + throw POSIXError(errno: _swift_get_errno()) + } + } + } + + private func receiveReply() throws -> memserver_resp { + var response = memserver_resp(addr: 0, len: 0) + try withUnsafeMutableBytes(of: &response){ ptr in + let ret = read(fd, ptr.baseAddress, ptr.count) + if ret < 0 || ret != ptr.count { + throw POSIXError(errno: _swift_get_errno()) + } + } + return response + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try buffer.withMemoryRebound(to: UInt8.self) { bytes in + try sendRequest(for: Size(bytes.count), from: addr) + + var done = 0 + while done < bytes.count { + let reply = try receiveReply() + + if reply.len < 0 { + throw MemserverError(message: "Unreadable at \(hex(addr))") + } + + if done + Int(reply.len) > bytes.count { + throw MemserverError(message: "Overrun at \(hex(addr)) trying to read \(bytes.count) bytes") + } + + let ret = read(fd, + bytes.baseAddress!.advanced(by: done), + Int(reply.len)) + + if ret < 0 || ret != reply.len { + throw POSIXError(errno: _swift_get_errno()) + } + + done += Int(reply.len) + } + } + } +} + +@_spi(MemoryReaders) public struct RemoteMemoryReader: MemoryReader { + private var pid: pid_t + + public init(pid: Any) { + self.pid = pid as! pid_t + } + + public func fetch(from address: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let size = size_t(MemoryLayout.stride * buffer.count) + var fromIOVec = iovec(iov_base: UnsafeMutableRawPointer( + bitPattern: UInt(address)), + iov_len: size) + var toIOVec = iovec(iov_base: buffer.baseAddress, iov_len: size) + let result = process_vm_readv(pid, &toIOVec, 1, &fromIOVec, 1, 0) + if result != size { + throw POSIXError(errno: _swift_get_errno()) + } + } +} + +@_spi(MemoryReaders) public struct LocalMemoryReader: MemoryReader { + private var reader: RemoteMemoryReader + + init() { + reader = RemoteMemoryReader(pid: getpid()) + } public func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws { - let reader = RemoteMemoryReader(task: _swift_backtrace_task_self()) return try reader.fetch(from: address, into: buffer) } } diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index 72d827c156744..bc5e7c4f5aaba 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -17,12 +17,12 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import OS.Darwin +#endif -@_silgen_name("_swift_isThunkFunction") -func _swift_isThunkFunction( - _ rawName: UnsafePointer? -) -> CBool +@_implementationOnly import OS.Libc +@_implementationOnly import Runtime /// A symbolicated backtrace public struct SymbolicatedBacktrace: CustomStringConvertible { @@ -84,15 +84,20 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } /// A textual description of this frame. - public var description: String { + public func description(width: Int) -> String { if let symbol = symbol { let isInlined = inlined ? " [inlined]" : "" let isThunk = isSwiftThunk ? " [thunk]" : "" - return "\(captured)\(isInlined)\(isThunk) \(symbol)" + return "\(captured.description(width: width))\(isInlined)\(isThunk) \(symbol)" } else { - return captured.description + return captured.description(width: width) } } + + /// A textual description of this frame. + public var description: String { + return description(width: MemoryLayout.size * 2) + } } /// Represents a symbol we've located @@ -136,7 +141,23 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// True if this symbol is a Swift thunk function. public var isSwiftThunk: Bool { - return _swift_isThunkFunction(rawName) + return _swift_backtrace_isThunkFunction(rawName) + } + + private func maybeUnderscore(_ sym: String) -> String { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + return "_" + sym + #else + return sym + #endif + } + + private func dylibName(_ dylib: String) -> String { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + return dylib + ".dylib" + #else + return dylib + ".so" + #endif } /// True if this symbol represents a system function. @@ -148,20 +169,21 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { if rawName == "start" && imageName == "dyld" { return true } + #endif if rawName.hasSuffix("5$mainyyFZ") || rawName.hasSuffix("5$mainyyYaFZTQ0_") - || rawName == "_async_MainTQ0_" { + || rawName == maybeUnderscore("async_MainTQ0_") { return true } - if rawName == "__ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE" && imageName == "libswift_Concurrency.dylib" { + if rawName == maybeUnderscore("_ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE") && imageName == dylibName("libswift_Concurrency") { return true } if let location = sourceLocation, - location.line == 0 && location.column == 0 - && !_swift_isThunkFunction(rawName) { + ((location.line == 0 && location.column == 0) + || location.path.hasSuffix("")) + && !_swift_backtrace_isThunkFunction(rawName) { return true } - #endif return false } @@ -177,9 +199,24 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// Demangle the raw name, if possible. private func demangleRawName() -> String { - // We don't actually need this function on macOS because we're using - // CoreSymbolication, which demangles the name when it does the lookup - // anyway. We will need it for Linux and Windows though. + var length: size_t = 0 + var status: CInt = 0 + if let demangled = _swift_backtrace_demangle(rawName, rawName.utf8.count, + nil, &length, &status) { + defer { free(demangled) } + + // length is the size of the buffer that was allocated, *not* the + // length of the string. + let stringLen = strlen(demangled) + if stringLen > 0 { + return demangled.withMemoryRebound(to: UInt8.self, + capacity: stringLen) { + let demangledBytes = UnsafeBufferPointer(start: $0, + count: stringLen) + return String(decoding: demangledBytes, as: UTF8.self) + } + } + } return rawName } @@ -206,6 +243,11 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } } + /// The width, in bits, of an address in this backtrace. + public var addressWidth: Int { + return backtrace.addressWidth + } + /// A list of captured frame information. public var frames: [Frame] @@ -246,8 +288,8 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) /// Convert a build ID to a CFUUIDBytes. private static func uuidBytesFromBuildID(_ buildID: [UInt8]) - -> __swift_backtrace_CFUUIDBytes { - return withUnsafeTemporaryAllocation(of: __swift_backtrace_CFUUIDBytes.self, + -> CFUUIDBytes { + return withUnsafeTemporaryAllocation(of: CFUUIDBytes.self, capacity: 1) { buf in buf.withMemoryRebound(to: UInt8.self) { _ = $0.initialize(from: buildID) @@ -263,15 +305,15 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { fn: (CSSymbolicatorRef) throws -> T) rethrows -> T { let binaryImageList = images.map{ image in BinaryImageInformation( - base: __swift_vm_address_t(image.baseAddress), - extent: __swift_vm_address_t(image.endOfText), + base: vm_address_t(image.baseAddress), + extent: vm_address_t(image.endOfText), uuid: uuidBytesFromBuildID(image.buildID!), arch: HostContext.coreSymbolicationArchitecture, path: image.path, relocations: [ BinaryRelocationInformation( - base: __swift_vm_address_t(image.baseAddress), - extent: __swift_vm_address_t(image.endOfText), + base: vm_address_t(image.baseAddress), + extent: vm_address_t(image.endOfText), name: "__TEXT" ) ], @@ -336,7 +378,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { let theSymbol = Symbol(imageIndex: imageIndex, imageName: imageName, rawName: rawName, - offset: Int(address - UInt(range.location)), + offset: Int(address - UInt64(range.location)), sourceLocation: location) theSymbol.name = name @@ -381,7 +423,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { case .omittedFrames(_), .truncated: frames.append(Frame(captured: frame, symbol: nil)) default: - let address = __swift_vm_address_t(frame.adjustedProgramCounter) + let address = vm_address_t(frame.adjustedProgramCounter) let owner = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, address, @@ -423,6 +465,101 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } } } + #elseif os(Linux) + var elf32Cache: [Int:Elf32Image] = [:] + var elf64Cache: [Int:Elf64Image] = [:] + + // This could be more efficient; at the moment we execute the line + // number programs once per frame, whereas we could just run them once + // for all the addresses we're interested in. + + for frame in backtrace.frames { + let address = FileImageSource.Address(frame.adjustedProgramCounter) + if let imageNdx = theImages.firstIndex( + where: { address >= $0.baseAddress + && address < $0.endOfText } + ) { + let relativeAddress = address - FileImageSource.Address(theImages[imageNdx].baseAddress) + var symbol: Symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: "", + offset: 0, + sourceLocation: nil) + var elf32Image = elf32Cache[imageNdx] + var elf64Image = elf64Cache[imageNdx] + + if elf32Image == nil && elf64Image == nil { + if let source = try? FileImageSource(path: theImages[imageNdx].path) { + if let elfImage = try? Elf32Image(source: source) { + elf32Image = elfImage + elf32Cache[imageNdx] = elfImage + } else if let elfImage = try? Elf64Image(source: source) { + elf64Image = elfImage + elf64Cache[imageNdx] = elfImage + } + } + } + + if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) { + var location = try? elf32Image!.sourceLocation(for: relativeAddress) + + for inline in elf32Image!.inlineCallSites(at: relativeAddress) { + let fakeSymbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: inline.rawName ?? "", + offset: 0, + sourceLocation: location) + frames.append(Frame(captured: frame, + symbol: fakeSymbol, + inlined: true)) + + location = SourceLocation(path: inline.filename, + line: inline.line, + column: inline.column) + } + + symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: theSymbol.name, + offset: theSymbol.offset, + sourceLocation: location) + } else if let theSymbol = elf64Image?.lookupSymbol(address: relativeAddress) { + var location = try? elf64Image!.sourceLocation(for: relativeAddress) + + for inline in elf64Image!.inlineCallSites(at: relativeAddress) { + let fakeSymbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: inline.rawName ?? "", + offset: 0, + sourceLocation: location) + frames.append(Frame(captured: frame, + symbol: fakeSymbol, + inlined: true)) + + location = SourceLocation(path: inline.filename, + line: inline.line, + column: inline.column) + } + + symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: theSymbol.name, + offset: theSymbol.offset, + sourceLocation: location) + } else { + symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: "", + offset: 0, + sourceLocation: nil) + } + + frames.append(Frame(captured: frame, symbol: symbol)) + continue + } + + frames.append(Frame(captured: frame, symbol: nil)) + } #else frames = backtrace.frames.map{ Frame(captured: $0, symbol: nil) } #endif @@ -436,10 +573,11 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// Provide a textual version of the backtrace. public var description: String { var lines: [String] = [] + let addressChars = (backtrace.addressWidth + 3) / 4 var n = 0 for frame in frames { - lines.append("\(n)\t\(frame)") + lines.append("\(n)\t\(frame.description(width: addressChars))") switch frame.captured { case let .omittedFrames(count): n += count @@ -452,7 +590,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { lines.append("Images:") lines.append("") for (n, image) in images.enumerated() { - lines.append("\(n)\t\(image)") + lines.append("\(n)\t\(image.description(width: addressChars))") } if let sharedCacheInfo = sharedCacheInfo { @@ -460,7 +598,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { lines.append("Shared Cache:") lines.append("") lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") - lines.append(" Base: \(hex(sharedCacheInfo.baseAddress))") + lines.append(" Base: \(hex(sharedCacheInfo.baseAddress, width: addressChars))") lines.append(" Active: \(!sharedCacheInfo.noCache)") } diff --git a/stdlib/public/Backtracing/Utils.swift b/stdlib/public/Backtracing/Utils.swift index 5913f047c3843..7a5f033a45e0c 100644 --- a/stdlib/public/Backtracing/Utils.swift +++ b/stdlib/public/Backtracing/Utils.swift @@ -16,17 +16,74 @@ import Swift +@_implementationOnly import OS.Libc + internal func hex(_ value: T, - withPrefix: Bool = true) -> String { + prefix shouldPrefix: Bool = true, + width: Int = MemoryLayout.size * 2) + -> String { let digits = String(value, radix: 16) - let padTo = value.bitWidth / 4 - let padding = digits.count >= padTo ? "" : String(repeating: "0", - count: padTo - digits.count) - let prefix = withPrefix ? "0x" : "" + let padding = digits.count >= width ? "" : String(repeating: "0", + count: width - digits.count) + let prefix = shouldPrefix ? "0x" : "" return "\(prefix)\(padding)\(digits)" } internal func hex(_ bytes: [UInt8]) -> String { - return bytes.map{ hex($0, withPrefix: false) }.joined(separator: "") + return bytes.map{ hex($0, prefix: false) }.joined(separator: "") +} + +enum PadAlignment { + case left + case right +} + +func pad(_ value: T, _ width: Int, align: PadAlignment = .left) -> String { + let string = String(describing: value) + let padding = string.count >= width ? "" : String(repeating: " ", + count: width - string.count) + switch align { + case .left: + return string + padding + case .right: + return padding + string + } +} + +@_spi(Utils) +public func readString(from file: String) -> String? { + let fd = _swift_open(file, O_RDONLY, 0) + if fd < 0 { + return nil + } + defer { close(fd) } + + // Files in /proc are awkward; you can't get their length and then + // read the data in one chunk, because they have zero length and don't + // support seeking. + var bytes: [UInt8] = [] + withUnsafeTemporaryAllocation(of: UInt8.self, capacity: 4096) { buffer in + while true { + let bytesRead = read(fd, buffer.baseAddress, buffer.count) + if bytesRead <= 0 { + break + } + + bytes.append(contentsOf: buffer[0..(_ s: S) + -> S.SubSequence { + guard let firstNonWhitespace = s.firstIndex(where: { !$0.isWhitespace }) + else { + return "" + } + let lastNonWhitespace = s.lastIndex(where: { !$0.isWhitespace })! + return s[firstNonWhitespace...lastNonWhitespace] } diff --git a/stdlib/public/Backtracing/Win32Extras.cpp b/stdlib/public/Backtracing/Win32Extras.cpp new file mode 100644 index 0000000000000..3c45650d791b8 --- /dev/null +++ b/stdlib/public/Backtracing/Win32Extras.cpp @@ -0,0 +1,57 @@ +//===--- Win32Extras.cpp - Windows support functions ------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines some extra functions that aren't available in the OS or C library +// on Windows. +// +//===----------------------------------------------------------------------===// + +#ifdef _WIN32 + +#include + +#include "modules/OS/Libc.h" + +extern "C" ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset) { + HANDLE hFile = _get_osfhandle(fd); + OVERLAPPED ovl = {0}; + DWORD dwBytesRead = 0; + + ovl.Offset = (DWORD)offset; + ovl.OffsetHigh = (DWORD)(offset >> 32); + + if (!ReadFile(hFile, buf, (DWORD)count, &dwBytesRead, &ovl)) { + errno = EIO; + return -1; + } + + return dwBytesRead; +} + +extern "C" ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset) { + HANDLE hFile = _get_osfhandle(fd); + OVERLAPPED ovl = {0}; + DWORD dwBytesRead = 0; + + ovl.Offset = (DWORD)offset; + ovl.OffsetHigh = (DWORD)(offset >> 32); + + if (!WriteFile(hFile, buf, (DWORD)count, &dwBytesRead, &ovl)) { + errno = EIO; + return -1; + } + + return dwBytesRead; +} + +#endif // _WIN32 + diff --git a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt index e1dc26aa68f50..50db65252bc84 100644 --- a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt +++ b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt @@ -21,10 +21,15 @@ if(NOT SWIFT_BUILD_STDLIB) set(BUILD_STANDALONE TRUE) endif() +set(BACKTRACING_COMPILE_FLAGS + "-I${SWIFT_STDLIB_SOURCE_DIR}/public/Backtracing/modules" + "-Xcc;-I${SWIFT_SOURCE_DIR}/include" + "-Xcc;-I${CMAKE_BINARY_DIR}/include") + add_swift_target_executable(swift-backtrace BUILD_WITH_LIBEXEC main.swift AnsiColor.swift - Target.swift + TargetMacOS.swift Themes.swift Utils.swift @@ -35,7 +40,9 @@ add_swift_target_executable(swift-backtrace BUILD_WITH_LIBEXEC SWIFT_MODULE_DEPENDS_LINUX ${glibc} INSTALL_IN_COMPONENT libexec - COMPILE_FLAGS -parse-as-library + COMPILE_FLAGS + ${BACKTRACING_COMPILE_FLAGS} + -parse-as-library TARGET_SDKS OSX) diff --git a/stdlib/public/libexec/swift-backtrace/Target.swift b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift similarity index 96% rename from stdlib/public/libexec/swift-backtrace/Target.swift rename to stdlib/public/libexec/swift-backtrace/TargetMacOS.swift index 2a0e454f9174c..32bbc7bc26683 100644 --- a/stdlib/public/libexec/swift-backtrace/Target.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift @@ -1,4 +1,4 @@ -//===--- Target.swift - Represents a process we are inspecting ------------===// +//===--- TargetMacOS.swift - Represents a process we are inspecting -------===// // // This source file is part of the Swift.org open source project // @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// // // Defines `Target`, which represents the process we are inspecting. -// There are a lot of system specifics in this file! +// This is the macOS version. // //===----------------------------------------------------------------------===// @@ -25,7 +25,8 @@ import _Backtracing @_spi(Contexts) import _Backtracing @_spi(MemoryReaders) import _Backtracing -import _SwiftBacktracingShims +@_implementationOnly import Runtime +@_implementationOnly import OS.Darwin #if arch(x86_64) typealias MContext = darwin_x86_64_mcontext @@ -157,7 +158,7 @@ class Target { task = parentTask - reader = RemoteMemoryReader(task: __swift_task_t(task)) + reader = RemoteMemoryReader(task: task_t(task)) name = Self.getProcessName(pid: pid) @@ -251,6 +252,7 @@ class Target { guard let backtrace = try? Backtrace.capture(from: ctx, using: reader, + images: nil, limit: limit, top: top) else { print("swift-backtrace: unable to capture backtrace from context for thread \(ndx)", @@ -283,6 +285,7 @@ class Target { guard let backtrace = try? Backtrace.capture(from: context, using: reader, + images: nil, limit: limit, top: top) else { print("swift-backtrace: unable to capture backtrace from context for thread \(ndx)", diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index 104aa3be99237..273d712b1dd74 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -843,8 +843,8 @@ namespace backtrace { /// @param mangledName is the symbol name to be tested. /// /// @returns `true` if `mangledName` represents a thunk function. -SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool -_swift_isThunkFunction(const char *mangledName) { +SWIFT_RUNTIME_STDLIB_SPI bool +_swift_backtrace_isThunkFunction(const char *mangledName) { swift::Demangle::Context ctx; return ctx.isThunkSymbol(mangledName); From e3b8e51ab61bf837585a39e7b0687d4a929849e9 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 5 Jun 2023 22:35:07 +0100 Subject: [PATCH 09/17] [Backtracing][Linux] Temporarily add a default addressWidth. This is for compatibility, so that I can split up the PRs. We'll remove it in the next PR. rdar://110261712 --- .../Backtracing/BacktraceFormatter.swift | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/stdlib/public/Backtracing/BacktraceFormatter.swift b/stdlib/public/Backtracing/BacktraceFormatter.swift index 7f0abf15f637d..f475e0c500830 100644 --- a/stdlib/public/Backtracing/BacktraceFormatter.swift +++ b/stdlib/public/Backtracing/BacktraceFormatter.swift @@ -523,7 +523,7 @@ public struct BacktraceFormatter { /// /// @result An array of strings, one per column. public func formatColumns(frame: Backtrace.Frame, - addressWidth: Int, + addressWidth: Int = 64, index: Int? = nil) -> [String] { let pc: String var attrs: [String] = [] @@ -563,7 +563,7 @@ public struct BacktraceFormatter { /// /// @result An array of table rows. public func formatRows(frame: Backtrace.Frame, - addressWidth: Int, + addressWidth: Int = 64, index: Int? = nil) -> [TableRow] { return [.columns(formatColumns(frame: frame, addressWidth: addressWidth, @@ -578,7 +578,7 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(frame: Backtrace.Frame, - addressWidth: Int, + addressWidth: Int = 64, index: Int? = nil) -> String { let rows = formatRows(frame: frame, addressWidth: addressWidth, @@ -593,7 +593,7 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(frames: some Sequence, - addressWidth: Int) -> String { + addressWidth: Int = 64) -> String { var rows: [TableRow] = [] var n = 0 @@ -734,7 +734,7 @@ public struct BacktraceFormatter { /// /// @result An array of strings, one per column. public func formatColumns(frame: SymbolicatedBacktrace.Frame, - addressWidth: Int, + addressWidth: Int = 64, index: Int? = nil) -> [String] { let pc: String var attrs: [String] = [] @@ -851,7 +851,7 @@ public struct BacktraceFormatter { /// /// @result An array of table rows. public func formatRows(frame: SymbolicatedBacktrace.Frame, - addressWidth: Int, + addressWidth: Int = 64, index: Int? = nil, showSource: Bool = true) -> [TableRow] { let columns = formatColumns(frame: frame, @@ -880,7 +880,7 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(frame: SymbolicatedBacktrace.Frame, - addressWidth: Int, + addressWidth: Int = 64, index: Int? = nil, showSource: Bool = true) -> String { let rows = formatRows(frame: frame, addressWidth: addressWidth, @@ -902,7 +902,7 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(frames: some Sequence, - addressWidth: Int) -> String { + addressWidth: Int = 64) -> String { var rows: [TableRow] = [] var sourceLocationsShown = Set() @@ -982,7 +982,7 @@ public struct BacktraceFormatter { /// /// @result An array of strings, one per column. public func formatColumns(image: Backtrace.Image, - addressWidth: Int) -> [String] { + addressWidth: Int = 64) -> [String] { let addressRange = "\(hex(image.baseAddress, width: addressWidth))–\(hex(image.endOfText, width: addressWidth))" let buildID: String if let bytes = image.buildID { @@ -1011,7 +1011,7 @@ public struct BacktraceFormatter { /// /// @result A string containing the formatted data. public func format(images: some Sequence, - addressWidth: Int) -> String { + addressWidth: Int = 64) -> String { let rows = images.map{ TableRow.columns( formatColumns(image: $0, From 3e2df1f83dc48f04a628c1b2dd653f7218808b83 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 6 Jun 2023 17:18:56 +0100 Subject: [PATCH 10/17] [Backtracing][Linux] Work correctly in the presence of partial reads. There's a chance that pipes might perform a partial read; we should handle that case. rdar://110261712 --- stdlib/public/Backtracing/MemoryReader.swift | 63 ++++++++++++++++---- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/stdlib/public/Backtracing/MemoryReader.swift b/stdlib/public/Backtracing/MemoryReader.swift index 67adabb300dd2..b63461a1a6e16 100644 --- a/stdlib/public/Backtracing/MemoryReader.swift +++ b/stdlib/public/Backtracing/MemoryReader.swift @@ -162,12 +162,50 @@ extension MemoryReader { self.fd = fd } + private func safeRead(_ fd: CInt, _ buffer: UnsafeMutableRawBufferPointer) throws -> Int { + var done = 0 + while done < buffer.count { + var ret: ssize_t = 0 + repeat { + ret = read(fd, buffer.baseAddress! + done, buffer.count - done) + } while ret < 0 && _swift_get_errno() == EINTR + if ret < 0 { + throw POSIXError(errno: _swift_get_errno()) + } + if ret == 0 { + break + } + done += Int(ret) + } + + return done + } + + private func safeWrite(_ fd: CInt, _ buffer: UnsafeRawBufferPointer) throws -> Int { + var done = 0 + while done < buffer.count { + var ret: ssize_t = 0 + repeat { + ret = write(fd, buffer.baseAddress! + done, buffer.count - done) + } while ret < 0 && _swift_get_errno() == EINTR + if ret < 0 { + throw POSIXError(errno: _swift_get_errno()) + } + if ret == 0 { + break + } + done += Int(ret) + } + + return done + } + private func sendRequest(for bytes: Size, from addr: Address) throws { var request = memserver_req(addr: addr, len: bytes) try withUnsafeBytes(of: &request){ ptr in - let ret = write(fd, ptr.baseAddress, ptr.count) - if ret < 0 || ret != ptr.count { - throw POSIXError(errno: _swift_get_errno()) + let ret = try safeWrite(fd, ptr) + if ret != ptr.count { + throw MemserverError(message: "Channel closed prematurely") } } } @@ -175,9 +213,9 @@ extension MemoryReader { private func receiveReply() throws -> memserver_resp { var response = memserver_resp(addr: 0, len: 0) try withUnsafeMutableBytes(of: &response){ ptr in - let ret = read(fd, ptr.baseAddress, ptr.count) - if ret < 0 || ret != ptr.count { - throw POSIXError(errno: _swift_get_errno()) + let ret = try safeRead(fd, ptr) + if ret != ptr.count { + throw MemserverError(message: "Channel closed prematurely") } } return response @@ -185,7 +223,8 @@ extension MemoryReader { public func fetch(from addr: Address, into buffer: UnsafeMutableBufferPointer) throws { - try buffer.withMemoryRebound(to: UInt8.self) { bytes in + try buffer.withMemoryRebound(to: UInt8.self) { + let bytes = UnsafeMutableRawBufferPointer($0) try sendRequest(for: Size(bytes.count), from: addr) var done = 0 @@ -200,12 +239,12 @@ extension MemoryReader { throw MemserverError(message: "Overrun at \(hex(addr)) trying to read \(bytes.count) bytes") } - let ret = read(fd, - bytes.baseAddress!.advanced(by: done), - Int(reply.len)) + let ret = try safeRead(fd, + UnsafeMutableRawBufferPointer( + rebasing: bytes[done.. Date: Wed, 7 Jun 2023 08:18:06 +0100 Subject: [PATCH 11/17] [Backtracing][Linux] Remove status argument. The `status` argument to the `_swift_backtrace_demangle()` function isn't especially useful, won't match behaviour on Windows, and we actually don't use it in the Swift code that calls this SPI. Remove it. rdar://110261712 --- include/swift/Runtime/Backtrace.h | 4 +--- stdlib/public/Backtracing/SymbolicatedBacktrace.swift | 3 +-- stdlib/public/runtime/Backtrace.cpp | 10 +++------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/include/swift/Runtime/Backtrace.h b/include/swift/Runtime/Backtrace.h index 4ce219f778eb6..5cc468d1f0e19 100644 --- a/include/swift/Runtime/Backtrace.h +++ b/include/swift/Runtime/Backtrace.h @@ -146,7 +146,6 @@ bool _swift_backtrace_isThunkFunction(const char *mangledName); /// @param outputBuffer is a pointer to a buffer in which to place the result. /// @param outputBufferSize points to a variable that contains the size of the /// output buffer. -/// @param status returns the status codes defined in the C++ ABI. /// /// If outputBuffer is nullptr, the function will allocate memory for the /// result using malloc(). In this case, outputBufferSize may be nullptr; @@ -168,8 +167,7 @@ SWIFT_RUNTIME_STDLIB_SPI char *_swift_backtrace_demangle(const char *mangledName, size_t mangledNameLength, char *outputBuffer, - size_t *outputBufferSize, - int *status); + size_t *outputBufferSize); #ifdef __cplusplus } // namespace backtrace } // namespace runtime diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index bc5e7c4f5aaba..2e7979e426234 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -200,9 +200,8 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// Demangle the raw name, if possible. private func demangleRawName() -> String { var length: size_t = 0 - var status: CInt = 0 if let demangled = _swift_backtrace_demangle(rawName, rawName.utf8.count, - nil, &length, &status) { + nil, &length) { defer { free(demangled) } // length is the size of the buffer that was allocated, *not* the diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index 273d712b1dd74..f0ee103d61ca8 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -855,8 +855,7 @@ SWIFT_RUNTIME_STDLIB_SPI char * _swift_backtrace_demangle(const char *mangledName, size_t mangledNameLength, char *outputBuffer, - size_t *outputBufferSize, - int *status) { + size_t *outputBufferSize) { llvm::StringRef name = llvm::StringRef(mangledName, mangledNameLength); // You must provide buffer size if you're providing your own output buffer @@ -884,13 +883,13 @@ _swift_backtrace_demangle(const char *mangledName, ::memcpy(outputBuffer, result.data(), toCopy); outputBuffer[toCopy] = '\0'; - *status = 0; return outputBuffer; #ifndef _WIN32 } else if (name.startswith("_Z")) { // Try C++ size_t resultLen; - char *result = abi::__cxa_demangle(mangledName, nullptr, &resultLen, status); + int status = 0; + char *result = abi::__cxa_demangle(mangledName, nullptr, &resultLen, &status); if (result) { size_t bufferSize; @@ -910,15 +909,12 @@ _swift_backtrace_demangle(const char *mangledName, free(result); - *status = 0; return outputBuffer; } #else // On Windows, the mangling is different. // ###TODO: Call __unDName() #endif - } else { - *status = -2; } return nullptr; From 3791b47a7b0637c093f5d3fcd39f878289f940bb Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 7 Jun 2023 08:22:40 +0100 Subject: [PATCH 12/17] [Backtracing][Linux] Add a comment about __cxa_demangle. `__cxa_demangle()` is a rather unusual API; one of its "features" is that the pointer you pass in must either be `nullptr`, in which case it will call `malloc()` itself, _or_ it has to be a pointer to a block of memory allocated with `malloc()`, because `__cxa_demangle()` may `realloc()` it for you. This seems to me to be something of a non-obvious footgun, so we never pass the caller's pointer through to `__cxa_demangle()`, which lets them decide how they want to allocate space. rdar://110261712 --- stdlib/public/runtime/Backtrace.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index f0ee103d61ca8..8552724864e73 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -886,7 +886,10 @@ _swift_backtrace_demangle(const char *mangledName, return outputBuffer; #ifndef _WIN32 } else if (name.startswith("_Z")) { - // Try C++ + // Try C++; note that we don't want to force callers to use malloc() to + // allocate their buffer, which is a requirement for __cxa_demangle + // because it may call realloc() on the incoming pointer. As a result, + // we never pass the caller's buffer to __cxa_demangle. size_t resultLen; int status = 0; char *result = abi::__cxa_demangle(mangledName, nullptr, &resultLen, &status); From b664339e98b755dbaf2c7ee77a3d74d032a068a0 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 7 Jun 2023 09:02:12 +0100 Subject: [PATCH 13/17] [Backtracing][Linux] Also remove the status argument from Runtime module. There's a separate declaration here because we can't include the `Backtrace.h` header from inside `modules/Runtime/Runtime.h`. rdar://110261712 --- stdlib/public/Backtracing/modules/Runtime/Runtime.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stdlib/public/Backtracing/modules/Runtime/Runtime.h b/stdlib/public/Backtracing/modules/Runtime/Runtime.h index 78537b2497b35..6cd602419efdd 100644 --- a/stdlib/public/Backtracing/modules/Runtime/Runtime.h +++ b/stdlib/public/Backtracing/modules/Runtime/Runtime.h @@ -32,7 +32,6 @@ bool _swift_backtrace_isThunkFunction(const char *rawName); char *_swift_backtrace_demangle(const char *rawName, size_t rawNameLength, char *outputBuffer, - size_t *outputBufferSize, - int *status); + size_t *outputBufferSize); #endif // SWIFT_BACKTRACING_RUNTIME_H From b25a04c376d35c8b7d36f38e3eb23db750bceef5 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 6 Jun 2023 11:04:06 +0100 Subject: [PATCH 14/17] [Backtracing][Linux][Tests] Fix verify_all_overlays test. The `_Backtracing` module has a number of private implementation only imports that aren't used outside of the module and that don't require any additional libraries (hence they aren't relevant to the outside world). `verify_all_overlays.py` needs to know about these when it does its test, because it loadas the module as the main module, which results in implementation only imports being required instead of ignored. rdar://110261712 --- validation-test/SIL/verify_all_overlays.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/validation-test/SIL/verify_all_overlays.py b/validation-test/SIL/verify_all_overlays.py index 3d68f7d6301d9..d4b6a15083db8 100755 --- a/validation-test/SIL/verify_all_overlays.py +++ b/validation-test/SIL/verify_all_overlays.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # RUN: ${python} %s %target-swiftmodule-name %platform-sdk-overlay-dir \ +# RUN: %swift_src_root \ # RUN: %target-sil-opt -sdk %sdk -enable-sil-verify-all \ # RUN: -F %sdk/System/Library/PrivateFrameworks \ # RUN: -F "%xcode-extra-frameworks-dir" @@ -14,9 +15,11 @@ target_swiftmodule_name = sys.argv[1] sdk_overlay_dir = sys.argv[2] -sil_opt_invocation = sys.argv[3:] +source_dir = sys.argv[3] +sil_opt_invocation = sys.argv[4:] for module_file in os.listdir(sdk_overlay_dir): + extra_args = [] module_name, ext = os.path.splitext(module_file) if ext != ".swiftmodule": continue @@ -33,6 +36,12 @@ # TODO: fix the DifferentiationUnittest module. if module_name == "DifferentiationUnittest": continue + # Backtracing needs its own additional modules in the module path + if module_name == "_Backtracing": + extra_args = ["-I", os.path.join(source_dir, "stdlib", + "public", "Backtracing", "modules"), + "-I", os.path.join(source_dir, "include")] + print("# " + module_name) module_path = os.path.join(sdk_overlay_dir, module_file) @@ -50,4 +59,5 @@ # We are deliberately discarding the output here; we're just making sure # it can be generated. subprocess.check_output(sil_opt_invocation + - [module_path, "-module-name", module_name]) + [module_path, "-module-name", module_name] + + extra_args) From 7cc2446960bc87286c7f4a3ca57be2b2a3a5d44f Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 5 Jun 2023 16:16:24 +0100 Subject: [PATCH 15/17] [Backtracing][Linux] Add Linux support to swift-backtrace. We need a Linux specific `Target` implementation, and a couple of minor tweaks to make things build everywhere. rdar://110262673 --- .../libexec/swift-backtrace/CMakeLists.txt | 3 +- .../libexec/swift-backtrace/TargetLinux.swift | 232 ++++++++++++++++++ .../libexec/swift-backtrace/Utils.swift | 6 +- .../public/libexec/swift-backtrace/main.swift | 40 ++- 4 files changed, 266 insertions(+), 15 deletions(-) create mode 100644 stdlib/public/libexec/swift-backtrace/TargetLinux.swift diff --git a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt index 50db65252bc84..10c8d6a9d3d70 100644 --- a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt +++ b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt @@ -30,6 +30,7 @@ add_swift_target_executable(swift-backtrace BUILD_WITH_LIBEXEC main.swift AnsiColor.swift TargetMacOS.swift + TargetLinux.swift Themes.swift Utils.swift @@ -44,5 +45,5 @@ add_swift_target_executable(swift-backtrace BUILD_WITH_LIBEXEC ${BACKTRACING_COMPILE_FLAGS} -parse-as-library - TARGET_SDKS OSX) + TARGET_SDKS OSX LINUX) diff --git a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift new file mode 100644 index 0000000000000..c66435a864e22 --- /dev/null +++ b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift @@ -0,0 +1,232 @@ +//===--- TargetLinux.swift - Represents a process we are inspecting -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Defines `Target`, which represents the process we are inspecting. +// This is the Linux version. +// +//===----------------------------------------------------------------------===// + +#if os(Linux) + +import Glibc + +import _Backtracing +@_spi(Internal) import _Backtracing +@_spi(Contexts) import _Backtracing +@_spi(MemoryReaders) import _Backtracing +@_spi(Utils) import _Backtracing + +@_implementationOnly import Runtime + +struct TargetThread { + typealias ThreadID = pid_t + + var id: ThreadID + var context: HostContext? + var name: String + var backtrace: SymbolicatedBacktrace +} + +class Target { + typealias Address = UInt64 + + var pid: pid_t + var name: String + var signal: UInt64 + var faultAddress: Address + var crashingThread: TargetThread.ThreadID + + var images: [Backtrace.Image] = [] + + var threads: [TargetThread] = [] + var crashingThreadNdx: Int = -1 + + var signalName: String { + switch signal { + case UInt64(SIGQUIT): return "SIGQUIT" + case UInt64(SIGABRT): return "SIGABRT" + case UInt64(SIGBUS): return "SIGBUS" + case UInt64(SIGFPE): return "SIGFPE" + case UInt64(SIGILL): return "SIGILL" + case UInt64(SIGSEGV): return "SIGSEGV" + case UInt64(SIGTRAP): return "SIGTRAP" + default: return "\(signal)" + } + } + + var signalDescription: String { + switch signal { + case UInt64(SIGQUIT): return "Terminated" + case UInt64(SIGABRT): return "Aborted" + case UInt64(SIGBUS): return "Bus error" + case UInt64(SIGFPE): return "Floating point exception" + case UInt64(SIGILL): return "Illegal instruction" + case UInt64(SIGSEGV): return "Bad pointer dereference" + case UInt64(SIGTRAP): return "System trap" + default: + return "Signal \(signal)" + } + } + + var reader: MemserverMemoryReader + + // Get the name of a process + private static func getProcessName(pid: pid_t) -> String { + let path = "/proc/\(pid)/comm" + guard let name = readString(from: path) else { + return "" + } + return String(stripWhitespace(name)) + } + + /// Get the name of a thread + private func getThreadName(tid: Int64) -> String { + let path = "/proc/\(pid)/task/\(tid)/comm" + guard let name = readString(from: path) else { + return "" + } + let trimmed = String(stripWhitespace(name)) + + // Allow the main thread to use the process' name, but other + // threads will have an empty name unless they've set the name + // explicitly + if trimmed == self.name && pid != tid { + return "" + } + return trimmed + } + + init(crashInfoAddr: UInt64, limit: Int?, top: Int, cache: Bool) { + // fd #4 is reserved for the memory server + let memserverFd: CInt = 4 + + pid = getppid() + reader = MemserverMemoryReader(fd: memserverFd) + name = Self.getProcessName(pid: pid) + + let crashInfo: CrashInfo + do { + crashInfo = try reader.fetch(from: crashInfoAddr, as: CrashInfo.self) + } catch { + print("swift-backtrace: unable to fetch crash info.") + exit(1) + } + + crashingThread = TargetThread.ThreadID(crashInfo.crashing_thread) + signal = crashInfo.signal + faultAddress = crashInfo.fault_address + + images = Backtrace.captureImages(using: reader, + forProcess: Int(pid)) + + do { + try fetchThreads(threadListHead: Address(crashInfo.thread_list), + limit: limit, top: top, cache: cache) + } catch { + print("swift-backtrace: failed to fetch thread information: \(error)") + exit(1) + } + } + + /// Fetch information about all of the process's threads; the crash_info + /// structure contains a linked list of thread ucontexts, which may not + /// include every thread. In particular, if a thread was stuck in an + /// uninterruptible wait, we won't have a ucontext for it. + func fetchThreads( + threadListHead: Address, + limit: Int?, top: Int, cache: Bool + ) throws { + var next = threadListHead + + while next != 0 { + let t = try reader.fetch(from: next, as: thread.self) + + next = t.next + + guard let ucontext + = try? reader.fetch(from: t.uctx, as: ucontext_t.self) else { + // This can happen if a thread is in an uninterruptible wait + continue + } + + let context = HostContext.fromHostMContext(ucontext.uc_mcontext) + let backtrace = try Backtrace.capture(from: context, + using: reader, + images: images, + limit: limit, + top: top) + guard let symbolicated + = backtrace.symbolicated(with: images, + sharedCacheInfo: nil, + useSymbolCache: cache) else { + print("unable to symbolicate backtrace for thread \(t.tid)") + exit(1) + } + + threads.append(TargetThread(id: TargetThread.ThreadID(t.tid), + context: context, + name: getThreadName(tid: t.tid), + backtrace: symbolicated)) + } + + // Sort the threads by thread ID; the main thread always sorts + // lower than any other. + threads.sort { + return $0.id == pid || ($1.id != pid && $0.id < $1.id) + } + + // Find the crashing thread index + if let ndx = threads.firstIndex(where: { $0.id == crashingThread }) { + crashingThreadNdx = ndx + } else { + print("unable to find the crashing thread") + exit(1) + } + } + + public func redoBacktraces(limit: Int?, top: Int, cache: Bool) { + for (ndx, thread) in threads.enumerated() { + guard let context = thread.context else { + continue + } + + guard let backtrace = try? Backtrace.capture(from: context, + using: reader, + images: images, + limit: limit, + top: top) else { + print("unable to capture backtrace from context for thread \(ndx)") + continue + } + + guard let symbolicated = backtrace.symbolicated(with: images, + sharedCacheInfo: nil, + useSymbolCache: cache) else { + print("unable to symbolicate backtrace from context for thread \(ndx)") + continue + } + + threads[ndx].backtrace = symbolicated + } + } + + public func withDebugger(_ body: () -> ()) throws { + print(""" + From another shell, please run + + lldb --attach-pid \(pid) -o c + """) + body() + } +} + +#endif // os(Linux) diff --git a/stdlib/public/libexec/swift-backtrace/Utils.swift b/stdlib/public/libexec/swift-backtrace/Utils.swift index a17b765df16a5..9d104a857f174 100644 --- a/stdlib/public/libexec/swift-backtrace/Utils.swift +++ b/stdlib/public/libexec/swift-backtrace/Utils.swift @@ -51,7 +51,7 @@ internal func parseUInt64(_ s: S) -> UInt64? { } } -#if os(macOS) +#if os(macOS) || os(Linux) struct PosixError: Error { var errno: Int32 @@ -139,6 +139,8 @@ internal func spawn(_ path: String, args: [String]) throws { } } +#endif // os(macOS) + struct CFileStream: TextOutputStream { var fp: UnsafeMutablePointer @@ -153,5 +155,3 @@ struct CFileStream: TextOutputStream { var standardOutput = CFileStream(fp: stdout) var standardError = CFileStream(fp: stderr) - -#endif // os(macOS) diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift index f31576ab42f86..038c44552e72c 100644 --- a/stdlib/public/libexec/swift-backtrace/main.swift +++ b/stdlib/public/libexec/swift-backtrace/main.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if os(macOS) +#if (os(macOS) || os(Linux)) && (arch(x86_64) || arch(arm64)) #if canImport(Darwin) import Darwin.C @@ -524,7 +524,13 @@ Generate a backtrace for the parent process. tcgetattr(0, &oldAttrs) var newAttrs = oldAttrs - newAttrs.c_lflag &= ~(UInt(ICANON) | UInt(ECHO)) + + #if os(Linux) + newAttrs.c_lflag &= ~UInt32(ICANON | ECHO) + #else + newAttrs.c_lflag &= ~UInt(ICANON | ECHO) + #endif + tcsetattr(0, TCSANOW, &newAttrs) return oldAttrs @@ -581,7 +587,7 @@ Generate a backtrace for the parent process. static func backtraceFormatter() -> BacktraceFormatter { var terminalSize = winsize(ws_row: 24, ws_col: 80, ws_xpixel: 1024, ws_ypixel: 768) - _ = ioctl(0, TIOCGWINSZ, &terminalSize) + _ = ioctl(0, CUnsignedLong(TIOCGWINSZ), &terminalSize) return BacktraceFormatter(formattingOptions .theme(theme) @@ -607,7 +613,6 @@ Generate a backtrace for the parent process. writeln("") writeln(theme.crashReason(description)) - writeln("") var mentionedImages = Set() let formatter = backtraceFormatter() @@ -615,7 +620,7 @@ Generate a backtrace for the parent process. func dump(ndx: Int, thread: TargetThread) { let crashed = thread.id == target.crashingThread ? " crashed" : "" let name = !thread.name.isEmpty ? " \"\(thread.name)\"" : "" - writeln("Thread \(ndx)\(name)\(crashed):\n") + writeln("\nThread \(ndx)\(name)\(crashed):\n") if args.registers! == .all { if let context = thread.context { @@ -660,6 +665,7 @@ Generate a backtrace for the parent process. } } + let addressWidthInChars = (crashingThread.backtrace.addressWidth + 3) / 4 switch args.showImages! { case .none: break @@ -671,10 +677,12 @@ Generate a backtrace for the parent process. } else { writeln("\n\nImages:\n") } - writeln(formatter.format(images: images)) + writeln(formatter.format(images: images, + addressWidth: addressWidthInChars)) case .all: writeln("\n\nImages:\n") - writeln(formatter.format(images: target.images)) + writeln(formatter.format(images: target.images, + addressWidth: addressWidthInChars)) } } @@ -750,11 +758,14 @@ Generate a backtrace for the parent process. let name = thread.name.isEmpty ? "" : " \(thread.name)" writeln("Thread \(currentThread) id=\(thread.id)\(name)\(crashed)\n") + let addressWidthInChars = (backtrace.addressWidth + 3) / 4 + if let frame = backtrace.frames.drop(while: { $0.isSwiftRuntimeFailure }).first { let formatter = backtraceFormatter() - let formatted = formatter.format(frame: frame) + let formatted = formatter.format(frame: frame, + addressWidth: addressWidthInChars) writeln("\(formatted)") } break @@ -809,6 +820,7 @@ Generate a backtrace for the parent process. var rows: [BacktraceFormatter.TableRow] = [] for (n, thread) in target.threads.enumerated() { let backtrace = thread.backtrace + let addressWidthInChars = (backtrace.addressWidth + 3) / 4 let crashed: String if n == target.crashingThreadNdx { @@ -827,7 +839,10 @@ Generate a backtrace for the parent process. $0.isSwiftRuntimeFailure }).first { - rows += formatter.formatRows(frame: frame).map{ row in + rows += formatter.formatRows( + frame: frame, + addressWidth: addressWidthInChars).map{ row in + switch row { case let .columns(columns): return .columns([ "", "" ] + columns) @@ -846,8 +861,11 @@ Generate a backtrace for the parent process. writeln(output) case "images": let formatter = backtraceFormatter() - let images = target.threads[currentThread].backtrace.images - let output = formatter.format(images: images) + let backtrace = target.threads[currentThread].backtrace + let images = backtrace.images + let addressWidthInChars = (backtrace.addressWidth + 3) / 4 + let output = formatter.format(images: images, + addressWidth: addressWidthInChars) writeln(output) case "set": From 937947747cb79138ea33bc51904014d8d89a1e66 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 5 Jun 2023 22:37:35 +0100 Subject: [PATCH 16/17] Revert "[Backtracing][Linux] Temporarily add a default addressWidth." This reverts commit 0d7c45d9dac68ca820baec816b8857070168136a. --- .../Backtracing/BacktraceFormatter.swift | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/stdlib/public/Backtracing/BacktraceFormatter.swift b/stdlib/public/Backtracing/BacktraceFormatter.swift index f475e0c500830..7f0abf15f637d 100644 --- a/stdlib/public/Backtracing/BacktraceFormatter.swift +++ b/stdlib/public/Backtracing/BacktraceFormatter.swift @@ -523,7 +523,7 @@ public struct BacktraceFormatter { /// /// @result An array of strings, one per column. public func formatColumns(frame: Backtrace.Frame, - addressWidth: Int = 64, + addressWidth: Int, index: Int? = nil) -> [String] { let pc: String var attrs: [String] = [] @@ -563,7 +563,7 @@ public struct BacktraceFormatter { /// /// @result An array of table rows. public func formatRows(frame: Backtrace.Frame, - addressWidth: Int = 64, + addressWidth: Int, index: Int? = nil) -> [TableRow] { return [.columns(formatColumns(frame: frame, addressWidth: addressWidth, @@ -578,7 +578,7 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(frame: Backtrace.Frame, - addressWidth: Int = 64, + addressWidth: Int, index: Int? = nil) -> String { let rows = formatRows(frame: frame, addressWidth: addressWidth, @@ -593,7 +593,7 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(frames: some Sequence, - addressWidth: Int = 64) -> String { + addressWidth: Int) -> String { var rows: [TableRow] = [] var n = 0 @@ -734,7 +734,7 @@ public struct BacktraceFormatter { /// /// @result An array of strings, one per column. public func formatColumns(frame: SymbolicatedBacktrace.Frame, - addressWidth: Int = 64, + addressWidth: Int, index: Int? = nil) -> [String] { let pc: String var attrs: [String] = [] @@ -851,7 +851,7 @@ public struct BacktraceFormatter { /// /// @result An array of table rows. public func formatRows(frame: SymbolicatedBacktrace.Frame, - addressWidth: Int = 64, + addressWidth: Int, index: Int? = nil, showSource: Bool = true) -> [TableRow] { let columns = formatColumns(frame: frame, @@ -880,7 +880,7 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(frame: SymbolicatedBacktrace.Frame, - addressWidth: Int = 64, + addressWidth: Int, index: Int? = nil, showSource: Bool = true) -> String { let rows = formatRows(frame: frame, addressWidth: addressWidth, @@ -902,7 +902,7 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(frames: some Sequence, - addressWidth: Int = 64) -> String { + addressWidth: Int) -> String { var rows: [TableRow] = [] var sourceLocationsShown = Set() @@ -982,7 +982,7 @@ public struct BacktraceFormatter { /// /// @result An array of strings, one per column. public func formatColumns(image: Backtrace.Image, - addressWidth: Int = 64) -> [String] { + addressWidth: Int) -> [String] { let addressRange = "\(hex(image.baseAddress, width: addressWidth))–\(hex(image.endOfText, width: addressWidth))" let buildID: String if let bytes = image.buildID { @@ -1011,7 +1011,7 @@ public struct BacktraceFormatter { /// /// @result A string containing the formatted data. public func format(images: some Sequence, - addressWidth: Int = 64) -> String { + addressWidth: Int) -> String { let rows = images.map{ TableRow.columns( formatColumns(image: $0, From f61e72cceea8bb80ad977d7202f7584277559404 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 6 Jun 2023 17:43:19 +0100 Subject: [PATCH 17/17] [Backtracing][Linux] Remove some stray "public" annotations. We don't need these here. They were a leftover from previous code. rdar://110262673 --- stdlib/public/libexec/swift-backtrace/TargetLinux.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift index c66435a864e22..f6dda1b37d895 100644 --- a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift @@ -193,7 +193,7 @@ class Target { } } - public func redoBacktraces(limit: Int?, top: Int, cache: Bool) { + func redoBacktraces(limit: Int?, top: Int, cache: Bool) { for (ndx, thread) in threads.enumerated() { guard let context = thread.context else { continue @@ -219,7 +219,7 @@ class Target { } } - public func withDebugger(_ body: () -> ()) throws { + func withDebugger(_ body: () -> ()) throws { print(""" From another shell, please run