From 0d6abe2b5a2e9da8afd5f082cfd11bd8a1ca88d9 Mon Sep 17 00:00:00 2001
From: Derek Bruening <bruening@google.com>
Date: Thu, 9 Jan 2014 21:08:08 +0000
Subject: [PATCH] i#58 MacOS: more Mach-O support

Share the core of module_add_segment_data() with ELF and call it for Mach-O
when iterating segments.

SVN-Revision: 2470
---
 core/unix/memquery_macos.c | 17 +---------
 core/unix/memquery_macos.h | 56 ++++++++++++++++++++++++++++++++
 core/unix/module.c         | 59 +++++++++++++++++++++++++++++++++-
 core/unix/module.h         | 10 +++++-
 core/unix/module_elf.c     | 65 ++++----------------------------------
 core/unix/module_macho.c   | 13 +++++++-
 6 files changed, 142 insertions(+), 78 deletions(-)
 create mode 100644 core/unix/memquery_macos.h

diff --git a/core/unix/memquery_macos.c b/core/unix/memquery_macos.c
index 48d900e92a7..2f69f23ce30 100644
--- a/core/unix/memquery_macos.c
+++ b/core/unix/memquery_macos.c
@@ -40,6 +40,7 @@
 
 #include "../globals.h"
 #include "memquery.h"
+#include "memquery_macos.h"
 #include "os_private.h"
 
 #include <mach/mach.h>
@@ -148,22 +149,6 @@ memquery_iterator_stop(memquery_iter_t *iter)
         mutex_unlock(&memquery_backing_lock);
 }
 
-/* Translate mach flags to memprot flags.  They happen to equal the mmap
- * flags, but best to not rely on that.
- */
-static inline uint
-vmprot_to_memprot(uint prot)
-{
-    uint mem_prot = 0;
-    if (TEST(VM_PROT_EXECUTE, prot))
-        mem_prot |= MEMPROT_EXEC;
-    if (TEST(VM_PROT_READ, prot))
-        mem_prot |= MEMPROT_READ;
-    if (TEST(VM_PROT_WRITE, prot))
-        mem_prot |= MEMPROT_WRITE;
-    return mem_prot;
-}
-
 bool
 memquery_iterator_next(memquery_iter_t *iter)
 {
diff --git a/core/unix/memquery_macos.h b/core/unix/memquery_macos.h
new file mode 100644
index 00000000000..9412271ce46
--- /dev/null
+++ b/core/unix/memquery_macos.h
@@ -0,0 +1,56 @@
+/* *******************************************************************************
+ * Copyright (c) 2014 Google, Inc.  All rights reserved.
+ * *******************************************************************************/
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Google, Inc. nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/*
+ * memquery_macos.h - Mac-specific memory utilities
+ */
+
+#ifndef _MEMQUERY_MACOS_H_
+#define _MEMQUERY_MACOS_H_ 1
+
+/* Translate mach flags to memprot flags.  They happen to equal the mmap
+ * flags, but best to not rely on that.
+ */
+static inline uint
+vmprot_to_memprot(uint prot)
+{
+    uint mem_prot = 0;
+    if (TEST(VM_PROT_EXECUTE, prot))
+        mem_prot |= MEMPROT_EXEC;
+    if (TEST(VM_PROT_READ, prot))
+        mem_prot |= MEMPROT_READ;
+    if (TEST(VM_PROT_WRITE, prot))
+        mem_prot |= MEMPROT_WRITE;
+    return mem_prot;
+}
+
+#endif /* _MEMQUERY_MACOS_H_ */
diff --git a/core/unix/module.c b/core/unix/module.c
index 9b02c3915be..682c198042c 100644
--- a/core/unix/module.c
+++ b/core/unix/module.c
@@ -1,5 +1,5 @@
 /* *******************************************************************************
- * Copyright (c) 2012-2013 Google, Inc.  All rights reserved.
+ * Copyright (c) 2012-2014 Google, Inc.  All rights reserved.
  * Copyright (c) 2011 Massachusetts Institute of Technology  All rights reserved.
  * Copyright (c) 2008-2010 VMware, Inc.  All rights reserved.
  * *******************************************************************************/
@@ -410,6 +410,63 @@ os_module_get_rct_htable(app_pc pc, rct_type_t which)
 }
 #endif
 
+/* Adds an entry for a segment to the out_data->segments array */
+void
+module_add_segment_data(OUT os_module_data_t *out_data,
+                        uint num_segments /*hint only*/,
+                        app_pc segment_start,
+                        size_t segment_size,
+                        uint segment_prot, /* MEMPROT_ */
+                        size_t alignment)
+{
+    uint seg, i;
+    if (out_data->alignment == 0) {
+        out_data->alignment = alignment;
+    } else {
+        /* We expect all segments to have the same alignment */
+        ASSERT_CURIOSITY(out_data->alignment == alignment);
+    }
+    /* Add segments to the module vector (i#160/PR 562667).
+     * For !HAVE_MEMINFO we should combine w/ the segment
+     * walk done in dl_iterate_get_areas_cb().
+     */
+    if (out_data->num_segments == 0) {
+        /* over-allocate to avoid 2 passes to count PT_LOAD */
+        out_data->alloc_segments = (num_segments == 0 ? 4 : num_segments);
+        out_data->segments = (module_segment_t *)
+            HEAP_ARRAY_ALLOC(GLOBAL_DCONTEXT, module_segment_t,
+                             out_data->alloc_segments, ACCT_OTHER, PROTECTED);
+        out_data->contiguous = true;
+    }
+    /* Keep array sorted in addr order.  I'm assuming segments are disjoint! */
+    for (i = 0; i < out_data->num_segments; i++) {
+        if (out_data->segments[i].start > segment_start)
+            break;
+    }
+    seg = i;
+    /* Shift remaining entries */
+    for (i = out_data->num_segments; i > seg; i++) {
+        out_data->segments[i] = out_data->segments[i - 1];
+    }
+    out_data->num_segments++;
+    ASSERT(out_data->num_segments <= out_data->alloc_segments);
+    /* ELF requires p_vaddr to already be aligned to p_align */
+    out_data->segments[seg].start = (app_pc) ALIGN_BACKWARD(segment_start, PAGE_SIZE);
+    out_data->segments[seg].end = (app_pc)
+        ALIGN_FORWARD(segment_start + segment_size, PAGE_SIZE);
+    out_data->segments[seg].prot = segment_prot;
+    if (seg > 0) {
+        ASSERT(out_data->segments[seg].start >= out_data->segments[seg - 1].end);
+        if (out_data->segments[seg].start > out_data->segments[seg - 1].end)
+            out_data->contiguous = false;
+    }
+    if (seg < out_data->num_segments - 1) {
+        ASSERT(out_data->segments[seg + 1].start >= out_data->segments[seg].end);
+        if (out_data->segments[seg + 1].start > out_data->segments[seg].end)
+            out_data->contiguous = false;
+    }
+}
+
 /* Returns true if the module has an nth segment, false otherwise. */
 bool
 module_get_nth_segment(app_pc module_base, uint n,
diff --git a/core/unix/module.h b/core/unix/module.h
index a8b4f694072..63e89327220 100644
--- a/core/unix/module.h
+++ b/core/unix/module.h
@@ -1,5 +1,5 @@
 /* **********************************************************
- * Copyright (c) 2011-2013 Google, Inc.  All rights reserved.
+ * Copyright (c) 2011-2014 Google, Inc.  All rights reserved.
  * Copyright (c) 2008-2010 VMware, Inc.  All rights reserved.
  * **********************************************************/
 
@@ -116,6 +116,14 @@ module_file_is_module64(file_t f);
 bool
 module_get_platform(file_t f, dr_platform_t *platform);
 
+void
+module_add_segment_data(OUT os_module_data_t *out_data,
+                        uint num_segments /*hint only*/,
+                        app_pc segment_start,
+                        size_t segment_size,
+                        uint segment_prot,
+                        size_t alignment);
+
 /* Redirected functions for loaded module,
  * they are also used by __wrap_* functions in instrument.c
  */
diff --git a/core/unix/module_elf.c b/core/unix/module_elf.c
index 3b5d278b194..ed0ab0a9dc6 100644
--- a/core/unix/module_elf.c
+++ b/core/unix/module_elf.c
@@ -1,5 +1,5 @@
 /* *******************************************************************************
- * Copyright (c) 2012-2013 Google, Inc.  All rights reserved.
+ * Copyright (c) 2012-2014 Google, Inc.  All rights reserved.
  * Copyright (c) 2011 Massachusetts Institute of Technology  All rights reserved.
  * Copyright (c) 2008-2010 VMware, Inc.  All rights reserved.
  * *******************************************************************************/
@@ -311,62 +311,6 @@ module_segment_prot_to_osprot(ELF_PROGRAM_HEADER_TYPE *prog_hdr)
 
 #ifndef NOT_DYNAMORIO_CORE_PROPER
 
-/* Adds an entry for a segment to the out_data->segments array */
-static void
-module_add_segment_data(OUT os_module_data_t *out_data,
-                        ELF_HEADER_TYPE *elf_hdr,
-                        ptr_int_t load_delta,
-                        ELF_PROGRAM_HEADER_TYPE *prog_hdr)
-{
-    uint seg, i;
-    if (out_data->alignment == 0) {
-        out_data->alignment = prog_hdr->p_align;
-    } else {
-        /* We expect all segments to have the same alignment */
-        ASSERT_CURIOSITY(out_data->alignment == prog_hdr->p_align);
-    }
-    /* Add segments to the module vector (i#160/PR 562667).
-     * For !HAVE_MEMINFO we should combine w/ the segment
-     * walk done in dl_iterate_get_areas_cb().
-     */
-    if (out_data->num_segments == 0) {
-        /* over-allocate to avoid 2 passes to count PT_LOAD */
-        out_data->alloc_segments = elf_hdr->e_phnum;
-        out_data->segments = (module_segment_t *)
-            HEAP_ARRAY_ALLOC(GLOBAL_DCONTEXT, module_segment_t,
-                             out_data->alloc_segments, ACCT_OTHER, PROTECTED);
-        out_data->contiguous = true;
-    }
-    /* Keep array sorted in addr order.  I'm assuming segments are disjoint! */
-    for (i = 0; i < out_data->num_segments; i++) {
-        if (out_data->segments[i].start > (app_pc)prog_hdr->p_vaddr + load_delta)
-            break;
-    }
-    seg = i;
-    /* Shift remaining entries */
-    for (i = out_data->num_segments; i > seg; i++) {
-        out_data->segments[i] = out_data->segments[i - 1];
-    }
-    out_data->num_segments++;
-    ASSERT(out_data->num_segments <= out_data->alloc_segments);
-    /* ELF requires p_vaddr to already be aligned to p_align */
-    out_data->segments[seg].start = (app_pc)
-        ALIGN_BACKWARD(prog_hdr->p_vaddr + load_delta, PAGE_SIZE);
-    out_data->segments[seg].end = (app_pc)
-        ALIGN_FORWARD(prog_hdr->p_vaddr + load_delta + prog_hdr->p_memsz, PAGE_SIZE);
-    out_data->segments[seg].prot = module_segment_prot_to_osprot(prog_hdr);
-    if (seg > 0) {
-        ASSERT(out_data->segments[seg].start >= out_data->segments[seg - 1].end);
-        if (out_data->segments[seg].start > out_data->segments[seg - 1].end)
-            out_data->contiguous = false;
-    }
-    if (seg < out_data->num_segments - 1) {
-        ASSERT(out_data->segments[seg + 1].start >= out_data->segments[seg].end);
-        if (out_data->segments[seg + 1].start > out_data->segments[seg].end)
-            out_data->contiguous = false;
-    }
-}
-
 /* common code to fill os_module_data_t for loader and module_area_t */
 static void
 module_fill_os_data(ELF_PROGRAM_HEADER_TYPE *prog_hdr, /* PT_DYNAMIC entry */
@@ -519,8 +463,11 @@ module_walk_program_headers(app_pc base, size_t view_size, bool at_map,
                 (base + elf_hdr->e_phoff + i * elf_hdr->e_phentsize);
             if (prog_hdr->p_type == PT_LOAD) {
                 if (out_data != NULL) {
-                    module_add_segment_data(out_data, elf_hdr,
-                                            load_delta, prog_hdr);
+                    module_add_segment_data(out_data, elf_hdr->e_phnum,
+                                            (app_pc) prog_hdr->p_vaddr + load_delta,
+                                            prog_hdr->p_memsz,
+                                            module_segment_prot_to_osprot(prog_hdr),
+                                            prog_hdr->p_align);
                 }
                 found_load = true;
             }
diff --git a/core/unix/module_macho.c b/core/unix/module_macho.c
index 2516323e483..e5686e88fda 100644
--- a/core/unix/module_macho.c
+++ b/core/unix/module_macho.c
@@ -46,6 +46,7 @@
 #include "../module_shared.h"
 #include "os_private.h"
 #include "module_private.h"
+#include "memquery_macos.h"
 #include <mach-o/ldsyms.h> /* _mh_dylib_header */
 #include <mach-o/loader.h> /* mach_header */
 #include <stddef.h> /* offsetof */
@@ -129,7 +130,7 @@ module_walk_program_headers(app_pc base, size_t view_size, bool at_map,
 {
     mach_header_t *hdr = (mach_header_t *) base;
     struct load_command *cmd, *cmd_stop;
-    app_pc seg_min_start = base + view_size;
+    app_pc seg_min_start = base + (view_size == 0 ? PAGE_SIZE : view_size);
     app_pc seg_max_end = base;
     bool found_seg = false;
     ASSERT(is_macho_header(base, view_size));
@@ -150,6 +151,16 @@ module_walk_program_headers(app_pc base, size_t view_size, bool at_map,
              * perhaps share some code there?).
              * We also need to fill in out_data (like ELF module_fill_os_data).
              */
+            if (out_data != NULL) {
+                module_add_segment_data(out_data, 0/*don't know*/,
+                                        (app_pc) seg->vmaddr, seg->vmsize,
+                                        /* assuming we want initprot and not maxprot */
+                                        vmprot_to_memprot(seg->initprot),
+                                        /* XXX: alignment is specified per section --
+                                         * ignoring for now
+                                         */
+                                        PAGE_SIZE);
+            }
         } else if (cmd->cmd == LC_ID_DYLIB) {
             struct dylib_command *dy = (struct dylib_command *) cmd;
             char *soname = (char *)cmd + dy->dylib.name.offset;