Skip to content

arch/sim: replace macOS C++ constructor runtime hack with post-link patch#18886

Open
xiaoxiang781216 wants to merge 1 commit into
apache:masterfrom
xiaoxiang781216:macos
Open

arch/sim: replace macOS C++ constructor runtime hack with post-link patch#18886
xiaoxiang781216 wants to merge 1 commit into
apache:masterfrom
xiaoxiang781216:macos

Conversation

@xiaoxiang781216
Copy link
Copy Markdown
Contributor

Summary

Replace the macOS sim C++ global constructor runtime hack (sim_macho_init.c)
with a post-link Mach-O section patching scheme so dyld no longer auto-runs
constructors before NuttX is initialized.

Impact

  • Area: arch/sim (macOS host only). Linux behavior is unchanged.

Testing

  • Build host: macOS (Ventura VM)
  • Target: sim:nsh with CONFIG_HAVE_CXX=y / CONFIG_HAVE_CXXINITIALIZE=y
  • Verified via standalone test (test_sinit7) that the C++ constructor is
    deferred past main() and is only invoked when explicitly called through
    the _sinit[]/_einit[] loop in lib_cxx_initialize().

Details

  1. Link with -Wl,-ld_classic,-no_fixup_chains to keep the classic
    __mod_init_func pointer format (prevents ld64 from converting it to
    __init_offsets).
  2. Post-link, run patch_macho_initsection.py (python3 + lief) to patch
    the Mach-O section type flags from MOD_INIT_FUNC_POINTERS /
    INIT_FUNC_OFFSETS to REGULAR, so dyld skips them.
  3. Use the Mach-O auto-generated boundary symbols
    section$start$__DATA_CONST$__mod_init_func /
    section$end$__DATA_CONST$__mod_init_func, mapped via __asm() labels in
    arch/sim/include/arch.h to the common _sinit[]/_einit[] names used
    by lib_cxx_initialize().
  4. Delete the old sim_macho_init.c runtime hack and the macOS-specific
    macho_call_saved_init_funcs() path in lib_cxx_initialize.c.

Dependency: pip install lief on the build host when
CONFIG_HAVE_CXXINITIALIZE=y.

Copilot AI review requested due to automatic review settings May 16, 2026 12:18
@github-actions github-actions Bot added Arch: simulator Issues related to the SIMulator Area: OS Components OS Components issues Size: M The size of the change in this PR is medium labels May 16, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces the macOS simulator C++ constructor deferral mechanism with post-link Mach-O section patching, so dyld does not run constructors before NuttX initialization.

Changes:

  • Removes the old runtime Mach-O constructor interception path.
  • Adds macOS linker flags and post-build patching for Mach-O init sections.
  • Maps macOS Mach-O section boundary symbols to _sinit / _einit.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
libs/libc/misc/lib_cxx_initialize.c Uses the common _sinit / _einit constructor loop for macOS too.
arch/sim/src/sim/posix/sim_macho_init.c Removes the previous runtime constructor-saving hack.
arch/sim/src/sim/CMakeLists.txt Adds macOS link options and a post-build Mach-O patch command.
arch/sim/src/patch_macho_initsection.py Adds the LIEF-based Mach-O section type patcher.
arch/sim/src/Makefile Adds macOS linker flags and invokes the patcher in make builds.
arch/sim/include/arch.h Adds macOS-specific _sinit / _einit asm symbol mappings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread arch/sim/src/Makefile Outdated
Comment thread arch/sim/src/sim/CMakeLists.txt
Comment thread arch/sim/include/arch.h
Comment thread arch/sim/src/Makefile Outdated
Comment thread arch/sim/src/sim/CMakeLists.txt
Comment thread arch/sim/src/patch_macho_initsection.py
Comment thread arch/sim/src/Makefile Outdated
…atch

The sim architecture needs to defer C++ global constructors until after
NuttX kernel initialization completes. On macOS this was previously done
via a runtime hack (sim_macho_init.c): a __attribute__((constructor))
function intercepted constructors, saved them, and replayed them later.
That approach was fragile because it depended on constructor ordering
and required mprotect() to patch the read-only __mod_init_func section
at runtime.

This commit replaces the runtime hack with a post-link patching scheme:

  1. Link with -Wl,-ld_classic,-no_fixup_chains to keep the classic
     __mod_init_func pointer format (prevents ld64 from converting it
     to __init_offsets).
  2. Post-link, run patch_macho_initsection.py (python3 + lief) to
     patch Mach-O section type flags from
     MOD_INIT_FUNC_POINTERS/INIT_FUNC_OFFSETS to REGULAR, so dyld
     skips them entirely.
  3. Use the Mach-O auto-generated boundary symbols
     section$start$__DATA_CONST$__mod_init_func /
     section$end$__DATA_CONST$__mod_init_func, mapped via __asm()
     labels in arch/sim/include/arch.h to the common _sinit[]/_einit[]
     names used by lib_cxx_initialize().

Linux behavior is unchanged.

Changes:
  - arch/sim/include/arch.h: add macOS __asm() declarations for
    _sinit/_einit
  - arch/sim/src/Makefile: drop sim_macho_init.c HEADSRC handling,
    always pass -ld_classic,-no_fixup_chains on macOS, run
    patch_macho_initsection.py after link when CONFIG_HAVE_CXXINITIALIZE
  - arch/sim/src/sim/CMakeLists.txt: same for the CMake build
  - arch/sim/src/patch_macho_initsection.py: new lief-based patcher
  - arch/sim/src/sim/posix/sim_macho_init.c: deleted (135-line hack)
  - libs/libc/misc/lib_cxx_initialize.c: remove
    macho_call_saved_init_funcs special case; single unified loop

Testing:
  - macOS (Ventura VM): verified lief patching with standalone test
    (test_sinit7) confirming the constructor is deferred past main()
    and only invoked when explicitly called via the _sinit/_einit loop.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
@toku-mac
Copy link
Copy Markdown

Hello. Regarding arch/sim/src/sim/CMakeLists.txt.

  add_custom_command(
    TARGET nuttx
    POST_BUILD
    COMMAND
      ${Python3_EXECUTABLE}
      ${CMAKE_CURRENT_SOURCE_DIR}/../patch_macho_initsection.py
      $<TARGET_FILE:nuttx>
    COMMENT "Patching Mach-O init section type flags")
endif()

add_custom_command(TARGET ...) can only be used in the directory where the target was created.
Since the nuttx executable is created in the top-level CMakeLists.txt, attaching a POST_BUILD command to it from here would not work.
Please use a custom target hooked into nuttx_post instead.

  add_custom_target(
    sim_patch_macho_initsection ALL
    COMMAND
      ${Python3_EXECUTABLE}
      ${CMAKE_CURRENT_SOURCE_DIR}/../patch_macho_initsection.py
      $<TARGET_FILE:nuttx>
    DEPENDS nuttx
    COMMENT "Patching Mach-O init section type flags")
  add_dependencies(nuttx_post sim_patch_macho_initsection)
endif()

@toku-mac
Copy link
Copy Markdown

about arch/sim/src/patch_macho_initsection.py.
On macOS 11 and later with Apple Silicon, binary re-signing is required.

Writing the file invalidates the ad-hoc signature that ld attached at link time, which causes the kernel to SIGKILL the process at exec.

import argparse
import subprocess
import sys
    if patched:
        fat.write(args.binary)

        if sys.platform == "darwin":
            subprocess.run(
                ["codesign", "--force", "--sign", "-", args.binary],
                check=True,
            )

    return 0

@xiaoxiang781216
Copy link
Copy Markdown
Contributor Author

xiaoxiang781216 commented May 17, 2026

@toku-mac I have added you to my repo collaborator, please run the below command in your local nuttx git:

git remote add xiaoxiang781216 git@github.com:xiaoxiang781216/incubator-nuttx.git
git fetch xiaoxiang781216
git branch --track macos xiaoxiang781216/macos
git checkout macos
do the change you want
git commit --amend -s
git push -f xiaoxiang781216

since it's simpler to modify and update this patch by you directly.

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

Labels

Arch: simulator Issues related to the SIMulator Area: OS Components OS Components issues Size: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] our hack around __mod_init_func doesn't work for the latest macOS with chained fixups

3 participants