mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-10-17 02:45:05 +00:00
Fix for macOS symbol wrapping
This commit is contained in:
parent
a03daa4675
commit
4ce62e7a87
@ -12,14 +12,12 @@ tactility_add_module(app-module
|
||||
PRIV_REQUIRES TactilityKernelCpp
|
||||
)
|
||||
|
||||
# Tells source/io.cpp its real-syscall fallback must go through __real_read/write/close()
|
||||
# rather than calling ::read/::write/::close() directly, on every platform whose build wraps
|
||||
# those symbols (ESP-IDF always; POSIX except macOS, whose linker doesn't support --wrap) -
|
||||
# see Tactility/CMakeLists.txt and the top-level CMakeLists.txt for where that's applied.
|
||||
if (NOT APPLE)
|
||||
tactility_get_module_name(app-module MODULE_NAME)
|
||||
target_compile_definitions(${MODULE_NAME} PRIVATE TT_APP_IO_WRAPS_STDIO)
|
||||
endif ()
|
||||
# Tells source/io.cpp its real-syscall fallback must go through __real_read/write/close() rather
|
||||
# than calling ::read/::write/::close() directly, on every platform where those are wrapped: via
|
||||
# -Wl,--wrap= on ESP-IDF and non-Apple POSIX (see Tactility/CMakeLists.txt and the top-level
|
||||
# CMakeLists.txt), or via AppStdioWrap.cpp's dyld interpose on Apple (whose linker lacks --wrap).
|
||||
tactility_get_module_name(app-module MODULE_NAME)
|
||||
target_compile_definitions(${MODULE_NAME} PRIVATE TT_APP_IO_WRAPS_STDIO)
|
||||
|
||||
# install.cpp resolves an installed binary's fixed bin/<platform>/<binary>.so path itself, so it
|
||||
# needs the same platform-arch string app-posix-module's own CMakeLists.txt defines for its
|
||||
|
||||
@ -6,22 +6,20 @@ enable_language(C CXX ASM)
|
||||
file(GLOB_RECURSE TEST_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/source/*.cpp)
|
||||
add_executable(AppModuleTests EXCLUDE_FROM_ALL ${TEST_SOURCES})
|
||||
|
||||
if (NOT APPLE)
|
||||
# Provides __wrap_read/write/close for the -Wl,--wrap= below (see Tactility/CMakeLists.txt
|
||||
# for the canonical pairing of this file with those flags).
|
||||
target_sources(AppModuleTests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../../Tactility/Source/AppStdioWrap.cpp)
|
||||
endif ()
|
||||
# Provides __wrap_read/write/close: paired with the -Wl,--wrap= below on non-Apple platforms (see
|
||||
# Tactility/CMakeLists.txt), or self-registered via dyld interpose on Apple (see the file itself).
|
||||
target_sources(AppModuleTests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../../Tactility/Source/AppStdioWrap.cpp)
|
||||
|
||||
target_include_directories(AppModuleTests PRIVATE ${DOCTESTINC} ${CMAKE_CURRENT_LIST_DIR}/../private)
|
||||
|
||||
add_test(NAME AppModuleTests COMMAND AppModuleTests)
|
||||
|
||||
# Matches app-module's own TT_APP_IO_WRAPS_STDIO (see Modules/app-module/CMakeLists.txt):
|
||||
# io.cpp calls __real_read/write/close() on non-Apple POSIX, so any final link of it needs
|
||||
# these wraps too, or those symbols go unresolved. The printf-family/getc-family flags are
|
||||
# needed for the same reason: AppStdioWrap.cpp compiles those __wrap_* functions unconditionally
|
||||
# on this platform (see its own #if guard), and they reference __real_vfprintf/fputs/fputc/
|
||||
# fgetc/fgets - those only exist once the matching --wrap flag is passed.
|
||||
# Matches app-module's own TT_APP_IO_WRAPS_STDIO (see Modules/app-module/CMakeLists.txt): io.cpp
|
||||
# calls __real_read/write/close() on every POSIX platform, so any final link of it needs these
|
||||
# wraps too, or those symbols go unresolved. The printf-family/getc-family flags are needed for
|
||||
# the same reason: AppStdioWrap.cpp compiles those __wrap_* functions unconditionally on this
|
||||
# platform, and they reference __real_vfprintf/fputs/fputc/fgetc/fgets. --wrap provides all of
|
||||
# these __real_* symbols on non-Apple; AppStdioWrap.cpp provides its own on Apple (see that file).
|
||||
if (NOT APPLE)
|
||||
target_link_options(AppModuleTests PRIVATE
|
||||
"-Wl,--wrap=read" "-Wl,--wrap=write" "-Wl,--wrap=close"
|
||||
|
||||
@ -20,8 +20,15 @@ int __wrap_pthread_attr_setstack(pthread_attr_t* attr, void* stackaddr, size_t s
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <mach-o/dyld-interposing.h>
|
||||
|
||||
DYLD_INTERPOSE(__wrap_pthread_attr_setstack, pthread_attr_setstack)
|
||||
// <mach-o/dyld-interposing.h> isn't a public SDK header (it ships with dyld's own source, not
|
||||
// Xcode/Command Line Tools), so this reimplements its DYLD_INTERPOSE macro locally.
|
||||
#define TT_DYLD_INTERPOSE(replacement, replacee) \
|
||||
__attribute__((used)) static struct { const void* replacement; const void* replacee; } \
|
||||
tt_interpose_##replacee __attribute__((section("__DATA,__interpose"))) = { \
|
||||
(const void*)(unsigned long)&(replacement), (const void*)(unsigned long)&(replacee) \
|
||||
};
|
||||
|
||||
TT_DYLD_INTERPOSE(__wrap_pthread_attr_setstack, pthread_attr_setstack)
|
||||
|
||||
#endif
|
||||
|
||||
@ -188,7 +188,8 @@ else ()
|
||||
# Routes every pthread_attr_setstack() call (FreeRTOS's POSIX port hands each task a stack
|
||||
# carved out of its own heap through this) to __wrap_pthread_attr_setstack() in
|
||||
# Platforms/platform-posix/source/pthread_stack_wrap.c, which no-ops it - see that file for why.
|
||||
# --wrap is a GNU ld option; Apple's linker doesn't support it.
|
||||
# --wrap is a GNU ld option; Apple's linker doesn't support it, so that file instead
|
||||
# self-registers a dyld interpose for this symbol on Apple, needing no link flag here.
|
||||
if (NOT APPLE)
|
||||
target_link_options(Tactility PRIVATE "-Wl,--wrap=pthread_attr_setstack")
|
||||
# Routes every read()/write()/close() call to Tactility/Source/AppStdioWrap.cpp's
|
||||
@ -196,6 +197,7 @@ else ()
|
||||
# dispatch that lets an app's own stdio (e.g. a fileselection dialog's printf'd result
|
||||
# path) reach an AppStream a parent bound via app_manager_start_with_streams(). Mirrors
|
||||
# what ESP-IDF's build already does for the ESP32 target (see top-level CMakeLists.txt).
|
||||
# On Apple, AppStdioWrap.cpp self-registers the same routing via dyld interpose instead.
|
||||
target_link_options(Tactility PRIVATE "-Wl,--wrap=read" "-Wl,--wrap=write" "-Wl,--wrap=close")
|
||||
# glibc's printf/fprintf/etc don't call the public write() symbol internally (they're
|
||||
# already compiled into libc.so, out of --wrap's reach), so the read/write/close wrap
|
||||
@ -203,7 +205,8 @@ else ()
|
||||
# wrappable syscall stubs - so this block is POSIX-only. Wrapping these symbols instead
|
||||
# redirects OUR OWN calls to them (the only ones --wrap can rewrite) through
|
||||
# AppStdioWrap.cpp's __wrap_* functions, which check the target stream (stdout/stdin) and
|
||||
# fall back to the real libc function for any other FILE*.
|
||||
# fall back to the real libc function for any other FILE*. Apple gets the same routing via
|
||||
# dyld interpose, self-registered in that file - see its own #ifdef __APPLE__ block.
|
||||
target_link_options(Tactility PRIVATE
|
||||
"-Wl,--wrap=printf" "-Wl,--wrap=fprintf" "-Wl,--wrap=vprintf" "-Wl,--wrap=vfprintf"
|
||||
"-Wl,--wrap=puts" "-Wl,--wrap=fputs" "-Wl,--wrap=putchar" "-Wl,--wrap=fputc"
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Paired with -Wl,--wrap=read/write/close - see Tactility/CMakeLists.txt (POSIX) and the
|
||||
// top-level CMakeLists.txt (ESP32) for where that's applied. On a platform where it isn't
|
||||
// (currently: macOS, whose linker doesn't support --wrap), these are simply never called - real
|
||||
// read()/write()/close() calls go straight through unredirected.
|
||||
// Paired with -Wl,--wrap=read/write/close on POSIX (Tactility/CMakeLists.txt) and ESP32
|
||||
// (top-level CMakeLists.txt); self-registered via dyld interpose below on Apple, whose linker
|
||||
// doesn't support --wrap.
|
||||
#include <app/io.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
@ -24,21 +23,64 @@ int __wrap_close(int fd) {
|
||||
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
// --wrap also synthesizes __real_read/write/close automatically; dyld interpose doesn't, so
|
||||
// io.cpp's TT_APP_IO_WRAPS_STDIO fallback needs them defined here. dlsym(RTLD_NEXT, ...) is the
|
||||
// standard way to reach the true libSystem implementation despite the interpose below: a direct
|
||||
// call to read/write/close from this file would just recurse into __wrap_read/write/close, since
|
||||
// interpose rewrites every reference to those symbols in the process, this file included.
|
||||
#include <dlfcn.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
ssize_t __real_read(int fd, void* buffer, size_t size) {
|
||||
static auto real = reinterpret_cast<ssize_t (*)(int, void*, size_t)>(dlsym(RTLD_NEXT, "read"));
|
||||
return real(fd, buffer, size);
|
||||
}
|
||||
|
||||
ssize_t __real_write(int fd, const void* buffer, size_t size) {
|
||||
static auto real = reinterpret_cast<ssize_t (*)(int, const void*, size_t)>(dlsym(RTLD_NEXT, "write"));
|
||||
return real(fd, buffer, size);
|
||||
}
|
||||
|
||||
int __real_close(int fd) {
|
||||
static auto real = reinterpret_cast<int (*)(int)>(dlsym(RTLD_NEXT, "close"));
|
||||
return real(fd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// <mach-o/dyld-interposing.h> isn't a public SDK header (it ships with dyld's own source, not
|
||||
// Xcode/Command Line Tools), so this reimplements its DYLD_INTERPOSE macro locally; reused below
|
||||
// for the printf-family interposes too.
|
||||
#define TT_DYLD_INTERPOSE(replacement, replacee) \
|
||||
__attribute__((used)) static struct { const void* replacement; const void* replacee; } \
|
||||
tt_interpose_##replacee __attribute__((section("__DATA,__interpose"))) = { \
|
||||
(const void*)(unsigned long)&(replacement), (const void*)(unsigned long)&(replacee) \
|
||||
};
|
||||
|
||||
TT_DYLD_INTERPOSE(__wrap_read, read)
|
||||
TT_DYLD_INTERPOSE(__wrap_write, write)
|
||||
TT_DYLD_INTERPOSE(__wrap_close, close)
|
||||
|
||||
#endif // __APPLE__
|
||||
|
||||
// region glibc stdio wraps
|
||||
//
|
||||
// glibc's printf/fprintf/etc are compiled into libc.so and call an internal, non-exported write()
|
||||
// alias - --wrap=write (above) can't reach that call, only calls WE make to the public symbol.
|
||||
// These wraps instead redirect calls WE make to printf/fprintf/etc, the same trick as read/write/
|
||||
// close above. Newlib (ESP-IDF) doesn't have this gap - its stdio does call the wrappable syscall
|
||||
// stubs - so Tactility/CMakeLists.txt only applies the matching -Wl,--wrap= flags on POSIX.
|
||||
// libc's printf/fprintf/etc are compiled into the C library and call an internal, non-exported
|
||||
// write() alias - --wrap=write/the read/write/close interpose above can't reach that call, only
|
||||
// calls WE make to the public symbol. These wraps instead redirect calls WE make to printf/
|
||||
// fprintf/etc, the same trick as read/write/close above. Newlib (ESP-IDF) doesn't have this gap -
|
||||
// its stdio does call the wrappable syscall stubs - so this block is POSIX-only.
|
||||
//
|
||||
// Scoped to the printf/getc families only: fread/fwrite take an arbitrary FILE* and are already
|
||||
// used sitewide for real file I/O (e.g. File.cpp's readBinaryInternal), so wrapping them would
|
||||
// route every such call through this file's stdin/stdout check - a correctness risk for unrelated
|
||||
// code that isn't worth taking here. putc/getc are excluded too since glibc defines them as
|
||||
// code that isn't worth taking here. putc/getc are excluded too since libc defines them as
|
||||
// macros, not real calls, so wrapping those symbols wouldn't reliably intercept them.
|
||||
|
||||
#if !defined(ESP_PLATFORM) && !defined(__APPLE__)
|
||||
#if !defined(ESP_PLATFORM)
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
@ -54,6 +96,43 @@ int __real_fgetc(FILE* stream);
|
||||
char* __real_fgets(char* buffer, int size, FILE* stream);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
// --wrap synthesizes these automatically elsewhere; on Apple they're defined here via
|
||||
// dlsym(RTLD_NEXT, ...) instead - see the read/write/close __real_* block above for why.
|
||||
#include <dlfcn.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
int __real_vfprintf(FILE* stream, const char* format, va_list args) {
|
||||
static auto real = reinterpret_cast<int (*)(FILE*, const char*, va_list)>(dlsym(RTLD_NEXT, "vfprintf"));
|
||||
return real(stream, format, args);
|
||||
}
|
||||
|
||||
int __real_fputs(const char* s, FILE* stream) {
|
||||
static auto real = reinterpret_cast<int (*)(const char*, FILE*)>(dlsym(RTLD_NEXT, "fputs"));
|
||||
return real(s, stream);
|
||||
}
|
||||
|
||||
int __real_fputc(int c, FILE* stream) {
|
||||
static auto real = reinterpret_cast<int (*)(int, FILE*)>(dlsym(RTLD_NEXT, "fputc"));
|
||||
return real(c, stream);
|
||||
}
|
||||
|
||||
int __real_fgetc(FILE* stream) {
|
||||
static auto real = reinterpret_cast<int (*)(FILE*)>(dlsym(RTLD_NEXT, "fgetc"));
|
||||
return real(stream);
|
||||
}
|
||||
|
||||
char* __real_fgets(char* buffer, int size, FILE* stream) {
|
||||
static auto real = reinterpret_cast<char* (*)(char*, int, FILE*)>(dlsym(RTLD_NEXT, "fgets"));
|
||||
return real(buffer, size, stream);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // __APPLE__
|
||||
|
||||
namespace {
|
||||
|
||||
void writeAllToStdout(const void* data, size_t size) {
|
||||
@ -195,6 +274,20 @@ char* __wrap_fgets(char* buffer, int size, FILE* stream) {
|
||||
|
||||
}
|
||||
|
||||
#endif // !ESP_PLATFORM && !__APPLE__
|
||||
#ifdef __APPLE__
|
||||
TT_DYLD_INTERPOSE(__wrap_vprintf, vprintf)
|
||||
TT_DYLD_INTERPOSE(__wrap_printf, printf)
|
||||
TT_DYLD_INTERPOSE(__wrap_vfprintf, vfprintf)
|
||||
TT_DYLD_INTERPOSE(__wrap_fprintf, fprintf)
|
||||
TT_DYLD_INTERPOSE(__wrap_puts, puts)
|
||||
TT_DYLD_INTERPOSE(__wrap_fputs, fputs)
|
||||
TT_DYLD_INTERPOSE(__wrap_putchar, putchar)
|
||||
TT_DYLD_INTERPOSE(__wrap_fputc, fputc)
|
||||
TT_DYLD_INTERPOSE(__wrap_getchar, getchar)
|
||||
TT_DYLD_INTERPOSE(__wrap_fgetc, fgetc)
|
||||
TT_DYLD_INTERPOSE(__wrap_fgets, fgets)
|
||||
#endif // __APPLE__
|
||||
|
||||
#endif // !ESP_PLATFORM
|
||||
|
||||
// endregion
|
||||
|
||||
@ -20,9 +20,10 @@ target_include_directories(TactilityTests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../P
|
||||
add_test(NAME TactilityTests COMMAND TactilityTests)
|
||||
|
||||
# Matches Tactility/CMakeLists.txt's own set of --wrap flags: this binary also compiles
|
||||
# AppStdioWrap.cpp and links app-module (whose io.cpp calls __real_read/write/close() on
|
||||
# non-Apple POSIX - see TT_APP_IO_WRAPS_STDIO in Modules/app-module/CMakeLists.txt), so it
|
||||
# needs the same wraps applied or those go unresolved.
|
||||
# AppStdioWrap.cpp and links app-module (whose io.cpp calls __real_read/write/close() on every
|
||||
# POSIX platform - see TT_APP_IO_WRAPS_STDIO in Modules/app-module/CMakeLists.txt), so it needs
|
||||
# the same wraps applied on non-Apple, or those go unresolved; AppStdioWrap.cpp provides its own
|
||||
# on Apple via dyld interpose (see that file).
|
||||
if (NOT APPLE)
|
||||
target_link_options(TactilityTests PRIVATE
|
||||
"-Wl,--wrap=pthread_attr_setstack" "-Wl,--wrap=read" "-Wl,--wrap=write" "-Wl,--wrap=close"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user