From 9cb9ebda313ad03dcfe29efd931ab94fdf6e02af Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 30 Jun 2025 14:21:27 +0100 Subject: [PATCH] [Backtracing][Linux] Fix crash handler for musl. Musl's `clone()` wrapper returns `EINVAL` if you try to use `CLONE_THREAD`, which seems a bit wrong (certainly it is in this particular application, since we *really* don't care whether the thread is a valid C library thread at this point). Also properly support ELF images that are built with a base address other than zero (this typically isn't an issue for dynamically linked programs, as they will be relocated at runtime anyway, but for statically linked binaries it's very common to set the base address to a non-zero value). rdar://154282813 --- stdlib/public/RuntimeModule/Elf.swift | 16 ++++++- .../RuntimeModule/SymbolicatedBacktrace.swift | 9 ++-- stdlib/public/runtime/CrashHandlerLinux.cpp | 47 ++++++++++++++----- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/stdlib/public/RuntimeModule/Elf.swift b/stdlib/public/RuntimeModule/Elf.swift index 006a62c456b79..d5b7d1573751e 100644 --- a/stdlib/public/RuntimeModule/Elf.swift +++ b/stdlib/public/RuntimeModule/Elf.swift @@ -1064,8 +1064,8 @@ struct ElfSymbolTable: ElfSymbolTableProtocol { continue } - // Ignore anything undefined - if symbol.st_shndx == SHN_UNDEF { + // Ignore anything undefined or absolute + if symbol.st_shndx == SHN_UNDEF || symbol.st_shndx == SHN_ABS { continue } @@ -1190,6 +1190,8 @@ final class ElfImage var sectionHeaders: [Traits.Shdr]? var shouldByteSwap: Bool { return header.shouldByteSwap } + var imageBase: ImageSource.Address + @_specialize(kind: full, where SomeElfTraits == Elf32Traits) @_specialize(kind: full, where SomeElfTraits == Elf64Traits) required init(source: ImageSource, @@ -1222,11 +1224,21 @@ final class ElfImage var phdrs: [Traits.Phdr] = [] var phAddr = ImageSource.Address(header.e_phoff) + var minAddr: Traits.Address? = nil for _ in 0.." var symbol: Symbol = Symbol(imageIndex: imageNdx, imageName: name, @@ -527,6 +524,9 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { if let hit = cache.lookup(path: theImages[imageNdx].path) { switch hit { case let .elf32Image(image): + let relativeAddress = ImageSource.Address( + address - theImages[imageNdx].baseAddress + ) + image.imageBase if let theSymbol = lookupSymbol(image: image, at: imageNdx, named: name, @@ -534,6 +534,9 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { symbol = theSymbol } case let .elf64Image(image): + let relativeAddress = ImageSource.Address( + address - theImages[imageNdx].baseAddress + ) + image.imageBase if let theSymbol = lookupSymbol(image: image, at: imageNdx, named: name, diff --git a/stdlib/public/runtime/CrashHandlerLinux.cpp b/stdlib/public/runtime/CrashHandlerLinux.cpp index 477d68bbf12b4..f8e0241be2c1e 100644 --- a/stdlib/public/runtime/CrashHandlerLinux.cpp +++ b/stdlib/public/runtime/CrashHandlerLinux.cpp @@ -49,6 +49,15 @@ #include #include +#define DEBUG_MEMSERVER 0 + +#if DEBUG_MEMSERVER +#include +#define memserver_error(x) perror(x) +#else +#define memserver_error(x) +#endif + #include "swift/Runtime/Backtrace.h" #include @@ -86,8 +95,8 @@ ssize_t safe_read(int fd, void *buf, size_t len) { ssize_t ret; do { ret = read(fd, buf, len); - } while (ret < 0 && errno == EINTR); - if (ret < 0) + } while (ret <= 0 && errno == EINTR); + if (ret <= 0) return ret; total += ret; ptr += ret; @@ -106,8 +115,8 @@ ssize_t safe_write(int fd, const void *buf, size_t len) { ssize_t ret; do { ret = write(fd, buf, len); - } while (ret < 0 && errno == EINTR); - if (ret < 0) + } while (ret <= 0 && errno == EINTR); + if (ret <= 0) return ret; total += ret; ptr += ret; @@ -657,20 +666,28 @@ memserver_start() int fds[2]; ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); - if (ret < 0) + if (ret < 0) { + memserver_error("memserver_start: socketpair failed"); 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, + #ifndef __musl__ + // Can't use CLONE_THREAD on musl because the clone() function + // there returns EINVAL if we do. + CLONE_THREAD | CLONE_SIGHAND | + #endif + CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_IO, #endif NULL); - if (ret < 0) + if (ret < 0) { + memserver_error("memserver_start: clone failed"); return ret; + } #if MEMSERVER_USE_PROCESS memserver_pid = ret; @@ -718,7 +735,7 @@ memserver_entry(void *dummy __attribute__((unused))) { int fd = memserver_fd; int result = 1; -#if MEMSERVER_USE_PROCESS +#if MEMSERVER_USE_PROCESS || defined(__musl__) prctl(PR_SET_NAME, "[backtrace]"); #endif @@ -743,8 +760,10 @@ memserver_entry(void *dummy __attribute__((unused))) { ssize_t ret; ret = safe_read(fd, &req, sizeof(req)); - if (ret != sizeof(req)) + if (ret != sizeof(req)) { + memserver_error("memserver: terminating because safe_read() returned wrong size"); break; + } uint64_t addr = req.addr; uint64_t bytes = req.len; @@ -761,15 +780,19 @@ memserver_entry(void *dummy __attribute__((unused))) { resp.len = ret; ret = safe_write(fd, &resp, sizeof(resp)); - if (ret != sizeof(resp)) + if (ret != sizeof(resp)) { + memserver_error("memserver: terminating because safe_write() failed"); goto fail; + } if (resp.len < 0) break; ret = safe_write(fd, memserver_buffer, resp.len); - if (ret != resp.len) + if (ret != resp.len) { + memserver_error("memserver: terminating because safe_write() failed (2)"); goto fail; + } addr += resp.len; bytes -= resp.len;