[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
This commit is contained in:
Alastair Houghton
2025-06-30 14:21:27 +01:00
parent 9c7f0c7550
commit b498b39b79
3 changed files with 55 additions and 17 deletions

View File

@@ -49,6 +49,15 @@
#include <string.h>
#include <unistd.h>
#define DEBUG_MEMSERVER 0
#if DEBUG_MEMSERVER
#include <stdio.h>
#define memserver_error(x) perror(x)
#else
#define memserver_error(x)
#endif
#include "swift/Runtime/Backtrace.h"
#include <cstring>
@@ -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;