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;