]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Bug 513533 - Support macOS 11.0 (Big Sur)
authorPaul Floyd <pjfloyd@wanadoo.fr>
Sat, 20 Dec 2025 12:01:26 +0000 (13:01 +0100)
committerPaul Floyd <pjfloyd@wanadoo.fr>
Sat, 20 Dec 2025 14:47:50 +0000 (15:47 +0100)
Most of the work for this was done by Louis Brunner.
Thanks Louis.

29 files changed:
NEWS
configure.ac
coregrind/Makefile.am
coregrind/fixup_macho_loadcmds.c
coregrind/m_debuginfo/debuginfo.c
coregrind/m_debuginfo/image.c
coregrind/m_debuginfo/priv_storage.h
coregrind/m_initimg/initimg-darwin.c
coregrind/m_mach/dyld_cache.c [new file with mode: 0644]
coregrind/m_mach/priv_dyld_internals.h [new file with mode: 0644]
coregrind/m_main.c
coregrind/m_replacemalloc/vg_replace_malloc.c
coregrind/m_syswrap/priv_syswrap-darwin.h
coregrind/m_syswrap/syswrap-darwin.c
coregrind/pub_core_debuginfo.h
coregrind/pub_core_initimg.h
darwin-drd.supp
darwin-helgrind.supp
darwin.supp
helgrind/tests/Makefile.am
helgrind/tests/filter_darwin.awk [new file with mode: 0644]
helgrind/tests/filter_stderr.in
include/vki/vki-darwin.h
include/vki/vki-scnums-darwin.h
memcheck/mc_main.c
memcheck/tests/Makefile.am
memcheck/tests/memalign_args.stderr.exp-darwin3 [new file with mode: 0644]
none/tests/darwin/apple-main-arg.c
none/tests/filter_fdleak

diff --git a/NEWS b/NEWS
index 6360e83a5bfc97e17439cc73f9b4410a47cc6820..51f0b301a86753b68f9ed3ee7068d484130cf51f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -6,7 +6,7 @@ PPC32/Linux, PPC64BE/Linux, PPC64LE/Linux, S390X/Linux, MIPS32/Linux,
 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 ===================
 
@@ -14,7 +14,11 @@ for nanoMIPS/Linux. macOS is supported up to version 10.15 Catalina.
 
 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 ===================
 
index 55d26b2a51480ce0932448bd1500743f1dbd80fc..528e44b30015eb82d1be541ce84a66c3fcda281f 100644 (file)
@@ -550,6 +550,10 @@ case "${host_os}" in
                  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)])
index 90d921db28ac944e5c6282d559e601a55c2481a6..f94aaf471ba6ddd8bf9a57720c09d23bf3148777 100644 (file)
@@ -433,6 +433,7 @@ COREGRIND_SOURCES_COMMON = \
        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 \
index b751829fbda797b84d61b751b1fbf371b2077bd8..0031177ee94f73722abaf1abb16288c5ebce1dc6 100644 (file)
 #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
 
 
@@ -267,7 +275,7 @@ static Int map_image_aboard ( /*OUT*/ImageInfo* ii, HChar* filename )
    { 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;
@@ -290,7 +298,7 @@ static Int map_image_aboard ( /*OUT*/ImageInfo* ii, HChar* filename )
                           + 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;
@@ -573,7 +581,7 @@ void modify_macho_loadcmds ( HChar* filename,
    seg__pagezero->vmaddr = 0;
 #  endif
 
-  out:   
+  out:
    if (ii.img)
       unmap_image(&ii);
 }
@@ -606,7 +614,7 @@ int main ( int argc, char** argv )
 
    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");
@@ -621,7 +629,7 @@ int main ( int argc, char** argv )
    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 );
index 66430668b52bbd2c40b6e8bd727ec90119f46359..6ed649bc27667fa4fe9ddba3f1c2478dfdb72ade 100644 (file)
@@ -326,6 +326,10 @@ DebugInfo* alloc_DebugInfo( const HChar* filename )
       di->ddump_frames = VG_(clo_debug_dump_frames);
    }
 
+#if DARWIN_VERS >= DARWIN_11_00
+   di->from_memory = False;
+#endif
+
    return di;
 }
 
@@ -1880,6 +1884,62 @@ void VG_(di_notify_pdb_debuginfo)( Int fd_obj, Addr avma_obj,
 
 #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
 
 /*------------------------------------------------------------*/
 /*---                                                      ---*/
index 21deabb3563906c702e6593dc33991b9a955be48..7b5847820cdfa5d1d5e8d1bbbe807def818d23c1 100644 (file)
@@ -139,8 +139,8 @@ static Bool is_sane_CEnt ( const HChar* who, const DiImage* img, UInt i )
    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;
@@ -432,7 +432,7 @@ static Bool parse_Frame_asciiz ( const Frame* fr, const HChar* tag,
 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);
@@ -581,13 +581,15 @@ static void set_CEnt ( const DiImage* img, UInt entNo, DiOffT off )
       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
@@ -610,6 +612,7 @@ static void set_CEnt ( const DiImage* img, UInt entNo, DiOffT off )
       }
 #endif
       vg_assert(!sr_isError(sr));
+      }
    } else {
       // Not so simple: poke the server
       vg_assert(img->source.session_id > 0);
@@ -671,7 +674,7 @@ static void set_CEnt ( const DiImage* img, UInt entNo, DiOffT off )
      end_of_else_clause:
       {}
    }
-   
+
    ce->off  = off;
    ce->used = len;
    ce->fromC = False;
@@ -888,7 +891,7 @@ DiImage* ML_(img_from_local_file)(const HChar* fullpath)
        || /* 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));
@@ -958,6 +961,39 @@ DiImage* ML_(img_from_fd)(Int fd, const HChar* fullpath)
    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
@@ -984,7 +1020,7 @@ DiImage* ML_(img_from_di_server)(const HChar* filename,
    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);
 
@@ -1116,9 +1152,11 @@ void ML_(img_done)(DiImage* img)
 {
    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
index c38dfd76f71135d503ad4c3adac1a4a00f19d475..cd1ac3431e9b30235a009408c93d2324d97e9748 100644 (file)
@@ -1094,6 +1094,12 @@ struct _DebugInfo {
       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 --------------------- */
index c15c023723f46880d4b772ed85e289e92c8fb370..4a8e8a0d78dd766b90b1552c0d12335ac7245fe5 100644 (file)
 #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"
 
 
 /*====================================================================*/
@@ -98,9 +98,14 @@ static void load_client ( /*OUT*/ExeInfo* info,
    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
@@ -111,7 +116,11 @@ static HChar** setup_client_env ( HChar** origenv, const HChar* toolname)
    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 );
@@ -194,7 +203,7 @@ static HChar** setup_client_env ( HChar** origenv, const HChar* toolname)
 
          *cpp = cp;
 
-         ld_preload_done = True;
+         dyld_cache_done = True;
       }
    }
 
@@ -371,8 +380,18 @@ Addr setup_client_stack( void*  init_sp,
    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);
 
@@ -387,7 +406,7 @@ Addr setup_client_stack( void*  init_sp,
       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;
@@ -409,10 +428,10 @@ Addr setup_client_stack( void*  init_sp,
    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 ==================== */
@@ -451,11 +470,25 @@ Addr setup_client_stack( void*  init_sp,
       *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);
@@ -470,7 +503,7 @@ Addr setup_client_stack( void*  init_sp,
       }
       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 */
@@ -675,3 +708,4 @@ void VG_(ii_finalise_image)( IIFinaliseImageInfo iifii )
 /*--------------------------------------------------------------------*/
 /*--- end                                                          ---*/
 /*--------------------------------------------------------------------*/
+
diff --git a/coregrind/m_mach/dyld_cache.c b/coregrind/m_mach/dyld_cache.c
new file mode 100644 (file)
index 0000000..953cccd
--- /dev/null
@@ -0,0 +1,504 @@
+
+/*--------------------------------------------------------------------*/
+/*--- 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
diff --git a/coregrind/m_mach/priv_dyld_internals.h b/coregrind/m_mach/priv_dyld_internals.h
new file mode 100644 (file)
index 0000000..32c31f9
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+   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
index ecd159e97399b9150153f59a18743aff32121a13..0bd929d0cc1430281bdfeefc001dc950fa4aaf19 100644 (file)
@@ -1970,6 +1970,18 @@ Int valgrind_main ( Int argc, HChar **argv, HChar **envp )
 
    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
index 7d92450c4e2e523741169e4dbb8f6fe395ef80df..12b01f678dd4ea6b469133fa3df36fc59b9091b1 100644 (file)
@@ -455,6 +455,19 @@ extern int * __error(void) __attribute__((weak));
  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);
@@ -943,6 +956,8 @@ extern int * __error(void) __attribute__((weak));
 #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 );
 
@@ -2156,8 +2171,9 @@ extern int * __error(void) __attribute__((weak));
  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)
@@ -2326,7 +2342,7 @@ extern int * __error(void) __attribute__((weak));
  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);
@@ -2495,6 +2511,17 @@ static size_t my_malloc_size ( void* zone, void* ptr )
    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. */
@@ -2507,7 +2534,7 @@ static vki_malloc_zone_t vg_default_zone = {
     (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
index 7ffc4f88f3d0a708edd64d81db3fe952fab7035b..f30136e291b519b35f74c07e9e154cdde84a6ebe 100644 (file)
@@ -362,7 +362,9 @@ DECL_TEMPLATE(darwin, gettid);                  // 286
 // 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
@@ -540,7 +542,9 @@ DECL_TEMPLATE(darwin, fileport_makeport);        // 430
 // 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
@@ -655,6 +659,19 @@ DECL_TEMPLATE(darwin, abort_with_payload);          // 521
 // 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);
index 09c1338cfa85b88f7061ed1ad2f36ac6acbbce40..25ac11eac3888bcd342db3f5734629f1379882a7 100644 (file)
@@ -44,6 +44,7 @@
 #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"
@@ -3312,10 +3313,34 @@ PRE(stat64)
    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)
@@ -11082,6 +11107,109 @@ POST(kernelrpc_mach_port_type_trap)
 
 #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
    ------------------------------------------------------------------ */
@@ -11433,7 +11561,9 @@ const SyscallTableEntry ML_(syscall_table)[] = {
 // _____(__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), 
@@ -11620,7 +11750,9 @@ const SyscallTableEntry ML_(syscall_table)[] = {
    _____(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),
@@ -11714,6 +11846,22 @@ const SyscallTableEntry ML_(syscall_table)[] = {
 // _____(__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)
 };
 
index ad484c4c27bb4617a5b0d00ab1a7e1143d886a3f..68ffed841e113675300eba276db0d5b7b4a11845 100644 (file)
@@ -76,6 +76,10 @@ extern void VG_(di_notify_pdb_debuginfo)( Int fd, Addr avma,
 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 );
index 40ef71d049aaee6880414be1309bc6e8fb2fe3bc..dcb45cf87e299bf84054d2c26988b3ee6c67261c 100644 (file)
@@ -117,6 +117,9 @@ struct _IIFinaliseImageInfo {
    Addr  initial_client_SP;
    /* ------ Per-OS fields ------ */
    Addr  initial_client_IP;
+#if defined(VGO_darwin)
+   Bool  dynamic;  /* False iff executable is static */
+#endif
 };
 
 /* ------------------------- Solaris ------------------------- */
index 35f036ca06d2ee7766c2d2a84fd8565e489d3037..ec0957139e56836f47a9c8eff8c61b75ed4f4527 100644 (file)
    ...
    fun:ds_user_byuid
 }
+
+{
+   macOS1100:_os_semaphore_wait.cold.1
+   drd:ConflictingAccess
+   fun:_os_semaphore_wait.cold.1
+}
+
index 1bd4175235781ecdef2859179dbfbca89f088388..2ce7d73bff014341a17a7e8ee79725f67137742e 100644 (file)
    ...
    fun:lookUpImpOrForward
 }
+
+{
+   maxOS1100:dlopen_internal
+   Helgrind:Race
+   ...
+   fun:dlopen_internal
+}
+
+{
+   macOS1100::__CFInitialize
+   Helgrind:Race
+   ...
+   fun:__CFInitialize
+}
index f256a3a068c86d8608381f1553b8ee2c716d201b..1e05044fa916d0b1f01af1c546bd05f1cd594d6b 100644 (file)
    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
+}
+
index cc6698ea048c8d29fec9c4d0448beade3e7b2b7a..aa194846378326b0d5288195e1fb4b28861dbd11 100644 (file)
@@ -7,6 +7,7 @@ dist_noinst_SCRIPTS = \
                      filter_xml \
                       filter_fiw \
                      filter_freebsd.awk \
+                     filter_darwin.awk \
                      filter_stderr_freebsd \
                      filter_bug392331
 
diff --git a/helgrind/tests/filter_darwin.awk b/helgrind/tests/filter_darwin.awk
new file mode 100644 (file)
index 0000000..b6c3c6c
--- /dev/null
@@ -0,0 +1,152 @@
+/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;}
index 3a89f54bd3a48fe0e6a16df6f5c6d21b00e5ef52..efb3e2f18865c990468993ac1b739102bfb8e233 100644 (file)
@@ -26,8 +26,7 @@ fi |
 
 # 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 |
index 5acd38a4f5b750b1a8aa3ecf97b4c0bcb928eeec..1aab28f33d37e2ea363f1a40cd8ee81d40d54d6d 100644 (file)
@@ -453,6 +453,10 @@ typedef uint32_t vki_u32;
 #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
@@ -1288,6 +1292,13 @@ struct vki_necp_aggregate_result {
 #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;
index 3f378d3018d4bbab5f26d1c66ef799a493222259..89f7fd3869b9ca6287419ed01e9028d4615278eb 100644 (file)
 #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
index d6c14923c53069cd38695ed492eec9ce1884774a..a9e5d558d1ae0d72b86abc47c0b681e1069abd3b 100644 (file)
@@ -1664,8 +1664,8 @@ void mc_STOREVn_slow ( Addr a, SizeT nBits, ULong vbytes, Bool bigendian )
 /*--- 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
index 4700429023ccf44620b075c29df9e8cc1cd91703..8f8f90015ab01e9f823f69ce3be0d78eebb52d11 100644 (file)
@@ -290,6 +290,7 @@ EXTRA_DIST = \
                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 \
diff --git a/memcheck/tests/memalign_args.stderr.exp-darwin3 b/memcheck/tests/memalign_args.stderr.exp-darwin3
new file mode 100644 (file)
index 0000000..949908d
--- /dev/null
@@ -0,0 +1,11 @@
+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)
index 945acc1a70e8586780a153c8c5a897ed8f0118ce..e49d5ed9a9a868057a2cad6175baf2e60e808826 100644 (file)
@@ -4,6 +4,7 @@
 #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
@@ -23,8 +24,17 @@ int main(int argc, char *argv[], char *envp[], char *apple[])
 
    // 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;
index 0de7184b14c119dddef65a2561d0d4eefc7ebec5..286b22a1352c8cdc743b13a9aa591584b5c9ee6c 100755 (executable)
@@ -25,9 +25,24 @@ sed '/ _so_socket /d;' |
 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 (/" |