From 150305cc997e9b8e566219947722cad5807b877d 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/Backtracing/Elf.swift | 16 ++++++- .../Backtracing/SymbolicatedBacktrace.swift | 8 +++- stdlib/public/runtime/CrashHandlerLinux.cpp | 47 ++++++++++++++----- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index da8ff0d6d097f..3758bbbc99a53 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -875,8 +875,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 } @@ -999,6 +999,8 @@ class ElfImage= $0.baseAddress && address < $0.endOfText } ) { - let relativeAddress = address - FileImageSource.Address(theImages[imageNdx].baseAddress) var symbol: Symbol = Symbol(imageIndex: imageNdx, imageName: theImages[imageNdx].name, rawName: "", @@ -504,19 +503,26 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { sourceLocation: nil) var elf32Image = elf32Cache[imageNdx] var elf64Image = elf64Cache[imageNdx] + var imageBase = FileImageSource.Address(0) 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 + imageBase = elfImage.imageBase } else if let elfImage = try? Elf64Image(source: source) { elf64Image = elfImage elf64Cache[imageNdx] = elfImage + imageBase = elfImage.imageBase } } } + let relativeAddress = FileImageSource.Address( + address - theImages[imageNdx].baseAddress + ) + imageBase + if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) { var location: SourceLocation? 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;