Skip to content

Instantly share code, notes, and snippets.

@mike-myers-tob
Last active February 10, 2025 10:24
Show Gist options
  • Save mike-myers-tob/9a6013124bad7ff074d3297db2c98247 to your computer and use it in GitHub Desktop.
Save mike-myers-tob/9a6013124bad7ff074d3297db2c98247 to your computer and use it in GitHub Desktop.
Steps to get GDB actually working in April 2021 on macOS (Intel x86-64 only)

Debug with GDB on macOS 11

The big reason to do this is that LLDB has no ability to "follow-fork-mode child", in other words, a multi-process target that doesn't have a single-process mode (or, a bug that only manifests when in multi-process mode) is going to be difficult or impossible to debug, especially if you have to run the target over and over in order to make the bug manifest. If you have a repeatable bug, no big deal, break on the fork from the parent process and attach to the child in a second lldb instance. Otherwise, read on.

Install GDB

Don't make the mistake of thinking you can just brew install gdb. Currently this is version 10.2 and it's mostly broken, with at least two annoying bugs as of April 29th 2021, but the big one is https://sourceware.org/bugzilla/show_bug.cgi?id=24069

$ xcode-select install  # install the XCode command-line tools
$ brew install --force --build-from-source domq/gdb/gdb  # a patched version based on 10.1

It's now installed at /usr/local/bin/gdb.

Create and Install a Self-Signed Code-Signing Certificate

  1. Start the Keychain Access application, found in /Applications/Utilities.
  2. From the Keychains list on the left, right-click on the System item and select Unlock Keychain "System".
  3. Choose Keychain Access > Certificate Assistant > Create a Certificate... from the menu.
  4. Choose a name (e.g. gdb-cert), set Identity Type to Self Signed Root, set Certificate Type to Code Signing and select the Let me override defaults. Click several times on Continue until you get to the Specify a Location For The Certificate screen, then set Keychain to System.
  5. If for some reason you create the certificate in the System keychain directly, create it in the Login keychain, then export it. You can then import it into the System keychain.
  6. Finally, using the context menu for the certificate, select Get Info, open the Trust item, and set Code Signing to Always Trust.
  7. From the Keychains list on the left, right-click on the System item and select Lock Keychain "System".
  8. You must quit the Keychain Access application and restart the system, in order to use the certificate.

Create the entitlements plist file (for macOS 10.14 and newer)

We'll call it gdb-entitlement.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.allow-dyld-environment-variables</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
    <key>com.apple.security.cs.disable-executable-page-protection</key>
    <true/>
    <key>com.apple.security.cs.debugger</key>
    <true/>
    <key>com.apple.security.get-task-allow</key>
    <true/>
</dict>
</plist>

Codesign and entitle the GDB executable

$ codesign --entitlements gdb-entitlement.xml -fs gdb-cert $(which gdb)

This seems to work even though that's Homebrew's link to the actual file on disk:

% ls -la $(which gdb)
lrwxr-xr-x  1 mmyers  admin  26 Apr 28 16:16 /usr/local/bin/gdb -> ../Cellar/gdb/10.2/bin/gdb
% codesign -vvv /usr/local/Cellar/gdb/10.2/bin/gdb
/usr/local/Cellar/gdb/10.2/bin/gdb: valid on disk
/usr/local/Cellar/gdb/10.2/bin/gdb: satisfies its Designated Requirement

Finally, Run GDB

$ gdb hello_world
(gdb) break main
(gdb) run

Troubleshooting

  • GDB will not understand "fat" executables. You can "lipo -thin x86_64" them.
  • If you tried debugging with gdb, but you get a "No symbol table is loaded" error, you might need to compile programs with the -ggdb option in gcc. I didn't have this issue personally.
  • If after hitting run in gdb, you get "Starting program: /path/to/your/executable [args] [New Thread 0x2303 of process 795]" followed by a blank line which does not respond to anything, then you have hit GDB bug 24069. Check that you built the patched version from source.
@ferdnyc
Copy link

ferdnyc commented Jan 4, 2023

@ken-salterdiazsolutions

All of the previous times "unhandled dyld version" has come up, it's been an issue of GDB just plain being out of date with the latest Apple changes to the dynamic-loading API. Whether that requires a simple version bump in a macro, or more involved changes to the code to support new/modified API features, is an open question. But if you're seeing that, it's likely you need a newer gdb... assuming a version even exists, yet, which has been patched to support dyld version 17.

@ken-salterdiazsolutions
Copy link

@ken-salterdiazsolutions

All of the previous times "unhandled dyld version" has come up, it's been an issue of GDB just plain being out of date with the latest Apple changes to the dynamic-loading API. Whether that requires a simple version bump in a macro, or more involved changes to the code to support new/modified API features, is an open question. But if you're seeing that, it's likely you need a newer gdb... assuming a version even exists, yet, which has been patched to support dyld version 17.

It is the latest I know of from brew (12.1)..anyway I think I've given up. If I run it via cmd line and use the Command+C to get to the actual program, because the program is written in Nim, the vars and function names are mangled. Was investigating if Nim would be something to have in my toolkit, but on the Mac at least it seems to be a PITA as far as debugging is concerned. I had also tried LLDB but no love there either. Ah well...thanks for the reply!

@ferdnyc
Copy link

ferdnyc commented Jan 5, 2023

@ken-salterdiazsolutions

Mmm, well, I certainly don't know a whole lot about Nim. From a quick skim online, I gather that it basically compiles down to source code in a number of different languages (C, C++, JS, etc.), but C by default. If you're seeing mangled symbols, sounds like it may be compiling to C++ (or maybe Nim does its own mangling).

You've probably seen most of these already, but...

This blog post recommends using all of these options on the nim c command line, for generating debuggable binaries:

$ nim c -d:debug --lineDir:on --debuginfo --debugger:native myprogram.nim

This documentation page also suggests including --opt:none (but that may be the default for -d:debug anyway). It sounds like --debugger:native is the really important flag.

It also looks like there are pre-made convenience wrappers available, when using gdb with Nim:

Compile this code with --debugger:native and load it to nim-gdb.

nim c --debugger:native test.nim
nim-gdb test

If you cannot find nim-gdb, you can download Nim/bin/nim-gdb and Nim/tools/nim-gdb.py from Nim repository. If you don't use nim-gdb, execute source Nim/tools/nim-gdb.py command after you run gdb. nim-gdb is a bash script that execute GDB and let GDB load Nim/tools/nim-gdb.py. And nim-gdb.py is a Python script that make GDB print Nim variables nicely.

@domq
Copy link

domq commented Jan 19, 2023

Many thanks @mike-myers-tob and all participants to this gist. I just updated the relevant Wiki page with the correct and up-to-date contents for gdb-entitlement.xml as seen above.

@Vegz78
Copy link

Vegz78 commented Jan 19, 2023

Great work!!!

@VenkyDevHub
Copy link

Great!!! It is working on my Mac. Thanks very much for this procedure.

@gpertea
Copy link

gpertea commented Aug 24, 2023

No luck here, Catalina 10.15.7, latest gdb 13.2 installed with brew, trying to debug C++ program in latest Eclipse. When I try debugging, it just runs ignoring any breakpoints I set. If I check the option to stop at "main", it does stop as the debug session starts, but in a nonsensical place, and the call stack also does not make any sense. The dreaded "unhandled dlyd version" issue might have something to do with this failure, I guess..

[New Thread 0x2803 of process 67174]
[New Thread 0x1e03 of process 67174]
[New Thread 0x2503 of process 67174]
warning: unhandled dyld version (16)

Thread 3 hit Temporary breakpoint 4.1, 0x00000001000f1f75 in ....

Same behavior no matter what compiler I use (tried: Xcode clang++ 12.0 and brew g++ 13.1)

@PrabhuUdurg
Copy link

Sonoma 14.4.1 with gdb 14.2 still the same problem as in @gpertea case above

@Wyvace
Copy link

Wyvace commented Jan 23, 2025

installer doesn't work on macos 11.

@ferdnyc
Copy link

ferdnyc commented Jan 24, 2025

@Wyvace what installer? There isn't actually any installer mentioned anywhere on this page. Which, BTW, was written in 2021, and it would be completely unsurprising to find out that it's sliiightly out of date.

If you're talking about the brew install command recommended above (again, in April 2021), that's unsurprising since Homebrew dropped macOS 11 support in 2023. Heck, even macOS 12 is unsupported now (since October 2024). Incompatibilities are to be expected when running old, unsupported OS releases.

(Edit: Also, what does "doesn't work" mean? That can take any of 100 different forms. Nobody here can actually see your screen and read the error messages or other information it's trying to share with you. You should generally follow its lead, and try to also share that information when reporting issues. Again, you're the only person here who can see your screen.)

@Wyvace
Copy link

Wyvace commented Jan 24, 2025

@ferdnyc my apologies for not providing any information, here is the problem:
Last 15 lines from /Users/wyvace/Library/Logs/Homebrew/gdb/02.make:
CC s390-opc.lo
CCLD libopcodes.la
/Library/Developer/CommandLineTools/usr/bin/ranlib: file: .libs/libopcodes.a(visium-opc.o) has no symbols
/Library/Developer/CommandLineTools/usr/bin/ranlib: file: .libs/libopcodes.a(visium-opc.o) has no symbols
libtooldir=/bin/sh ./libtool --config | sed -n -e 's/^objdir=//p';
if [ -f $libtooldir/libopcodes.a ]; then
cp $libtooldir/libopcodes.a libopcodes.tmp;
ranlib libopcodes.tmp;
/bin/sh ../../opcodes/../move-if-change libopcodes.tmp libopcodes.a;
else true; fi
/Library/Developer/CommandLineTools/usr/bin/ranlib: file: libopcodes.tmp(visium-opc.o) has no symbols
touch stamp-lib
Making all in po
make[4]: Nothing to be done for `all'.
make: *** [all] Error 2

Do not report this issue to Homebrew/brew or Homebrew/homebrew-core!

These open issues may also help:
gdb start two threads and the former has to be sent SIGINT(Ctrl + C) domq/homebrew-gdb#3

(Edit: and yeah i know that apple and homebrew dropped macos 11 support a long long time ago, but I don't have any alternative devices at the moment and it would be fine if i could launch gdb debugger on my old macos.)

@ferdnyc
Copy link

ferdnyc commented Jan 25, 2025

@Wyvace

The gdb formula in Homebrew is updated quite frequently; the low-hanging fruit is to assume the latest 16.x versions just aren't compatible with macOS 11. (They're definitely not tested on it, so who'd even know if it is or isn't compatible?)

The simplest solution might be to just try building an older version, from back when macOS 11 was supported. You can do that using a variation of the Homebrew FAQ instructions for building a formula from a pull request:

HOMEBREW_NO_INSTALL_FROM_API=1 brew tap --force homebrew/core
# That'll take a while, if you don't already have the repo cloned
cd $(brew --repository homebrew/core)
git log --oneline --follow Formula/g/gdb.rb
# Pick a commit you want to roll back to
git checkout <commit_sha>
brew install ./Formula/g/gdb.rb # Or Formula/gdb.rb, if you check out
                                # a commit from before the rename

That'll build gdb version <whatever-it-was-at-in-the-commit-you-checked-out> which may be more compatible than the latest 16.1.

Oh, BTW, you're on an Intel machine, right? Gdb has apparently gained support for Apple Silicon, but only very recently and, I'm sure it doesn't support macOS 11 on Apple Silicon.

Really, as Andrew said on StackOverflow (2 years ago, so the details aren't fully current, but the gist still very much is): "GDB has not (yet) been ported to MacOS running on the M2 (AArch64) architecture. Even GDB for MacOS on the old x86-64 was not very well tested as far as I know."

@gromgit
Copy link

gromgit commented Jan 25, 2025

@Wyvace @ferdnyc Note that editing the main Homebrew core branch is absolutely not recommended, as it may lead to Git conflicts down the line.

Instead, extract the desired version to your own tap, then you can change that to your heart's content without impacting other local Homebrew operations:

# Tap Homebrew core locally
export HOMEBREW_NO_INSTALL_FROM_API=1
brew tap --force homebrew/core

# Create your own tap, then extract a specific gdb version to it
brew tap-new wyvace/local
brew extract --version=14.1 gdb wyvace/local

# Edit and install
brew edit wyvace/local/gdb@14.1
brew install -s wyvace/local/gdb@14.1

@ferdnyc
Copy link

ferdnyc commented Jan 25, 2025

Thanks @gromgit!

(I especially never knew about brew extract --version=14.1 gdb, that's handy!)

@Wyvace
Copy link

Wyvace commented Jan 25, 2025

Thanks for your support @ferdnyc @gromgit! 👍

@CallumGage
Copy link

It helped me also, Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment