Bug
When building a dylib / cdylib / proc-macro crate with -C strip=debuginfo and a deployment target that enables chained fixups (i.e. MACOSX_DEPLOYMENT_TARGET >= 12.0, e.g. 27.0), rustc produces a dylib whose LC_SYMTAB.stroff (the LINKEDIT string pool) is not 8-byte aligned. dyld on macOS 27 rejects such images, so the resulting dylib fails to load with the mis-aligned LINKEDIT string pool error.
(Edit: see below for a more minimal reproduction that does not require chained fixups nor MACOSX_DEPLOYMENT_TARGET.)
This can be reproduced by the following cdylib crate:
src/lib.rs:
#[unsafe(no_mangle)]
pub extern "C" fn answer() -> usize {
"hello from a static string".len()
}
Cargo.toml:
[package]
name = "cargo_cdylib_linkedit_repro"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[profile.dev]
strip = "debuginfo"
$ MACOSX_DEPLOYMENT_TARGET=12.0 cargo build
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s
$ cat main.c
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
typedef size_t (*answer_fn)(void);
int main(int argc, char **argv) {
const char *path = argc > 1 ? argv[1] : "target/debug/libcargo_cdylib_linkedit_repro.dylib";
void *handle = dlopen(path, RTLD_NOW);
if (!handle) {
fprintf(stderr, "dlopen failed: %s\n", dlerror());
return 1;
}
dlerror();
answer_fn answer = (answer_fn)dlsym(handle, "answer");
const char *err = dlerror();
if (err) {
fprintf(stderr, "dlsym failed: %s\n", err);
return 2;
}
printf("answer() = %zu\n", answer());
dlclose(handle);
return 0;
}
$ cc -o dlopen-test main.c
$ ./dlopen-test
dlopen failed: dlopen(target/debug/libcargo_cdylib_linkedit_repro.dylib, 0x0002): tried: 'target/debug/libcargo_cdylib_linkedit_repro.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004B90C), '/System/Volumes/Preboot/Cryptexes/OStarget/debug/libcargo_cdylib_linkedit_repro.dylib' (no such file), '/usr/lib/target/debug/libcargo_cdylib_linkedit_repro.dylib' (no such file, not in dyld cache), 'target/debug/libcargo_cdylib_linkedit_repro.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004B90C), '/Users/sungbin/Developer/Personal/cargo-cdylib-linkedit-repro/target/debug/libcargo_cdylib_linkedit_repro.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004B90C), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/Developer/Personal/cargo-cdylib-linkedit-repro/target/debug/libcargo_cdylib_linkedit_repro.dylib' (no such file), '/Users/sungbin/Developer/Personal/cargo-cdylib-linkedit-repro/target/debug/libcargo_cdylib_linkedit_repro.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004B90C)
$ cargo clean
Removed 21 files, 809.3KiB total
$ MACOSX_DEPLOYMENT_TARGET=11.0 cargo build
Compiling cargo_cdylib_linkedit_repro v0.1.0 (/Users/sungbin/Developer/Personal/cargo-cdylib-linkedit-repro)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.15s
$ ./dlopen-test
answer() = 26
$ cargo clean
Removed 21 files, 809.9KiB total
$ vi Cargo.toml
$ cat Cargo.toml
[package]
name = "cargo_cdylib_linkedit_repro"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[profile.dev]
# strip = "debuginfo"
$ MACOSX_DEPLOYMENT_TARGET=12.0 cargo build
Compiling cargo_cdylib_linkedit_repro v0.1.0 (/Users/sungbin/Developer/Personal/cargo-cdylib-linkedit-repro)
Finished [`dev` profile [unoptimized + debuginfo]](https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles) target(s) in 0.52s
$ ./dlopen-test
answer() = 26
More details on how the dylib is not aligned can be seen in the reproduction script and the results.
Context
The context for this bug report here is that I installed macOS 27 Beta on my machine, and building rust via MacPorts started to fail. It surfaced as failing to import proc macros (i.e. can't find crate for clap_derive) when building the bootstrap.
Apparently, cargo builds proc macros as dylibs with -C strip=debuginfo set and triggers the above bug. MacPorts sets MACOSX_DEPLOYMENT_TARGET to the current version of macOS (so 27.0), and it seems that macOS 27 started to validate dylibs more strictly, hence the new error.
I've done some research on whether this is a llvm-objcopy bug or a macOS bug, and while I am not sure macOS will keep the new strict behavior, it seems that both ld64 and lld-macho does align the section to pointer size, while llvm-objcopy does not. Seems like it would be beneficial if this issue be fixed regardless of whether macOS relaxes this behavior not.
Below is a patch that I applied to rustc-1.96.0-src in order to succeed building a rustc that does not suffer from this bug. I will be reporting this bug to llvm/llvm-project as well.
--- src/llvm-project/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp.orig
+++ src/llvm-project/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp
@@ -280,6 +280,7 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
uint64_t StartOfSymbols = updateOffset(NListSize * O.SymTable.Symbols.size());
uint64_t StartOfIndirectSymbols =
updateOffset(sizeof(uint32_t) * O.IndirectSymTable.Symbols.size());
+ Offset = alignTo(Offset, Is64Bit ? 8 : 4);
uint64_t StartOfSymbolStrings = updateOffset(StrTableBuilder.getSize());
uint64_t StartOfDylibCodeSignDRs = updateOffset(O.DylibCodeSignDRs.Data.size());
Meta
rustc --version --verbose:
rustc 1.96.0 (ac68faa20 2026-05-25)
binary: rustc
commit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96
commit-date: 2026-05-25
host: aarch64-apple-darwin
release: 1.96.0
LLVM version: 22.1.2
The bug seems to affect all recent rustc versions, I checked both 1.93.0 and 1.96.0.
Reproduction script and run results
For reproducing, I created the following script rust-linkedit-repro.sh:
#!/usr/bin/env bash
set -euo pipefail
MDT="${1:-${MACOSX_DEPLOYMENT_TARGET:-$(sw_vers -productVersion | cut -d. -f1).0}}"
RUSTC="${RUSTC:-$(rustup which rustc)}"
ROOT="${ROOT:-$PWD/linkedit-repro-${MDT}}"
mkdir -p "$ROOT"
cd "$ROOT"
cat > pm.rs <<'RS'
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(Foo)]
pub fn foo(_input: TokenStream) -> TokenStream { TokenStream::new() }
RS
cat > cdylib_simple.rs <<'RS'
#[no_mangle]
pub extern "C" fn answer() -> i32 { 42 }
RS
cat > cdylib_static_str.rs <<'RS'
#[no_mangle]
pub extern "C" fn answer() -> usize { "hello from a static string".len() }
RS
cat > cdylib_std_env.rs <<'RS'
#[no_mangle]
pub extern "C" fn answer() -> usize {
std::env::var("PATH").unwrap_or_default().len()
}
RS
cat > main.rs <<'RS'
fn main() { println!("hello from executable"); }
RS
build() {
local label="$1"; shift
echo "--- building $label"
MACOSX_DEPLOYMENT_TARGET="$MDT" "$RUSTC" "$@"
}
report_macho() {
local label="$1" file="$2"
echo
echo "=== $label: $file"
file "$file"
/usr/bin/otool -l "$file" | awk '
/cmd LC_DYLD/ { print " " $0 }
/cmd LC_BUILD_VERSION/ { b=1; next }
b && /cmd / { b=0 }
b && /(minos|sdk)/ { print " " $0 }
/cmd LC_SYMTAB/ { s=1; next }
s && /cmd / { s=0 }
s && /(symoff|nsyms|stroff|strsize)/ { print " " $0 }
/cmd LC_DYSYMTAB/ { d=1; next }
d && /cmd / { d=0 }
d && /(indirectsymoff|nindirectsyms)/ { print " " $0 }
'
local stroff
stroff=$(/usr/bin/otool -l "$file" | awk '/cmd LC_SYMTAB/{s=1;next} s&&/cmd /{s=0} s&&/stroff/{print $2}')
if [ -n "$stroff" ]; then
echo " stroff % 8 = $((stroff % 8))"
if [ $((stroff % 8)) -ne 0 ]; then
echo " LINKEDIT string pool is MISALIGNED for dyld's 8-byte check"
else
echo " LINKEDIT string pool is aligned"
fi
fi
}
dlcheck() {
local file="$1"
python3 - "$file" <<'PY'
import ctypes, sys
p = sys.argv[1]
try:
lib = ctypes.CDLL(p)
msg = " dlopen: OK"
if hasattr(lib, "answer"):
lib.answer.restype = ctypes.c_size_t
msg += f" answer()={lib.answer()}"
print(msg)
except OSError as e:
print(" dlopen: FAIL")
print(" ", e)
PY
}
# Build with Rust's problematic strip path.
build proc-macro --crate-name p --crate-type proc-macro pm.rs -C strip=debuginfo -o libpm.dylib
build cdylib-simple --crate-type cdylib cdylib_simple.rs -C strip=debuginfo -o libcdylib_simple.dylib
build cdylib-static-str --crate-type cdylib cdylib_static_str.rs -C strip=debuginfo -o libcdylib_static_str.dylib
build cdylib-std-env --crate-type cdylib cdylib_std_env.rs -C strip=debuginfo -o libcdylib_std_env.dylib
build executable main.rs -C strip=debuginfo -o app
report_macho proc-macro libpm.dylib
cat > use_pm.rs <<'RS'
extern crate p;
RS
if "$RUSTC" --crate-type lib use_pm.rs --extern p="$PWD/libpm.dylib" -o libuse_pm.rlib > proc_macro_load.log 2>&1; then
echo " rustc proc-macro load: OK"
else
echo " rustc proc-macro load: FAIL"
sed -n '1,4p' proc_macro_load.log | sed 's/^/ /'
fi
dlcheck "$PWD/libpm.dylib"
report_macho cdylib-simple libcdylib_simple.dylib
dlcheck "$PWD/libcdylib_simple.dylib"
report_macho cdylib-static-str libcdylib_static_str.dylib
dlcheck "$PWD/libcdylib_static_str.dylib"
report_macho cdylib-std-env libcdylib_std_env.dylib
dlcheck "$PWD/libcdylib_std_env.dylib"
report_macho executable app
if ./app; then echo " exec: OK"; else echo " exec: FAIL"; fi
echo
echo "Repro dir: $PWD"
echo "Rustc: $($RUSTC -Vv | tr '\n' '; ')"
echo "MACOSX_DEPLOYMENT_TARGET=$MDT"
Running this script with MACOSX_DEPLOYMENT_TARGET set to 27.0, 11.0, 26.0, and 12.0 in respective order:
$ ./rust-linkedit-repro.sh 27.0
--- building proc-macro
--- building cdylib-simple
--- building cdylib-static-str
--- building cdylib-std-env
--- building executable
=== proc-macro: libpm.dylib
libpm.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 330040
nsyms 936
stroff 345476
strsize 86168
indirectsymoff 345016
nindirectsyms 115
minos 27.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
rustc proc-macro load: FAIL
error: /Users/sungbin/linkedit-repro-27.0/libpm.dylib: dlopen(/Users/sungbin/linkedit-repro-27.0/libpm.dylib, 0x0005): tried: '/Users/sungbin/linkedit-repro-27.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-27.0/libpm.dylib' (no such file), '/Users/sungbin/linkedit-repro-27.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584)
--> use_pm.rs:1:1
|
1 | extern crate p;
dlopen: FAIL
dlopen(/Users/sungbin/linkedit-repro-27.0/libpm.dylib, 0x0006): tried: '/Users/sungbin/linkedit-repro-27.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-27.0/libpm.dylib' (no such file), '/Users/sungbin/linkedit-repro-27.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584)
=== cdylib-simple: libcdylib_simple.dylib
libcdylib_simple.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 16464
nsyms 1
stroff 16480
strsize 16
indirectsymoff 0
nindirectsyms 0
minos 27.0
sdk 27.0
stroff % 8 = 0
LINKEDIT string pool is aligned
dlopen: OK answer()=42
=== cdylib-static-str: libcdylib_static_str.dylib
libcdylib_static_str.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 296976
nsyms 756
stroff 309516
strsize 71176
indirectsymoff 309072
nindirectsyms 111
minos 27.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
dlopen: FAIL
dlopen(/Users/sungbin/linkedit-repro-27.0/libcdylib_static_str.dylib, 0x0006): tried: '/Users/sungbin/linkedit-repro-27.0/libcdylib_static_str.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004B90C), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-27.0/libcdylib_static_str.dylib' (no such file), '/Users/sungbin/linkedit-repro-27.0/libcdylib_static_str.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004B90C)
=== cdylib-std-env: libcdylib_std_env.dylib
libcdylib_std_env.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 297000
nsyms 775
stroff 309844
strsize 72464
indirectsymoff 309400
nindirectsyms 111
minos 27.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
dlopen: FAIL
dlopen(/Users/sungbin/linkedit-repro-27.0/libcdylib_std_env.dylib, 0x0006): tried: '/Users/sungbin/linkedit-repro-27.0/libcdylib_std_env.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004BA54), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-27.0/libcdylib_std_env.dylib' (no such file), '/Users/sungbin/linkedit-repro-27.0/libcdylib_std_env.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004BA54)
=== executable: app
app: Mach-O 64-bit executable arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 335648
nsyms 841
stroff 349652
strsize 78096
indirectsymoff 349104
nindirectsyms 137
minos 27.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
hello from executable
exec: OK
Repro dir: /Users/sungbin/linkedit-repro-27.0
Rustc: rustc 1.96.0 (ac68faa20 2026-05-25);binary: rustc;commit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96;commit-date: 2026-05-25;host: aarch64-apple-darwin;release: 1.96.0;LLVM version: 22.1.2;
MACOSX_DEPLOYMENT_TARGET=27.0
$ ./rust-linkedit-repro.sh 11.0
--- building proc-macro
--- building cdylib-simple
--- building cdylib-static-str
--- building cdylib-std-env
--- building executable
=== proc-macro: libpm.dylib
libpm.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_INFO_ONLY
symoff 330336
nsyms 938
stroff 345808
strsize 86200
indirectsymoff 345344
nindirectsyms 116
minos 11.0
sdk 27.0
stroff % 8 = 0
LINKEDIT string pool is aligned
rustc proc-macro load: OK
dlopen: OK
=== cdylib-simple: libcdylib_simple.dylib
libcdylib_simple.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_INFO_ONLY
symoff 16416
nsyms 1
stroff 16432
strsize 16
indirectsymoff 0
nindirectsyms 0
minos 11.0
sdk 27.0
stroff % 8 = 0
LINKEDIT string pool is aligned
dlopen: OK answer()=42
=== cdylib-static-str: libcdylib_static_str.dylib
libcdylib_static_str.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_INFO_ONLY
symoff 297208
nsyms 758
stroff 309784
strsize 71208
indirectsymoff 309336
nindirectsyms 112
minos 11.0
sdk 27.0
stroff % 8 = 0
LINKEDIT string pool is aligned
dlopen: OK answer()=26
=== cdylib-std-env: libcdylib_std_env.dylib
libcdylib_std_env.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_INFO_ONLY
symoff 297232
nsyms 777
stroff 310112
strsize 72496
indirectsymoff 309664
nindirectsyms 112
minos 11.0
sdk 27.0
stroff % 8 = 0
LINKEDIT string pool is aligned
dlopen: OK answer()=572
=== executable: app
app: Mach-O 64-bit executable arm64
cmd LC_DYLD_INFO_ONLY
symoff 335936
nsyms 843
stroff 349976
strsize 78128
indirectsymoff 349424
nindirectsyms 138
minos 11.0
sdk 27.0
stroff % 8 = 0
LINKEDIT string pool is aligned
hello from executable
exec: OK
Repro dir: /Users/sungbin/linkedit-repro-11.0
Rustc: rustc 1.96.0 (ac68faa20 2026-05-25);binary: rustc;commit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96;commit-date: 2026-05-25;host: aarch64-apple-darwin;release: 1.96.0;LLVM version: 22.1.2;
MACOSX_DEPLOYMENT_TARGET=11.0
$ ./rust-linkedit-repro.sh 26.0
--- building proc-macro
--- building cdylib-simple
--- building cdylib-static-str
--- building cdylib-std-env
--- building executable
=== proc-macro: libpm.dylib
libpm.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 330040
nsyms 936
stroff 345476
strsize 86168
indirectsymoff 345016
nindirectsyms 115
minos 26.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
rustc proc-macro load: FAIL
error: /Users/sungbin/linkedit-repro-26.0/libpm.dylib: dlopen(/Users/sungbin/linkedit-repro-26.0/libpm.dylib, 0x0005): tried: '/Users/sungbin/linkedit-repro-26.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-26.0/libpm.dylib' (no such file), '/Users/sungbin/linkedit-repro-26.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584)
--> use_pm.rs:1:1
|
1 | extern crate p;
dlopen: FAIL
dlopen(/Users/sungbin/linkedit-repro-26.0/libpm.dylib, 0x0006): tried: '/Users/sungbin/linkedit-repro-26.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-26.0/libpm.dylib' (no such file), '/Users/sungbin/linkedit-repro-26.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584)
=== cdylib-simple: libcdylib_simple.dylib
libcdylib_simple.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 16464
nsyms 1
stroff 16480
strsize 16
indirectsymoff 0
nindirectsyms 0
minos 26.0
sdk 27.0
stroff % 8 = 0
LINKEDIT string pool is aligned
dlopen: OK answer()=42
=== cdylib-static-str: libcdylib_static_str.dylib
libcdylib_static_str.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 296976
nsyms 756
stroff 309516
strsize 71176
indirectsymoff 309072
nindirectsyms 111
minos 26.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
dlopen: FAIL
dlopen(/Users/sungbin/linkedit-repro-26.0/libcdylib_static_str.dylib, 0x0006): tried: '/Users/sungbin/linkedit-repro-26.0/libcdylib_static_str.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004B90C), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-26.0/libcdylib_static_str.dylib' (no such file), '/Users/sungbin/linkedit-repro-26.0/libcdylib_static_str.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004B90C)
=== cdylib-std-env: libcdylib_std_env.dylib
libcdylib_std_env.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 297000
nsyms 775
stroff 309844
strsize 72464
indirectsymoff 309400
nindirectsyms 111
minos 26.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
dlopen: FAIL
dlopen(/Users/sungbin/linkedit-repro-26.0/libcdylib_std_env.dylib, 0x0006): tried: '/Users/sungbin/linkedit-repro-26.0/libcdylib_std_env.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004BA54), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-26.0/libcdylib_std_env.dylib' (no such file), '/Users/sungbin/linkedit-repro-26.0/libcdylib_std_env.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004BA54)
=== executable: app
app: Mach-O 64-bit executable arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 335648
nsyms 841
stroff 349652
strsize 78096
indirectsymoff 349104
nindirectsyms 137
minos 26.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
hello from executable
exec: OK
Repro dir: /Users/sungbin/linkedit-repro-26.0
Rustc: rustc 1.96.0 (ac68faa20 2026-05-25);binary: rustc;commit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96;commit-date: 2026-05-25;host: aarch64-apple-darwin;release: 1.96.0;LLVM version: 22.1.2;
MACOSX_DEPLOYMENT_TARGET=26.0
$ ./rust-linkedit-repro.sh 12.0
--- building proc-macro
--- building cdylib-simple
--- building cdylib-static-str
--- building cdylib-std-env
--- building executable
=== proc-macro: libpm.dylib
libpm.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 330040
nsyms 936
stroff 345476
strsize 86168
indirectsymoff 345016
nindirectsyms 115
minos 12.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
rustc proc-macro load: FAIL
error: /Users/sungbin/linkedit-repro-12.0/libpm.dylib: dlopen(/Users/sungbin/linkedit-repro-12.0/libpm.dylib, 0x0005): tried: '/Users/sungbin/linkedit-repro-12.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-12.0/libpm.dylib' (no such file), '/Users/sungbin/linkedit-repro-12.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584)
--> use_pm.rs:1:1
|
1 | extern crate p;
dlopen: FAIL
dlopen(/Users/sungbin/linkedit-repro-12.0/libpm.dylib, 0x0006): tried: '/Users/sungbin/linkedit-repro-12.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-12.0/libpm.dylib' (no such file), '/Users/sungbin/linkedit-repro-12.0/libpm.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x00054584)
=== cdylib-simple: libcdylib_simple.dylib
libcdylib_simple.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 16464
nsyms 1
stroff 16480
strsize 16
indirectsymoff 0
nindirectsyms 0
minos 12.0
sdk 27.0
stroff % 8 = 0
LINKEDIT string pool is aligned
dlopen: OK answer()=42
=== cdylib-static-str: libcdylib_static_str.dylib
libcdylib_static_str.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 296976
nsyms 756
stroff 309516
strsize 71176
indirectsymoff 309072
nindirectsyms 111
minos 12.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
dlopen: FAIL
dlopen(/Users/sungbin/linkedit-repro-12.0/libcdylib_static_str.dylib, 0x0006): tried: '/Users/sungbin/linkedit-repro-12.0/libcdylib_static_str.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004B90C), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-12.0/libcdylib_static_str.dylib' (no such file), '/Users/sungbin/linkedit-repro-12.0/libcdylib_static_str.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004B90C)
=== cdylib-std-env: libcdylib_std_env.dylib
libcdylib_std_env.dylib: Mach-O 64-bit dynamically linked shared library arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 297000
nsyms 775
stroff 309844
strsize 72464
indirectsymoff 309400
nindirectsyms 111
minos 12.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
dlopen: FAIL
dlopen(/Users/sungbin/linkedit-repro-12.0/libcdylib_std_env.dylib, 0x0006): tried: '/Users/sungbin/linkedit-repro-12.0/libcdylib_std_env.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004BA54), '/System/Volumes/Preboot/Cryptexes/OS/Users/sungbin/linkedit-repro-12.0/libcdylib_std_env.dylib' (no such file), '/Users/sungbin/linkedit-repro-12.0/libcdylib_std_env.dylib' (mis-aligned LINKEDIT string pool, fileOffset=0x0004BA54)
=== executable: app
app: Mach-O 64-bit executable arm64
cmd LC_DYLD_CHAINED_FIXUPS
cmd LC_DYLD_EXPORTS_TRIE
symoff 335648
nsyms 841
stroff 349652
strsize 78096
indirectsymoff 349104
nindirectsyms 137
minos 12.0
sdk 27.0
stroff % 8 = 4
LINKEDIT string pool is MISALIGNED for dyld's 8-byte check
hello from executable
exec: OK
Repro dir: /Users/sungbin/linkedit-repro-12.0
Rustc: rustc 1.96.0 (ac68faa20 2026-05-25);binary: rustc;commit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96;commit-date: 2026-05-25;host: aarch64-apple-darwin;release: 1.96.0;LLVM version: 22.1.2;
MACOSX_DEPLOYMENT_TARGET=12.0
This is run on a macOS 27.0 machine.
I have also verified that running the same script with 26.0, 12.0, and 11.0 on a macOS 26.5 virtual machine succeeds.
Disclosure: I have used LLMs in first encountering and researching the bug. However I have personally verified the bug myself, checked that every single part of this bug report is true as far as my knoweldge goes, did the research on the ld64/lld behavior, and wrote every single part of the bug report myself.
Bug
When building a dylib / cdylib / proc-macro crate with
-C strip=debuginfoand a deployment target that enables chained fixups (i.e.MACOSX_DEPLOYMENT_TARGET>= 12.0, e.g. 27.0),rustcproduces a dylib whoseLC_SYMTAB.stroff(theLINKEDITstring pool) is not 8-byte aligned. dyld on macOS 27 rejects such images, so the resulting dylib fails to load with themis-aligned LINKEDIT string poolerror.(Edit: see below for a more minimal reproduction that does not require chained fixups nor
MACOSX_DEPLOYMENT_TARGET.)This can be reproduced by the following cdylib crate:
src/lib.rs:Cargo.toml:More details on how the dylib is not aligned can be seen in the reproduction script and the results.
Context
The context for this bug report here is that I installed macOS 27 Beta on my machine, and building rust via MacPorts started to fail. It surfaced as failing to import proc macros (i.e. can't find crate for
clap_derive) when building the bootstrap.Apparently,
cargobuilds proc macros as dylibs with-C strip=debuginfoset and triggers the above bug. MacPorts setsMACOSX_DEPLOYMENT_TARGETto the current version of macOS (so 27.0), and it seems that macOS 27 started to validate dylibs more strictly, hence the new error.I've done some research on whether this is a
llvm-objcopybug or a macOS bug, and while I am not sure macOS will keep the new strict behavior, it seems that both ld64 and lld-macho does align the section to pointer size, whilellvm-objcopydoes not. Seems like it would be beneficial if this issue be fixed regardless of whether macOS relaxes this behavior not.Below is a patch that I applied to rustc-1.96.0-src in order to succeed building a rustc that does not suffer from this bug. I will be reporting this bug to llvm/llvm-project as well.
Meta
rustc --version --verbose:The bug seems to affect all recent
rustcversions, I checked both 1.93.0 and 1.96.0.Reproduction script and run results
For reproducing, I created the following script rust-linkedit-repro.sh:
Running this script with
MACOSX_DEPLOYMENT_TARGETset to 27.0, 11.0, 26.0, and 12.0 in respective order:This is run on a macOS 27.0 machine.
I have also verified that running the same script with 26.0, 12.0, and 11.0 on a macOS 26.5 virtual machine succeeds.
Disclosure: I have used LLMs in first encountering and researching the bug. However I have personally verified the bug myself, checked that every single part of this bug report is true as far as my knoweldge goes, did the research on the
ld64/lldbehavior, and wrote every single part of the bug report myself.