Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codespellrc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ ignore-words-list =
infor,
inport,
ist,
lief,
lod,
mot,
mis,
Expand Down
38 changes: 38 additions & 0 deletions arch/sim/include/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,42 @@
#ifndef __ARCH_SIM_INCLUDE_ARCH_H
#define __ARCH_SIM_INCLUDE_ARCH_H

/****************************************************************************
* Pre-processor Definitions
****************************************************************************/

#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif

/****************************************************************************
* Public Data
****************************************************************************/

/* _sinit and _einit mark the beginning and end of the C++ constructor
* array. They are mapped to platform-specific linker symbols:
* macOS: section$start$__DATA_CONST$__mod_init_func /
* section$end$__DATA_CONST$__mod_init_func (Mach-O auto)
* On macOS, the section type flags are patched post-link to prevent
* dyld from auto-running constructors before NuttX is initialized.
*/

#ifdef CONFIG_HAVE_CXXINITIALIZE
# if defined(CONFIG_HOST_MACOS)
extern void (*_sinit[])(void)
__asm("section$start$__DATA_CONST$__mod_init_func");
extern void (*_einit[])(void)
__asm("section$end$__DATA_CONST$__mod_init_func");
# endif
#endif

#undef EXTERN
#ifdef __cplusplus
}
#endif

#endif /* __ARCH_SIM_INCLUDE_ARCH_H */
22 changes: 8 additions & 14 deletions arch/sim/src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -139,24 +139,15 @@ sim_hostfs.c: hostfs.h

STDLIBS += -lpthread
ifeq ($(CONFIG_HOST_MACOS),y)
ifeq ($(CONFIG_HAVE_CXXINITIALIZE),y)
# Note: sim_macho_init.c is not in CSRCS because it's picky about
# the place in the object list for linking. Namely, its constructor
# should be the first one in the executable.
HEADSRC = sim_macho_init.c

# sim_macho_init.c is not compatible with chained fixups.
# cf. https://github.com/apache/nuttx/issues/15208
ifeq ($(shell $(LD) -ld_classic -no_fixup_chains 2>&1 | grep "unknown option"),)
ifeq ($(CONFIG_HAVE_CXXINITIALIZE),y)
LDLINKFLAGS += -ld_classic -no_fixup_chains
LDFLAGS += -Wl,-ld_classic,-no_fixup_chains
endif
endif

# Keep the simulator executable from exporting NuttX symbols. Otherwise dyld
# may resolve host libc references against NuttX's internal libc implementation
# during process initialization, which can crash before main() runs.
LDFLAGS += -Wl,-exported_symbol,__mh_execute_header
# Keep the simulator executable from exporting NuttX symbols. Otherwise dyld
# may resolve host libc references against NuttX's internal libc implementation
# during process initialization, which can crash before main() runs.
LDFLAGS += -Wl,-exported_symbol,__mh_execute_header
else
STDLIBS += -lrt
endif
Expand Down Expand Up @@ -517,6 +508,9 @@ else
$(Q) $(call LINK_ALLSYMS_KASAN)
$(Q) $(call LINK_ALLSYMS_KASAN)
$(Q) $(call LINK_ALLSYMS_KASAN)
endif
ifeq ($(CONFIG_HOST_MACOS)$(CONFIG_HAVE_CXXINITIALIZE),yy)
$(Q) python3 $(ARCH_SRCDIR)/patch_macho_initsection.py $(TOPDIR)/$@
endif
$(Q) $(NM) $(TOPDIR)/$@ | \
grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
Expand Down
3 changes: 3 additions & 0 deletions arch/sim/src/cmake/Toolchain.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ endif()
if(CONFIG_COVERAGE_ALL)
if(CONFIG_ARCH_TOOLCHAIN_GCC)
add_compile_options(-fprofile-arcs -ftest-coverage -fno-inline)
if(APPLE)
add_link_options(-fprofile-arcs -ftest-coverage)
endif()
elseif(CONFIG_ARCH_TOOLCHAIN_CLANG)
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
add_link_options(-fprofile-instr-generate)
Expand Down
83 changes: 83 additions & 0 deletions arch/sim/src/patch_macho_initsection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
############################################################################
# arch/sim/src/patch_macho_initsection.py
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.
#
############################################################################

"""Patch Mach-O init section type flags to prevent dyld from
auto-running C++ constructors.

Changes MOD_INIT_FUNC_POINTERS (0x9) section types to REGULAR (0x0)
so that dyld ignores them. NuttX will invoke the constructors
explicitly from lib_cxx_initialize().

Requires: pip install lief
"""

import argparse
import subprocess
import sys

try:
import lief
except ImportError:
print(
"Error: lief is required. Install with: pip install lief",
file=sys.stderr,
)
sys.exit(1)


def main():
parser = argparse.ArgumentParser(description="Patch Mach-O init section type flags")
parser.add_argument("binary", help="Path to the Mach-O binary")
args = parser.parse_args()

binary = lief.MachO.parse(args.binary)
if binary is None:
print(f"Error: failed to parse {args.binary}", file=sys.stderr)
sys.exit(1)

fat = binary.at(0)
T = lief.MachO.Section.TYPE
patched = 0
for section in fat.sections:
if section.type == T.MOD_INIT_FUNC_POINTERS:
section.type = T.REGULAR
patched += 1

if patched:
fat.write(args.binary)

# Re-sign the binary on macOS: writing the file invalidates the
# ad-hoc signature that ld attached at link time, which causes the
# kernel to SIGKILL the process at exec with
# "CODE SIGNING: rejecting invalid page ...".
if sys.platform == "darwin":
subprocess.run(
["codesign", "--force", "--sign", "-", args.binary],
check=True,
)

return 0


if __name__ == "__main__":
sys.exit(main())
34 changes: 26 additions & 8 deletions arch/sim/src/sim/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ set(HOSTSRCS)
set(HOST_INCLUDE_DIRS)
set(STDLIBS pthread)

if(CONFIG_COVERAGE_TOOLCHAIN)
if(CONFIG_COVERAGE_TOOLCHAIN
AND CONFIG_ARCH_TOOLCHAIN_GCC
AND NOT APPLE)
list(APPEND STDLIBS gcov)
endif()

Expand Down Expand Up @@ -150,14 +152,30 @@ list(
sim_hosttime.c
sim_hostuart.c)

# Note: sim_macho_init.c is picky about the place in the object list for
# linking. Namely, its constructor should be the first one in the executable.
# For now, we are just assuming no other files in HOSTSRCS provide constructors.
if(CONFIG_HOST_MACOS)
if(CONFIG_HAVE_CXXINITIALIZE)
list(APPEND HOSTSRCS sim_macho_init.c)
target_link_options(nuttx PRIVATE -Wl,-ld_classic,-no_fixup_chains)
endif()
# Provide a sim/macOS specific override of clock_gettime() that translates
# Darwin clock IDs to NuttX's CLOCK_MONOTONIC. Keep all macOS specific logic
# in arch/sim/ instead of polluting sched/clock/clock_gettime.c.
list(APPEND SRCS macos/sim_clock_gettime.c)
endif()

if(CONFIG_HOST_MACOS AND CONFIG_HAVE_CXXINITIALIZE)
# Keep classic __mod_init_func format so post-link lief patching works
target_link_options(nuttx PRIVATE -Wl,-ld_classic,-no_fixup_chains)

# NOTE: add_custom_command(TARGET ...) can only be used in the directory where
# the target was created. The `nuttx` executable is created in the top-level
# CMakeLists.txt, so we cannot attach a POST_BUILD command to it here. 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()

if(CONFIG_SIM_CAMERA_V4L2)
Expand Down
94 changes: 94 additions & 0 deletions arch/sim/src/sim/macos/sim_clock_gettime.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/****************************************************************************
* arch/sim/src/sim/macos/sim_clock_gettime.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/

/****************************************************************************
* Included Files
****************************************************************************/

#include <nuttx/config.h>

#include <time.h>
#include <errno.h>

#include <nuttx/clock.h>

/****************************************************************************
* Pre-processor Definitions
****************************************************************************/

/* Rust code (and any other code) built for the macOS host uses Darwin's libc
* clock IDs. When such code is linked into the NuttX simulator, those
* Darwin-specific values reach NuttX's clock_gettime() implementation
* instead of Darwin's one. Translate them here so that the common
* sched/clock implementation does not need to know about Darwin clock IDs.
*
* _CLOCK_MONOTONIC == 6 (Darwin / XNU)
* _CLOCK_UPTIME_RAW == 8 (Darwin / XNU)
*/

#define DARWIN_CLOCK_MONOTONIC 6
#define DARWIN_CLOCK_UPTIME_RAW 8

/****************************************************************************
* Public Functions
****************************************************************************/

/****************************************************************************
* Name: clock_gettime
*
* Description:
* sim/macOS specific override of clock_gettime() that translates Darwin
* clock IDs (CLOCK_MONOTONIC=6, CLOCK_UPTIME_RAW=8) into NuttX's
* CLOCK_MONOTONIC before delegating to the common nxclock_gettime()
* implementation.
*
* This override is required because Rust crates built for the macOS host
* call libc::clock_gettime() with Darwin's clock IDs. When the resulting
* object code is linked into the NuttX simulator, the call is resolved to
* NuttX's clock_gettime() instead of Darwin's one.
*
* The common POSIX clock_gettime() in sched/clock/clock_gettime.c is
* declared as a weak symbol, so this strong definition takes precedence
* when CONFIG_HOST_MACOS is enabled.
*
****************************************************************************/

__attribute__((visibility("hidden")))
int clock_gettime(clockid_t clock_id, struct timespec *tp)
{
int ret;

if (clock_id == DARWIN_CLOCK_MONOTONIC ||
clock_id == DARWIN_CLOCK_UPTIME_RAW)
{
clock_id = CLOCK_MONOTONIC;
}

ret = nxclock_gettime(clock_id, tp);
if (ret < 0)
{
set_errno(-ret);
return ERROR;
}

return OK;
}
Loading
Loading