Skip to content

Commit

Permalink
macos: fix lldb (#611)
Browse files Browse the repository at this point in the history
Debugging vere on macOS is quite something because as soon as we get a
single SIGSEGV lldb enters an infinite loop. The cause of that is a lldb
bug and the root cause of that is a macos kernel bug. Here we implement
the same workaround that Go does to make the debugger work.

https://bugs.llvm.org/show_bug.cgi?id=22868#c1
  • Loading branch information
pkova authored Apr 10, 2024
2 parents c9db2c2 + 3556e4b commit b199984
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 3 deletions.
14 changes: 14 additions & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@ This will take a few minutes.

After installing `automake`, `autoconf-archive`, `pkg-config`, and `libtool`, you're ready to build Vere.

#### Debugger

macOS is curious operating system because the kernel is derived from from two codebases, the Mach kernel and the BSD kernel. It inherits two different hardware exception handling facilities, Mach exceptions and POSIX signals. We use `libsigsegv` and utilize the POSIX signals which is usually fine except when it comes to debugging with `lldb`.

`lldb` hijacks the Mach exception ports for the task when it attaches to the process. Mach exceptions get handled before POSIX signals which means that as soon as vere faults (this happens often) `lldb` stop with a `EXC_BAD_ACCESS`. It is impossible to continue debugging from this state without the workaround we implemented in https://github.com/urbit/vere/pull/611.

There are more annoying warts with `lldb` currently. First, if you attach the debugger when booting the ship with `lldb -- your-ship/.run` you have to specify `-t`, otherwise the ship is unable to boot for mysterious reasons. The other option is to start the ship and attach afterwards with `lldb -p PID`. Afterwards you should do this dance:

```
p (void)darwin_register_mach_exception_handler()
pro hand -p true -s false -n false SIGBUS
pro hand -p true -s false -n false SIGSEGV
```

## Build Commands

Once you install the prerequisites, you're ready to build:
Expand Down
4 changes: 2 additions & 2 deletions bazel/common_settings.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ string_flag = rule(
def vere_library(copts = [], linkopts = [], **kwargs):
native.cc_library(
copts = copts + select({
"//:debug": ["-O0", "-g3", "-DC3DBG"],
"//:debug": ["-O0", "-g3", "-DC3DBG", "-fdebug-compilation-dir=."],
"//conditions:default": ["-O3"]
}) + select({
"//:lto": ['-flto'],
Expand All @@ -39,7 +39,7 @@ def vere_library(copts = [], linkopts = [], **kwargs):
def vere_binary(copts = [], linkopts = [], **kwargs):
native.cc_binary(
copts = copts + select({
"//:debug": ["-O0", "-g3", "-DC3DBG"],
"//:debug": ["-O0", "-g3", "-DC3DBG", "-fdebug-compilation-dir=."],
"//conditions:default": ["-O3"]
}) + select({
"//:lto": ['-flto'],
Expand Down
1 change: 1 addition & 0 deletions pkg/vere/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ vere_library(
"@platforms//os:macos": [
"platform/darwin/daemon.c",
"platform/darwin/ptty.c",
"platform/darwin/mach.c",
],
"@platforms//os:linux": [
"platform/linux/daemon.c",
Expand Down
5 changes: 4 additions & 1 deletion pkg/vere/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1140,7 +1140,6 @@ _cw_serf_commence(c3_i argc, c3_c* argv[])
fprintf(stderr, "serf: missing args\n");
exit(1);
}

// XX use named arguments and getopt

c3_d eve_d = 0;
Expand Down Expand Up @@ -2935,6 +2934,10 @@ main(c3_i argc,

_main_init();

#if defined(U3_OS_osx)
darwin_register_mach_exception_handler();
#endif

c3_c* bin_c = strdup(argv[0]);

// parse for subcommands
Expand Down
28 changes: 28 additions & 0 deletions pkg/vere/platform/darwin/mach.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "log.h"

#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/exception.h>
#include <mach/task.h>


// lldb does not listen to BSD signals, it only listens to Mach exceptions.
// The Mach exception EXC_BAD_ACCESS corresponds to SIGSEGV, but lldb has
// problems converting between them because of a longstanding macOS kernel bug.
// This means that without this workaround we cannot debug our binaries with
// lldb. The first segfault we hit causes an infinite loop in lldb no matter
// how many times you try to continue. This workaround is implemented in projects
// such as the Go runtime.
// See https://bugs.llvm.org/show_bug.cgi?id=22868#c1 for more details.

void darwin_register_mach_exception_handler() {
kern_return_t kr = task_set_exception_ports(
mach_task_self(),
EXC_MASK_BAD_ACCESS, // SIGSEGV
MACH_PORT_NULL,
EXCEPTION_STATE_IDENTITY,
MACHINE_THREAD_STATE);
if ( KERN_SUCCESS != kr) {
u3l_log("mach: unable to register exception handler");
}
}
7 changes: 7 additions & 0 deletions pkg/vere/vere.h
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,13 @@
void
u3_daemon_init();

#if defined(U3_OS_osx)
/* darwin_register_mach_exception_handler(): make lldb work
*/
void
darwin_register_mach_exception_handler();
#endif

/* u3_write_fd(): retry interrupts, continue partial writes, assert errors.
*/
void
Expand Down

0 comments on commit b199984

Please sign in to comment.