Skip to content

v0.1.205 upgrade leaves /opt/nb absent, allowing cross-prefix runtime loading #355

Description

@StayBlue

Environment

  • nanobrew: v0.1.205
  • macOS: 26.5.1
  • arch: arm64
  • Nanobrew prefix: /opt/nanobrew/prefix
  • Homebrew prefix: /opt/homebrew
  • upgraded installation originally initialized before v0.1.204

Summary

On an existing macOS installation upgraded to v0.1.205, /opt/nb is not created automatically. When a bottle needs whole-file Mach-O relocation, nb install prints a warning but continues successfully using the load-command-only fallback. Compile-time paths in .rodata remain under /opt/homebrew.

If the same formula is installed with Homebrew, the Nanobrew process can then silently load Homebrew modules and libraries. This is a remaining upgrade/coexistence failure after #347.

Reproduction with Perl

Start with Nanobrew v0.1.205 and an existing prefix, but no short-prefix link:

test -e /opt/nb
# exit 1: /opt/nb is absent

brew install perl
nb install perl

Nanobrew warns but reports the package installed:

nb: note: /opt/nb short-prefix symlink unavailable — Mach-O .rodata not relocated; run `sudo nb init` and reinstall affected packages
...
    ✓ perl
==> Done

The Nanobrew executable and direct libperl dependency are correctly isolated:

/opt/nanobrew/prefix/Cellar/perl/5.42.2/bin/perl
/opt/nanobrew/prefix/Cellar/perl/5.42.2/lib/perl5/5.42/darwin-thread-multi-2level/CORE/libperl.dylib

But its compiled Perl configuration remains Homebrew-prefixed:

env -i HOME="$HOME" PATH=/usr/bin:/bin LC_ALL=C \
  /opt/nanobrew/prefix/Cellar/perl/5.42.2/bin/perl \
  -MConfig -e 'print "$Config{prefix}\n$INC{\"Config.pm\"}\n", join("\n", @INC), "\n"'
/opt/homebrew/opt/perl
/opt/homebrew/opt/perl/lib/perl5/5.42/darwin-thread-multi-2level/Config.pm
...
/opt/homebrew/opt/perl/lib/perl5/5.42

With DYLD_PRINT_LIBRARIES=1, Nanobrew Perl loaded Brew's XS bundles and dependency library:

/opt/homebrew/Cellar/perl/5.42.2/.../auto/Encode/Encode.bundle
/opt/homebrew/Cellar/perl/5.42.2/.../auto/GDBM_File/GDBM_File.bundle
/opt/homebrew/Cellar/gdbm/1.26/lib/libgdbm.6.dylib

Temporarily hiding Brew's /opt/homebrew/opt/perl and public Perl links made the Nanobrew copy fail:

Can't locate strict.pm in @INC
Can't locate Config.pm in @INC

Restoring the Brew links made it pass again. Brew was therefore masking a broken Nanobrew installation.

Impact

  • A same-version Homebrew formula silently supplies Nanobrew runtime components.
  • Independent upgrades can create module/ABI mismatches rather than an immediate, diagnosable failure.
  • A user without the corresponding Homebrew formula gets a broken Nanobrew package despite nb install reporting success.
  • This affects more than Perl: the current installation contained /opt/homebrew in 139 Mach-O files across the selected Nanobrew kegs.

Relevant implementation

src/macho/relocate.zig explicitly leaves .rodata untouched when /opt/nb is unavailable and falls back to install_name_tool for load commands only. src/main.zig creates /opt/nb during nb init, but an ordinary upgrade does not rerun privileged initialization.

Expected

An upgraded installation should not successfully install a keg with known-incomplete runtime relocation. Possible acceptable outcomes include:

  • bootstrap or explicitly require /opt/nb as part of the v0.1.204+ upgrade path; or
  • fail the affected package installation instead of warning and continuing.

The installed record should not be reported as successful while loadable files retain foreign-prefix runtime paths.

Refs #347.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions