Most of the work for this was done by Louis Brunner.
Thanks Louis.
MIPS64/Linux, RISCV64/Linux, ARM/Android, ARM64/Android, MIPS32/Android,
X86/Android, X86/Solaris, AMD64/Solaris, X86/macOS, AMD64/macOS.
X86/FreeBSD, AMD64/FreeBSD and ARM64/FreeBSD. There is preliminary support
-for nanoMIPS/Linux. macOS is supported up to version 10.15 Catalina.
+for nanoMIPS/Linux. macOS is supported up to version 11 Big Sur (amd64 only).
* ==================== CORE CHANGES ===================
s390x: Machine models older than z196 are no longer supported.
-Initial support for macOS 10.14 Mojave has been added.
+Support for the following macOS versions has been added
+10,13 High Sierra (bug fixes)
+10.14 Mojave
+10.15 Calalina
+11.0 Big Sur
* ==================== TOOL CHANGES ===================
AC_MSG_RESULT([Darwin 19.x (${kernel}) / macOS 10.15 Catalina])
DARWIN_VERS=$DARWIN_10_15
;;
+ 20.*)
+ AC_MSG_RESULT([Darwin 20.x (${kernel}) / macOS 11 Big Sur])
+ DARWIN_VERS=$DARWIN_11_00
+ ;;
*)
AC_MSG_RESULT([unsupported (${darwin_platform} ${kernel})])
AC_MSG_ERROR([Valgrind works on Darwin 12.x-19.x (Mac OS X 10.8-10.11, macOS 10.12-10.15)])
m_mach/mach_traps-x86-darwin.S \
m_mach/mach_traps-amd64-darwin.S \
m_mach/mig_strncpy.c \
+ m_mach/dyld_cache.c \
m_replacemalloc/replacemalloc_core.c \
m_scheduler/sched-lock.c \
m_scheduler/sched-lock-generic.c \
#include <mach-o/fat.h>
#include <mach/i386/thread_status.h>
-/* Check that DARWIN_VERS is defined */
+/* Get hold of DARWIN_VERS, and check it has a sane value. */
#include "config.h"
-#if !defined(DARWIN_VERS)
-# error "DARWIN_VERS not defind. This file only compiles on Darwin."
+#if DARWIN_VERS != DARWIN_10_5 && DARWIN_VERS != DARWIN_10_6 \
+ && DARWIN_VERS != DARWIN_10_7 && DARWIN_VERS != DARWIN_10_8 \
+ && DARWIN_VERS != DARWIN_10_9 && DARWIN_VERS != DARWIN_10_10 \
+ && DARWIN_VERS != DARWIN_10_11 && DARWIN_VERS != DARWIN_10_12 \
+ && DARWIN_VERS != DARWIN_10_13 && DARWIN_VERS != DARWIN_10_14 \
+ && DARWIN_VERS != DARWIN_10_15 && DARWIN_VERS != DARWIN_11_00 \
+ && DARWIN_VERS != DARWIN_12_00 && DARWIN_VERS != DARWIN_13_00 \
+ && DARWIN_VERS != DARWIN_14_00 && DARWIN_VERS != DARWIN_15_00 \
+ && DARWIN_VERS != DARWIN_15_04 && DARWIN_VERS != DARWIN_26_00
+# error "Unknown DARWIN_VERS value. This file only compiles on Darwin."
#endif
{ struct fat_header* fh_be;
struct fat_header fh;
struct mach_header_64* mh;
-
+
// Assume initially that we have a thin image, and update
// these if it turns out to be fat.
ii->macho_img = ii->img;
+ fh.nfat_arch * sizeof(struct fat_arch))
fail("Invalid Mach-O file (1 too small).");
- for (f = 0, arch_be = (struct fat_arch *)(fh_be+1);
+ for (f = 0, arch_be = (struct fat_arch *)(fh_be+1);
f < fh.nfat_arch;
f++, arch_be++) {
Int cputype;
seg__pagezero->vmaddr = 0;
# endif
- out:
+ out:
if (ii.img)
unmap_image(&ii);
}
if (argc != 4)
fail("args: -stack_addr-arg -stack_size-arg "
- "name-of-tool-executable-to-modify");
+ "name-of-tool-executable-to-modify");
r= sscanf(argv[1], "0x%llx", &req_stack_addr);
if (r != 1) fail("invalid stack_addr arg");
if (!is_plausible_tool_exe_name(argv[3]))
fail("implausible tool exe name -- not of the form *-{x86,amd64}-darwin");
- fprintf(stderr, "fixup_macho_loadcmds: examining tool exe: %s\n",
+ fprintf(stderr, "fixup_macho_loadcmds: examining tool exe: %s\n",
argv[3] );
modify_macho_loadcmds( argv[3], req_stack_addr - req_stack_size,
req_stack_size );
di->ddump_frames = VG_(clo_debug_dump_frames);
}
+#if DARWIN_VERS >= DARWIN_11_00
+ di->from_memory = False;
+#endif
+
return di;
}
#endif /* defined(VGO_linux) || defined(VGO_darwin) || defined(VGO_solaris) || defined(VGO_freebsd) */
+#if defined(VGO_darwin) && DARWIN_VERS >= DARWIN_11_00
+// Special version of VG_(di_notify_mmap) specifically to read debug info from the DYLD Shared Cache (DSC)
+// We only use this on macOS 11.0 and later, because Apple stopped shipping dylib on-disk then.
+
+ULong VG_(di_notify_dsc)( const HChar* filename, Addr header, SizeT len )
+{
+ DebugInfo* di;
+ Int rw_load_count;
+ const Bool debug = VG_(debugLog_getLevel)() >= 3;
+
+ if (debug)
+ VG_(dmsg)("di_notify_dsc-1: %s at %#lx-%#lx\n", filename, header, header+len);
+
+ if (!ML_(check_macho_and_get_rw_loads_from_memory)( (const void*) header, len, &rw_load_count ))
+ return 0;
+
+ /* See if we have a DebugInfo for this filename. If not,
+ create one. */
+ di = find_or_create_DebugInfo_for( filename );
+ vg_assert(di);
+
+ di->from_memory = True;
+
+ if (di->have_dinfo) {
+ if (debug)
+ VG_(dmsg)("di_notify_dsc-2x: "
+ "ignoring mapping because we already read debuginfo "
+ "for DebugInfo* %p\n", di);
+ return 0;
+ }
+
+ if (debug)
+ VG_(dmsg)("di_notify_dsc-2: "
+ "noting details in DebugInfo* at %p\n", di);
+
+ /* Note the details about the mapping. */
+ DebugInfoMapping map;
+ map.avma = header;
+ map.size = len;
+ map.foff = 0;
+ map.rx = True;
+ map.rw = False;
+ map.ro = False;
+ VG_(addToXA)(di->fsm.maps, &map);
+
+ /* Update flags about what kind of mappings we've already seen. */
+ di->fsm.have_rx_map |= True;
+
+ vg_assert(!di->have_dinfo);
+
+ if (debug)
+ VG_(dmsg)("di_notify_dsc-3: "
+ "achieved accept state for %s\n", filename);
+ return di_notify_ACHIEVE_ACCEPT_STATE ( di );
+}
+#endif
/*------------------------------------------------------------*/
/*--- ---*/
if (!(ce->used <= ce->size)) goto fail;
if (ce->fromC) {
// ce->size can be anything, but ce->used must be either the
- // same or zero, in the case that it hasn't been set yet.
- // Similarly, ce->off must either be above the real_size
+ // same or zero, in the case that it hasn't been set yet.
+ // Similarly, ce->off must either be above the real_size
// threshold, or zero if it hasn't been set yet.
if (!(ce->off >= img->real_size || ce->off == 0)) goto fail;
if (!(ce->off + ce->used <= img->size)) goto fail;
static Bool parse_Frame_le64_le64_le64_bytes (
const Frame* fr, const HChar* tag,
/*OUT*/ULong* n1, /*OUT*/ULong* n2, /*OUT*/ULong* n3,
- /*OUT*/UChar** data, /*OUT*/ULong* n_data
+ /*OUT*/UChar** data, /*OUT*/ULong* n_data
)
{
vg_assert(VG_(strlen)(tag) == 4);
UInt delay = now - t_last;
t_last = now;
nread += len;
- VG_(printf)("XXXXXXXX (tot %'llu) read %'lu offset %'llu delay %'u\n",
+ VG_(printf)("XXXXXXXX (tot %'llu) read %'lu offset %'llu delay %'u\n",
nread, len, off, delay);
}
if (img->source.is_local) {
// Simple: just read it
-
+ if (img->source.fd == -1) {
+ VG_(memcpy)(&ce->data[0], ((const char *)img->source.session_id) + off, len);
+ } else {
// PJF not quite so simple - see
// https://bugs.kde.org/show_bug.cgi?id=480405
// if img->source.fd was opened with O_DIRECT the memory needs
}
#endif
vg_assert(!sr_isError(sr));
+ }
} else {
// Not so simple: poke the server
vg_assert(img->source.session_id > 0);
end_of_else_clause:
{}
}
-
+
ce->off = off;
ce->used = len;
ce->fromC = False;
|| /* size is unrepresentable as a SizeT */
size != (DiOffT)(SizeT)(size)) {
VG_(close)(sr_Res(fd));
- return NULL;
+ return NULL;
}
DiImage* img = ML_(dinfo_zalloc)("di.image.ML_iflf.1", sizeof(DiImage));
return img;
}
+/* Create an image from a place in memory, this is to support certain use cases (DSC on macOS)
+ where images are already loaded in memory without changing every usage of DiImage. */
+DiImage* ML_(img_from_memory)(Addr a, SizeT size, const HChar* fullpath)
+{
+ if (size == 0 || size == DiOffT_INVALID
+ || /* size is unrepresentable as a SizeT */
+ size != (DiOffT)(SizeT)(size)) {
+ return NULL;
+ }
+
+ DiImage* img = ML_(dinfo_zalloc)("di.image.ML_iflf.1", sizeof(DiImage));
+ img->source.is_local = True;
+ img->source.fd = -1;
+ img->source.session_id = a; // FIXME: hacky, but avoids a new variable
+ img->size = size;
+ img->real_size = size;
+ img->ces_used = 0;
+ img->source.name = ML_(dinfo_strdup)("di.image.ML_iflf.2", fullpath);
+ img->cslc = NULL;
+ img->cslc_size = 0;
+ img->cslc_used = 0;
+
+ /* Force the zeroth entry to be the first chunk of the file.
+ That's likely to be the first part that's requested anyway, and
+ loading it at this point forcing img->cent[0] to always be
+ non-empty, thereby saving us an is-it-empty check on the fast
+ path in get(). */
+ UInt entNo = alloc_CEnt(img, CACHE_ENTRY_SIZE, False/*!fromC*/);
+ vg_assert(entNo == 0);
+ set_CEnt(img, 0, 0);
+
+ return img;
+}
/* Create an image from a file on a remote debuginfo server. This is
if (!set_blocking(sd))
return NULL;
Int one = 1;
- Int sr = VG_(setsockopt)(sd, VKI_IPPROTO_TCP, VKI_TCP_NODELAY,
+ Int sr = VG_(setsockopt)(sd, VKI_IPPROTO_TCP, VKI_TCP_NODELAY,
&one, sizeof(one));
vg_assert(sr == 0);
{
vg_assert(img != NULL);
if (img->source.is_local) {
+ if (img->source.fd != -1) {
/* Close the file; nothing else to do. */
vg_assert(img->source.session_id == 0);
VG_(close)(img->source.fd);
+ }
} else {
/* Close the socket. The server can detect this and will scrub
the connection when it happens, so there's no need to tell it
This helps performance a lot during ML_(addLineInfo) etc., which can
easily be invoked hundreds of thousands of times. */
DebugInfoMapping* last_rx_map;
+
+#if DARWIN_VERS >= DARWIN_11_00
+ /* Indicate that this debug info was loaded from memory (i.e. DSC)
+ instead than from a file. This means that some data might be missing (e.g. rw data). */
+ Bool from_memory;
+#endif
};
/* --------------------- functions --------------------- */
#include "pub_core_mallocfree.h"
#include "pub_core_machine.h"
#include "pub_core_ume.h"
+#include "pub_core_mach.h"
#include "pub_core_options.h"
#include "pub_core_tooliface.h" /* VG_TRACK */
#include "pub_core_threadstate.h" /* ThreadArchState */
#include "pub_core_pathscan.h" /* find_executable */
#include "pub_core_initimg.h" /* self */
-#include "pub_core_mach.h"
/*====================================================================*/
Also, remove any binding for VALGRIND_LAUNCHER=. The client should
not be able to see this.
+ Before macOS 11:
Also, add DYLD_SHARED_REGION=avoid, because V doesn't know how
to process the dyld shared cache file.
+ Since macOS 11:
+ Use DYLD_SHARED_REGION=use because system libraries aren't provided outside the cache anymore.
+ This means we need to start processing the dyld shared cache file.
+
Also, change VYLD_* (mangled by launcher) back to DYLD_*.
If this needs to handle any more variables it should be hacked
const HChar* preload_core = "vgpreload_core";
const HChar* ld_preload = "DYLD_INSERT_LIBRARIES=";
const HChar* dyld_cache = "DYLD_SHARED_REGION=";
+#if DARWIN_VERS >= DARWIN_11_00
+ const HChar* dyld_cache_value= "use";
+#else
const HChar* dyld_cache_value= "avoid";
+#endif
const HChar* v_launcher = VALGRIND_LAUNCHER "=";
Int ld_preload_len = VG_(strlen)( ld_preload );
Int dyld_cache_len = VG_(strlen)( dyld_cache );
*cpp = cp;
- ld_preload_done = True;
+ dyld_cache_done = True;
}
}
auxsize += 2 * sizeof(HChar **);
if (info->executable_path) {
stringsize += 1 + VG_(strlen)(info->executable_path);
+#if SDK_VERS >= SDK_10_14_6
+ stringsize += 16; // executable_path=
+#endif
}
+#if defined(VGA_arm64)
+ // This is required so that dyld can load our dylib specified in DYLD_INSERT_LIBRARIES
+#define EXTRA_APPLE_ARG "arm64e_abi=all"
+ stringsize += VG_(strlen)(EXTRA_APPLE_ARG) + 1;
+ auxsize += sizeof(Word);
+#endif
+
/* Darwin mach_header */
if (info->dynamic) auxsize += sizeof(Word);
auxsize + /* auxv */
VG_ROUNDUP(stringsize, sizeof(Word)); /* strings (aligned) */
- if (0) VG_(printf)("stacksize = %d\n", stacksize);
+ if (0) VG_(printf)("stacksize = %u\n", stacksize);
/* client_SP is the client's stack pointer */
client_SP = clstack_end + 1 - stacksize;
VG_(clstk_end) = clstack_end;
if (0)
- VG_(printf)("stringsize=%d auxsize=%d stacksize=%d maxsize=0x%x\n"
+ VG_(printf)("stringsize=%u auxsize=%u stacksize=%u maxsize=0x%x\n"
"clstack_start %p\n"
"clstack_end %p\n",
- stringsize, auxsize, stacksize, (Int)clstack_max_size,
+ stringsize, auxsize, stacksize, (UInt)clstack_max_size,
(void*)clstack_start, (void*)clstack_end);
/* ==================== allocate space ==================== */
*ptr = (Addr)copy_str(&strtab, *cpp);
*ptr++ = 0;
- /* --- executable_path + NULL --- */
- if (info->executable_path)
+ /* --- executable_path --- */
+ if (info->executable_path) {
+#if SDK_VERS >= SDK_10_14_6
+ Int executable_path_len = VG_(strlen)(info->executable_path) + 16 + 1;
+ HChar *executable_path = VG_(malloc)("initimg-darwin.scs.1", executable_path_len);
+ VG_(snprintf)(executable_path, executable_path_len, "executable_path=%s", info->executable_path);
+ *ptr++ = (Addr)copy_str(&strtab, executable_path);
+ VG_(free)(executable_path);
+#else
*ptr++ = (Addr)copy_str(&strtab, info->executable_path);
- else
- *ptr++ = 0;
+#endif
+ }
+ // FIXME PJF there was an extra *ptr++ = 0; in an else here
+ // there is a good chance that executable_path is never NULL so itr was nevwer used
+
+#if defined(VGA_arm64)
+ *ptr++ = (Addr)copy_str(&strtab, EXTRA_APPLE_ARG);
+#endif
+
*ptr++ = 0;
vg_assert((strtab-stringbase) == stringsize);
}
HChar resolved_name[VKI_PATH_MAX];
VG_(realpath)(exe_name, resolved_name);
- VG_(resolved_exename) = VG_(strdup)("initimg-darwin.sre.1", resolved_name);
+ VG_(resolved_exename) = VG_(strdup)("initimg-darwin.scs.2", resolved_name);
}
/* client_SP is pointing at client's argc/argv */
/*--------------------------------------------------------------------*/
/*--- end ---*/
/*--------------------------------------------------------------------*/
+
--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- DYLD Cache dyld_cache.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (c) 2020-2025 Louis Brunner <louis.brunner.fr@gmail.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+// While dyld_caching as existed for longer than that
+// we have used DYLD_SHARED_REGION=avoid in the past
+//
+// Starting with macOS 11 (Big Sur), it isn't an option anymore
+// as some dylib are not provided in file format anymore
+#if defined(VGO_darwin) && DARWIN_VERS >= DARWIN_11_00
+
+#include "pub_core_debuginfo.h" // VG_(di_notify_dsc)
+#include "pub_core_debuglog.h" // VG_(debugLog)
+#include "pub_core_mach.h" // VG_(dyld_cache_*)
+#include "pub_core_syscall.h" // VG_(do_syscall1)
+#include "pub_core_libcbase.h" // VG_(strncmp)
+#include "pub_core_libcprint.h" // VG_(dmsg)
+#include "pub_core_libcfile.h" // VG_(stat)
+#include "vki/vki-scnums-darwin.h" // __NR_shared_region_check_np
+#include "priv_dyld_internals.h" // CACHE_MAGIC_*, dyld_cache_header, etc
+
+// Required by private headers underneath
+#include "pub_core_libcassert.h" // vg_assert
+#include "pub_core_threadstate.h" // ThreadState
+
+// FIXME: probably shouldn't include this directly?
+#include "m_syswrap/priv_syswrap-generic.h" // ML_(notify_core_and_tool_of_mmap)
+
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+
+// Only supported on macOS 11 onwards which is 64bit only
+# define MACH_HEADER mach_header_64
+# define MAGIC MH_MAGIC_64
+
+static void output_text_debug_info(const dyld_cache_image_text_info* textInfo);
+static void output_debug_info(const dyld_cache_header* dyld_cache);
+
+typedef struct {
+ const dyld_cache_header* header;
+ Addr slide;
+ Bool tried;
+} DYLDCache;
+
+static DYLDCache dyld_cache = {
+ .header = NULL,
+ .slide = 0,
+ .tried = False,
+};
+
+static Addr calculate_relative(const dyld_cache_header * header, Addr offset) {
+ return (Addr)header + offset;
+}
+
+static Addr calculate_unslid(Addr addr) {
+ return addr + dyld_cache.slide;
+}
+
+static int try_to_init_header(Addr address) {
+ const dyld_cache_header* header = (const dyld_cache_header *) address;
+
+ if (
+#if defined(VGA_amd64)
+ VG_(strcmp)(header->magic, CACHE_MAGIC_x86_64) != 0
+ && VG_(strcmp)(header->magic, CACHE_MAGIC_x86_64_HASWELL) != 0
+#elif defined(VGA_arm64)
+ VG_(strcmp)(header->magic, CACHE_MAGIC_arm64) != 0
+ && VG_(strcmp)(header->magic, CACHE_MAGIC_arm64e) != 0
+#else
+ 0
+#error "unknown architecture"
+#endif
+ ) {
+ VG_(debugLog)(2, "dyld_cache", "ERROR: incompatible shared dyld cache (%s)\n", header->magic);
+ return 0;
+ }
+
+ if (header->mappingCount < 1) {
+ VG_(debugLog)(2, "dyld_cache", "ERROR: no mappings in the dyld cache\n");
+ return 0;
+ }
+
+ VG_(debugLog)(2, "dyld_cache", "shared dyld cache format: %d / %#x\n", header->formatVersion, header->mappingOffset);
+ output_debug_info(header);
+
+ const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)(calculate_relative(header, header->mappingOffset));
+ for (int i = 0; i < header->mappingCount; ++i) {
+ const dyld_cache_mapping_info* mapping = &mappings[i];
+ Addr map_addr = calculate_unslid(mapping->address);
+ VG_(debugLog)(5, "dyld_cache",
+ "mapping[%d]{\n"
+ " .address: %#lx,\n"
+ " .size: %llu (%#llx),\n"
+ " .fileOffset: %#lx,\n"
+ " .maxProt: %#x,\n"
+ " .initProt: %#x,\n"
+ "}\n",
+ i,
+ map_addr,
+ mapping->size,
+ mapping->size,
+ calculate_relative(header, mapping->fileOffset),
+ mapping->maxProt,
+ mapping->initProt
+ );
+ ML_(notify_core_and_tool_of_mmap)(map_addr, mapping->size, mapping->initProt, VKI_MAP_PRIVATE | VKI_MAP_ANONYMOUS, -1, 0);
+ }
+
+ if (dyld_cache.header->mappingOffset >= __offsetof(dyld_cache_header, dynamicDataMaxSize) && header->dynamicDataMaxSize > 0) {
+ ML_(notify_core_and_tool_of_mmap)(calculate_relative(header, header->dynamicDataOffset), header->dynamicDataMaxSize, VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE | VKI_MAP_ANONYMOUS, -1, 0);
+ }
+
+ return 1;
+}
+
+static int try_to_init(void) {
+ // Read address of the shared cache which is mapped in our address space
+ // and tell Valgrind about it so we avoid false-positives and massive suppression files
+ {
+ Addr cache_address;
+ if (sr_Res(VG_(do_syscall1)(__NR_shared_region_check_np, (UWord)&cache_address)) != 0) {
+ VG_(debugLog)(2, "dyld_cache", "ERROR: could not get shared dyld cache address\n");
+ return 0;
+ }
+ VG_(debugLog)(2, "dyld_cache", "shared dyld cache found: %#lx\n", cache_address);
+
+ // FIXME: should be after `try_to_init_header` but we also need the slide calculate _before_
+ dyld_cache.header = (const dyld_cache_header *) cache_address;
+ const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)(calculate_relative(dyld_cache.header, dyld_cache.header->mappingOffset));
+ dyld_cache.slide = cache_address - mappings[0].address;
+ VG_(debugLog)(2, "dyld_cache", "dyld cache slide: %#lx\n", dyld_cache.slide);
+
+ if (!try_to_init_header(cache_address)) {
+ return 0;
+ }
+
+ if (dyld_cache.header->mappingOffset >= __offsetof(dyld_cache_header, subCacheArrayCount)) {
+ Bool sub_cache_v2 = dyld_cache.header->mappingOffset > __offsetof(dyld_cache_header, cacheSubType);
+ Addr sub_caches = calculate_relative(dyld_cache.header, dyld_cache.header->subCacheArrayOffset);
+
+ for (int i = 0; i < dyld_cache.header->subCacheArrayCount; ++i) {
+ Addr sub_cache_addr;
+
+ VG_(debugLog)(2, "dyld_cache", "found sub cache %d (v2: %d)\n", i, sub_cache_v2);
+
+ if (sub_cache_v2) {
+ const dyld_subcache_entry* sub_cache = &((const dyld_subcache_entry*) sub_caches)[i];
+ const uint8_t* u = sub_cache->uuid;
+ sub_cache_addr = calculate_relative(dyld_cache.header, sub_cache->cacheVMOffset);
+ VG_(debugLog)(5, "dyld_cache",
+ "sub_cache_v2[%d]{\n"
+ " .uuid: %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x,\n"
+ " .cacheVMOffset: %#lx,\n"
+ " .fileSuffix: %s,\n"
+ "}\n",
+ i,
+ u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15],
+ sub_cache_addr,
+ sub_cache->fileSuffix
+ );
+
+ } else {
+ const dyld_subcache_entry_v1* sub_cache = &((const dyld_subcache_entry_v1*) sub_caches)[i];
+ const uint8_t* u = sub_cache->uuid;
+ sub_cache_addr = calculate_relative(dyld_cache.header, sub_cache->cacheVMOffset);
+ VG_(debugLog)(5, "dyld_cache",
+ "sub_cache_v1[%d]{\n"
+ " .uuid: %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x,\n"
+ " .cacheVMOffset: %#lx,\n"
+ "}\n",
+ i,
+ u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15],
+ sub_cache_addr
+ );
+ }
+
+ if (!try_to_init_header(sub_cache_addr)) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+Addr VG_(dyld_cache_get_slide)(void) {
+ return dyld_cache.slide;
+}
+
+int ensure_init(void) {
+ if (dyld_cache.header != NULL) {
+ return 1;
+ }
+
+ // FIXME: unlikely race condition?
+ if (dyld_cache.tried) {
+ return 0;
+ }
+ dyld_cache.tried = True;
+
+ if (!try_to_init()) {
+ VG_(dmsg)(
+ "WARNING: could not read from dyld shared cache (DSC)\n"
+ "Some reports (especially memory leaks) might be missing or incorrect (false-positives)\n"
+ );
+ return 0;
+ }
+ // We currently detect if dyld is loading/using a library by checking if stat64 fails.
+ // However, dyld doesn't seem to call stat64 for all of them anymore.
+ // All arm64 binaries are executables but some x86 ones might not be so let's avoid them just to be safe.
+ VG_(dyld_cache_load_library)("/usr/lib/system/libsystem_kernel.dylib");
+ VG_(dyld_cache_load_library)("/usr/lib/system/libsystem_pthread.dylib");
+ VG_(dyld_cache_load_library)("/usr/lib/system/libsystem_platform.dylib");
+
+ return 1;
+}
+
+void VG_(dyld_cache_init)(const HChar* tool) {
+ // drd crashes if you map memory segments in m_main
+ if (VG_(strcmp)(tool, "drd") == 0) {
+ return;
+ }
+
+ ensure_init();
+}
+
+int VG_(dyld_cache_might_be_in)(const HChar* path) {
+ // If not init'd, there is no point
+ if (!ensure_init()) {
+ return 0;
+ }
+
+ if (VG_(strncmp)(path, "/usr/lib/", 9) == 0) {
+ return 1;
+ }
+ if (VG_(strncmp)(path, "/System/Library/", 16) == 0) {
+ return 1;
+ }
+ // FIXME: more flexible heuristics around extensions?
+ return 0;
+}
+
+static struct MACH_HEADER* find_image_text(const dyld_cache_header* header, const char* path, SizeT* len) {
+ vg_assert(len);
+ *len = 0;
+
+ const dyld_cache_image_text_info* textInfos = (const dyld_cache_image_text_info*) calculate_relative(header, header->imagesTextOffset);
+
+ for (int i = 0; i < header->imagesTextCount; ++i) {
+ const dyld_cache_image_text_info* textInfo = &textInfos[i];
+ const char* imagePath = (const char*) calculate_relative(header, textInfo->pathOffset);
+
+ if (VG_(strcmp)(imagePath, path) == 0) {
+ output_text_debug_info(textInfo);
+ *len = textInfo->textSegmentSize;
+ return (struct MACH_HEADER*) calculate_unslid(textInfo->loadAddress);
+ }
+ }
+
+ return NULL;
+}
+
+int VG_(dyld_cache_load_library)(const HChar* path) {
+ struct MACH_HEADER *image = NULL;
+ ULong res = 0;
+ SizeT len = 0;
+
+ if (VG_(strstr)(path, "/PrivateFrameworks/") != NULL) {
+ return 0;
+ }
+
+ // If not init'd, there is no point trying
+ if (!ensure_init()) {
+ return 0;
+ }
+
+ VG_(debugLog)(2, "dyld_cache", "potential dylib to check in the cache: %s\n", path);
+
+ image = find_image_text(dyld_cache.header, path, &len);
+ if (image == NULL) {
+ VG_(debugLog)(2, "dyld_cache", "image not found: %s\n", path);
+ return 0;
+ }
+
+ if (image->magic != MAGIC) {
+ VG_(debugLog)(2, "dyld_cache", "image not mach-o (%#x): %s\n", image->magic, path);
+ return 0;
+ }
+
+ VG_(debugLog)(2, "dyld_cache", "image (%p) is valid, forwarding to debuginfo: %s\n", image, path);
+ res = VG_(di_notify_dsc)(path, (Addr)image, len);
+ if (res == 0) {
+ VG_(debugLog)(2, "dyld_cache", "failed to load debuginfo from: %s\n", path);
+ return 0;
+ }
+
+ VG_(debugLog)(2, "dyld_cache", "image fully loaded: %s\n", path);
+
+ return 1;
+}
+
+static void output_text_debug_info(const dyld_cache_image_text_info* textInfo) {
+ const uint8_t* u = textInfo->uuid;
+ VG_(debugLog)(5, "dyld_cache",
+ "image_text_info{\n"
+ " .uuid: %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x,\n"
+ " .loadAddress: %#llx,\n"
+ " .textSegmentSize: %u,\n"
+ " .pathOffset: %#x,\n"
+ "}\n",
+ u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
+ u[9], u[10], u[11], u[12], u[13], u[14], u[15],
+ textInfo->loadAddress,
+ textInfo->textSegmentSize,
+ textInfo->pathOffset
+ );
+}
+
+static void output_debug_info(const dyld_cache_header* cache) {
+ const uint8_t* u1 = cache->uuid;
+ const uint8_t* u2 = cache->symbolFileUUID;
+ VG_(debugLog)(5, "dyld_cache",
+ "shared dyld content: {\n"
+ " .magic: %s,\n"
+ " .mappingOffset: %#x,\n"
+ " .mappingCount: %u,\n"
+ " .imagesOffsetOld: %#x,\n"
+ " .imagesCountOld: %u,\n"
+ " .dyldBaseAddress: %#llx,\n"
+ " .codeSignatureOffset: %#llx,\n"
+ " .codeSignatureSize: %llu,\n"
+ " .slideInfoOffsetUnused: %#llx,\n"
+ " .slideInfoSizeUnused: %llu,\n"
+ " .localSymbolsOffset: %#llx,\n"
+ " .localSymbolsSize: %llu,\n"
+ " .uuid: %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x,\n"
+ " .cacheType: %llu,\n"
+ " .branchPoolsOffset: %#x,\n"
+ " .branchPoolsCount: %u,\n"
+ " .dyldInCacheMH: %#llx,\n"
+ " .dyldInCacheEntry: %#llx,\n"
+ " .imagesTextOffset: %#llx,\n"
+ " .imagesTextCount: %llu,\n"
+ " .patchInfoAddr: %#llx,\n"
+ " .patchInfoSize: %llu,\n"
+ " .otherImageGroupAddrUnused: %#llx,\n"
+ " .otherImageGroupSizeUnused: %llu,\n"
+ " .progClosuresAddr: %#llx,\n"
+ " .progClosuresSize: %llu,\n"
+ " .progClosuresTrieAddr: %#llx,\n"
+ " .progClosuresTrieSize: %llu,\n"
+ " .platform: %#x,\n"
+ " .formatVersion: %#x,\n"
+ " .dylibsExpectedOnDisk: %d,\n"
+ " .simulator: %d,\n"
+ " .locallyBuiltCache: %d,\n"
+ " .builtFromChainedFixups: %d,\n"
+ " .padding: %d,\n"
+ " .sharedRegionStart: %#llx,\n"
+ " .sharedRegionSize: %llu,\n"
+ " .maxSlide: %#llx,\n"
+ " .dylibsImageArrayAddr: %#llx,\n"
+ " .dylibsImageArraySize: %llu,\n"
+ " .dylibsTrieAddr: %#llx,\n"
+ " .dylibsTrieSize: %llu,\n"
+ " .otherImageArrayAddr: %#llx,\n"
+ " .otherImageArraySize: %llu,\n"
+ " .otherTrieAddr: %#llx,\n"
+ " .otherTrieSize: %llu,\n"
+ " .mappingWithSlideOffset: %#x,\n"
+ " .mappingWithSlideCount: %u,\n"
+ " .dylibsPBLStateArrayAddrUnused: %llu,\n"
+ " .dylibsPBLSetAddr: %llx,\n"
+ " .programsPBLSetPoolAddr: %#llx,\n"
+ " .programsPBLSetPoolSize: %llu,\n"
+ " .programTrieAddr: %#llx,\n"
+ " .programTrieSize: %u,\n"
+ " .osVersion: %#x,\n"
+ " .altPlatform: %#x,\n"
+ " .altOsVersion: %#x,\n"
+ " .swiftOptsOffset: %#llx,\n"
+ " .swiftOptsSize: %llu,\n"
+ " .subCacheArrayOffset: %#x,\n"
+ " .subCacheArrayCount: %u,\n"
+ " .symbolFileUUID: %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x,\n"
+ " .rosettaReadOnlyAddr: %#llx,\n"
+ " .rosettaReadOnlySize: %llu,\n"
+ " .rosettaReadWriteAddr: %#llx,\n"
+ " .rosettaReadWriteSize: %llu,\n"
+ " .imagesOffset: %#x,\n"
+ " .imagesCount: %u,\n"
+ " .cacheSubType: %#x,\n"
+ " .objcOptsOffset: %#llx,\n"
+ " .objcOptsSize: %llu,\n"
+ " .cacheAtlasOffset: %#llx,\n"
+ " .cacheAtlasSize: %llu,\n"
+ " .dynamicDataOffset: %#llx,\n"
+ " .dynamicDataMaxSize: %llu,\n"
+ "}\n",
+ cache->magic,
+ cache->mappingOffset,
+ cache->mappingCount,
+ cache->imagesOffsetOld,
+ cache->imagesCountOld,
+ cache->dyldBaseAddress,
+ cache->codeSignatureOffset,
+ cache->codeSignatureSize,
+ cache->slideInfoOffsetUnused,
+ cache->slideInfoSizeUnused,
+ cache->localSymbolsOffset,
+ cache->localSymbolsSize,
+ u1[0], u1[1], u1[2], u1[3], u1[4], u1[5], u1[6], u1[7], u1[8],
+ u1[9], u1[10], u1[11], u1[12], u1[13], u1[14], u1[15],
+ cache->cacheType,
+ cache->branchPoolsOffset,
+ cache->branchPoolsCount,
+ cache->dyldInCacheMH,
+ cache->dyldInCacheEntry,
+ cache->imagesTextOffset,
+ cache->imagesTextCount,
+ cache->patchInfoAddr,
+ cache->patchInfoSize,
+ cache->otherImageGroupAddrUnused,
+ cache->otherImageGroupSizeUnused,
+ cache->progClosuresAddr,
+ cache->progClosuresSize,
+ cache->progClosuresTrieAddr,
+ cache->progClosuresTrieSize,
+ cache->platform,
+ (UInt)cache->formatVersion,
+ cache->dylibsExpectedOnDisk,
+ cache->simulator,
+ cache->locallyBuiltCache,
+ cache->builtFromChainedFixups,
+ cache->padding,
+ cache->sharedRegionStart,
+ cache->sharedRegionSize,
+ cache->maxSlide,
+ cache->dylibsImageArrayAddr,
+ cache->dylibsImageArraySize,
+ cache->dylibsTrieAddr,
+ cache->dylibsTrieSize,
+ cache->otherImageArrayAddr,
+ cache->otherImageArraySize,
+ cache->otherTrieAddr,
+ cache->otherTrieSize,
+ cache->mappingWithSlideOffset,
+ cache->mappingWithSlideCount,
+ cache->dylibsPBLStateArrayAddrUnused,
+ cache->dylibsPBLSetAddr,
+ cache->programsPBLSetPoolAddr,
+ cache->programsPBLSetPoolSize,
+ cache->programTrieAddr,
+ cache->programTrieSize,
+ cache->osVersion,
+ cache->altPlatform,
+ cache->altOsVersion,
+ cache->swiftOptsOffset,
+ cache->swiftOptsSize,
+ cache->subCacheArrayOffset,
+ cache->subCacheArrayCount,
+ u2[0], u2[1], u2[2], u2[3], u2[4], u2[5], u2[6], u2[7], u2[8],
+ u2[9], u2[10], u2[11], u2[12], u2[13], u2[14], u2[15],
+ cache->rosettaReadOnlyAddr,
+ cache->rosettaReadOnlySize,
+ cache->rosettaReadWriteAddr,
+ cache->rosettaReadWriteSize,
+ cache->imagesOffset,
+ cache->imagesCount,
+ cache->cacheSubType,
+ cache->objcOptsOffset,
+ cache->objcOptsSize,
+ cache->cacheAtlasOffset,
+ cache->cacheAtlasSize,
+ cache->dynamicDataOffset,
+ cache->dynamicDataMaxSize
+ );
+}
+
+#endif
--- /dev/null
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (c) 2020-2025 Louis Brunner <louis.brunner.fr@gmail.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+#ifndef __PRIV_DYLD_INTERNALS_H
+#define __PRIV_DYLD_INTERNALS_H
+
+#if defined(VGO_darwin)
+
+// This file contains a bunch of structure defined in Apple's dyld
+
+// From Apple's `dyld/dyld/SharedCacheRuntime.cpp`
+#define CACHE_MAGIC_x86_64 "dyld_v1 x86_64"
+#define CACHE_MAGIC_x86_64_HASWELL "dyld_v1 x86_64h"
+#define CACHE_MAGIC_arm64 "dyld_v1 arm64"
+#define CACHE_MAGIC_arm64e "dyld_v1 arm64e"
+
+// From Apple's `dyld/cache-builder/dyld_cache_format.h`
+typedef struct {
+ char magic[16]; // e.g. "dyld_v0 i386"
+ uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info
+ uint32_t mappingCount; // number of dyld_cache_mapping_info entries
+ uint32_t imagesOffsetOld; // UNUSED: moved to imagesOffset to prevent older dsc_extarctors from crashing
+ uint32_t imagesCountOld; // UNUSED: moved to imagesCount to prevent older dsc_extarctors from crashing
+ uint64_t dyldBaseAddress; // base address of dyld when cache was built
+ uint64_t codeSignatureOffset; // file offset of code signature blob
+ uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file)
+ uint64_t slideInfoOffsetUnused; // unused. Used to be file offset of kernel slid info
+ uint64_t slideInfoSizeUnused; // unused. Used to be size of kernel slid info
+ uint64_t localSymbolsOffset; // file offset of where local symbols are stored
+ uint64_t localSymbolsSize; // size of local symbols information
+ uint8_t uuid[16]; // unique value for each shared cache file
+ uint64_t cacheType; // 0 for development, 1 for production, 2 for multi-cache
+ uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses
+ uint32_t branchPoolsCount; // number of uint64_t entries
+ uint64_t dyldInCacheMH; // (unslid) address of mach_header of dyld in cache
+ uint64_t dyldInCacheEntry; // (unslid) address of entry point (_dyld_start) of dyld in cache
+ uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info
+ uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries
+ uint64_t patchInfoAddr; // (unslid) address of dyld_cache_patch_info
+ uint64_t patchInfoSize; // Size of all of the patch information pointed to via the dyld_cache_patch_info
+ uint64_t otherImageGroupAddrUnused; // unused
+ uint64_t otherImageGroupSizeUnused; // unused
+ uint64_t progClosuresAddr; // (unslid) address of list of program launch closures
+ uint64_t progClosuresSize; // size of list of program launch closures
+ uint64_t progClosuresTrieAddr; // (unslid) address of trie of indexes into program launch closures
+ uint64_t progClosuresTrieSize; // size of trie of indexes into program launch closures
+ uint32_t platform; // platform number (macOS=1, etc)
+ uint32_t formatVersion : 8, // dyld3::closure::kFormatVersion
+ dylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid
+ simulator : 1, // for simulator of specified platform
+ locallyBuiltCache : 1, // 0 for B&I built cache, 1 for locally built cache
+ builtFromChainedFixups : 1, // some dylib in cache was built using chained fixups, so patch tables must be used for overrides
+ padding : 20; // TBD
+ uint64_t sharedRegionStart; // base load address of cache if not slid
+ uint64_t sharedRegionSize; // overall size required to map the cache and all subCaches, if any
+ uint64_t maxSlide; // runtime slide of cache can be between zero and this value
+ uint64_t dylibsImageArrayAddr; // (unslid) address of ImageArray for dylibs in this cache
+ uint64_t dylibsImageArraySize; // size of ImageArray for dylibs in this cache
+ uint64_t dylibsTrieAddr; // (unslid) address of trie of indexes of all cached dylibs
+ uint64_t dylibsTrieSize; // size of trie of cached dylib paths
+ uint64_t otherImageArrayAddr; // (unslid) address of ImageArray for dylibs and bundles with dlopen closures
+ uint64_t otherImageArraySize; // size of ImageArray for dylibs and bundles with dlopen closures
+ uint64_t otherTrieAddr; // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures
+ uint64_t otherTrieSize; // size of trie of dylibs and bundles with dlopen closures
+ uint32_t mappingWithSlideOffset; // file offset to first dyld_cache_mapping_and_slide_info
+ uint32_t mappingWithSlideCount; // number of dyld_cache_mapping_and_slide_info entries
+ uint64_t dylibsPBLStateArrayAddrUnused; // unused
+ uint64_t dylibsPBLSetAddr; // (unslid) address of PrebuiltLoaderSet of all cached dylibs
+ uint64_t programsPBLSetPoolAddr; // (unslid) address of pool of PrebuiltLoaderSet for each program
+ uint64_t programsPBLSetPoolSize; // size of pool of PrebuiltLoaderSet for each program
+ uint64_t programTrieAddr; // (unslid) address of trie mapping program path to PrebuiltLoaderSet
+ uint32_t programTrieSize;
+ uint32_t osVersion; // OS Version of dylibs in this cache for the main platform
+ uint32_t altPlatform; // e.g. iOSMac on macOS
+ uint32_t altOsVersion; // e.g. 14.0 for iOSMac
+ uint64_t swiftOptsOffset; // VM offset from cache_header* to Swift optimizations header
+ uint64_t swiftOptsSize; // size of Swift optimizations header
+ uint32_t subCacheArrayOffset; // file offset to first dyld_subcache_entry
+ uint32_t subCacheArrayCount; // number of subCache entries
+ uint8_t symbolFileUUID[16]; // unique value for the shared cache file containing unmapped local symbols
+ uint64_t rosettaReadOnlyAddr; // (unslid) address of the start of where Rosetta can add read-only/executable data
+ uint64_t rosettaReadOnlySize; // maximum size of the Rosetta read-only/executable region
+ uint64_t rosettaReadWriteAddr; // (unslid) address of the start of where Rosetta can add read-write data
+ uint64_t rosettaReadWriteSize; // maximum size of the Rosetta read-write region
+ uint32_t imagesOffset; // file offset to first dyld_cache_image_info
+ uint32_t imagesCount; // number of dyld_cache_image_info entries
+ uint32_t cacheSubType; // 0 for development, 1 for production, when cacheType is multi-cache(2)
+ uint64_t objcOptsOffset; // VM offset from cache_header* to ObjC optimizations header
+ uint64_t objcOptsSize; // size of ObjC optimizations header
+ uint64_t cacheAtlasOffset; // VM offset from cache_header* to embedded cache atlas for process introspection
+ uint64_t cacheAtlasSize; // size of embedded cache atlas
+ uint64_t dynamicDataOffset; // VM offset from cache_header* to the location of dyld_cache_dynamic_data_header
+ uint64_t dynamicDataMaxSize; // maximum size of space reserved from dynamic data
+} dyld_cache_header;
+
+// From Apple's `dyld/cache-builder/dyld_cache_format.h`
+typedef struct {
+ uint64_t address;
+ uint64_t size;
+ uint64_t fileOffset;
+ uint32_t maxProt;
+ uint32_t initProt;
+} dyld_cache_mapping_info;
+
+// From Apple's `dyld/cache-builder/dyld_cache_format.h`
+typedef struct {
+ uint8_t uuid[16]; // The UUID of the subCache file
+ uint64_t cacheVMOffset; // The offset of this subcache from the main cache base address
+ char fileSuffix[32]; // The file name suffix of the subCache file e.g. ".25.data", ".03.development"
+} dyld_subcache_entry;
+
+// From Apple's `dyld/cache-builder/dyld_cache_format.h`
+typedef struct {
+ uint8_t uuid[16]; // The UUID of the subCache file
+ uint64_t cacheVMOffset; // The offset of this subcache from the main cache base address
+} dyld_subcache_entry_v1;
+
+// From Apple's `dyld/cache-builder/dyld_cache_format.h`
+typedef struct {
+ uuid_t uuid;
+ uint64_t loadAddress; // unslid address of start of __TEXT
+ uint32_t textSegmentSize;
+ uint32_t pathOffset; // offset from start of cache file
+} dyld_cache_image_text_info;
+
+#endif
+
+#endif
VG_(init_Threads)();
+ //--------------------------------------------------------------
+ // Initialize the dyld cache, which is required with macOS 11 (Big Sur) and onwards
+ // as some system libraries aren't provided on the disk anymore
+ // p: none
+ // Note: some tools don't like to start mapping memory right way, so we do it lazily in those cases.
+ //--------------------------------------------------------------
+# if defined(VGO_darwin) && DARWIN_VERS >= DARWIN_11_00
+ if (the_iifii.dynamic) {
+ VG_(dyld_cache_init)(VG_(clo_toolname));
+ }
+# endif
+
//--------------------------------------------------------------
// Initialise the scheduler (phase 1) [generates tid_main]
// p: none, afaics
ALLOC_or_NULL(SO_SYN_MALLOC, malloc, malloc);
ZONEALLOC_or_NULL(VG_Z_LIBC_SONAME, malloc_zone_malloc, malloc);
ZONEALLOC_or_NULL(SO_SYN_MALLOC, malloc_zone_malloc, malloc);
+#if DARWIN_VERS >= DARWIN_15_00
+#if defined(VGA_arm64)
+ // on arm64, malloc_type_malloc is used for malloc, new and new[]
+ // __typed_operator_new_impl[abi:ne180100]@libc++abi.dylib calls it for new and new[]
+ // all other usages (Swift, ObjC, C) it calls it for malloc
+ // this matters as we need to put the right tag in the allocation
+ // otherwise the tool might report a mismatch between allocation func and free func
+ TYPE_ALLOC_or_NULL(VG_Z_LIBC_SONAME, malloc_type_malloc);
+#else
+ ALLOC_or_NULL(VG_Z_LIBC_SONAME, malloc_type_malloc, malloc);
+#endif
+ ZONEALLOC_or_NULL(VG_Z_LIBC_SONAME, malloc_type_zone_malloc, malloc);
+#endif
#elif defined(VGO_solaris)
ALLOC_or_NULL(VG_Z_LIBSTDCXX_SONAME, malloc, malloc);
#elif defined(VGO_darwin)
FREE(VG_Z_LIBC_SONAME, free, free );
FREE(SO_SYN_MALLOC, free, free );
+ FREE(VG_Z_LIBC_SONAME, vfree, free );
+ FREE(SO_SYN_MALLOC, vfree, free );
ZONEFREE(VG_Z_LIBC_SONAME, malloc_zone_free, free );
ZONEFREE(SO_SYN_MALLOC, malloc_zone_free, free );
POSIX_MEMALIGN(SO_SYN_MALLOC, posix_memalign);
#elif defined(VGO_darwin)
-#if (DARWIN_VERSIO >= DARWIN_10_6)
+#if (DARWIN_VERS >= DARWIN_10_6)
POSIX_MEMALIGN(VG_Z_LIBC_SONAME, posix_memalign);
+ POSIX_MEMALIGN(SO_SYN_MALLOC, posix_memalign);
#endif
#elif defined(VGO_solaris)
ALIGNED_ALLOC(SO_SYN_MALLOC, aligned_alloc);
#elif defined(VGO_darwin)
- //ALIGNED_ALLOC(VG_Z_LIBC_SONAME, aligned_alloc);
+ ALIGNED_ALLOC(VG_Z_LIBC_SONAME, aligned_alloc);
#elif defined(VGO_solaris)
ALIGNED_ALLOC(VG_Z_LIBC_SONAME, aligned_alloc);
return res;
}
+#define ZONE_DESTROY(soname, fnname) \
+ \
+ void VG_REPLACE_FUNCTION_EZU(10291,soname,fnname)(void* zone); \
+ void VG_REPLACE_FUNCTION_EZU(10291,soname,fnname)(void* zone) \
+ { \
+ TRIGGER_MEMCHECK_ERROR_IF_UNDEFINED(zone); \
+ }
+
+ZONE_DESTROY(VG_Z_LIBC_SONAME, malloc_zone_destroy);
+ZONE_DESTROY(SO_SYN_MALLOC, malloc_zone_destroy);
+
/* Note that the (void*) casts below are a kludge which stops
compilers complaining about the fact that the replacement
functions aren't really of the right type. */
(void*)VG_REPLACE_FUNCTION_EZU(10130,VG_Z_LIBC_SONAME,malloc_zone_valloc),
(void*)VG_REPLACE_FUNCTION_EZU(10040,VG_Z_LIBC_SONAME,malloc_zone_free),
(void*)VG_REPLACE_FUNCTION_EZU(10080,VG_Z_LIBC_SONAME,malloc_zone_realloc),
- NULL, // GrP fixme: destroy
+ (void*)VG_REPLACE_FUNCTION_EZU(10291,VG_Z_LIBC_SONAME,malloc_zone_destroy),
"ValgrindMallocZone",
NULL, // batch_malloc
NULL, // batch_free
// NYI mkfifo_extended 291
// NYI mkdir_extended 292
// NYI identitysvc 293
-// NYI shared_region_check_np 294
+#if DARWIN_VERS >= DARWIN_11_00
+DECL_TEMPLATE(darwin, shared_region_check_np); // 294
+#endif
// NYI shared_region_map_np 295
#if DARWIN_VERS >= DARWIN_10_6
// NYI vm_pressure_monitor 296
// NYI pid_shutdown_sockets 436
#endif /* DARWIN_VERS >= DARWIN_10_10 */
// old old shared_region_slide_np 437
-// NYI shared_region_map_and_slide_np // 438
+#if DARWIN_VERS >= DARWIN_11_00
+DECL_TEMPLATE(darwin, shared_region_map_and_slide_np); // 438
+#endif
// NYI kas_info // 439
// NYI memorystatus_control // 440
DECL_TEMPLATE(darwin, guarded_open_np); // 441
// NYI log_data // 533
// NYI memorystatus_available_memory // 534
#endif
+#if DARWIN_VERS >= DARWIN_11_00
+DECL_TEMPLATE(darwin, objc_bp_assist_cfg_np); // 535
+// NYI shared_region_map_and_slide_2_np // 536
+// NYI pivot_root // 537
+// NYI task_inspect_for_pid // 538
+DECL_TEMPLATE(darwin, task_read_for_pid); // 539
+// NYI sys_preadv // 540
+// NYI sys_pwritev // 541
+// NYI sys_preadv_nocancel // 542
+// NYI sys_pwritev_nocancel // 543
+DECL_TEMPLATE(darwin, ulock_wait2); // 544
+// NYI proc_info_extended_id // 545
+#endif
// Mach message helpers
DECL_TEMPLATE(darwin, mach_port_set_context);
#include "pub_core_libcprint.h"
#include "pub_core_libcproc.h"
#include "pub_core_libcsignal.h"
+#include "pub_core_mach.h" // VG_(dyld_cache_*)
#include "pub_core_machine.h" // VG_(get_SP)
#include "pub_core_mallocfree.h"
#include "pub_core_options.h"
PRE_REG_READ2(long, "stat", const char *,path, struct stat64 *,buf);
PRE_MEM_RASCIIZ("stat64(path)", ARG1);
PRE_MEM_WRITE( "stat64(buf)", ARG2, sizeof(struct vki_stat64) );
+
+#if DARWIN_VERS >= DARWIN_11_00
+ // Starting with macOS 11.0, some system libraries are not provided on the disk but only though
+ // shared dyld cache, thus we try to detect if dyld tried (and failed) to load a dylib,
+ // in which case we do the same thing as dyld and load the info from the cache directly
+ //
+ // This is our entry point for checking a particular dylib: if it looks like one,
+ // we want to see the error result, if any, and subsequently check the cache
+ if (ARG1 != 0 && VG_(dyld_cache_might_be_in)((HChar *)ARG1)) {
+ *flags |= SfPostOnFail;
+ }
+#endif
}
POST(stat64)
{
- POST_MEM_WRITE( ARG2, sizeof(struct vki_stat64) );
+ if (SUCCESS) {
+ POST_MEM_WRITE( ARG2, sizeof(struct vki_stat64) );
+ }
+
+#if DARWIN_VERS >= DARWIN_11_00
+ if (SUCCESS || (FAILURE && ERR == VKI_ENOENT)) {
+ // It failed and `SfPostOnFail` was set, thus this is probably a dylib,
+ // try to load it from cache which will call VG_(di_notify_mmap) like the previous versions did
+ if (VG_(dyld_cache_load_library)((HChar *)ARG1)) {
+ ML_(sync_mappings)("after", "stat64", 0);
+ }
+ }
+#endif
}
PRE(lstat64)
#endif /* DARWIN_VERS >= DARWIN_10_15 */
+
+/* ---------------------------------------------------------------------
+ Added for macOS 11.0 (Big Sur)
+ ------------------------------------------------------------------ */
+
+#if DARWIN_VERS >= DARWIN_11_00
+
+#define DYLD_VM_END_MWL (-1ull)
+
+PRE(shared_region_check_np)
+{
+ // Special value used by dyld to forbid further uses of map_with_linking_np on macOS 13+
+ Bool special_call = DARWIN_VERS >= DARWIN_13_00 && ARG1 == DYLD_VM_END_MWL;
+
+ if (special_call) {
+ PRINT("shared_region_check_np(disable_map_with_linking)");
+ } else {
+ PRINT("shared_region_check_np(%#lx)", ARG1);
+ }
+ PRE_REG_READ1(kern_return_t, "shared_region_check_np", uint64_t*, start_address);
+
+ if (!special_call) {
+ PRE_MEM_WRITE("shared_region_check_np(start_address)", ARG1, sizeof(uint64_t));
+}
+}
+
+POST(shared_region_check_np)
+{
+ Bool special_call = DARWIN_VERS >= DARWIN_13_00 && ARG1 == DYLD_VM_END_MWL;
+
+ if (special_call) {
+ return;
+ }
+
+ if (RES == 0) {
+ POST_MEM_WRITE(ARG1, sizeof(uint64_t));
+ PRINT("shared dyld cache %#llx", *((uint64_t*) ARG1));
+ }
+}
+
+PRE(shared_region_map_and_slide_np)
+{
+ PRINT("shared_region_map_and_slide_np(%ld, %lu, %#lx, %lu, %#lx, %lu)", SARG1, ARG2, ARG3, ARG4, ARG5, ARG6);
+ PRE_REG_READ6(kern_return_t, "shared_region_map_and_slide_np",
+ int, fd, uint32_t, count, const struct shared_file_mapping_np*, mappings,
+ uint32_t, slide, uint64_t*, slide_start, uint32_t, slide_size);
+}
+
+PRE(task_read_for_pid)
+{
+ PRINT("task_read_for_pid(%s, %ld, %#lx)", name_for_port(ARG1), SARG2, ARG3);
+ PRE_REG_READ3(kern_return_t, "task_read_for_pid", mach_port_name_t, target_tport, int, pid, mach_port_name_t*, t);
+
+ if (ARG3 != 0) {
+ PRE_MEM_WRITE("task_read_for_pid(t)", ARG3, sizeof(mach_port_name_t));
+ }
+}
+
+POST(task_read_for_pid)
+{
+ if (RES == 0 && ARG3 != 0) {
+ POST_MEM_WRITE(ARG3, sizeof(mach_port_name_t));
+ PRINT("-> t:%s", name_for_port(*(mach_port_name_t*)ARG3));
+ }
+}
+
+PRE(ulock_wait2)
+{
+ PRINT("ulock_wait2(%ld, %#lx, %ld, %#lx, %ld)",
+ SARG1, ARG2, SARG3, ARG4, SARG5);
+ PRE_REG_READ5(int, "ulock_wait2",
+ uint32_t, operation, void*, addr, uint64_t, value,
+ uint32_t, timeout, uint64_t, value2);
+ Int value_size = 4;
+ if (ARG1 == VKI_UL_COMPARE_AND_WAIT64
+ || ARG1 == VKI_UL_COMPARE_AND_WAIT64_SHARED
+ || ARG1 == VKI_UL_COMPARE_AND_WAIT_SHARED) {
+ value_size = 8;
+ }
+ if (ARG2 != 0) {
+ PRE_MEM_READ("ulock_wait2(addr)", ARG2, value_size);
+ *flags |= SfMayBlock;
+ } else {
+ SET_STATUS_Failure( VKI_EINVAL );
+ }
+}
+
+#if defined(VGA_arm64)
+PRE(sys_crossarch_trap)
+{
+ PRINT("sys_crossarch_trap(%lu)", ARG1);
+ PRE_REG_READ1(kern_return_t, "sys_crossarch_trap", uint32_t, name);
+}
+
+PRE(objc_bp_assist_cfg_np)
+{
+ PRINT("objc_bp_assist_cfg_np(%#lx, %#lx)", ARG1, ARG2);
+}
+#endif
+
+#endif /* DARWIN_VERS >= DARWIN_11_00 */
+
+
/* ---------------------------------------------------------------------
syscall tables
------------------------------------------------------------------ */
// _____(__NR_mkfifo_extended),
// _____(__NR_mkdir_extended),
// _____(__NR_identitysvc),
-// _____(__NR_shared_region_check_np),
+#if DARWIN_VERS >= DARWIN_11_00
+ MACXY(__NR_shared_region_check_np, shared_region_check_np), // 294
+#endif
// _____(__NR_shared_region_map_np),
#if DARWIN_VERS >= DARWIN_10_6
// _____(__NR_vm_pressure_monitor),
_____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(435)), // ???
_____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(436)), // ???
_____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(437)), // ???
-// _____(__NR_shared_region_map_and_slide_np), // 438
+#if DARWIN_VERS >= DARWIN_11_00
+ MACX_(__NR_shared_region_map_and_slide_np, shared_region_map_and_slide_np), // 438
+#endif
// _____(__NR_kas_info), // 439
// _____(__NR_memorystatus_control), // 440
MACX_(__NR_guarded_open_np, guarded_open_np),
// _____(__NR_log_data), // 533
// _____(__NR_memorystatus_available_memory), // 534
#endif
+#if DARWIN_VERS >= DARWIN_11_00
+#if defined(VGP_arm64_darwin)
+ MACX_(__NR_objc_bp_assist_cfg_np, objc_bp_assist_cfg_np), // 535
+#endif
+// _____(__NR_shared_region_map_and_slide_2_np), // 536
+// _____(__NR_pivot_root), // 537
+// _____(__NR_task_inspect_for_pid), // 538
+ MACXY(__NR_task_read_for_pid, task_read_for_pid), // 539
+// _____(__NR_sys_preadv), // 540
+// _____(__NR_sys_pwritev), // 541
+// _____(__NR_sys_preadv_nocancel), // 542
+// _____(__NR_sys_pwritev_nocancel), // 543
+ MACX_(__NR_ulock_wait2, ulock_wait2), // 544
+// _____(__NR_proc_info_extended_id), // 545
+#endif
+
MACX_(__NR_darwin_fake_sigreturn, fake_sigreturn)
};
extern void VG_(di_notify_vm_protect)( Addr a, SizeT len, UInt prot );
#endif
+#if defined(VGO_darwin) && DARWIN_VERS >= DARWIN_11_00
+extern ULong VG_(di_notify_dsc)( const HChar* path, Addr header, SizeT len );
+#endif
+
extern void VG_(addr_load_di)( Addr a );
extern void VG_(di_load_di)( DebugInfo *di );
Addr initial_client_SP;
/* ------ Per-OS fields ------ */
Addr initial_client_IP;
+#if defined(VGO_darwin)
+ Bool dynamic; /* False iff executable is static */
+#endif
};
/* ------------------------- Solaris ------------------------- */
...
fun:ds_user_byuid
}
+
+{
+ macOS1100:_os_semaphore_wait.cold.1
+ drd:ConflictingAccess
+ fun:_os_semaphore_wait.cold.1
+}
+
...
fun:lookUpImpOrForward
}
+
+{
+ maxOS1100:dlopen_internal
+ Helgrind:Race
+ ...
+ fun:dlopen_internal
+}
+
+{
+ macOS1100::__CFInitialize
+ Helgrind:Race
+ ...
+ fun:__CFInitialize
+}
fun:_mh_execute_header
}
+{
+ macOS1100:_dyld_start
+ Memcheck:Addr8
+ ...
+ fun:_dyld_start
+}
+
+{
+ macOS1100:_dyld_start
+ Memcheck:Addr4
+ ...
+ fun:_dyld_start
+}
+
+{
+ macOS1100:__chkstk_darwin_probe
+ Memcheck:Addr1
+ fun:__chkstk_darwin_probe
+}
+
+{
+ macOS1100:_dyld_start
+ Memcheck:Addr1
+ ...
+ fun:_dyld_start
+}
+
filter_xml \
filter_fiw \
filter_freebsd.awk \
+ filter_darwin.awk \
filter_stderr_freebsd \
filter_bug392331
--- /dev/null
+/pthread_create_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /pthread_create \(hg_intercepts.c/)) {
+ print " by 0x........: pthread_create@* (hg_intercepts.c:...)";
+ print;
+ } else {
+ sub(/pthread_create/, "pthread_create@*");
+ print;
+ }
+ next;
+}
+/pthread_cond_timedwait_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /pthread_cond_timedwait \(hg_intercepts.c/)) {
+ print " by 0x........: pthread_cond_timedwait@* (hg_intercepts.c:...)";
+ print;
+ } else {
+ sub(/pthread_cond_timedwait/, "pthread_cond_timedwait@*");
+ print;
+ }
+ next;
+}
+/pthread_cond_wait_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /pthread_cond_wait \(hg_intercepts.c/)) {
+ print " by 0x........: pthread_cond_wait@* (hg_intercepts.c:...)";
+ print;
+ } else {
+ sub(/pthread_cond_wait/, "pthread_cond_wait@*");
+ print;
+ }
+ next;
+}
+/pthread_cond_destroy_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /pthread_cond_destroy \(hg_intercepts.c/)) {
+ print " by 0x........: pthread_cond_destroy@* (hg_intercepts.c:...)";
+ print;
+ } else {
+ sub(/pthread_cond_destroy/, "pthread_cond_destroy@*");
+ print;
+ }
+ next;
+}
+/pthread_cond_signal_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /pthread_cond_signal \(hg_intercepts.c/)) {
+ print " by 0x........: pthread_cond_signal (hg_intercepts.c:...)";
+ print;
+ } else {
+ print;
+ }
+ next;
+}
+/mutex_lock_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /pthread_mutex_lock \(hg_intercepts.c/)) {
+ print " by 0x........: pthread_mutex_lock (hg_intercepts.c:...)";
+ print;
+ } else {
+ print;
+ }
+ next;
+}
+/mutex_unlock_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /pthread_mutex_unlock \(hg_intercepts.c/)) {
+ print " by 0x........: pthread_mutex_unlock (hg_intercepts.c:...)";
+ print;
+ } else {
+ print;
+ }
+ next;
+}
+/pthread_rwlock_unlock_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /pthread_rwlock_unlock \(hg_intercepts.c/)) {
+ print " by 0x........: pthread_rwlock_unlock (hg_intercepts.c:...)";
+ if (!match($0, /pthread_rwlock_unlock\*/)) {
+ print;
+ }
+ } else {
+ print;
+ }
+ next;
+}
+/pthread_rwlock_init_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /pthread_rwlock_init \(hg_intercepts.c/)) {
+ print " by 0x........: pthread_rwlock_init (hg_intercepts.c:...)";
+ if (!match($0, /pthread_rwlock_init*/)) {
+ print;
+ }
+ } else {
+ print;
+ }
+ next;
+}
+/sem_init_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /sem_init \(hg_intercepts.c/)) {
+ print " by 0x........: sem_init (hg_intercepts.c:...)";
+ print;
+ } else {
+ print;
+ }
+ next;
+}
+/sem_wait_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /sem_wait \(hg_intercepts.c/)) {
+ print " by 0x........: sem_wait (hg_intercepts.c:...)";
+ print;
+ } else {
+ print;
+ }
+ next;
+}
+/sem_post_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /sem_post \(hg_intercepts.c/)) {
+ print " by 0x........: sem_post (hg_intercepts.c:...)";
+ print;
+ } else {
+ print;
+ }
+ next;
+}
+/sem_destroy_WRK \(hg_intercepts.c/ {
+ print;
+ getline;
+ if (!match($0, /sem_destroy \(hg_intercepts.c/)) {
+ print " by 0x........: sem_destroy (hg_intercepts.c:...)";
+ print;
+ } else {
+ print;
+ }
+ next;
+}
+{print;}
# And Darwin
if $dir/../../tests/os_test darwin; then
- # reuse the FreeBSD filter to start with
- awk -f $dir/filter_freebsd.awk
+ awk -f $dir/filter_darwin.awk
else
cat
fi |
#define VKI_MAP_RESERVED0080 MAP_RESERVED0080
#define VKI_MAP_NOEXTEND MAP_NOEXTEND
#define VKI_MAP_HASSEMAPHORE MAP_HASSEMAPHORE
+#if DARWIN_VERS >= DARWIN_11_00
+#define VKI_MAP_NOCACHE MAP_NOCACHE
+#define VKI_MAP_JIT MAP_JIT
+#endif
#define VKI_MAP_FILE MAP_FILE
#define VKI_MAP_ANON MAP_ANON
#define VKI_MAP_FAILED MAP_FAILED
#define VKI_IFXNAMSIZ IFNAMSIZ + 8
#define VKI_IFNET_SIGNATURELEN 20
+#define VKI_UL_COMPARE_AND_WAIT 1
+#define VKI_UL_UNFAIR_LOCK 2
+#define VKI_UL_COMPARE_AND_WAIT_SHARED 3
+#define VKI_UL_UNFAIR_LOCK64_SHARED 4
+#define VKI_UL_COMPARE_AND_WAIT64 5
+#define VKI_UL_COMPARE_AND_WAIT64_SHARED 6
+
struct vki_necp_client_signable {
uuid_t client_id;
u_int32_t sign_type;
#define __NR_memorystatus_available_memory VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(534)
#endif
+#if DARWIN_VERS >= DARWIN_11_00
+#define __NR_objc_bp_assist_cfg_np VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(535)
+#define __NR_shared_region_map_and_slide_2_np VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(536)
+#define __NR_pivot_root VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(537)
+#define __NR_task_inspect_for_pid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(538)
+#define __NR_task_read_for_pid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(539)
+#define __NR_sys_preadv VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(540)
+#define __NR_sys_pwritev VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(541)
+#define __NR_sys_preadv_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(542)
+#define __NR_sys_pwritev_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(543)
+#define __NR_ulock_wait2 VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(544)
+#define __NR_proc_info_extended_id VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(545)
+#endif
+
#define __NR_darwin_fake_sigreturn VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(1000)
#endif
/*--- Setting permissions over address ranges. ---*/
/*------------------------------------------------------------*/
-#if defined(VGO_darwin)
-#if DARWIN_VERS >= DARWIN_26_00 && 0
+#if defined(VGO_darwin) && DARWIN_VERS >= DARWIN_11_00
+#if DARWIN_VERS >= DARWIN_26_00
// The new xzm_main_malloc_zone_create makes a 25GB (0x600000000) map in memory so, no choice but to raise the limit...
# define VA_LARGE_RANGE ( 25UL * 1024 * 1024 * 1024)
# else
memalign_args.stderr.exp-glibc \
memalign_args.stderr.exp-darwin \
memalign_args.stderr.exp-darwin2 \
+ memalign_args.stderr.exp-darwin3 \
memalign_args.stderr.exp-solaris \
memccpy1.stderr.exp memccpy1.stdout.exp memccpy1.vgtest \
memccpy2.stderr.exp memccpy2.vgtest \
--- /dev/null
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: posix_memalign (vg_replace_malloc.c:...)
+ by 0x........: main (memalign_args.c:23)
+
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: aligned_alloc (vg_replace_malloc.c:...)
+ by 0x........: main (memalign_args.c:27)
+
+Conditional jump or move depends on uninitialised value(s)
+ at 0x........: valloc (vg_replace_malloc.c:...)
+ by 0x........: main (memalign_args.c:31)
#include <assert.h>
#include <unistd.h>
#include <sys/syslimits.h>
+#include "../../../config.h"
// On Darwin there's this secret fourth argument, 'apple', which is a pointer
// to a string that contains the executable path, like argv[0], but unlike
// Make sure realpath(argv[0]) == realpath(apple[0]). (realpath resolves
// symlinks.)
- realpath(argv[0], pargv);
+ // PJF this changed with macOS 11, apple path now has a prefix
+#if (DARWIN_VERS >= DARWIN_11_00)
+ const char prefix[] = "executable_path=";
+ const size_t prefix_len = strlen(prefix);
+ assert(strncmp(apple[0], prefix, prefix_len) == 0);
+ realpath(apple[0]+prefix_len, pappl);
+ exit(0);
+#else
realpath(apple[0], pappl);
+#endif
+ realpath(argv[0], pargv);
assert(0 == strcmp(pargv, pappl));
return 0;
sed 's/__xnet_socket/socket/' |
awk '/ socket /{sub(/ by /, " at "); print; next}{print}' |
awk '/ at .* _syscall6 /{getline; getline; sub(/ by /, " at "); print; next}{print}' |
-sed '/__open/d' |
sed 's/__systemcall6/creat/' |
+# handling '__open' is different for Solaris and macOS 11+
+
+# Perform Solaris-specific filtering.
+if $dir/../../tests/os_test solaris; then
+ sed '/__open/d'
+else
+ cat
+fi |
+
+if $dir/../../tests/os_test darwin; then
+ sed 's/__open/creat/'
+else
+ cat
+fi |
+
+
# arm systems substitute open for creat
perl -p -e 's/open(?:64)? \(open64\.c:[1-9][0-9]*\)/creat (in \/...libc...)/' |
perl -p -e "s/: open \(/: creat (/" |