dbghelp: Use CONTAINING_RECORD instead of reimplementing it.
[wine.git] / dlls / dbghelp / macho_module.c
blob01fa300eff7ce72c097ecf1f0f77f3da0bc8586d
1 /*
2 * File macho_module.c - processing of Mach-O files
3 * Originally based on elf_module.c
5 * Copyright (C) 1996, Eric Youngdale.
6 * 1999-2007 Eric Pouech
7 * 2009 Ken Thomases, CodeWeavers Inc.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #ifdef HAVE_MACH_O_LOADER_H
28 #include <CoreFoundation/CFString.h>
29 #define LoadResource mac_LoadResource
30 #define GetCurrentThread mac_GetCurrentThread
31 #include <CoreServices/CoreServices.h>
32 #undef LoadResource
33 #undef GetCurrentThread
34 #undef DPRINTF
35 #endif
37 #include <stdio.h>
38 #include <assert.h>
39 #include <stdarg.h>
40 #include <errno.h>
41 #ifdef HAVE_SYS_STAT_H
42 # include <sys/stat.h>
43 #endif
44 #ifdef HAVE_SYS_MMAN_H
45 # include <sys/mman.h>
46 #endif
48 #include "ntstatus.h"
49 #define WIN32_NO_STATUS
50 #include "dbghelp_private.h"
51 #include "winternl.h"
52 #include "wine/library.h"
53 #include "wine/debug.h"
54 #include "image_private.h"
56 #ifdef HAVE_MACH_O_LOADER_H
58 #include <mach-o/fat.h>
59 #include <mach-o/loader.h>
60 #include <mach-o/nlist.h>
61 #include <mach-o/dyld.h>
63 #ifdef HAVE_MACH_O_DYLD_IMAGES_H
64 #include <mach-o/dyld_images.h>
65 #else
66 struct dyld_image_info {
67 const struct mach_header *imageLoadAddress;
68 const char *imageFilePath;
69 uintptr_t imageFileModDate;
72 struct dyld_all_image_infos {
73 uint32_t version;
74 uint32_t infoArrayCount;
75 const struct dyld_image_info *infoArray;
76 void* notification;
77 int processDetachedFromSharedRegion;
79 #endif
81 #ifdef WORDS_BIGENDIAN
82 #define swap_ulong_be_to_host(n) (n)
83 #else
84 #define swap_ulong_be_to_host(n) (RtlUlongByteSwap(n))
85 #endif
87 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp_macho);
90 #ifdef _WIN64
91 typedef struct segment_command_64 macho_segment_command;
92 typedef struct nlist_64 macho_nlist;
94 #define TARGET_CPU_TYPE CPU_TYPE_X86_64
95 #define TARGET_MH_MAGIC MH_MAGIC_64
96 #define TARGET_SEGMENT_COMMAND LC_SEGMENT_64
97 #else
98 typedef struct segment_command macho_segment_command;
99 typedef struct nlist macho_nlist;
101 #define TARGET_CPU_TYPE CPU_TYPE_X86
102 #define TARGET_MH_MAGIC MH_MAGIC
103 #define TARGET_SEGMENT_COMMAND LC_SEGMENT
104 #endif
107 /* Bitmask for Mach-O image header flags indicating that the image is in dyld's
108 shared cached. That implies that its segments are mapped non-contiguously.
109 This value isn't defined anywhere in headers. It's used in dyld and in
110 debuggers which support OS X as a magic number.
112 The flag also isn't set in the on-disk image file. It's only set in
113 memory by dyld. */
114 #define MACHO_DYLD_IN_SHARED_CACHE 0x80000000
117 #define UUID_STRING_LEN 37 /* 16 bytes at 2 hex digits apiece, 4 dashes, and the null terminator */
120 struct macho_module_info
122 struct image_file_map file_map;
123 unsigned long load_addr;
124 unsigned short in_use : 1,
125 is_loader : 1;
128 struct section_info
130 BOOL split_segs;
131 unsigned int section_index;
134 #define MACHO_INFO_DEBUG_HEADER 0x0001
135 #define MACHO_INFO_MODULE 0x0002
136 #define MACHO_INFO_NAME 0x0004
138 struct macho_info
140 unsigned flags; /* IN one (or several) of the MACHO_INFO constants */
141 unsigned long dbg_hdr_addr; /* OUT address of debug header (if MACHO_INFO_DEBUG_HEADER is set) */
142 struct module* module; /* OUT loaded module (if MACHO_INFO_MODULE is set) */
143 const WCHAR* module_name; /* OUT found module name (if MACHO_INFO_NAME is set) */
146 static void macho_unmap_file(struct image_file_map* fmap);
148 static char* format_uuid(const uint8_t uuid[16], char out[UUID_STRING_LEN])
150 sprintf(out, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
151 uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
152 uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
153 return out;
156 /******************************************************************
157 * macho_calc_range
159 * For a range (offset & length) of a single architecture within
160 * a Mach-O file, calculate the page-aligned range of the whole file
161 * that encompasses it. For a fat binary, the architecture will
162 * itself be offset within the file, so take that into account.
164 static void macho_calc_range(const struct macho_file_map* fmap, unsigned long offset,
165 unsigned long len, unsigned long* out_aligned_offset,
166 unsigned long* out_aligned_end, unsigned long* out_aligned_len,
167 unsigned long* out_misalign)
169 unsigned long pagemask = sysconf( _SC_PAGESIZE ) - 1;
170 unsigned long file_offset, misalign;
172 file_offset = fmap->arch_offset + offset;
173 misalign = file_offset & pagemask;
174 *out_aligned_offset = file_offset - misalign;
175 *out_aligned_end = (file_offset + len + pagemask) & ~pagemask;
176 if (out_aligned_len)
177 *out_aligned_len = *out_aligned_end - *out_aligned_offset;
178 if (out_misalign)
179 *out_misalign = misalign;
182 /******************************************************************
183 * macho_map_range
185 * Maps a range (offset, length in bytes) from a Mach-O file into memory
187 static const char* macho_map_range(const struct macho_file_map* fmap, unsigned long offset, unsigned long len,
188 const char** base)
190 unsigned long misalign, aligned_offset, aligned_map_end, map_size;
191 const void* aligned_ptr;
193 TRACE("(%p/%d, 0x%08lx, 0x%08lx)\n", fmap, fmap->fd, offset, len);
195 macho_calc_range(fmap, offset, len, &aligned_offset, &aligned_map_end,
196 &map_size, &misalign);
198 aligned_ptr = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fmap->fd, aligned_offset);
200 TRACE("Mapped (0x%08lx - 0x%08lx) to %p\n", aligned_offset, aligned_map_end, aligned_ptr);
202 if (aligned_ptr == MAP_FAILED) return IMAGE_NO_MAP;
203 if (base)
204 *base = aligned_ptr;
205 return (const char*)aligned_ptr + misalign;
208 /******************************************************************
209 * macho_unmap_range
211 * Unmaps a range (offset, length in bytes) of a Mach-O file from memory
213 static void macho_unmap_range(const char** base, const void** mapped, const struct macho_file_map* fmap,
214 unsigned long offset, unsigned long len)
216 TRACE("(%p, %p, %p/%d, 0x%08lx, 0x%08lx)\n", base, mapped, fmap, fmap->fd, offset, len);
218 if ((mapped && *mapped != IMAGE_NO_MAP) || (base && *base != IMAGE_NO_MAP))
220 unsigned long misalign, aligned_offset, aligned_map_end, map_size;
221 void* aligned_ptr;
223 macho_calc_range(fmap, offset, len, &aligned_offset, &aligned_map_end,
224 &map_size, &misalign);
226 if (mapped)
227 aligned_ptr = (char*)*mapped - misalign;
228 else
229 aligned_ptr = (void*)*base;
230 if (munmap(aligned_ptr, map_size) < 0)
231 WARN("Couldn't unmap the range\n");
232 TRACE("Unmapped (0x%08lx - 0x%08lx) from %p - %p\n", aligned_offset, aligned_map_end, aligned_ptr, (char*)aligned_ptr + map_size);
233 if (mapped)
234 *mapped = IMAGE_NO_MAP;
235 if (base)
236 *base = IMAGE_NO_MAP;
240 /******************************************************************
241 * macho_map_ranges
243 * Maps two ranges (offset, length in bytes) from a Mach-O file
244 * into memory. If the two ranges overlap, use one mmap so that
245 * the munmap doesn't fragment the mapping.
247 static BOOL macho_map_ranges(const struct macho_file_map* fmap,
248 unsigned long offset1, unsigned long len1,
249 unsigned long offset2, unsigned long len2,
250 const void** mapped1, const void** mapped2)
252 unsigned long aligned_offset1, aligned_map_end1;
253 unsigned long aligned_offset2, aligned_map_end2;
255 TRACE("(%p/%d, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, %p, %p)\n", fmap, fmap->fd,
256 offset1, len1, offset2, len2, mapped1, mapped2);
258 macho_calc_range(fmap, offset1, len1, &aligned_offset1, &aligned_map_end1, NULL, NULL);
259 macho_calc_range(fmap, offset2, len2, &aligned_offset2, &aligned_map_end2, NULL, NULL);
261 if (aligned_map_end1 < aligned_offset2 || aligned_map_end2 < aligned_offset1)
263 *mapped1 = macho_map_range(fmap, offset1, len1, NULL);
264 if (*mapped1 != IMAGE_NO_MAP)
266 *mapped2 = macho_map_range(fmap, offset2, len2, NULL);
267 if (*mapped2 == IMAGE_NO_MAP)
268 macho_unmap_range(NULL, mapped1, fmap, offset1, len1);
271 else
273 if (offset1 < offset2)
275 *mapped1 = macho_map_range(fmap, offset1, offset2 + len2 - offset1, NULL);
276 if (*mapped1 != IMAGE_NO_MAP)
277 *mapped2 = (const char*)*mapped1 + offset2 - offset1;
279 else
281 *mapped2 = macho_map_range(fmap, offset2, offset1 + len1 - offset2, NULL);
282 if (*mapped2 != IMAGE_NO_MAP)
283 *mapped1 = (const char*)*mapped2 + offset1 - offset2;
287 TRACE(" => %p, %p\n", *mapped1, *mapped2);
289 return (*mapped1 != IMAGE_NO_MAP) && (*mapped2 != IMAGE_NO_MAP);
292 /******************************************************************
293 * macho_unmap_ranges
295 * Unmaps two ranges (offset, length in bytes) of a Mach-O file
296 * from memory. Use for ranges which were mapped by
297 * macho_map_ranges.
299 static void macho_unmap_ranges(const struct macho_file_map* fmap,
300 unsigned long offset1, unsigned long len1,
301 unsigned long offset2, unsigned long len2,
302 const void** mapped1, const void** mapped2)
304 unsigned long aligned_offset1, aligned_map_end1;
305 unsigned long aligned_offset2, aligned_map_end2;
307 TRACE("(%p/%d, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, %p/%p, %p/%p)\n", fmap, fmap->fd,
308 offset1, len1, offset2, len2, mapped1, *mapped1, mapped2, *mapped2);
310 macho_calc_range(fmap, offset1, len1, &aligned_offset1, &aligned_map_end1, NULL, NULL);
311 macho_calc_range(fmap, offset2, len2, &aligned_offset2, &aligned_map_end2, NULL, NULL);
313 if (aligned_map_end1 < aligned_offset2 || aligned_map_end2 < aligned_offset1)
315 macho_unmap_range(NULL, mapped1, fmap, offset1, len1);
316 macho_unmap_range(NULL, mapped2, fmap, offset2, len2);
318 else
320 if (offset1 < offset2)
322 macho_unmap_range(NULL, mapped1, fmap, offset1, offset2 + len2 - offset1);
323 *mapped2 = IMAGE_NO_MAP;
325 else
327 macho_unmap_range(NULL, mapped2, fmap, offset2, offset1 + len1 - offset2);
328 *mapped1 = IMAGE_NO_MAP;
333 /******************************************************************
334 * macho_find_section
336 BOOL macho_find_section(struct image_file_map* ifm, const char* segname, const char* sectname, struct image_section_map* ism)
338 struct macho_file_map* fmap;
339 unsigned i;
340 char tmp[sizeof(fmap->sect[0].section->sectname)];
342 /* Other parts of dbghelp use section names like ".eh_frame". Mach-O uses
343 names like "__eh_frame". Convert those. */
344 if (sectname[0] == '.')
346 lstrcpynA(tmp, "__", sizeof(tmp));
347 lstrcpynA(tmp + 2, sectname + 1, sizeof(tmp) - 2);
348 sectname = tmp;
351 while (ifm)
353 fmap = &ifm->u.macho;
354 for (i = 0; i < fmap->num_sections; i++)
356 if (!fmap->sect[i].ignored &&
357 strcmp(fmap->sect[i].section->sectname, sectname) == 0 &&
358 (!segname || strcmp(fmap->sect[i].section->segname, segname) == 0))
360 ism->fmap = ifm;
361 ism->sidx = i;
362 return TRUE;
365 ifm = fmap->dsym;
368 ism->fmap = NULL;
369 ism->sidx = -1;
370 return FALSE;
373 /******************************************************************
374 * macho_map_section
376 const char* macho_map_section(struct image_section_map* ism)
378 struct macho_file_map* fmap = &ism->fmap->u.macho;
380 assert(ism->fmap->modtype == DMT_MACHO);
381 if (ism->sidx < 0 || ism->sidx >= ism->fmap->u.macho.num_sections || fmap->sect[ism->sidx].ignored)
382 return IMAGE_NO_MAP;
384 return macho_map_range(fmap, fmap->sect[ism->sidx].section->offset, fmap->sect[ism->sidx].section->size,
385 &fmap->sect[ism->sidx].mapped);
388 /******************************************************************
389 * macho_unmap_section
391 void macho_unmap_section(struct image_section_map* ism)
393 struct macho_file_map* fmap = &ism->fmap->u.macho;
395 if (ism->sidx >= 0 && ism->sidx < fmap->num_sections && fmap->sect[ism->sidx].mapped != IMAGE_NO_MAP)
397 macho_unmap_range(&fmap->sect[ism->sidx].mapped, NULL, fmap, fmap->sect[ism->sidx].section->offset,
398 fmap->sect[ism->sidx].section->size);
402 /******************************************************************
403 * macho_get_map_rva
405 DWORD_PTR macho_get_map_rva(const struct image_section_map* ism)
407 if (ism->sidx < 0 || ism->sidx >= ism->fmap->u.macho.num_sections ||
408 ism->fmap->u.macho.sect[ism->sidx].ignored)
409 return 0;
410 return ism->fmap->u.macho.sect[ism->sidx].section->addr - ism->fmap->u.macho.segs_start;
413 /******************************************************************
414 * macho_get_map_size
416 unsigned macho_get_map_size(const struct image_section_map* ism)
418 if (ism->sidx < 0 || ism->sidx >= ism->fmap->u.macho.num_sections ||
419 ism->fmap->u.macho.sect[ism->sidx].ignored)
420 return 0;
421 return ism->fmap->u.macho.sect[ism->sidx].section->size;
424 /******************************************************************
425 * macho_map_load_commands
427 * Maps the load commands from a Mach-O file into memory
429 static const struct load_command* macho_map_load_commands(struct macho_file_map* fmap)
431 if (fmap->load_commands == IMAGE_NO_MAP)
433 fmap->load_commands = (const struct load_command*) macho_map_range(
434 fmap, sizeof(fmap->mach_header), fmap->mach_header.sizeofcmds, NULL);
435 TRACE("Mapped load commands: %p\n", fmap->load_commands);
438 return fmap->load_commands;
441 /******************************************************************
442 * macho_unmap_load_commands
444 * Unmaps the load commands of a Mach-O file from memory
446 static void macho_unmap_load_commands(struct macho_file_map* fmap)
448 if (fmap->load_commands != IMAGE_NO_MAP)
450 TRACE("Unmapping load commands: %p\n", fmap->load_commands);
451 macho_unmap_range(NULL, (const void**)&fmap->load_commands, fmap,
452 sizeof(fmap->mach_header), fmap->mach_header.sizeofcmds);
456 /******************************************************************
457 * macho_next_load_command
459 * Advance to the next load command
461 static const struct load_command* macho_next_load_command(const struct load_command* lc)
463 return (const struct load_command*)((const char*)lc + lc->cmdsize);
466 /******************************************************************
467 * macho_enum_load_commands
469 * Enumerates the load commands for a Mach-O file, selecting by
470 * the command type, calling a callback for each. If the callback
471 * returns <0, that indicates an error. If it returns >0, that means
472 * it's not interested in getting any more load commands.
473 * If this function returns <0, that's an error produced by the
474 * callback. If >=0, that's the count of load commands successfully
475 * processed.
477 static int macho_enum_load_commands(struct macho_file_map* fmap, unsigned cmd,
478 int (*cb)(struct macho_file_map*, const struct load_command*, void*),
479 void* user)
481 const struct load_command* lc;
482 int i;
483 int count = 0;
485 TRACE("(%p/%d, %u, %p, %p)\n", fmap, fmap->fd, cmd, cb, user);
487 if ((lc = macho_map_load_commands(fmap)) == IMAGE_NO_MAP) return -1;
489 TRACE("%d total commands\n", fmap->mach_header.ncmds);
491 for (i = 0; i < fmap->mach_header.ncmds; i++, lc = macho_next_load_command(lc))
493 int result;
495 if (cmd && cmd != lc->cmd) continue;
496 count++;
498 result = cb(fmap, lc, user);
499 TRACE("load_command[%d] (%p), cmd %u; callback => %d\n", i, lc, lc->cmd, result);
500 if (result) return (result < 0) ? result : count;
503 return count;
506 /******************************************************************
507 * macho_count_sections
509 * Callback for macho_enum_load_commands. Counts the number of
510 * significant sections in a Mach-O file. All commands are
511 * expected to be of LC_SEGMENT[_64] type.
513 static int macho_count_sections(struct macho_file_map* fmap, const struct load_command* lc, void* user)
515 const macho_segment_command* sc = (const macho_segment_command*)lc;
517 TRACE("(%p/%d, %p, %p) segment %s\n", fmap, fmap->fd, lc, user, debugstr_an(sc->segname, sizeof(sc->segname)));
519 fmap->num_sections += sc->nsects;
520 return 0;
523 /******************************************************************
524 * macho_load_section_info
526 * Callback for macho_enum_load_commands. Accumulates the address
527 * range covered by the segments of a Mach-O file and builds the
528 * section map. All commands are expected to be of LC_SEGMENT[_64] type.
530 static int macho_load_section_info(struct macho_file_map* fmap, const struct load_command* lc, void* user)
532 const macho_segment_command* sc = (const macho_segment_command*)lc;
533 struct section_info* info = user;
534 BOOL ignore;
535 const macho_section* section;
536 int i;
537 unsigned long tmp, page_mask = sysconf( _SC_PAGESIZE ) - 1;
539 TRACE("(%p/%d, %p, %p) before: 0x%08lx - 0x%08lx\n", fmap, fmap->fd, lc, user,
540 (unsigned long)fmap->segs_start, (unsigned long)fmap->segs_size);
541 TRACE("Segment command vm: 0x%08lx - 0x%08lx\n", (unsigned long)sc->vmaddr,
542 (unsigned long)(sc->vmaddr + sc->vmsize));
544 /* Images in the dyld shared cache have their segments mapped non-contiguously.
545 We don't know how to properly locate any of the segments other than __TEXT,
546 so ignore them. */
547 ignore = (info->split_segs && strcmp(sc->segname, SEG_TEXT));
549 if (!strncmp(sc->segname, "WINE_", 5))
550 TRACE("Ignoring special Wine segment %s\n", debugstr_an(sc->segname, sizeof(sc->segname)));
551 else if (!strncmp(sc->segname, "__PAGEZERO", 10))
552 TRACE("Ignoring __PAGEZERO segment\n");
553 else if (ignore)
554 TRACE("Ignoring %s segment because image has split segments\n", sc->segname);
555 else
557 /* If this segment starts before previously-known earliest, record new earliest. */
558 if (sc->vmaddr < fmap->segs_start)
559 fmap->segs_start = sc->vmaddr;
561 /* If this segment extends beyond previously-known furthest, record new furthest. */
562 tmp = (sc->vmaddr + sc->vmsize + page_mask) & ~page_mask;
563 if (fmap->segs_size < tmp) fmap->segs_size = tmp;
565 TRACE("after: 0x%08lx - 0x%08lx\n", (unsigned long)fmap->segs_start, (unsigned long)fmap->segs_size);
568 section = (const macho_section*)(sc + 1);
569 for (i = 0; i < sc->nsects; i++)
571 fmap->sect[info->section_index].section = &section[i];
572 fmap->sect[info->section_index].mapped = IMAGE_NO_MAP;
573 fmap->sect[info->section_index].ignored = ignore;
574 info->section_index++;
577 return 0;
580 /******************************************************************
581 * find_uuid
583 * Callback for macho_enum_load_commands. Records the UUID load
584 * command of a Mach-O file.
586 static int find_uuid(struct macho_file_map* fmap, const struct load_command* lc, void* user)
588 fmap->uuid = (const struct uuid_command*)lc;
589 return 1;
592 /******************************************************************
593 * reset_file_map
595 static inline void reset_file_map(struct image_file_map* ifm)
597 struct macho_file_map* fmap = &ifm->u.macho;
599 fmap->fd = -1;
600 fmap->dsym = NULL;
601 fmap->load_commands = IMAGE_NO_MAP;
602 fmap->uuid = NULL;
603 fmap->num_sections = 0;
604 fmap->sect = NULL;
607 /******************************************************************
608 * macho_map_file
610 * Maps a Mach-O file into memory (and checks it's a real Mach-O file)
612 static BOOL macho_map_file(const WCHAR* filenameW, BOOL split_segs, struct image_file_map* ifm)
614 struct macho_file_map* fmap = &ifm->u.macho;
615 struct fat_header fat_header;
616 struct stat statbuf;
617 int i;
618 char* filename;
619 unsigned len;
620 struct section_info info;
621 BOOL ret = FALSE;
623 TRACE("(%s, %p)\n", debugstr_w(filenameW), fmap);
625 reset_file_map(ifm);
627 ifm->modtype = DMT_MACHO;
628 #ifdef _WIN64
629 ifm->addr_size = 64;
630 #else
631 ifm->addr_size = 32;
632 #endif
634 len = WideCharToMultiByte(CP_UNIXCP, 0, filenameW, -1, NULL, 0, NULL, NULL);
635 if (!(filename = HeapAlloc(GetProcessHeap(), 0, len)))
637 WARN("failed to allocate filename buffer\n");
638 return FALSE;
640 WideCharToMultiByte(CP_UNIXCP, 0, filenameW, -1, filename, len, NULL, NULL);
642 /* check that the file exists */
643 if (stat(filename, &statbuf) == -1 || S_ISDIR(statbuf.st_mode))
645 TRACE("stat() failed or %s is directory: %s\n", debugstr_a(filename), strerror(errno));
646 goto done;
649 /* Now open the file, so that we can mmap() it. */
650 if ((fmap->fd = open(filename, O_RDONLY)) == -1)
652 TRACE("failed to open file %s: %d\n", debugstr_a(filename), errno);
653 goto done;
656 if (read(fmap->fd, &fat_header, sizeof(fat_header)) != sizeof(fat_header))
658 TRACE("failed to read fat header: %d\n", errno);
659 goto done;
661 TRACE("... got possible fat header\n");
663 /* Fat header is always in big-endian order. */
664 if (swap_ulong_be_to_host(fat_header.magic) == FAT_MAGIC)
666 int narch = swap_ulong_be_to_host(fat_header.nfat_arch);
667 for (i = 0; i < narch; i++)
669 struct fat_arch fat_arch;
670 if (read(fmap->fd, &fat_arch, sizeof(fat_arch)) != sizeof(fat_arch))
671 goto done;
672 if (swap_ulong_be_to_host(fat_arch.cputype) == TARGET_CPU_TYPE)
674 fmap->arch_offset = swap_ulong_be_to_host(fat_arch.offset);
675 break;
678 if (i >= narch) goto done;
679 TRACE("... found target arch (%d)\n", TARGET_CPU_TYPE);
681 else
683 fmap->arch_offset = 0;
684 TRACE("... not a fat header\n");
687 /* Individual architecture (standalone or within a fat file) is in its native byte order. */
688 lseek(fmap->fd, fmap->arch_offset, SEEK_SET);
689 if (read(fmap->fd, &fmap->mach_header, sizeof(fmap->mach_header)) != sizeof(fmap->mach_header))
690 goto done;
691 TRACE("... got possible Mach header\n");
692 /* and check for a Mach-O header */
693 if (fmap->mach_header.magic != TARGET_MH_MAGIC ||
694 fmap->mach_header.cputype != TARGET_CPU_TYPE) goto done;
695 /* Make sure the file type is one of the ones we expect. */
696 switch (fmap->mach_header.filetype)
698 case MH_EXECUTE:
699 case MH_DYLIB:
700 case MH_DYLINKER:
701 case MH_BUNDLE:
702 case MH_DSYM:
703 break;
704 default:
705 goto done;
707 TRACE("... verified Mach header\n");
709 fmap->num_sections = 0;
710 if (macho_enum_load_commands(fmap, TARGET_SEGMENT_COMMAND, macho_count_sections, NULL) < 0)
711 goto done;
712 TRACE("%d sections\n", fmap->num_sections);
714 fmap->sect = HeapAlloc(GetProcessHeap(), 0, fmap->num_sections * sizeof(fmap->sect[0]));
715 if (!fmap->sect)
716 goto done;
718 fmap->segs_size = 0;
719 fmap->segs_start = ~0L;
721 info.split_segs = split_segs;
722 info.section_index = 0;
723 if (macho_enum_load_commands(fmap, TARGET_SEGMENT_COMMAND, macho_load_section_info, &info) < 0)
725 fmap->num_sections = 0;
726 goto done;
729 fmap->segs_size -= fmap->segs_start;
730 TRACE("segs_start: 0x%08lx, segs_size: 0x%08lx\n", (unsigned long)fmap->segs_start,
731 (unsigned long)fmap->segs_size);
733 if (macho_enum_load_commands(fmap, LC_UUID, find_uuid, NULL) < 0)
734 goto done;
735 if (fmap->uuid)
737 char uuid_string[UUID_STRING_LEN];
738 TRACE("UUID %s\n", format_uuid(fmap->uuid->uuid, uuid_string));
740 else
741 TRACE("no UUID found\n");
743 ret = TRUE;
744 done:
745 if (!ret)
746 macho_unmap_file(ifm);
747 HeapFree(GetProcessHeap(), 0, filename);
748 return ret;
751 /******************************************************************
752 * macho_unmap_file
754 * Unmaps a Mach-O file from memory (previously mapped with macho_map_file)
756 static void macho_unmap_file(struct image_file_map* ifm)
758 struct image_file_map* cursor;
760 TRACE("(%p/%d)\n", ifm, ifm->u.macho.fd);
762 cursor = ifm;
763 while (cursor)
765 struct image_file_map* next;
767 if (ifm->u.macho.fd != -1)
769 struct image_section_map ism;
771 ism.fmap = ifm;
772 for (ism.sidx = 0; ism.sidx < ifm->u.macho.num_sections; ism.sidx++)
773 macho_unmap_section(&ism);
775 HeapFree(GetProcessHeap(), 0, ifm->u.macho.sect);
776 macho_unmap_load_commands(&ifm->u.macho);
777 close(ifm->u.macho.fd);
778 ifm->u.macho.fd = -1;
781 next = cursor->u.macho.dsym;
782 if (cursor != ifm)
783 HeapFree(GetProcessHeap(), 0, cursor);
784 cursor = next;
788 /******************************************************************
789 * macho_sect_is_code
791 * Checks if a section, identified by sectidx which is a 1-based
792 * index into the sections of all segments, in order of load
793 * commands, contains code.
795 static BOOL macho_sect_is_code(struct macho_file_map* fmap, unsigned char sectidx)
797 BOOL ret;
799 TRACE("(%p/%d, %u)\n", fmap, fmap->fd, sectidx);
801 if (!sectidx) return FALSE;
803 sectidx--; /* convert from 1-based to 0-based */
804 if (sectidx >= fmap->num_sections || fmap->sect[sectidx].ignored) return FALSE;
806 ret = (!(fmap->sect[sectidx].section->flags & SECTION_TYPE) &&
807 (fmap->sect[sectidx].section->flags & (S_ATTR_PURE_INSTRUCTIONS|S_ATTR_SOME_INSTRUCTIONS)));
808 TRACE("-> %d\n", ret);
809 return ret;
812 struct symtab_elt
814 struct hash_table_elt ht_elt;
815 struct symt_compiland* compiland;
816 unsigned long addr;
817 unsigned char is_code:1,
818 is_public:1,
819 is_global:1,
820 used:1;
823 struct macho_debug_info
825 struct macho_file_map* fmap;
826 struct module* module;
827 struct pool pool;
828 struct hash_table ht_symtab;
831 /******************************************************************
832 * macho_stabs_def_cb
834 * Callback for stabs_parse. Collect symbol definitions.
836 static void macho_stabs_def_cb(struct module* module, unsigned long load_offset,
837 const char* name, unsigned long offset,
838 BOOL is_public, BOOL is_global, unsigned char sectidx,
839 struct symt_compiland* compiland, void* user)
841 struct macho_debug_info* mdi = user;
842 struct symtab_elt* ste;
844 TRACE("(%p, 0x%08lx, %s, 0x%08lx, %d, %d, %u, %p, %p/%p/%d)\n", module, load_offset,
845 debugstr_a(name), offset, is_public, is_global, sectidx,
846 compiland, mdi, mdi->fmap, mdi->fmap->fd);
848 /* Defer the creation of new non-debugging symbols until after we've
849 * finished parsing the stabs. */
850 ste = pool_alloc(&mdi->pool, sizeof(*ste));
851 ste->ht_elt.name = pool_strdup(&mdi->pool, name);
852 ste->compiland = compiland;
853 ste->addr = load_offset + offset;
854 ste->is_code = !!macho_sect_is_code(mdi->fmap, sectidx);
855 ste->is_public = !!is_public;
856 ste->is_global = !!is_global;
857 ste->used = 0;
858 hash_table_add(&mdi->ht_symtab, &ste->ht_elt);
861 /******************************************************************
862 * macho_parse_symtab
864 * Callback for macho_enum_load_commands. Processes the LC_SYMTAB
865 * load commands from the Mach-O file.
867 static int macho_parse_symtab(struct macho_file_map* fmap,
868 const struct load_command* lc, void* user)
870 const struct symtab_command* sc = (const struct symtab_command*)lc;
871 struct macho_debug_info* mdi = user;
872 const macho_nlist* stab;
873 const char* stabstr;
874 int ret = 0;
876 TRACE("(%p/%d, %p, %p) %u syms at 0x%08x, strings 0x%08x - 0x%08x\n", fmap, fmap->fd, lc,
877 user, sc->nsyms, sc->symoff, sc->stroff, sc->stroff + sc->strsize);
879 if (!macho_map_ranges(fmap, sc->symoff, sc->nsyms * sizeof(macho_nlist),
880 sc->stroff, sc->strsize, (const void**)&stab, (const void**)&stabstr))
881 return 0;
883 if (!stabs_parse(mdi->module,
884 mdi->module->format_info[DFI_MACHO]->u.macho_info->load_addr - fmap->segs_start,
885 stab, sc->nsyms * sizeof(macho_nlist),
886 stabstr, sc->strsize, macho_stabs_def_cb, mdi))
887 ret = -1;
889 macho_unmap_ranges(fmap, sc->symoff, sc->nsyms * sizeof(macho_nlist),
890 sc->stroff, sc->strsize, (const void**)&stab, (const void**)&stabstr);
892 return ret;
895 /******************************************************************
896 * macho_finish_stabs
898 * Integrate the non-debugging symbols we've gathered into the
899 * symbols that were generated during stabs parsing.
901 static void macho_finish_stabs(struct module* module, struct hash_table* ht_symtab)
903 struct hash_table_iter hti_ours;
904 struct symtab_elt* ste;
905 BOOL adjusted = FALSE;
907 TRACE("(%p, %p)\n", module, ht_symtab);
909 /* For each of our non-debugging symbols, see if it can provide some
910 * missing details to one of the module's known symbols. */
911 hash_table_iter_init(ht_symtab, &hti_ours, NULL);
912 while ((ste = hash_table_iter_up(&hti_ours)))
914 struct hash_table_iter hti_modules;
915 void* ptr;
916 struct symt_ht* sym;
917 struct symt_function* func;
918 struct symt_data* data;
920 hash_table_iter_init(&module->ht_symbols, &hti_modules, ste->ht_elt.name);
921 while ((ptr = hash_table_iter_up(&hti_modules)))
923 sym = CONTAINING_RECORD(ptr, struct symt_ht, hash_elt);
925 if (strcmp(sym->hash_elt.name, ste->ht_elt.name))
926 continue;
928 switch (sym->symt.tag)
930 case SymTagFunction:
931 func = (struct symt_function*)sym;
932 if (func->address == module->format_info[DFI_MACHO]->u.macho_info->load_addr)
934 TRACE("Adjusting function %p/%s!%s from 0x%08lx to 0x%08lx\n", func,
935 debugstr_w(module->module.ModuleName), sym->hash_elt.name,
936 func->address, ste->addr);
937 func->address = ste->addr;
938 adjusted = TRUE;
940 if (func->address == ste->addr)
941 ste->used = 1;
942 break;
943 case SymTagData:
944 data = (struct symt_data*)sym;
945 switch (data->kind)
947 case DataIsGlobal:
948 case DataIsFileStatic:
949 if (data->u.var.offset == module->format_info[DFI_MACHO]->u.macho_info->load_addr)
951 TRACE("Adjusting data symbol %p/%s!%s from 0x%08lx to 0x%08lx\n",
952 data, debugstr_w(module->module.ModuleName), sym->hash_elt.name,
953 data->u.var.offset, ste->addr);
954 data->u.var.offset = ste->addr;
955 adjusted = TRUE;
957 if (data->u.var.offset == ste->addr)
959 enum DataKind new_kind;
961 new_kind = ste->is_global ? DataIsGlobal : DataIsFileStatic;
962 if (data->kind != new_kind)
964 WARN("Changing kind for %p/%s!%s from %d to %d\n", sym,
965 debugstr_w(module->module.ModuleName), sym->hash_elt.name,
966 (int)data->kind, (int)new_kind);
967 data->kind = new_kind;
968 adjusted = TRUE;
970 ste->used = 1;
972 break;
973 default:;
975 break;
976 default:
977 TRACE("Ignoring tag %u\n", sym->symt.tag);
978 break;
983 if (adjusted)
985 /* since we may have changed some addresses, mark the module to be resorted */
986 module->sortlist_valid = FALSE;
989 /* Mark any of our non-debugging symbols which fall on an already-used
990 * address as "used". This allows us to skip them in the next loop,
991 * below. We do this in separate loops because symt_new_* marks the
992 * list as needing sorting and symt_find_nearest sorts if needed,
993 * causing thrashing. */
994 if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY))
996 hash_table_iter_init(ht_symtab, &hti_ours, NULL);
997 while ((ste = hash_table_iter_up(&hti_ours)))
999 struct symt_ht* sym;
1000 ULONG64 addr;
1002 if (ste->used) continue;
1004 sym = symt_find_nearest(module, ste->addr);
1005 if (sym)
1006 symt_get_address(&sym->symt, &addr);
1007 if (sym && ste->addr == addr)
1009 ULONG64 size = 0;
1010 DWORD kind = -1;
1012 ste->used = 1;
1014 /* If neither symbol has a correct size (ours never does), we
1015 * consider them both to be markers. No warning is needed in
1016 * that case.
1017 * Also, we check that we don't have two symbols, one local, the other
1018 * global, which is legal.
1020 symt_get_info(module, &sym->symt, TI_GET_LENGTH, &size);
1021 symt_get_info(module, &sym->symt, TI_GET_DATAKIND, &kind);
1022 if (size && kind == (ste->is_global ? DataIsGlobal : DataIsFileStatic))
1023 FIXME("Duplicate in %s: %s<%08lx> %s<%s-%s>\n",
1024 debugstr_w(module->module.ModuleName),
1025 ste->ht_elt.name, ste->addr,
1026 sym->hash_elt.name,
1027 wine_dbgstr_longlong(addr), wine_dbgstr_longlong(size));
1032 /* For any of our remaining non-debugging symbols which have no match
1033 * among the module's known symbols, add them as new symbols. */
1034 hash_table_iter_init(ht_symtab, &hti_ours, NULL);
1035 while ((ste = hash_table_iter_up(&hti_ours)))
1037 if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY) && !ste->used)
1039 if (ste->is_code)
1041 symt_new_function(module, ste->compiland, ste->ht_elt.name,
1042 ste->addr, 0, NULL);
1044 else
1046 struct location loc;
1048 loc.kind = loc_absolute;
1049 loc.reg = 0;
1050 loc.offset = ste->addr;
1051 symt_new_global_variable(module, ste->compiland, ste->ht_elt.name,
1052 !ste->is_global, loc, 0, NULL);
1055 ste->used = 1;
1058 if (ste->is_public && !(dbghelp_options & SYMOPT_NO_PUBLICS))
1060 symt_new_public(module, ste->compiland, ste->ht_elt.name, ste->addr, 0);
1065 /******************************************************************
1066 * try_dsym
1068 * Try to load a debug symbol file from the given path and check
1069 * if its UUID matches the UUID of an already-mapped file. If so,
1070 * stash the file map in the "dsym" field of the file and return
1071 * TRUE. If it can't be mapped or its UUID doesn't match, return
1072 * FALSE.
1074 static BOOL try_dsym(const WCHAR* path, struct macho_file_map* fmap)
1076 struct image_file_map dsym_ifm;
1078 if (macho_map_file(path, FALSE, &dsym_ifm))
1080 char uuid_string[UUID_STRING_LEN];
1082 if (dsym_ifm.u.macho.uuid && !memcmp(dsym_ifm.u.macho.uuid->uuid, fmap->uuid->uuid, sizeof(fmap->uuid->uuid)))
1084 TRACE("found matching debug symbol file at %s\n", debugstr_w(path));
1085 fmap->dsym = HeapAlloc(GetProcessHeap(), 0, sizeof(dsym_ifm));
1086 *fmap->dsym = dsym_ifm;
1087 return TRUE;
1090 TRACE("candidate debug symbol file at %s has wrong UUID %s; ignoring\n", debugstr_w(path),
1091 format_uuid(dsym_ifm.u.macho.uuid->uuid, uuid_string));
1093 macho_unmap_file(&dsym_ifm);
1095 else
1096 TRACE("couldn't map file at %s\n", debugstr_w(path));
1098 return FALSE;
1101 /******************************************************************
1102 * find_and_map_dsym
1104 * Search for a debugging symbols file associated with a module and
1105 * map it. First look for a .dSYM bundle next to the module file
1106 * (e.g. <path>.dSYM/Contents/Resources/DWARF/<basename of path>)
1107 * as produced by dsymutil. Next, look for a .dwarf file next to
1108 * the module file (e.g. <path>.dwarf) as produced by
1109 * "dsymutil --flat". Finally, use Spotlight to search for a
1110 * .dSYM bundle with the same UUID as the module file.
1112 static void find_and_map_dsym(struct module* module)
1114 static const WCHAR dot_dsym[] = {'.','d','S','Y','M',0};
1115 static const WCHAR dsym_subpath[] = {'/','C','o','n','t','e','n','t','s','/','R','e','s','o','u','r','c','e','s','/','D','W','A','R','F','/',0};
1116 static const WCHAR dot_dwarf[] = {'.','d','w','a','r','f',0};
1117 struct macho_file_map* fmap = &module->format_info[DFI_MACHO]->u.macho_info->file_map.u.macho;
1118 const WCHAR* p;
1119 size_t len;
1120 WCHAR* path = NULL;
1121 char uuid_string[UUID_STRING_LEN];
1122 CFStringRef uuid_cfstring;
1123 CFStringRef query_string;
1124 MDQueryRef query = NULL;
1126 /* Without a UUID, we can't verify that any debug info file we find corresponds
1127 to this file. Better to have no debug info than incorrect debug info. */
1128 if (!fmap->uuid)
1129 return;
1131 if ((p = strrchrW(module->module.LoadedImageName, '/')))
1132 p++;
1133 else
1134 p = module->module.LoadedImageName;
1135 len = strlenW(module->module.LoadedImageName) + strlenW(dot_dsym) + strlenW(dsym_subpath) + strlenW(p) + 1;
1136 path = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1137 if (!path)
1138 return;
1139 strcpyW(path, module->module.LoadedImageName);
1140 strcatW(path, dot_dsym);
1141 strcatW(path, dsym_subpath);
1142 strcatW(path, p);
1144 if (try_dsym(path, fmap))
1145 goto found;
1147 strcpyW(path + strlenW(module->module.LoadedImageName), dot_dwarf);
1149 if (try_dsym(path, fmap))
1150 goto found;
1152 format_uuid(fmap->uuid->uuid, uuid_string);
1153 uuid_cfstring = CFStringCreateWithCString(NULL, uuid_string, kCFStringEncodingASCII);
1154 query_string = CFStringCreateWithFormat(NULL, NULL, CFSTR("com_apple_xcode_dsym_uuids == \"%@\""), uuid_cfstring);
1155 CFRelease(uuid_cfstring);
1156 query = MDQueryCreate(NULL, query_string, NULL, NULL);
1157 CFRelease(query_string);
1158 MDQuerySetMaxCount(query, 1);
1159 if (MDQueryExecute(query, kMDQuerySynchronous) && MDQueryGetResultCount(query) >= 1)
1161 MDItemRef item = (MDItemRef)MDQueryGetResultAtIndex(query, 0);
1162 CFStringRef item_path = MDItemCopyAttribute(item, kMDItemPath);
1163 if (item_path)
1165 CFIndex item_path_len = CFStringGetLength(item_path);
1166 if (item_path_len + strlenW(dsym_subpath) + strlenW(p) >= len)
1168 HeapFree(GetProcessHeap(), 0, path);
1169 len = item_path_len + strlenW(dsym_subpath) + strlenW(p) + 1;
1170 path = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1172 CFStringGetCharacters(item_path, CFRangeMake(0, item_path_len), (UniChar*)path);
1173 strcpyW(path + item_path_len, dsym_subpath);
1174 strcatW(path, p);
1175 CFRelease(item_path);
1177 if (try_dsym(path, fmap))
1178 goto found;
1182 found:
1183 HeapFree(GetProcessHeap(), 0, path);
1184 if (query) CFRelease(query);
1187 /******************************************************************
1188 * image_uses_split_segs
1190 * Determine if the Mach-O image loaded at a particular address in
1191 * the given process is in the dyld shared cache and therefore has
1192 * its segments mapped non-contiguously.
1194 * The image header has to be loaded from the process's memory
1195 * because the relevant flag is only set in memory, not in the file.
1197 static BOOL image_uses_split_segs(HANDLE process, unsigned long load_addr)
1199 BOOL split_segs = FALSE;
1201 if (process && load_addr)
1203 macho_mach_header header;
1204 if (ReadProcessMemory(process, (void*)load_addr, &header, sizeof(header), NULL) &&
1205 header.magic == TARGET_MH_MAGIC && header.cputype == TARGET_CPU_TYPE &&
1206 header.flags & MACHO_DYLD_IN_SHARED_CACHE)
1208 split_segs = TRUE;
1212 return split_segs;
1215 /******************************************************************
1216 * macho_load_debug_info
1218 * Loads Mach-O debugging information from the module image file.
1220 BOOL macho_load_debug_info(struct module* module)
1222 BOOL ret = FALSE;
1223 struct macho_debug_info mdi;
1224 int result;
1225 struct macho_file_map *fmap;
1227 if (module->type != DMT_MACHO || !module->format_info[DFI_MACHO]->u.macho_info)
1229 ERR("Bad Mach-O module '%s'\n", debugstr_w(module->module.LoadedImageName));
1230 return FALSE;
1233 fmap = &module->format_info[DFI_MACHO]->u.macho_info->file_map.u.macho;
1235 TRACE("(%p, %p/%d)\n", module, fmap, fmap->fd);
1237 module->module.SymType = SymExport;
1239 if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY))
1241 find_and_map_dsym(module);
1243 if (dwarf2_parse(module, module->reloc_delta, NULL /* FIXME: some thunks to deal with ? */,
1244 &module->format_info[DFI_MACHO]->u.macho_info->file_map))
1245 ret = TRUE;
1248 mdi.fmap = fmap;
1249 mdi.module = module;
1250 pool_init(&mdi.pool, 65536);
1251 hash_table_init(&mdi.pool, &mdi.ht_symtab, 256);
1252 result = macho_enum_load_commands(fmap, LC_SYMTAB, macho_parse_symtab, &mdi);
1253 if (result > 0)
1254 ret = TRUE;
1255 else if (result < 0)
1256 WARN("Couldn't correctly read stabs\n");
1258 if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY) && fmap->dsym)
1260 mdi.fmap = &fmap->dsym->u.macho;
1261 result = macho_enum_load_commands(mdi.fmap, LC_SYMTAB, macho_parse_symtab, &mdi);
1262 if (result > 0)
1263 ret = TRUE;
1264 else if (result < 0)
1265 WARN("Couldn't correctly read stabs\n");
1268 macho_finish_stabs(module, &mdi.ht_symtab);
1270 pool_destroy(&mdi.pool);
1271 return ret;
1274 /******************************************************************
1275 * macho_fetch_file_info
1277 * Gathers some more information for a Mach-O module from a given file
1279 BOOL macho_fetch_file_info(HANDLE process, const WCHAR* name, unsigned long load_addr, DWORD_PTR* base,
1280 DWORD* size, DWORD* checksum)
1282 struct image_file_map fmap;
1283 BOOL split_segs;
1285 TRACE("(%s, %p, %p, %p)\n", debugstr_w(name), base, size, checksum);
1287 split_segs = image_uses_split_segs(process, load_addr);
1288 if (!macho_map_file(name, split_segs, &fmap)) return FALSE;
1289 if (base) *base = fmap.u.macho.segs_start;
1290 *size = fmap.u.macho.segs_size;
1291 *checksum = calc_crc32(fmap.u.macho.fd);
1292 macho_unmap_file(&fmap);
1293 return TRUE;
1296 /******************************************************************
1297 * macho_module_remove
1299 static void macho_module_remove(struct process* pcs, struct module_format* modfmt)
1301 macho_unmap_file(&modfmt->u.macho_info->file_map);
1302 HeapFree(GetProcessHeap(), 0, modfmt);
1306 /******************************************************************
1307 * get_dyld_image_info_address
1309 static ULONG_PTR get_dyld_image_info_address(struct process* pcs)
1311 NTSTATUS status;
1312 PROCESS_BASIC_INFORMATION pbi;
1313 ULONG_PTR dyld_image_info_address = 0;
1315 /* Get address of PEB */
1316 status = NtQueryInformationProcess(pcs->handle, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
1317 if (status == STATUS_SUCCESS)
1319 /* Read dyld image info address from PEB */
1320 if (ReadProcessMemory(pcs->handle, &pbi.PebBaseAddress->Reserved[0],
1321 &dyld_image_info_address, sizeof(dyld_image_info_address), NULL))
1323 TRACE("got dyld_image_info_address 0x%08lx from PEB %p MacDyldImageInfo %p\n",
1324 (unsigned long)dyld_image_info_address, pbi.PebBaseAddress, &pbi.PebBaseAddress->Reserved);
1328 #ifndef __LP64__ /* No reading the symtab with nlist(3) in LP64 */
1329 if (!dyld_image_info_address)
1331 static void* dyld_all_image_infos_addr;
1333 /* Our next best guess is that dyld was loaded at its base address
1334 and we can find the dyld image infos address by looking up its symbol. */
1335 if (!dyld_all_image_infos_addr)
1337 struct nlist nl[2];
1338 memset(nl, 0, sizeof(nl));
1339 nl[0].n_un.n_name = (char*)"_dyld_all_image_infos";
1340 if (!nlist("/usr/lib/dyld", nl))
1341 dyld_all_image_infos_addr = (void*)nl[0].n_value;
1344 if (dyld_all_image_infos_addr)
1346 TRACE("got dyld_image_info_address %p from /usr/lib/dyld symbol table\n",
1347 dyld_all_image_infos_addr);
1348 dyld_image_info_address = (ULONG_PTR)dyld_all_image_infos_addr;
1351 #endif
1353 return dyld_image_info_address;
1356 /******************************************************************
1357 * macho_load_file
1359 * Loads the information for Mach-O module stored in 'filename'.
1360 * The module has been loaded at 'load_addr' address.
1361 * returns
1362 * FALSE if the file cannot be found/opened or if the file doesn't
1363 * contain symbolic info (or this info cannot be read or parsed)
1364 * TRUE on success
1366 static BOOL macho_load_file(struct process* pcs, const WCHAR* filename,
1367 unsigned long load_addr, struct macho_info* macho_info)
1369 BOOL ret = TRUE;
1370 BOOL split_segs;
1371 struct image_file_map fmap;
1373 TRACE("(%p/%p, %s, 0x%08lx, %p/0x%08x)\n", pcs, pcs->handle, debugstr_w(filename),
1374 load_addr, macho_info, macho_info->flags);
1376 split_segs = image_uses_split_segs(pcs->handle, load_addr);
1377 if (!macho_map_file(filename, split_segs, &fmap)) return FALSE;
1379 /* Find the dynamic loader's table of images loaded into the process.
1381 if (macho_info->flags & MACHO_INFO_DEBUG_HEADER)
1383 macho_info->dbg_hdr_addr = (unsigned long)get_dyld_image_info_address(pcs);
1384 ret = TRUE;
1387 if (macho_info->flags & MACHO_INFO_MODULE)
1389 struct macho_module_info *macho_module_info;
1390 struct module_format* modfmt =
1391 HeapAlloc(GetProcessHeap(), 0, sizeof(struct module_format) + sizeof(struct macho_module_info));
1392 if (!modfmt) goto leave;
1393 if (!load_addr)
1394 load_addr = fmap.u.macho.segs_start;
1395 macho_info->module = module_new(pcs, filename, DMT_MACHO, FALSE, load_addr,
1396 fmap.u.macho.segs_size, 0, calc_crc32(fmap.u.macho.fd));
1397 if (!macho_info->module)
1399 HeapFree(GetProcessHeap(), 0, modfmt);
1400 goto leave;
1402 macho_info->module->reloc_delta = macho_info->module->module.BaseOfImage - fmap.u.macho.segs_start;
1403 macho_module_info = (void*)(modfmt + 1);
1404 macho_info->module->format_info[DFI_MACHO] = modfmt;
1406 modfmt->module = macho_info->module;
1407 modfmt->remove = macho_module_remove;
1408 modfmt->loc_compute = NULL;
1409 modfmt->u.macho_info = macho_module_info;
1411 macho_module_info->load_addr = load_addr;
1413 macho_module_info->file_map = fmap;
1414 reset_file_map(&fmap);
1415 if (dbghelp_options & SYMOPT_DEFERRED_LOADS)
1416 macho_info->module->module.SymType = SymDeferred;
1417 else if (!macho_load_debug_info(macho_info->module))
1418 ret = FALSE;
1420 macho_info->module->format_info[DFI_MACHO]->u.macho_info->in_use = 1;
1421 macho_info->module->format_info[DFI_MACHO]->u.macho_info->is_loader = 0;
1422 TRACE("module = %p\n", macho_info->module);
1425 if (macho_info->flags & MACHO_INFO_NAME)
1427 WCHAR* ptr;
1428 ptr = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
1429 if (ptr)
1431 strcpyW(ptr, filename);
1432 macho_info->module_name = ptr;
1434 else ret = FALSE;
1435 TRACE("module_name = %p %s\n", macho_info->module_name, debugstr_w(macho_info->module_name));
1437 leave:
1438 macho_unmap_file(&fmap);
1440 TRACE(" => %d\n", ret);
1441 return ret;
1444 /******************************************************************
1445 * macho_load_file_from_path
1446 * Tries to load a Mach-O file from a set of paths (separated by ':')
1448 static BOOL macho_load_file_from_path(struct process* pcs,
1449 const WCHAR* filename,
1450 unsigned long load_addr,
1451 const char* path,
1452 struct macho_info* macho_info)
1454 BOOL ret = FALSE;
1455 WCHAR *s, *t, *fn;
1456 WCHAR* pathW = NULL;
1457 unsigned len;
1459 TRACE("(%p/%p, %s, 0x%08lx, %s, %p)\n", pcs, pcs->handle, debugstr_w(filename), load_addr,
1460 debugstr_a(path), macho_info);
1462 if (!path) return FALSE;
1464 len = MultiByteToWideChar(CP_UNIXCP, 0, path, -1, NULL, 0);
1465 pathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1466 if (!pathW) return FALSE;
1467 MultiByteToWideChar(CP_UNIXCP, 0, path, -1, pathW, len);
1469 for (s = pathW; s && *s; s = (t) ? (t+1) : NULL)
1471 t = strchrW(s, ':');
1472 if (t) *t = '\0';
1473 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1 + lstrlenW(s) + 1) * sizeof(WCHAR));
1474 if (!fn) break;
1475 strcpyW(fn, s);
1476 strcatW(fn, S_SlashW);
1477 strcatW(fn, filename);
1478 ret = macho_load_file(pcs, fn, load_addr, macho_info);
1479 HeapFree(GetProcessHeap(), 0, fn);
1480 if (ret) break;
1481 s = (t) ? (t+1) : NULL;
1484 TRACE(" => %d\n", ret);
1485 HeapFree(GetProcessHeap(), 0, pathW);
1486 return ret;
1489 /******************************************************************
1490 * macho_load_file_from_dll_path
1492 * Tries to load a Mach-O file from the dll path
1494 static BOOL macho_load_file_from_dll_path(struct process* pcs,
1495 const WCHAR* filename,
1496 unsigned long load_addr,
1497 struct macho_info* macho_info)
1499 BOOL ret = FALSE;
1500 unsigned int index = 0;
1501 const char *path;
1503 TRACE("(%p/%p, %s, 0x%08lx, %p)\n", pcs, pcs->handle, debugstr_w(filename), load_addr,
1504 macho_info);
1506 while (!ret && (path = wine_dll_enum_load_path( index++ )))
1508 WCHAR *name;
1509 unsigned len;
1511 len = MultiByteToWideChar(CP_UNIXCP, 0, path, -1, NULL, 0);
1513 name = HeapAlloc( GetProcessHeap(), 0,
1514 (len + lstrlenW(filename) + 2) * sizeof(WCHAR) );
1516 if (!name) break;
1517 MultiByteToWideChar(CP_UNIXCP, 0, path, -1, name, len);
1518 strcatW( name, S_SlashW );
1519 strcatW( name, filename );
1520 ret = macho_load_file(pcs, name, load_addr, macho_info);
1521 HeapFree( GetProcessHeap(), 0, name );
1523 TRACE(" => %d\n", ret);
1524 return ret;
1527 /******************************************************************
1528 * macho_search_and_load_file
1530 * Lookup a file in standard Mach-O locations, and if found, load it
1532 static BOOL macho_search_and_load_file(struct process* pcs, const WCHAR* filename,
1533 unsigned long load_addr,
1534 struct macho_info* macho_info)
1536 BOOL ret = FALSE;
1537 struct module* module;
1538 static const WCHAR S_libstdcPPW[] = {'l','i','b','s','t','d','c','+','+','\0'};
1539 const WCHAR* p;
1541 TRACE("(%p/%p, %s, 0x%08lx, %p)\n", pcs, pcs->handle, debugstr_w(filename), load_addr,
1542 macho_info);
1544 if (filename == NULL || *filename == '\0') return FALSE;
1545 if ((module = module_is_already_loaded(pcs, filename)))
1547 macho_info->module = module;
1548 module->format_info[DFI_MACHO]->u.macho_info->in_use = 1;
1549 return module->module.SymType;
1552 if (strstrW(filename, S_libstdcPPW)) return FALSE; /* We know we can't do it */
1554 /* If has no directories, try PATH first. */
1555 if (!strchrW(filename, '/'))
1557 ret = macho_load_file_from_path(pcs, filename, load_addr,
1558 getenv("PATH"), macho_info);
1560 /* Try DYLD_LIBRARY_PATH, with just the filename (no directories). */
1561 if (!ret)
1563 if ((p = strrchrW(filename, '/'))) p++;
1564 else p = filename;
1565 ret = macho_load_file_from_path(pcs, p, load_addr,
1566 getenv("DYLD_LIBRARY_PATH"), macho_info);
1568 /* Try the path as given. */
1569 if (!ret)
1570 ret = macho_load_file(pcs, filename, load_addr, macho_info);
1571 /* Try DYLD_FALLBACK_LIBRARY_PATH, with just the filename (no directories). */
1572 if (!ret)
1574 const char* fallback = getenv("DYLD_FALLBACK_LIBRARY_PATH");
1575 if (!fallback)
1576 fallback = "/usr/local/lib:/lib:/usr/lib";
1577 ret = macho_load_file_from_path(pcs, p, load_addr, fallback, macho_info);
1579 if (!ret && !strchrW(filename, '/'))
1580 ret = macho_load_file_from_dll_path(pcs, filename, load_addr, macho_info);
1582 return ret;
1585 /******************************************************************
1586 * macho_enum_modules_internal
1588 * Enumerate Mach-O modules from a running process
1590 static BOOL macho_enum_modules_internal(const struct process* pcs,
1591 const WCHAR* main_name,
1592 enum_modules_cb cb, void* user)
1594 struct dyld_all_image_infos image_infos;
1595 struct dyld_image_info* info_array = NULL;
1596 unsigned long len;
1597 int i;
1598 char bufstr[256];
1599 WCHAR bufstrW[MAX_PATH];
1600 BOOL ret = FALSE;
1602 TRACE("(%p/%p, %s, %p, %p)\n", pcs, pcs->handle, debugstr_w(main_name), cb,
1603 user);
1605 if (!pcs->dbg_hdr_addr ||
1606 !ReadProcessMemory(pcs->handle, (void*)pcs->dbg_hdr_addr,
1607 &image_infos, sizeof(image_infos), NULL) ||
1608 !image_infos.infoArray)
1609 goto done;
1610 TRACE("Process has %u image infos at %p\n", image_infos.infoArrayCount, image_infos.infoArray);
1612 len = image_infos.infoArrayCount * sizeof(info_array[0]);
1613 info_array = HeapAlloc(GetProcessHeap(), 0, len);
1614 if (!info_array ||
1615 !ReadProcessMemory(pcs->handle, image_infos.infoArray,
1616 info_array, len, NULL))
1617 goto done;
1618 TRACE("... read image infos\n");
1620 for (i = 0; i < image_infos.infoArrayCount; i++)
1622 if (info_array[i].imageFilePath != NULL &&
1623 ReadProcessMemory(pcs->handle, info_array[i].imageFilePath, bufstr, sizeof(bufstr), NULL))
1625 bufstr[sizeof(bufstr) - 1] = '\0';
1626 TRACE("[%d] image file %s\n", i, debugstr_a(bufstr));
1627 MultiByteToWideChar(CP_UNIXCP, 0, bufstr, -1, bufstrW, sizeof(bufstrW) / sizeof(WCHAR));
1628 if (main_name && !bufstrW[0]) strcpyW(bufstrW, main_name);
1629 if (!cb(bufstrW, (unsigned long)info_array[i].imageLoadAddress, user)) break;
1633 ret = TRUE;
1634 done:
1635 HeapFree(GetProcessHeap(), 0, info_array);
1636 return ret;
1639 struct macho_sync
1641 struct process* pcs;
1642 struct macho_info macho_info;
1645 static BOOL macho_enum_sync_cb(const WCHAR* name, unsigned long addr, void* user)
1647 struct macho_sync* ms = user;
1649 TRACE("(%s, 0x%08lx, %p)\n", debugstr_w(name), addr, user);
1650 macho_search_and_load_file(ms->pcs, name, addr, &ms->macho_info);
1651 return TRUE;
1654 /******************************************************************
1655 * macho_synchronize_module_list
1657 * Rescans the debuggee's modules list and synchronizes it with
1658 * the one from 'pcs', ie:
1659 * - if a module is in debuggee and not in pcs, it's loaded into pcs
1660 * - if a module is in pcs and not in debuggee, it's unloaded from pcs
1662 BOOL macho_synchronize_module_list(struct process* pcs)
1664 struct module* module;
1665 struct macho_sync ms;
1667 TRACE("(%p/%p)\n", pcs, pcs->handle);
1669 for (module = pcs->lmodules; module; module = module->next)
1671 if (module->type == DMT_MACHO && !module->is_virtual)
1672 module->format_info[DFI_MACHO]->u.macho_info->in_use = 0;
1675 ms.pcs = pcs;
1676 ms.macho_info.flags = MACHO_INFO_MODULE;
1677 if (!macho_enum_modules_internal(pcs, NULL, macho_enum_sync_cb, &ms))
1678 return FALSE;
1680 module = pcs->lmodules;
1681 while (module)
1683 if (module->type == DMT_MACHO && !module->is_virtual &&
1684 !module->format_info[DFI_MACHO]->u.macho_info->in_use &&
1685 !module->format_info[DFI_MACHO]->u.macho_info->is_loader)
1687 module_remove(pcs, module);
1688 /* restart all over */
1689 module = pcs->lmodules;
1691 else module = module->next;
1693 return TRUE;
1696 /******************************************************************
1697 * macho_search_loader
1699 * Lookup in a running Mach-O process the loader, and sets its Mach-O link
1700 * address (for accessing the list of loaded images) in pcs.
1701 * If flags is MACHO_INFO_MODULE, the module for the loader is also
1702 * added as a module into pcs.
1704 static BOOL macho_search_loader(struct process* pcs, struct macho_info* macho_info)
1706 BOOL ret = FALSE;
1707 ULONG_PTR dyld_image_info_address;
1708 struct dyld_all_image_infos image_infos;
1709 struct dyld_image_info image_info;
1710 uint32_t len;
1711 char path[PATH_MAX];
1712 BOOL got_path = FALSE;
1714 dyld_image_info_address = get_dyld_image_info_address(pcs);
1715 if (dyld_image_info_address &&
1716 ReadProcessMemory(pcs->handle, (void*)dyld_image_info_address, &image_infos, sizeof(image_infos), NULL) &&
1717 image_infos.infoArray && image_infos.infoArrayCount &&
1718 ReadProcessMemory(pcs->handle, image_infos.infoArray, &image_info, sizeof(image_info), NULL) &&
1719 image_info.imageFilePath)
1721 for (len = sizeof(path); len > 0; len /= 2)
1723 if (ReadProcessMemory(pcs->handle, image_info.imageFilePath, path, len, NULL))
1725 path[len - 1] = 0;
1726 got_path = TRUE;
1727 TRACE("got executable path from target's dyld image info: %s\n", debugstr_a(path));
1728 break;
1733 /* If we couldn't get the executable path from the target process, try our
1734 own. It will almost always be the same. */
1735 if (!got_path)
1737 len = sizeof(path);
1738 if (!_NSGetExecutablePath(path, &len))
1740 got_path = TRUE;
1741 TRACE("using own executable path: %s\n", debugstr_a(path));
1745 if (got_path)
1747 WCHAR* pathW;
1749 len = MultiByteToWideChar(CP_UNIXCP, 0, path, -1, NULL, 0);
1750 pathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1751 if (pathW)
1753 MultiByteToWideChar(CP_UNIXCP, 0, path, -1, pathW, len);
1754 ret = macho_load_file(pcs, pathW, 0, macho_info);
1755 HeapFree(GetProcessHeap(), 0, pathW);
1759 if (!ret)
1760 ret = macho_search_and_load_file(pcs, get_wine_loader_name(), 0, macho_info);
1761 return ret;
1764 /******************************************************************
1765 * macho_read_wine_loader_dbg_info
1767 * Try to find a decent wine executable which could have loaded the debuggee
1769 BOOL macho_read_wine_loader_dbg_info(struct process* pcs)
1771 struct macho_info macho_info;
1773 TRACE("(%p/%p)\n", pcs, pcs->handle);
1774 macho_info.flags = MACHO_INFO_DEBUG_HEADER | MACHO_INFO_MODULE;
1775 if (!macho_search_loader(pcs, &macho_info)) return FALSE;
1776 macho_info.module->format_info[DFI_MACHO]->u.macho_info->is_loader = 1;
1777 module_set_module(macho_info.module, S_WineLoaderW);
1778 return (pcs->dbg_hdr_addr = macho_info.dbg_hdr_addr) != 0;
1781 /******************************************************************
1782 * macho_enum_modules
1784 * Enumerates the Mach-O loaded modules from a running target (hProc)
1785 * This function doesn't require that someone has called SymInitialize
1786 * on this very process.
1788 BOOL macho_enum_modules(HANDLE hProc, enum_modules_cb cb, void* user)
1790 struct process pcs;
1791 struct macho_info macho_info;
1792 BOOL ret;
1794 TRACE("(%p, %p, %p)\n", hProc, cb, user);
1795 memset(&pcs, 0, sizeof(pcs));
1796 pcs.handle = hProc;
1797 macho_info.flags = MACHO_INFO_DEBUG_HEADER | MACHO_INFO_NAME;
1798 if (!macho_search_loader(&pcs, &macho_info)) return FALSE;
1799 pcs.dbg_hdr_addr = macho_info.dbg_hdr_addr;
1800 ret = macho_enum_modules_internal(&pcs, macho_info.module_name, cb, user);
1801 HeapFree(GetProcessHeap(), 0, (char*)macho_info.module_name);
1802 return ret;
1805 struct macho_load
1807 struct process* pcs;
1808 struct macho_info macho_info;
1809 const WCHAR* name;
1810 BOOL ret;
1813 /******************************************************************
1814 * macho_load_cb
1816 * Callback for macho_load_module, used to walk the list of loaded
1817 * modules.
1819 static BOOL macho_load_cb(const WCHAR* name, unsigned long addr, void* user)
1821 struct macho_load* ml = user;
1822 const WCHAR* p;
1824 TRACE("(%s, 0x%08lx, %p)\n", debugstr_w(name), addr, user);
1826 /* memcmp is needed for matches when bufstr contains also version information
1827 * ml->name: libc.so, name: libc.so.6.0
1829 p = strrchrW(name, '/');
1830 if (!p++) p = name;
1831 if (!memcmp(p, ml->name, lstrlenW(ml->name) * sizeof(WCHAR)))
1833 ml->ret = macho_search_and_load_file(ml->pcs, name, addr, &ml->macho_info);
1834 return FALSE;
1836 return TRUE;
1839 /******************************************************************
1840 * macho_load_module
1842 * Loads a Mach-O module and stores it in process' module list.
1843 * Also, find module real name and load address from
1844 * the real loaded modules list in pcs address space.
1846 struct module* macho_load_module(struct process* pcs, const WCHAR* name, unsigned long addr)
1848 struct macho_load ml;
1850 TRACE("(%p/%p, %s, 0x%08lx)\n", pcs, pcs->handle, debugstr_w(name), addr);
1852 ml.macho_info.flags = MACHO_INFO_MODULE;
1853 ml.ret = FALSE;
1855 if (pcs->dbg_hdr_addr) /* we're debugging a live target */
1857 ml.pcs = pcs;
1858 /* do only the lookup from the filename, not the path (as we lookup module
1859 * name in the process' loaded module list)
1861 ml.name = strrchrW(name, '/');
1862 if (!ml.name++) ml.name = name;
1863 ml.ret = FALSE;
1865 if (!macho_enum_modules_internal(pcs, NULL, macho_load_cb, &ml))
1866 return NULL;
1868 else if (addr)
1870 ml.name = name;
1871 ml.ret = macho_search_and_load_file(pcs, ml.name, addr, &ml.macho_info);
1873 if (!ml.ret) return NULL;
1874 assert(ml.macho_info.module);
1875 return ml.macho_info.module;
1878 #else /* HAVE_MACH_O_LOADER_H */
1880 BOOL macho_find_section(struct image_file_map* ifm, const char* segname, const char* sectname, struct image_section_map* ism)
1882 return FALSE;
1885 const char* macho_map_section(struct image_section_map* ism)
1887 return NULL;
1890 void macho_unmap_section(struct image_section_map* ism)
1894 DWORD_PTR macho_get_map_rva(const struct image_section_map* ism)
1896 return 0;
1899 unsigned macho_get_map_size(const struct image_section_map* ism)
1901 return 0;
1904 BOOL macho_synchronize_module_list(struct process* pcs)
1906 return FALSE;
1909 BOOL macho_fetch_file_info(HANDLE process, const WCHAR* name, unsigned long load_addr, DWORD_PTR* base,
1910 DWORD* size, DWORD* checksum)
1912 return FALSE;
1915 BOOL macho_read_wine_loader_dbg_info(struct process* pcs)
1917 return FALSE;
1920 BOOL macho_enum_modules(HANDLE hProc, enum_modules_cb cb, void* user)
1922 return FALSE;
1925 struct module* macho_load_module(struct process* pcs, const WCHAR* name, unsigned long addr)
1927 return NULL;
1930 BOOL macho_load_debug_info(struct module* module)
1932 return FALSE;
1934 #endif /* HAVE_MACH_O_LOADER_H */