--- /dev/null
+/*
+ * File macho_module.c - processing of Mach-O files
+ * Originally based on elf_module.c
+ *
+ * Copyright (C) 1996, Eric Youngdale.
+ * 1999-2007 Eric Pouech
+ * 2009 Ken Thomases, CodeWeavers Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include "dbghelp_private.h"
+
+#ifdef __MACH__
+
+#include <assert.h>
+#include <stdarg.h>
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
+
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+
+#ifdef HAVE_MACH_O_DYLD_IMAGES_H
+#include <mach-o/dyld_images.h>
+#else
+struct dyld_image_info {
+ const struct mach_header *imageLoadAddress;
+ const char *imageFilePath;
+ uintptr_t imageFileModDate;
+};
+
+struct dyld_all_image_infos {
+ uint32_t version;
+ uint32_t infoArrayCount;
+ const struct dyld_image_info *infoArray;
+ void* notification;
+ int processDetachedFromSharedRegion;
+};
+#endif
+
+#include "winternl.h"
+#include "wine/library.h"
+#include "wine/debug.h"
+
+#ifdef WORDS_BIGENDIAN
+#define swap_ulong_be_to_host(n) (n)
+#else
+#define swap_ulong_be_to_host(n) (RtlUlongByteSwap(n))
+#endif
+
+WINE_DEFAULT_DEBUG_CHANNEL(dbghelp_macho);
+
+
+struct macho_module_info
+{
+ unsigned long load_addr;
+ unsigned short in_use : 1,
+ is_loader : 1;
+};
+
+#define MACHO_INFO_DEBUG_HEADER 0x0001
+#define MACHO_INFO_MODULE 0x0002
+#define MACHO_INFO_NAME 0x0004
+
+struct macho_info
+{
+ unsigned flags; /* IN one (or several) of the MACHO_INFO constants */
+ unsigned long dbg_hdr_addr; /* OUT address of debug header (if MACHO_INFO_DEBUG_HEADER is set) */
+ struct module* module; /* OUT loaded module (if MACHO_INFO_MODULE is set) */
+ const WCHAR* module_name; /* OUT found module name (if MACHO_INFO_NAME is set) */
+};
+
+/* structure holding information while handling a Mach-O image */
+#define BITS_PER_ULONG (sizeof(ULONG) * 8)
+#define ULONGS_FOR_BITS(nbits) (((nbits) + BITS_PER_ULONG - 1) / BITS_PER_ULONG)
+struct macho_file_map
+{
+ /* A copy of the Mach-O header for an individual architecture. */
+ struct mach_header mach_header;
+
+ /* The mapped load commands. */
+ const struct load_command* load_commands;
+
+ /* The portion of the file which is this architecture. mach_header was
+ * read from arch_offset. */
+ unsigned arch_offset;
+ unsigned arch_size;
+
+ /* The range of address space covered by all segments. */
+ size_t segs_start;
+ size_t segs_size;
+
+ /* Map of which sections contain code. Sections are accessed using 1-based
+ * index. Bit 0 of this bitset indicates if the bitset has been initialized. */
+ RTL_BITMAP sect_is_code;
+ ULONG sect_is_code_buff[ULONGS_FOR_BITS(MAX_SECT + 1)];
+
+ /* The file. */
+ int fd;
+};
+
+static void macho_unmap_file(struct macho_file_map* fmap);
+
+/******************************************************************
+ * macho_calc_range
+ *
+ * For a range (offset & length) of a single architecture within
+ * a Mach-O file, calculate the page-aligned range of the whole file
+ * that encompasses it. For a fat binary, the architecture will
+ * itself be offset within the file, so take that into account.
+ */
+static void macho_calc_range(const struct macho_file_map* fmap, unsigned offset,
+ unsigned len, unsigned* out_aligned_offset,
+ unsigned* out_aligned_end, unsigned* out_aligned_len,
+ unsigned* out_misalign)
+{
+ unsigned pagemask = getpagesize() - 1;
+ unsigned file_offset, misalign;
+
+ file_offset = fmap->arch_offset + offset;
+ misalign = file_offset & pagemask;
+ *out_aligned_offset = file_offset - misalign;
+ *out_aligned_end = (file_offset + len + pagemask) & ~pagemask;
+ if (out_aligned_len)
+ *out_aligned_len = *out_aligned_end - *out_aligned_offset;
+ if (out_misalign)
+ *out_misalign = misalign;
+}
+
+/******************************************************************
+ * macho_map_range
+ *
+ * Maps a range (offset, length in bytes) from a Mach-O file into memory
+ */
+static const char* macho_map_range(const struct macho_file_map* fmap, unsigned offset, unsigned len)
+{
+ unsigned misalign, aligned_offset, aligned_map_end, map_size;
+ const void* aligned_ptr;
+
+ TRACE("(%p/%d, 0x%08x, 0x%08x)\n", fmap, fmap->fd, offset, len);
+
+ macho_calc_range(fmap, offset, len, &aligned_offset, &aligned_map_end,
+ &map_size, &misalign);
+
+ aligned_ptr = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fmap->fd, aligned_offset);
+
+ TRACE("Mapped (0x%08x - 0x%08x) to %p\n", aligned_offset, aligned_map_end, aligned_ptr);
+
+ if (aligned_ptr == MAP_FAILED) return MACHO_NO_MAP;
+ return (const char*)aligned_ptr + misalign;
+}
+
+/******************************************************************
+ * macho_unmap_range
+ *
+ * Unmaps a range (offset, length in bytes) of a Mach-O file from memory
+ */
+static void macho_unmap_range(const void** mapped, const struct macho_file_map* fmap,
+ unsigned offset, unsigned len)
+{
+ TRACE("(%p, %p/%d, 0x%08x, 0x%08x)\n", mapped, fmap, fmap->fd, offset, len);
+
+ if (mapped && *mapped != MACHO_NO_MAP)
+ {
+ unsigned misalign, aligned_offset, aligned_map_end, map_size;
+ void* aligned_ptr;
+
+ macho_calc_range(fmap, offset, len, &aligned_offset, &aligned_map_end,
+ &map_size, &misalign);
+
+ aligned_ptr = (char*)*mapped - misalign;
+ if (munmap(aligned_ptr, map_size) < 0)
+ WARN("Couldn't unmap the range\n");
+ TRACE("Unmapped (0x%08x - 0x%08x) from %p - %p\n", aligned_offset, aligned_map_end, aligned_ptr, (char*)aligned_ptr + map_size);
+ *mapped = MACHO_NO_MAP;
+ }
+}
+
+/******************************************************************
+ * macho_map_ranges
+ *
+ * Maps two ranges (offset, length in bytes) from a Mach-O file
+ * into memory. If the two ranges overlap, use one mmap so that
+ * the munmap doesn't fragment the mapping.
+ */
+static BOOL macho_map_ranges(const struct macho_file_map* fmap,
+ unsigned offset1, unsigned len1,
+ unsigned offset2, unsigned len2,
+ const void** mapped1, const void** mapped2)
+{
+ unsigned aligned_offset1, aligned_map_end1;
+ unsigned aligned_offset2, aligned_map_end2;
+
+ TRACE("(%p/%d, 0x%08x, 0x%08x, 0x%08x, 0x%08x, %p, %p)\n", fmap, fmap->fd,
+ offset1, len1, offset2, len2, mapped1, mapped2);
+
+ macho_calc_range(fmap, offset1, len1, &aligned_offset1, &aligned_map_end1, NULL, NULL);
+ macho_calc_range(fmap, offset2, len2, &aligned_offset2, &aligned_map_end2, NULL, NULL);
+
+ if (aligned_map_end1 < aligned_offset2 || aligned_map_end2 < aligned_offset1)
+ {
+ *mapped1 = macho_map_range(fmap, offset1, len1);
+ if (*mapped1 != MACHO_NO_MAP)
+ {
+ *mapped2 = macho_map_range(fmap, offset2, len2);
+ if (*mapped2 == MACHO_NO_MAP)
+ macho_unmap_range(mapped1, fmap, offset1, len1);
+ }
+ }
+ else
+ {
+ if (offset1 < offset2)
+ {
+ *mapped1 = macho_map_range(fmap, offset1, offset2 + len2 - offset1);
+ if (*mapped1 != MACHO_NO_MAP)
+ *mapped2 = (const char*)*mapped1 + offset2 - offset1;
+ }
+ else
+ {
+ *mapped2 = macho_map_range(fmap, offset2, offset1 + len1 - offset2);
+ if (*mapped2 != MACHO_NO_MAP)
+ *mapped1 = (const char*)*mapped2 + offset1 - offset2;
+ }
+ }
+
+ TRACE(" => %p, %p\n", *mapped1, *mapped2);
+
+ return (*mapped1 != MACHO_NO_MAP) && (*mapped2 != MACHO_NO_MAP);
+}
+
+/******************************************************************
+ * macho_unmap_ranges
+ *
+ * Unmaps two ranges (offset, length in bytes) of a Mach-O file
+ * from memory. Use for ranges which were mapped by
+ * macho_map_ranges.
+ */
+static void macho_unmap_ranges(const struct macho_file_map* fmap,
+ unsigned offset1, unsigned len1,
+ unsigned offset2, unsigned len2,
+ const void** mapped1, const void** mapped2)
+{
+ unsigned aligned_offset1, aligned_map_end1;
+ unsigned aligned_offset2, aligned_map_end2;
+
+ TRACE("(%p/%d, 0x%08x, 0x%08x, 0x%08x, 0x%08x, %p/%p, %p/%p)\n", fmap, fmap->fd,
+ offset1, len1, offset2, len2, mapped1, *mapped1, mapped2, *mapped2);
+
+ macho_calc_range(fmap, offset1, len1, &aligned_offset1, &aligned_map_end1, NULL, NULL);
+ macho_calc_range(fmap, offset2, len2, &aligned_offset2, &aligned_map_end2, NULL, NULL);
+
+ if (aligned_map_end1 < aligned_offset2 || aligned_map_end2 < aligned_offset1)
+ {
+ macho_unmap_range(mapped1, fmap, offset1, len1);
+ macho_unmap_range(mapped2, fmap, offset2, len2);
+ }
+ else
+ {
+ if (offset1 < offset2)
+ {
+ macho_unmap_range(mapped1, fmap, offset1, offset2 + len2 - offset1);
+ *mapped2 = MACHO_NO_MAP;
+ }
+ else
+ {
+ macho_unmap_range(mapped2, fmap, offset2, offset1 + len1 - offset2);
+ *mapped1 = MACHO_NO_MAP;
+ }
+ }
+}
+
+/******************************************************************
+ * macho_map_load_commands
+ *
+ * Maps the load commands from a Mach-O file into memory
+ */
+static const struct load_command* macho_map_load_commands(struct macho_file_map* fmap)
+{
+ if (fmap->load_commands == MACHO_NO_MAP)
+ {
+ fmap->load_commands = (const struct load_command*) macho_map_range(
+ fmap, sizeof(fmap->mach_header), fmap->mach_header.sizeofcmds);
+ TRACE("Mapped load commands: %p\n", fmap->load_commands);
+ }
+
+ return fmap->load_commands;
+}
+
+/******************************************************************
+ * macho_unmap_load_commands
+ *
+ * Unmaps the load commands of a Mach-O file from memory
+ */
+static void macho_unmap_load_commands(struct macho_file_map* fmap)
+{
+ if (fmap->load_commands != MACHO_NO_MAP)
+ {
+ TRACE("Unmapping load commands: %p\n", fmap->load_commands);
+ macho_unmap_range((const void**)&fmap->load_commands, fmap,
+ sizeof(fmap->mach_header), fmap->mach_header.sizeofcmds);
+ }
+}
+
+/******************************************************************
+ * macho_next_load_command
+ *
+ * Advance to the next load command
+ */
+static const struct load_command* macho_next_load_command(const struct load_command* lc)
+{
+ return (const struct load_command*)((const char*)lc + lc->cmdsize);
+}
+
+/******************************************************************
+ * macho_enum_load_commands
+ *
+ * Enumerates the load commands for a Mach-O file, selecting by
+ * the command type, calling a callback for each. If the callback
+ * returns <0, that indicates an error. If it returns >0, that means
+ * it's not interested in getting any more load commands.
+ * If this function returns <0, that's an error produced by the
+ * callback. If >=0, that's the count of load commands successfully
+ * processed.
+ */
+static int macho_enum_load_commands(struct macho_file_map* fmap, unsigned cmd,
+ int (*cb)(struct macho_file_map*, const struct load_command*, void*),
+ void* user)
+{
+ const struct load_command* lc;
+ int i;
+ int count = 0;
+
+ TRACE("(%p/%d, %u, %p, %p)\n", fmap, fmap->fd, cmd, cb, user);
+
+ if ((lc = macho_map_load_commands(fmap)) == MACHO_NO_MAP) return -1;
+
+ TRACE("%d total commands\n", fmap->mach_header.ncmds);
+
+ for (i = 0; i < fmap->mach_header.ncmds; i++, lc = macho_next_load_command(lc))
+ {
+ int result;
+
+ if (cmd && cmd != lc->cmd) continue;
+ count++;
+
+ result = cb(fmap, lc, user);
+ TRACE("load_command[%d] (%p), cmd %u; callback => %d\n", i, lc, lc->cmd, result);
+ if (result) return (result < 0) ? result : count;
+ }
+
+ return count;
+}
+
+/******************************************************************
+ * macho_accum_segs_range
+ *
+ * Callback for macho_enum_load_commands. Accumulates the address
+ * range covered by the segments of a Mach-O file. All commands
+ * are expected to be of LC_SEGMENT type.
+ */
+static int macho_accum_segs_range(struct macho_file_map* fmap,
+ const struct load_command* lc, void* user)
+{
+ const struct segment_command* sc = (const struct segment_command*)lc;
+ unsigned tmp, page_mask = getpagesize() - 1;
+
+ TRACE("(%p/%d, %p, %p) before: 0x%08x - 0x%08x\n", fmap, fmap->fd, lc, user,
+ (unsigned)fmap->segs_start, (unsigned)fmap->segs_size);
+ TRACE("Segment command vm: 0x%08x - 0x%08x\n", (unsigned)sc->vmaddr,
+ (unsigned)sc->vmaddr + sc->vmsize);
+
+ if (!strncmp(sc->segname, "WINE_DOS", sizeof(sc->segname)) ||
+ !strncmp(sc->segname, "WINE_OLE32", sizeof(sc->segname)) ||
+ !strncmp(sc->segname, "WINE_SHARED_HEAP", sizeof(sc->segname)))
+ {
+ TRACE("Ignoring special Wine segment %s\n", debugstr_an(sc->segname, sizeof(sc->segname)));
+ return 0;
+ }
+
+ /* If this segment starts before previously-known earliest, record
+ * new earliest. */
+ if (sc->vmaddr < fmap->segs_start)
+ fmap->segs_start = sc->vmaddr;
+
+ /* If this segment extends beyond previously-known furthest, record
+ * new furthest. */
+ tmp = (sc->vmaddr + sc->vmsize + page_mask) & ~page_mask;
+ if (fmap->segs_size < tmp) fmap->segs_size = tmp;
+
+ TRACE("after: 0x%08x - 0x%08x\n", (unsigned)fmap->segs_start, (unsigned)fmap->segs_size);
+
+ return 0;
+}
+
+/******************************************************************
+ * macho_map_file
+ *
+ * Maps a Mach-O file into memory (and checks it's a real Mach-O file)
+ */
+static BOOL macho_map_file(const WCHAR* filenameW, struct macho_file_map* fmap)
+{
+ struct fat_header fat_header;
+ struct stat statbuf;
+ int i;
+ char* filename;
+ unsigned len;
+ BOOL ret = FALSE;
+
+ TRACE("(%s, %p)\n", debugstr_w(filenameW), fmap);
+
+ fmap->fd = -1;
+ fmap->load_commands = MACHO_NO_MAP;
+ RtlInitializeBitMap(&fmap->sect_is_code, fmap->sect_is_code_buff, MAX_SECT + 1);
+
+ len = WideCharToMultiByte(CP_UNIXCP, 0, filenameW, -1, NULL, 0, NULL, NULL);
+ if (!(filename = HeapAlloc(GetProcessHeap(), 0, len))) return FALSE;
+ WideCharToMultiByte(CP_UNIXCP, 0, filenameW, -1, filename, len, NULL, NULL);
+
+ /* check that the file exists */
+ if (stat(filename, &statbuf) == -1 || S_ISDIR(statbuf.st_mode)) goto done;
+
+ /* Now open the file, so that we can mmap() it. */
+ if ((fmap->fd = open(filename, O_RDONLY)) == -1) goto done;
+
+ if (read(fmap->fd, &fat_header, sizeof(fat_header)) != sizeof(fat_header))
+ goto done;
+ TRACE("... got possible fat header\n");
+
+ /* Fat header is always in big-endian order. */
+ if (swap_ulong_be_to_host(fat_header.magic) == FAT_MAGIC)
+ {
+ int narch = swap_ulong_be_to_host(fat_header.nfat_arch);
+ for (i = 0; i < narch; i++)
+ {
+ struct fat_arch fat_arch;
+ if (read(fmap->fd, &fat_arch, sizeof(fat_arch)) != sizeof(fat_arch))
+ goto done;
+ if (swap_ulong_be_to_host(fat_arch.cputype) == CPU_TYPE_X86)
+ {
+ fmap->arch_offset = swap_ulong_be_to_host(fat_arch.offset);
+ fmap->arch_size = swap_ulong_be_to_host(fat_arch.size);
+ break;
+ }
+ }
+ if (i >= narch) goto done;
+ TRACE("... found x86 arch\n");
+ }
+ else
+ {
+ fmap->arch_offset = 0;
+ fmap->arch_size = statbuf.st_size;
+ TRACE("... not a fat header\n");
+ }
+
+ /* Individual architecture (standalone or within a fat file) is in its native byte order. */
+ lseek(fmap->fd, fmap->arch_offset, SEEK_SET);
+ if (read(fmap->fd, &fmap->mach_header, sizeof(fmap->mach_header)) != sizeof(fmap->mach_header))
+ goto done;
+ TRACE("... got possible Mach header\n");
+ /* and check for a Mach-O header */
+ if (fmap->mach_header.magic != MH_MAGIC ||
+ fmap->mach_header.cputype != CPU_TYPE_X86) goto done;
+ /* Make sure the file type is one of the ones we expect. */
+ switch (fmap->mach_header.filetype)
+ {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_DYLINKER:
+ case MH_BUNDLE:
+ break;
+ default:
+ goto done;
+ }
+ TRACE("... verified Mach x86 header\n");
+
+ fmap->segs_size = 0;
+ fmap->segs_start = ~0L;
+
+ if (macho_enum_load_commands(fmap, LC_SEGMENT, macho_accum_segs_range, NULL) < 0)
+ goto done;
+
+ fmap->segs_size -= fmap->segs_start;
+ TRACE("segs_start: 0x%08x, segs_size: 0x%08x\n", (unsigned)fmap->segs_start,
+ (unsigned)fmap->segs_size);
+
+ ret = TRUE;
+done:
+ if (!ret)
+ macho_unmap_file(fmap);
+ HeapFree(GetProcessHeap(), 0, filename);
+ return ret;
+}
+
+/******************************************************************
+ * macho_unmap_file
+ *
+ * Unmaps a Mach-O file from memory (previously mapped with macho_map_file)
+ */
+static void macho_unmap_file(struct macho_file_map* fmap)
+{
+ TRACE("(%p/%d)\n", fmap, fmap->fd);
+ if (fmap->fd != -1)
+ {
+ macho_unmap_load_commands(fmap);
+ close(fmap->fd);
+ fmap->fd = -1;
+ }
+}
+
+/******************************************************************
+ * macho_fill_sect_is_code
+ *
+ * Callback for macho_enum_load_commands. Determines which segments
+ * of a Mach-O file contain code. All commands are expected to be
+ * of LC_SEGMENT type.
+ */
+static int macho_fill_sect_is_code(struct macho_file_map* fmap,
+ const struct load_command* lc, void* user)
+{
+ const struct segment_command* sc = (const struct segment_command*)lc;
+ const struct section* sections;
+ int* cursect = user;
+ int i;
+
+ TRACE("(%p/%d, %p, %p/%d) scanning %u sections\n", fmap, fmap->fd, lc,
+ cursect, *cursect, sc->nsects);
+
+ sections = (const struct section*)(sc + 1);
+ for (i = 0; i < sc->nsects; i++)
+ {
+ if (*cursect > MAX_SECT) return -1;
+ (*cursect)++;
+
+ if (!(sections[i].flags & SECTION_TYPE) &&
+ (sections[i].flags & (S_ATTR_PURE_INSTRUCTIONS|S_ATTR_SOME_INSTRUCTIONS)))
+ RtlSetBits(&fmap->sect_is_code, *cursect, 1);
+ else
+ RtlClearBits(&fmap->sect_is_code, *cursect, 1);
+ TRACE("Section %d (%d of this segment) is%s code\n", *cursect, i,
+ (RtlAreBitsSet(&fmap->sect_is_code, *cursect, 1) ? "" : " not"));
+ }
+
+ return 0;
+}
+
+/******************************************************************
+ * macho_sect_is_code
+ *
+ * Checks if a section, identified by sectidx which is a 1-based
+ * index into the sections of all segments, in order of load
+ * commands, contains code.
+ */
+static BOOL macho_sect_is_code(struct macho_file_map* fmap, unsigned char sectidx)
+{
+ TRACE("(%p/%d, %u)\n", fmap, fmap->fd, sectidx);
+
+ if (!RtlAreBitsSet(&fmap->sect_is_code, 0, 1))
+ {
+ int cursect = 0;
+ if (macho_enum_load_commands(fmap, LC_SEGMENT, macho_fill_sect_is_code, &cursect) < 0)
+ WARN("Couldn't load sect_is_code map\n");
+ RtlSetBits(&fmap->sect_is_code, 0, 1);
+ }
+
+ return RtlAreBitsSet(&fmap->sect_is_code, sectidx, 1);
+}
+
+struct symtab_elt
+{
+ struct hash_table_elt ht_elt;
+ struct symt_compiland* compiland;
+ unsigned long addr;
+ unsigned char is_code:1,
+ is_public:1,
+ is_global:1,
+ used:1;
+};
+
+struct macho_debug_info
+{
+ struct macho_file_map* fmap;
+ struct module* module;
+ struct pool pool;
+ struct hash_table ht_symtab;
+};
+
+/******************************************************************
+ * macho_stabs_def_cb
+ *
+ * Callback for stabs_parse. Collect symbol definitions.
+ */
+static void macho_stabs_def_cb(struct module* module, unsigned long load_offset,
+ const char* name, unsigned long offset,
+ BOOL is_public, BOOL is_global, unsigned char sectidx,
+ struct symt_compiland* compiland, void* user)
+{
+ struct macho_debug_info* mdi = user;
+ struct symtab_elt* ste;
+
+ TRACE("(%p, 0x%08lx, %s, 0x%08lx, %d, %d, %u, %p, %p/%p/%d)\n", module, load_offset,
+ debugstr_a(name), offset, is_public, is_global, sectidx,
+ compiland, mdi, mdi->fmap, mdi->fmap->fd);
+
+ /* Defer the creation of new non-debugging symbols until after we've
+ * finished parsing the stabs. */
+ ste = pool_alloc(&mdi->pool, sizeof(*ste));
+ ste->ht_elt.name = pool_strdup(&mdi->pool, name);
+ ste->compiland = compiland;
+ ste->addr = load_offset + offset;
+ ste->is_code = !!macho_sect_is_code(mdi->fmap, sectidx);
+ ste->is_public = !!is_public;
+ ste->is_global = !!is_global;
+ ste->used = 0;
+ hash_table_add(&mdi->ht_symtab, &ste->ht_elt);
+}
+
+/******************************************************************
+ * macho_parse_symtab
+ *
+ * Callback for macho_enum_load_commands. Processes the LC_SYMTAB
+ * load commands from the Mach-O file.
+ */
+static int macho_parse_symtab(struct macho_file_map* fmap,
+ const struct load_command* lc, void* user)
+{
+ const struct symtab_command* sc = (const struct symtab_command*)lc;
+ struct macho_debug_info* mdi = user;
+ const struct nlist* stab;
+ const char* stabstr;
+ int ret = 0;
+
+ TRACE("(%p/%d, %p, %p) %u syms at 0x%08x, strings 0x%08x - 0x%08x\n", fmap, fmap->fd, lc,
+ user, sc->nsyms, sc->symoff, sc->stroff, sc->stroff + sc->strsize);
+
+ if (!macho_map_ranges(fmap, sc->symoff, sc->nsyms * sizeof(struct nlist),
+ sc->stroff, sc->strsize, (const void**)&stab, (const void**)&stabstr))
+ return 0;
+
+ if (!stabs_parse(mdi->module, mdi->module->macho_info->load_addr - fmap->segs_start,
+ stab, sc->nsyms * sizeof(struct nlist),
+ stabstr, sc->strsize, macho_stabs_def_cb, mdi))
+ ret = -1;
+
+ macho_unmap_ranges(fmap, sc->symoff, sc->nsyms * sizeof(struct nlist),
+ sc->stroff, sc->strsize, (const void**)&stab, (const void**)&stabstr);
+
+ return ret;
+}
+
+/******************************************************************
+ * macho_finish_stabs
+ *
+ * Integrate the non-debugging symbols we've gathered into the
+ * symbols that were generated during stabs parsing.
+ */
+static void macho_finish_stabs(struct module* module, struct hash_table* ht_symtab)
+{
+ struct hash_table_iter hti_ours;
+ struct symtab_elt* ste;
+ BOOL adjusted = FALSE;
+
+ TRACE("(%p, %p)\n", module, ht_symtab);
+
+ /* For each of our non-debugging symbols, see if it can provide some
+ * missing details to one of the module's known symbols. */
+ hash_table_iter_init(ht_symtab, &hti_ours, NULL);
+ while ((ste = hash_table_iter_up(&hti_ours)))
+ {
+ struct hash_table_iter hti_modules;
+ void* ptr;
+ struct symt_ht* sym;
+ struct symt_function* func;
+ struct symt_data* data;
+
+ hash_table_iter_init(&module->ht_symbols, &hti_modules, ste->ht_elt.name);
+ while ((ptr = hash_table_iter_up(&hti_modules)))
+ {
+ sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
+
+ if (strcmp(sym->hash_elt.name, ste->ht_elt.name))
+ continue;
+
+ switch (sym->symt.tag)
+ {
+ case SymTagFunction:
+ func = (struct symt_function*)sym;
+ if (func->address == module->macho_info->load_addr)
+ {
+ TRACE("Adjusting function %p/%s!%s from 0x%08lx to 0x%08lx\n", func,
+ debugstr_w(module->module.ModuleName), sym->hash_elt.name,
+ func->address, ste->addr);
+ func->address = ste->addr;
+ adjusted = TRUE;
+ }
+ if (func->address == ste->addr)
+ ste->used = 1;
+ break;
+ case SymTagData:
+ data = (struct symt_data*)sym;
+ switch (data->kind)
+ {
+ case DataIsGlobal:
+ case DataIsFileStatic:
+ if (data->u.var.offset == module->macho_info->load_addr)
+ {
+ TRACE("Adjusting data symbol %p/%s!%s from 0x%08lx to 0x%08lx\n",
+ data, debugstr_w(module->module.ModuleName), sym->hash_elt.name,
+ data->u.var.offset, ste->addr);
+ data->u.var.offset = ste->addr;
+ adjusted = TRUE;
+ }
+ if (data->u.var.offset == ste->addr)
+ {
+ enum DataKind new_kind;
+
+ new_kind = ste->is_global ? DataIsGlobal : DataIsFileStatic;
+ if (data->kind != new_kind)
+ {
+ WARN("Changing kind for %p/%s!%s from %d to %d\n", sym,
+ debugstr_w(module->module.ModuleName), sym->hash_elt.name,
+ (int)data->kind, (int)new_kind);
+ data->kind = new_kind;
+ adjusted = TRUE;
+ }
+ ste->used = 1;
+ }
+ break;
+ default:;
+ }
+ break;
+ default:
+ TRACE("Ignoring tag %u\n", sym->symt.tag);
+ break;
+ }
+ }
+ }
+
+ if (adjusted)
+ {
+ /* since we may have changed some addresses, mark the module to be resorted */
+ module->sortlist_valid = FALSE;
+ }
+
+ /* Mark any of our non-debugging symbols which fall on an already-used
+ * address as "used". This allows us to skip them in the next loop,
+ * below. We do this in separate loops because symt_new_* marks the
+ * list as needing sorting and symt_find_nearest sorts if needed,
+ * causing thrashing. */
+ if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY))
+ {
+ hash_table_iter_init(ht_symtab, &hti_ours, NULL);
+ while ((ste = hash_table_iter_up(&hti_ours)))
+ {
+ struct symt_ht* sym;
+ ULONG64 addr;
+
+ if (ste->used) continue;
+
+ sym = symt_find_nearest(module, ste->addr);
+ if (sym)
+ symt_get_info(&sym->symt, TI_GET_ADDRESS, &addr);
+ if (sym && ste->addr == addr)
+ {
+ ULONG64 size = 0;
+ DWORD kind = -1;
+
+ ste->used = 1;
+
+ /* If neither symbol has a correct size (ours never does), we
+ * consider them both to be markers. No warning is needed in
+ * that case.
+ * Also, we check that we don't have two symbols, one local, the other
+ * global, which is legal.
+ */
+ symt_get_info(&sym->symt, TI_GET_LENGTH, &size);
+ symt_get_info(&sym->symt, TI_GET_DATAKIND, &kind);
+ if (size && kind == (ste->is_global ? DataIsGlobal : DataIsFileStatic))
+ FIXME("Duplicate in %s: %s<%08lx> %s<%s-%s>\n",
+ debugstr_w(module->module.ModuleName),
+ ste->ht_elt.name, ste->addr,
+ sym->hash_elt.name,
+ wine_dbgstr_longlong(addr), wine_dbgstr_longlong(size));
+ }
+ }
+ }
+
+ /* For any of our remaining non-debugging symbols which have no match
+ * among the module's known symbols, add them as new symbols. */
+ hash_table_iter_init(ht_symtab, &hti_ours, NULL);
+ while ((ste = hash_table_iter_up(&hti_ours)))
+ {
+ if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY) && !ste->used)
+ {
+ if (ste->is_code)
+ {
+ symt_new_function(module, ste->compiland, ste->ht_elt.name,
+ ste->addr, 0, NULL);
+ }
+ else
+ {
+ symt_new_global_variable(module, ste->compiland, ste->ht_elt.name,
+ !ste->is_global, ste->addr, 0, NULL);
+ }
+
+ ste->used = 1;
+ }
+
+ if (ste->is_public && !(dbghelp_options & SYMOPT_NO_PUBLICS))
+ {
+ symt_new_public(module, ste->compiland, ste->ht_elt.name,
+ ste->addr, 0, ste->is_code, ste->is_code);
+ }
+ }
+}
+
+/******************************************************************
+ * macho_load_debug_info_from_map
+ *
+ * Loads the symbolic information from a Mach-O module.
+ * Returns
+ * FALSE if the file doesn't contain symbolic info (or this info
+ * cannot be read or parsed)
+ * TRUE on success
+ */
+static BOOL macho_load_debug_info_from_map(struct module* module,
+ struct macho_file_map* fmap)
+{
+ BOOL ret = FALSE;
+ struct macho_debug_info mdi;
+ int result;
+
+ TRACE("(%p, %p/%d)\n", module, fmap, fmap->fd);
+
+ module->module.SymType = SymExport;
+
+ mdi.fmap = fmap;
+ mdi.module = module;
+ pool_init(&mdi.pool, 65536);
+ hash_table_init(&mdi.pool, &mdi.ht_symtab, 256);
+ result = macho_enum_load_commands(fmap, LC_SYMTAB, macho_parse_symtab, &mdi);
+ if (result > 0)
+ ret = TRUE;
+ else if (result < 0)
+ WARN("Couldn't correctly read stabs\n");
+
+ macho_finish_stabs(module, &mdi.ht_symtab);
+
+ pool_destroy(&mdi.pool);
+ return ret;
+}
+
+/******************************************************************
+ * macho_load_debug_info
+ *
+ * Loads Mach-O debugging information from the module image file.
+ */
+BOOL macho_load_debug_info(struct module* module, struct macho_file_map* fmap)
+{
+ BOOL ret = TRUE;
+ struct macho_file_map my_fmap;
+
+ TRACE("(%p, %p/%d)\n", module, fmap, fmap ? fmap->fd : -1);
+
+ if (module->type != DMT_MACHO || !module->macho_info)
+ {
+ ERR("Bad Mach-O module '%s'\n", debugstr_w(module->module.LoadedImageName));
+ return FALSE;
+ }
+
+ if (!fmap)
+ {
+ fmap = &my_fmap;
+ ret = macho_map_file(module->module.LoadedImageName, fmap);
+ }
+ if (ret)
+ ret = macho_load_debug_info_from_map(module, fmap);
+
+ if (fmap == &my_fmap) macho_unmap_file(fmap);
+ return ret;
+}
+
+/******************************************************************
+ * macho_fetch_file_info
+ *
+ * Gathers some more information for a Mach-O module from a given file
+ */
+BOOL macho_fetch_file_info(const WCHAR* name, DWORD* base,
+ DWORD* size, DWORD* checksum)
+{
+ struct macho_file_map fmap;
+
+ TRACE("(%s, %p, %p, %p)\n", debugstr_w(name), base, size, checksum);
+
+ if (!macho_map_file(name, &fmap)) return FALSE;
+ if (base) *base = fmap.segs_start;
+ *size = fmap.segs_size;
+ *checksum = calc_crc32(fmap.fd);
+ macho_unmap_file(&fmap);
+ return TRUE;
+}
+
+/******************************************************************
+ * macho_load_file
+ *
+ * Loads the information for Mach-O module stored in 'filename'.
+ * The module has been loaded at 'load_addr' address.
+ * returns
+ * FALSE if the file cannot be found/opened or if the file doesn't
+ * contain symbolic info (or this info cannot be read or parsed)
+ * TRUE on success
+ */
+static BOOL macho_load_file(struct process* pcs, const WCHAR* filename,
+ unsigned long load_addr, struct macho_info* macho_info)
+{
+ BOOL ret = TRUE;
+ struct macho_file_map fmap;
+
+ TRACE("(%p/%p, %s, 0x%08lx, %p/0x%08x)\n", pcs, pcs->handle, debugstr_w(filename),
+ load_addr, macho_info, macho_info->flags);
+
+ if (!macho_map_file(filename, &fmap)) return FALSE;
+
+ /* Find the dynamic loader's table of images loaded into the process.
+ */
+ if (macho_info->flags & MACHO_INFO_DEBUG_HEADER)
+ {
+ static void* dyld_all_image_infos_addr;
+
+ /* This symbol should be in the same place in all processes. */
+ if (!dyld_all_image_infos_addr)
+ {
+ struct nlist nl[2];
+ memset(nl, 0, sizeof(nl));
+ nl[0].n_un.n_name = (char*)"_dyld_all_image_infos";
+ if (!nlist("/usr/lib/dyld", nl))
+ dyld_all_image_infos_addr = (void*)nl[0].n_value;
+ }
+
+ if (dyld_all_image_infos_addr)
+ macho_info->dbg_hdr_addr = (unsigned long)dyld_all_image_infos_addr;
+ else
+ ret = FALSE;
+ TRACE("dbg_hdr_addr = 0x%08lx\n", macho_info->dbg_hdr_addr);
+ }
+
+ if (macho_info->flags & MACHO_INFO_MODULE)
+ {
+ struct macho_module_info *macho_module_info =
+ HeapAlloc(GetProcessHeap(), 0, sizeof(struct macho_module_info));
+ if (!macho_module_info) goto leave;
+ macho_info->module = module_new(pcs, filename, DMT_MACHO, FALSE, load_addr,
+ fmap.segs_size, 0, calc_crc32(fmap.fd));
+ if (!macho_info->module)
+ {
+ HeapFree(GetProcessHeap(), 0, macho_module_info);
+ goto leave;
+ }
+ macho_info->module->macho_info = macho_module_info;
+ macho_info->module->macho_info->load_addr = load_addr;
+
+ if (dbghelp_options & SYMOPT_DEFERRED_LOADS)
+ macho_info->module->module.SymType = SymDeferred;
+ else if (!macho_load_debug_info(macho_info->module, &fmap))
+ ret = FALSE;
+
+ macho_info->module->macho_info->in_use = 1;
+ macho_info->module->macho_info->is_loader = 0;
+ TRACE("module = %p\n", macho_info->module);
+ }
+
+ if (macho_info->flags & MACHO_INFO_NAME)
+ {
+ WCHAR* ptr;
+ ptr = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
+ if (ptr)
+ {
+ strcpyW(ptr, filename);
+ macho_info->module_name = ptr;
+ }
+ else ret = FALSE;
+ TRACE("module_name = %p %s\n", macho_info->module_name, debugstr_w(macho_info->module_name));
+ }
+leave:
+ macho_unmap_file(&fmap);
+
+ TRACE(" => %d\n", ret);
+ return ret;
+}
+
+/******************************************************************
+ * macho_load_file_from_path
+ * Tries to load a Mach-O file from a set of paths (separated by ':')
+ */
+static BOOL macho_load_file_from_path(HANDLE hProcess,
+ const WCHAR* filename,
+ unsigned long load_addr,
+ const char* path,
+ struct macho_info* macho_info)
+{
+ BOOL ret = FALSE;
+ WCHAR *s, *t, *fn;
+ WCHAR* pathW = NULL;
+ unsigned len;
+
+ TRACE("(%p, %s, 0x%08lx, %s, %p)\n", hProcess, debugstr_w(filename), load_addr,
+ debugstr_a(path), macho_info);
+
+ if (!path) return FALSE;
+
+ len = MultiByteToWideChar(CP_UNIXCP, 0, path, -1, NULL, 0);
+ pathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ if (!pathW) return FALSE;
+ MultiByteToWideChar(CP_UNIXCP, 0, path, -1, pathW, len);
+
+ for (s = pathW; s && *s; s = (t) ? (t+1) : NULL)
+ {
+ t = strchrW(s, ':');
+ if (t) *t = '\0';
+ fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1 + lstrlenW(s) + 1) * sizeof(WCHAR));
+ if (!fn) break;
+ strcpyW(fn, s);
+ strcatW(fn, S_SlashW);
+ strcatW(fn, filename);
+ ret = macho_load_file(hProcess, fn, load_addr, macho_info);
+ HeapFree(GetProcessHeap(), 0, fn);
+ if (ret) break;
+ s = (t) ? (t+1) : NULL;
+ }
+
+ TRACE(" => %d\n", ret);
+ HeapFree(GetProcessHeap(), 0, pathW);
+ return ret;
+}
+
+/******************************************************************
+ * macho_load_file_from_dll_path
+ *
+ * Tries to load a Mach-O file from the dll path
+ */
+static BOOL macho_load_file_from_dll_path(HANDLE hProcess,
+ const WCHAR* filename,
+ unsigned long load_addr,
+ struct macho_info* macho_info)
+{
+ BOOL ret = FALSE;
+ unsigned int index = 0;
+ const char *path;
+
+ TRACE("(%p, %s, 0x%08lx, %p)\n", hProcess, debugstr_w(filename), load_addr,
+ macho_info);
+
+ while (!ret && (path = wine_dll_enum_load_path( index++ )))
+ {
+ WCHAR *name;
+ unsigned len;
+
+ len = MultiByteToWideChar(CP_UNIXCP, 0, path, -1, NULL, 0);
+
+ name = HeapAlloc( GetProcessHeap(), 0,
+ (len + lstrlenW(filename) + 2) * sizeof(WCHAR) );
+
+ if (!name) break;
+ MultiByteToWideChar(CP_UNIXCP, 0, path, -1, name, len);
+ strcatW( name, S_SlashW );
+ strcatW( name, filename );
+ ret = macho_load_file(hProcess, name, load_addr, macho_info);
+ HeapFree( GetProcessHeap(), 0, name );
+ }
+ TRACE(" => %d\n", ret);
+ return ret;
+}
+
+/******************************************************************
+ * macho_search_and_load_file
+ *
+ * Lookup a file in standard Mach-O locations, and if found, load it
+ */
+static BOOL macho_search_and_load_file(struct process* pcs, const WCHAR* filename,
+ unsigned long load_addr,
+ struct macho_info* macho_info)
+{
+ BOOL ret = FALSE;
+ struct module* module;
+ static WCHAR S_libstdcPPW[] = {'l','i','b','s','t','d','c','+','+','\0'};
+ const WCHAR* p;
+
+ TRACE("(%p/%p, %s, 0x%08lx, %p)\n", pcs, pcs->handle, debugstr_w(filename), load_addr,
+ macho_info);
+
+ if (filename == NULL || *filename == '\0') return FALSE;
+ if ((module = module_is_already_loaded(pcs, filename)))
+ {
+ macho_info->module = module;
+ module->macho_info->in_use = 1;
+ return module->module.SymType;
+ }
+
+ if (strstrW(filename, S_libstdcPPW)) return FALSE; /* We know we can't do it */
+
+ /* If has no directories, try LD_LIBRARY_PATH first. */
+ if (!strchrW(filename, '/'))
+ {
+ ret = macho_load_file_from_path(pcs, filename, load_addr,
+ getenv("PATH"), macho_info);
+ }
+ /* Try DYLD_LIBRARY_PATH, with just the filename (no directories). */
+ if (!ret)
+ {
+ if ((p = strrchrW(filename, '/'))) p++;
+ else p = filename;
+ ret = macho_load_file_from_path(pcs, p, load_addr,
+ getenv("DYLD_LIBRARY_PATH"), macho_info);
+ }
+ /* Try the path as given. */
+ if (!ret)
+ ret = macho_load_file(pcs, filename, load_addr, macho_info);
+ /* Try DYLD_FALLBACK_LIBRARY_PATH, with just the filename (no directories). */
+ if (!ret)
+ {
+ ret = macho_load_file_from_path(pcs, p, load_addr,
+ getenv("DYLD_FALLBACK_LIBRARY_PATH"), macho_info);
+ }
+ if (!ret && !strchrW(filename, '/'))
+ ret = macho_load_file_from_dll_path(pcs, filename, load_addr, macho_info);
+
+ return ret;
+}
+
+/******************************************************************
+ * macho_enum_modules_internal
+ *
+ * Enumerate Mach-O modules from a running process
+ */
+static BOOL macho_enum_modules_internal(const struct process* pcs,
+ const WCHAR* main_name,
+ enum_modules_cb cb, void* user)
+{
+ struct dyld_all_image_infos image_infos;
+ struct dyld_image_info* info_array = NULL;
+ unsigned long len;
+ int i;
+ char bufstr[256];
+ WCHAR bufstrW[MAX_PATH];
+ BOOL ret = FALSE;
+
+ TRACE("(%p/%p, %s, %p, %p)\n", pcs, pcs->handle, debugstr_w(main_name), cb,
+ user);
+
+ if (!pcs->dbg_hdr_addr ||
+ !ReadProcessMemory(pcs->handle, (void*)pcs->dbg_hdr_addr,
+ &image_infos, sizeof(image_infos), NULL) ||
+ (image_infos.version != 1 && image_infos.version != 2) ||
+ !image_infos.infoArray)
+ goto done;
+ TRACE("Process has %u image infos at %p\n", image_infos.infoArrayCount, image_infos.infoArray);
+
+ len = image_infos.infoArrayCount * sizeof(info_array[0]);
+ info_array = HeapAlloc(GetProcessHeap(), 0, len);
+ if (!info_array ||
+ !ReadProcessMemory(pcs->handle, image_infos.infoArray,
+ info_array, len, NULL))
+ goto done;
+ TRACE("... read image infos\n");
+
+ for (i = 0; i < image_infos.infoArrayCount; i++)
+ {
+ if (info_array[i].imageFilePath != NULL &&
+ ReadProcessMemory(pcs->handle, info_array[i].imageFilePath, bufstr, sizeof(bufstr), NULL))
+ {
+ bufstr[sizeof(bufstr) - 1] = '\0';
+ TRACE("[%d] image file %s\n", i, debugstr_a(bufstr));
+ MultiByteToWideChar(CP_UNIXCP, 0, bufstr, -1, bufstrW, sizeof(bufstrW) / sizeof(WCHAR));
+ if (main_name && !bufstrW[0]) strcpyW(bufstrW, main_name);
+ if (!cb(bufstrW, (unsigned long)info_array[i].imageLoadAddress, user)) break;
+ }
+ }
+
+ ret = TRUE;
+done:
+ HeapFree(GetProcessHeap(), 0, info_array);
+ return ret;
+}
+
+struct macho_sync
+{
+ struct process* pcs;
+ struct macho_info macho_info;
+};
+
+static BOOL macho_enum_sync_cb(const WCHAR* name, unsigned long addr, void* user)
+{
+ struct macho_sync* ms = user;
+
+ TRACE("(%s, 0x%08lx, %p)\n", debugstr_w(name), addr, user);
+ macho_search_and_load_file(ms->pcs, name, addr, &ms->macho_info);
+ return TRUE;
+}
+
+/******************************************************************
+ * macho_synchronize_module_list
+ *
+ * Rescans the debuggee's modules list and synchronizes it with
+ * the one from 'pcs', ie:
+ * - if a module is in debuggee and not in pcs, it's loaded into pcs
+ * - if a module is in pcs and not in debuggee, it's unloaded from pcs
+ */
+BOOL macho_synchronize_module_list(struct process* pcs)
+{
+ struct module* module;
+ struct macho_sync ms;
+
+ TRACE("(%p/%p)\n", pcs, pcs->handle);
+
+ for (module = pcs->lmodules; module; module = module->next)
+ {
+ if (module->type == DMT_MACHO && !module->is_virtual)
+ module->macho_info->in_use = 0;
+ }
+
+ ms.pcs = pcs;
+ ms.macho_info.flags = MACHO_INFO_MODULE;
+ if (!macho_enum_modules_internal(pcs, NULL, macho_enum_sync_cb, &ms))
+ return FALSE;
+
+ module = pcs->lmodules;
+ while (module)
+ {
+ if (module->type == DMT_MACHO && !module->is_virtual &&
+ !module->macho_info->in_use && !module->macho_info->is_loader)
+ {
+ module_remove(pcs, module);
+ /* restart all over */
+ module = pcs->lmodules;
+ }
+ else module = module->next;
+ }
+ return TRUE;
+}
+
+/******************************************************************
+ * macho_search_loader
+ *
+ * Lookup in a running Mach-O process the loader, and sets its Mach-O link
+ * address (for accessing the list of loaded images) in pcs.
+ * If flags is MACHO_INFO_MODULE, the module for the loader is also
+ * added as a module into pcs.
+ */
+static BOOL macho_search_loader(struct process* pcs, struct macho_info* macho_info)
+{
+ BOOL ret;
+ const char* ptr;
+
+ TRACE("(%p/%p, %p)\n", pcs, pcs->handle, macho_info);
+
+ /* All binaries are loaded with WINELOADER (if run from tree) or by the
+ * main executable
+ */
+ if ((ptr = getenv("WINELOADER")))
+ {
+ WCHAR tmp[MAX_PATH];
+ MultiByteToWideChar(CP_UNIXCP, 0, ptr, -1, tmp, sizeof(tmp) / sizeof(WCHAR));
+ ret = macho_search_and_load_file(pcs, tmp, 0, macho_info);
+ }
+ else
+ {
+ ret = macho_search_and_load_file(pcs, S_WineW, 0, macho_info);
+ }
+ return ret;
+}
+
+/******************************************************************
+ * macho_read_wine_loader_dbg_info
+ *
+ * Try to find a decent wine executable which could have loaded the debuggee
+ */
+BOOL macho_read_wine_loader_dbg_info(struct process* pcs)
+{
+ struct macho_info macho_info;
+
+ TRACE("(%p/%p)\n", pcs, pcs->handle);
+ macho_info.flags = MACHO_INFO_DEBUG_HEADER | MACHO_INFO_MODULE;
+ if (!macho_search_loader(pcs, &macho_info)) return FALSE;
+ macho_info.module->macho_info->is_loader = 1;
+ module_set_module(macho_info.module, S_WineLoaderW);
+ return (pcs->dbg_hdr_addr = macho_info.dbg_hdr_addr) != 0;
+}
+
+/******************************************************************
+ * macho_enum_modules
+ *
+ * Enumerates the Mach-O loaded modules from a running target (hProc)
+ * This function doesn't require that someone has called SymInitialize
+ * on this very process.
+ */
+BOOL macho_enum_modules(HANDLE hProc, enum_modules_cb cb, void* user)
+{
+ struct process pcs;
+ struct macho_info macho_info;
+ BOOL ret;
+
+ TRACE("(%p, %p, %p)\n", hProc, cb, user);
+ memset(&pcs, 0, sizeof(pcs));
+ pcs.handle = hProc;
+ macho_info.flags = MACHO_INFO_DEBUG_HEADER | MACHO_INFO_NAME;
+ if (!macho_search_loader(&pcs, &macho_info)) return FALSE;
+ pcs.dbg_hdr_addr = macho_info.dbg_hdr_addr;
+ ret = macho_enum_modules_internal(&pcs, macho_info.module_name, cb, user);
+ HeapFree(GetProcessHeap(), 0, (char*)macho_info.module_name);
+ return ret;
+}
+
+struct macho_load
+{
+ struct process* pcs;
+ struct macho_info macho_info;
+ const WCHAR* name;
+ BOOL ret;
+};
+
+/******************************************************************
+ * macho_load_cb
+ *
+ * Callback for macho_load_module, used to walk the list of loaded
+ * modules.
+ */
+static BOOL macho_load_cb(const WCHAR* name, unsigned long addr, void* user)
+{
+ struct macho_load* ml = user;
+ const WCHAR* p;
+
+ TRACE("(%s, 0x%08lx, %p)\n", debugstr_w(name), addr, user);
+
+ /* memcmp is needed for matches when bufstr contains also version information
+ * ml->name: libc.so, name: libc.so.6.0
+ */
+ p = strrchrW(name, '/');
+ if (!p++) p = name;
+ if (!memcmp(p, ml->name, lstrlenW(ml->name) * sizeof(WCHAR)))
+ {
+ ml->ret = macho_search_and_load_file(ml->pcs, name, addr, &ml->macho_info);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/******************************************************************
+ * macho_load_module
+ *
+ * Loads a Mach-O module and stores it in process' module list.
+ * Also, find module real name and load address from
+ * the real loaded modules list in pcs address space.
+ */
+struct module* macho_load_module(struct process* pcs, const WCHAR* name, unsigned long addr)
+{
+ struct macho_load ml;
+
+ TRACE("(%p/%p, %s, 0x%08lx)\n", pcs, pcs->handle, debugstr_w(name), addr);
+
+ ml.macho_info.flags = MACHO_INFO_MODULE;
+ ml.ret = FALSE;
+
+ if (pcs->dbg_hdr_addr) /* we're debugging a live target */
+ {
+ ml.pcs = pcs;
+ /* do only the lookup from the filename, not the path (as we lookup module
+ * name in the process' loaded module list)
+ */
+ ml.name = strrchrW(name, '/');
+ if (!ml.name++) ml.name = name;
+ ml.ret = FALSE;
+
+ if (!macho_enum_modules_internal(pcs, NULL, macho_load_cb, &ml))
+ return NULL;
+ }
+ else if (addr)
+ {
+ ml.name = name;
+ ml.ret = macho_search_and_load_file(pcs, ml.name, addr, &ml.macho_info);
+ }
+ if (!ml.ret) return NULL;
+ assert(ml.macho_info.module);
+ return ml.macho_info.module;
+}
+
+#else /* !__MACH__ */
+
+BOOL macho_synchronize_module_list(struct process* pcs)
+{
+ return FALSE;
+}
+
+BOOL macho_fetch_file_info(const WCHAR* name, DWORD* base,
+ DWORD* size, DWORD* checksum)
+{
+ return FALSE;
+}
+
+BOOL macho_read_wine_loader_dbg_info(struct process* pcs)
+{
+ return FALSE;
+}
+
+BOOL macho_enum_modules(HANDLE hProc, enum_modules_cb cb, void* user)
+{
+ return FALSE;
+}
+
+struct module* macho_load_module(struct process* pcs, const WCHAR* name, unsigned long addr)
+{
+ return NULL;
+}
+
+BOOL macho_load_debug_info(struct module* module, struct macho_file_map* fmap)
+{
+ return FALSE;
+}
+#endif /* __MACH__ */