Skip to content

Instantly share code, notes, and snippets.

@loderunner
Last active January 1, 2026 15:33
Show Gist options
  • Select an option

  • Save loderunner/b6846dd82967ac048439 to your computer and use it in GitHub Desktop.

Select an option

Save loderunner/b6846dd82967ac048439 to your computer and use it in GitHub Desktop.
potential blog posts

ld – Wading through Mac OS X linker hell

Intro

– I tried looking at static linking in Mac OS X and it seems nearly impossible. Take a look at this http://stackoverflow.com/a/3801032 – I have no idea what that -static flag does, but I'm pretty sure that's not how you link to a library. Let me RTFM a bit.

Minutes later...

– I'm gonna have to write this stuff down.

Reading the fantastic manuals

FANTASTIC!

First things first, gcc isn't the default compiler in Mac OS X anymore. Since Xcode 5, the Apple developer toolchain uses clang, and gcc only aliases to clang.

$ gcc -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.3.0
Thread model: posix
NOTE: All the shell outputs here were produced with the default bash on Mac OS X El Capitan 10.11.3 with Xcode 7.2 installed.
man ld:

OPTIONS
   Options that control the kind of output
     -execute    The default.  Produce a mach-o main executable that has file type MH_EXECUTE.
     
     -dylib      Produce a mach-o shared library that has file type MH_DYLIB.
     
     -bundle     Produce a mach-o bundle that has file type MH_BUNDLE.
     
     -dynamic    The default.  Implied by -dylib, -bundle, or -execute
     
     -static     Produces a mach-o file that does not use the dyld.  Only used building the kernel.
     
@technusm1
Copy link

You, kind sir, are a gem. This tutorial saved me hours of work and provided me a better understanding of how linking on macOS works. Thanks to this I've been able to compile Kiwix command-line tools as standalone executables on macOS. 👍

Here's the closed issue I got to solve: kiwix/kiwix-tools#464

@adilmezghouti
Copy link

A million thanks for this article. Much appreciated.

@lupinthird
Copy link

If sainthood existed in the world of development, you would be the first one bestowed with the title. Many thanks!

@cjjavellana
Copy link

cjjavellana commented Sep 24, 2023

If sainthood existed in the world of development, you would be the first one bestowed with the title. Many thanks!

Couldn't agree more with this! Wasted 5 hrs banging my head why my program keeps on referencing the system libyaml instead of the one in my in my -L path. This article explains it all. Much thanks!

All i needed to do was rm -f *.dylib, and voila.

@gapisback
Copy link

HI, @loderunner, Charles! Very well-written article! Great job. Thanks for your effort in getting this out-there.

I came across this article while trying to figure out how-to unpack and find the address of a section named "__cstring" that can be produced using llvm-readelf -x -p __cstring command-line tool.

I am trying to programatically find out some of this info from within a running program. In Linux I can do:

154     /* Linux: Let's find where rodata is loaded. */
155     Dl_info info;
156     if (!dladdr("test string", &info))
157     {
158         errno = L3_DLADDR_NOT_FOUND_ERRNO;
159         return -1;
160     }
161     l3_log->fbase_addr = (intptr_t) info.dli_fbase;

I am searching for equivalent call in Mac/OSX land as dladdr() did not quite give me what I wanted.

I looked elsewhere and came upon interfaces like (ignore the commented out code, those were my experiments):

143     char *secstart = NULL;
144     size_t secsize;
145     // secstart = getsectdata("__SEGMENT", "__section", &secsize);
146     // secstart = getsectdata("__SECTION", "__cstring", &secsize);
147     // secstart = getsectdata("__SEGMENT", SECT_OBJC_STRINGS, &secsize);
148     secstart = getsectdata("__TEXT", "__cstring", &secsize);
149     // printf("secstart=%p, secsize=%lu\n", secstart, secsize);
150     l3_log->fbase_addr = (intptr_t) secstart;

But nothing seems to work.

Your write-up seems to hold some potential to help me figure out how to navigate Mach-O's linker / loader info.

If this comment tickles your brain and you may have some ideas to help me out, would appreciate a small ping.

Btw, I also found this article that you linked in the reference to be very useful!
Even with this WWW it is SO very difficult to find info for esoteric topics like these. So, thanks again, for putting that reference out there.

@amitabhadatta14
Copy link

amitabhadatta14 commented Oct 1, 2024

Hi,

I have an issue with linker as well. I am building a IBM MQ driver using Erlang driver. It is building ok in Linux x64/Arm64 but doesn't build in macOS Arm64.

Can you help please.

e072513@VL-K4YR0QX62K c_src % gcc -I /opt/homebrew/Cellar/erlang@26/26.2.5/lib/erlang/usr/include -I /opt/mqm/inc -shared -fPIC -L /opt/mqm/lib64 -L /opt/homebrew/Cellar/erlang@26/26.2.5/lib/erlang/lib/erl_interface-5.5.1/lib  -lei -lmqic_r -lpthread  mq_series_drv.c -o mq_series_drv.so -v
Apple clang version 16.0.0 (clang-1600.0.26.3)
Target: arm64-apple-darwin23.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
 "/Library/Developer/CommandLineTools/usr/bin/clang" -cc1 -triple arm64-apple-macosx14.0.0 -Wundef-prefix=TARGET_OS_ -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -emit-obj -mrelax-all -dumpdir mq_series_drv.so- -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name mq_series_drv.c -mrelocation-model pic -pic-level 2 -mframe-pointer=non-leaf -fno-strict-return -ffp-contract=on -fno-rounding-math -funwind-tables=1 -fobjc-msgsend-selector-stubs -target-sdk-version=15.0 -fvisibility-inlines-hidden-static-local-var -fno-modulemap-allow-subdirectory-search -target-cpu apple-m1 -target-feature +v8.5a -target-feature +aes -target-feature +crc -target-feature +dotprod -target-feature +fp-armv8 -target-feature +fp16fml -target-feature +lse -target-feature +ras -target-feature +rcpc -target-feature +rdm -target-feature +sha2 -target-feature +sha3 -target-feature +neon -target-feature +zcm -target-feature +zcz -target-feature +fullfp16 -target-abi darwinpcs -debugger-tuning=lldb -target-linker-version 1115.7.3 -v -fcoverage-compilation-dir=/Users/e072513/ips-tch-switch/_checkouts/mq_series/c_src -resource-dir /Library/Developer/CommandLineTools/usr/lib/clang/16 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -I /opt/homebrew/Cellar/erlang@26/26.2.5/lib/erlang/usr/include -I /opt/mqm/inc -I/usr/local/include -internal-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/local/include -internal-isystem /Library/Developer/CommandLineTools/usr/lib/clang/16/include -internal-externc-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include -internal-externc-isystem /Library/Developer/CommandLineTools/usr/include -Wno-reorder-init-list -Wno-implicit-int-float-conversion -Wno-c99-designator -Wno-final-dtor-non-final-class -Wno-extra-semi-stmt -Wno-misleading-indentation -Wno-quoted-include-in-framework-header -Wno-implicit-fallthrough -Wno-enum-enum-conversion -Wno-enum-float-conversion -Wno-elaborated-enum-base -Wno-reserved-identifier -Wno-gnu-folding-constant -fdebug-compilation-dir=/Users/e072513/ips-tch-switch/_checkouts/mq_series/c_src -ferror-limit 19 -stack-protector 1 -fstack-check -mdarwin-stkchk-strong-link -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fmax-type-align=16 -fcommon -fcolor-diagnostics -clang-vendor-feature=+disableNonDependentMemberExprInCurrentInstantiation -fno-odr-hash-protocols -clang-vendor-feature=+enableAggressiveVLAFolding -clang-vendor-feature=+revert09abecef7bbf -clang-vendor-feature=+thisNoAlignAttr -clang-vendor-feature=+thisNoNullAttr -clang-vendor-feature=+disableAtImportPrivateFrameworkInImplementationError -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/folders/zw/xt5wvkzj43v_p458qgx16bxw0000gn/T/mq_series_drv-d90068.o -x c mq_series_drv.c
clang -cc1 version 16.0.0 (clang-1600.0.26.3) default target arm64-apple-darwin23.6.0
ignoring nonexistent directory "/usr/local/include"
ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/local/include"
ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/Library/Frameworks"
#include "..." search starts here:
#include <...> search starts here:
 /opt/homebrew/Cellar/erlang@26/26.2.5/lib/erlang/usr/include
 /opt/mqm/inc
 /Library/Developer/CommandLineTools/usr/lib/clang/16/include
 /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include
 /Library/Developer/CommandLineTools/usr/include
 /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks (framework directory)
End of search list.
 "/Library/Developer/CommandLineTools/usr/bin/ld" -demangle -lto_library /Library/Developer/CommandLineTools/usr/lib/libLTO.dylib -no_deduplicate -dynamic -dylib -arch arm64 -platform_version macos 14.0.0 15.0 -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -mllvm -enable-linkonceodr-outlining -o mq_series_drv.so -L/opt/mqm/lib64 -L/opt/homebrew/Cellar/erlang@26/26.2.5/lib/erlang/lib/erl_interface-5.5.1/lib -L/usr/local/lib -lei -lmqic_r -lpthread /var/folders/zw/xt5wvkzj43v_p458qgx16bxw0000gn/T/mq_series_drv-d90068.o -lSystem /Library/Developer/CommandLineTools/usr/lib/clang/16/lib/darwin/libclang_rt.osx.a
Undefined symbols for architecture arm64:
  "_driver_alloc", referenced from:
      _mq_drv_start in mq_series_drv-d90068.o
      _do_connect in mq_series_drv-d90068.o
      _do_put in mq_series_drv-d90068.o
      _do_get in mq_series_drv-d90068.o
  "_driver_async", referenced from:
      _do_connect in mq_series_drv-d90068.o
      _do_put in mq_series_drv-d90068.o
      _do_get in mq_series_drv-d90068.o
  "_driver_free", referenced from:
      _mq_drv_stop in mq_series_drv-d90068.o
      _ready_async in mq_series_drv-d90068.o
      _do_free in mq_series_drv-d90068.o
  "_driver_output", referenced from:
      _ready_async in mq_series_drv-d90068.o
      _error_bad_arg in mq_series_drv-d90068.o
      _return_error in mq_series_drv-d90068.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

@acb764se
Copy link

I hope I am not intruding ... but I need help, and I am not able to understand all the above.

My (pet-)project (in MacOSX High Sierra) uses curl, imageMagick and the "ROOT" package
from CERN to get images from my safety cameras, interpret selected regions in them,
and find statistically meaningful differences (-->ALARM!).
Now, even if my program runs OK in standalone, it won't under lldb (see below)

My (idle) question is why all ROOT libraries, the @rpath id notwithstanding,
are found in standalone mode but not under lldb?

The important one is "what can I do to debug my code?"

Thank you for any ideas!

~/custodian>otool -L IMcustodian
IMcustodian:
	@rpath/libCore.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libImt.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libRIO.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libNet.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libHist.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libGraf.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libGraf3d.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libGpad.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libTree.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libTreePlayer.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libRint.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libPostscript.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libMatrix.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libPhysics.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libMathCore.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libThread.so (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libMultiProc.so (compatibility version 0.0.0, current version 0.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.50.4)
	/opt/local/lib/libcurl.4.dylib (compatibility version 13.0.0, current version 13.0.0)
	/opt/local/lib/libMagick++-6.Q16.9.dylib (compatibility version 10.0.0, current version 10.0.0)
	/opt/local/lib/libMagickWand-6.Q16.7.dylib (compatibility version 8.0.0, current version 8.0.0)
	/opt/local/lib/libMagickCore-6.Q16.7.dylib (compatibility version 8.0.0, current version 8.0.0)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0)

~/custodian>lldb IMcustodian
(lldb) target create "IMcustodian"
Current executable set to 'IMcustodian' (x86_64).
(lldb) break 46
invalid command 'breakpoint 46'.
(lldb) b 46
Breakpoint 1: where = IMcustodian`main + 616 at IMcustodian.cc:46, address = 0x0000000100012548
(lldb) run 6
Process 12147 launched: '/Users/a.calcaterra/custodian/IMcustodian' (x86_64)
dyld: Library not loaded: @rpath/libCore.so
  Referenced from: /Users/a.calcaterra/custodian/IMcustodian
  Reason: image not found
Process 12147 stopped
* thread #1, stop reason = signal SIGABRT
    frame #0: 0x0000000100055256 dyld`__abort_with_payload + 10
dyld`__abort_with_payload:
->  0x100055256 <+10>: jae    0x100055260               ; <+20>
    0x100055258 <+12>: movq   %rax, %rdi
    0x10005525b <+15>: jmp    0x100054ab4               ; cerror_nocancel
    0x100055260 <+20>: retq   
Target 0: (IMcustodian) stopped.
(lldb) 

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