dbghelp: Ignore the __PAGEZERO segment.
[wine/multimedia.git] / dlls / dbghelp / macho_module.c
blob2eb2b94a614df25c827bc12f364eb797b6c1d999
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 #include "ntstatus.h"
28 #define WIN32_NO_STATUS
29 #include "dbghelp_private.h"
31 #ifdef HAVE_MACH_O_LOADER_H
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <errno.h>
36 #ifdef HAVE_SYS_STAT_H
37 # include <sys/stat.h>
38 #endif
39 #ifdef HAVE_SYS_MMAN_H
40 # include <sys/mman.h>
41 #endif
43 #include <mach-o/fat.h>
44 #include <mach-o/loader.h>
45 #include <mach-o/nlist.h>
47 #ifdef HAVE_MACH_O_DYLD_IMAGES_H
48 #include <mach-o/dyld_images.h>
49 #else
50 struct dyld_image_info {
51 const struct mach_header *imageLoadAddress;
52 const char *imageFilePath;
53 uintptr_t imageFileModDate;
56 struct dyld_all_image_infos {
57 uint32_t version;
58 uint32_t infoArrayCount;
59 const struct dyld_image_info *infoArray;
60 void* notification;
61 int processDetachedFromSharedRegion;
63 #endif
65 #include "winternl.h"
66 #include "wine/library.h"
67 #include "wine/debug.h"
69 #ifdef WORDS_BIGENDIAN
70 #define swap_ulong_be_to_host(n) (n)
71 #else
72 #define swap_ulong_be_to_host(n) (RtlUlongByteSwap(n))
73 #endif
75 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp_macho);
78 struct macho_module_info
80 unsigned long load_addr;
81 unsigned short in_use : 1,
82 is_loader : 1;
85 #define MACHO_INFO_DEBUG_HEADER 0x0001
86 #define MACHO_INFO_MODULE 0x0002
87 #define MACHO_INFO_NAME 0x0004
89 struct macho_info
91 unsigned flags; /* IN one (or several) of the MACHO_INFO constants */
92 unsigned long dbg_hdr_addr; /* OUT address of debug header (if MACHO_INFO_DEBUG_HEADER is set) */
93 struct module* module; /* OUT loaded module (if MACHO_INFO_MODULE is set) */
94 const WCHAR* module_name; /* OUT found module name (if MACHO_INFO_NAME is set) */
97 /* structure holding information while handling a Mach-O image */
98 #define BITS_PER_ULONG (sizeof(ULONG) * 8)
99 #define ULONGS_FOR_BITS(nbits) (((nbits) + BITS_PER_ULONG - 1) / BITS_PER_ULONG)
100 struct macho_file_map
102 /* A copy of the Mach-O header for an individual architecture. */
103 struct mach_header mach_header;
105 /* The mapped load commands. */
106 const struct load_command* load_commands;
108 /* The portion of the file which is this architecture. mach_header was
109 * read from arch_offset. */
110 unsigned arch_offset;
111 unsigned arch_size;
113 /* The range of address space covered by all segments. */
114 size_t segs_start;
115 size_t segs_size;
117 /* Map of which sections contain code. Sections are accessed using 1-based
118 * index. Bit 0 of this bitset indicates if the bitset has been initialized. */
119 RTL_BITMAP sect_is_code;
120 ULONG sect_is_code_buff[ULONGS_FOR_BITS(MAX_SECT + 1)];
122 /* The file. */
123 int fd;
126 static void macho_unmap_file(struct macho_file_map* fmap);
128 /******************************************************************
129 * macho_calc_range
131 * For a range (offset & length) of a single architecture within
132 * a Mach-O file, calculate the page-aligned range of the whole file
133 * that encompasses it. For a fat binary, the architecture will
134 * itself be offset within the file, so take that into account.
136 static void macho_calc_range(const struct macho_file_map* fmap, unsigned offset,
137 unsigned len, unsigned* out_aligned_offset,
138 unsigned* out_aligned_end, unsigned* out_aligned_len,
139 unsigned* out_misalign)
141 unsigned pagemask = sysconf( _SC_PAGESIZE ) - 1;
142 unsigned file_offset, misalign;
144 file_offset = fmap->arch_offset + offset;
145 misalign = file_offset & pagemask;
146 *out_aligned_offset = file_offset - misalign;
147 *out_aligned_end = (file_offset + len + pagemask) & ~pagemask;
148 if (out_aligned_len)
149 *out_aligned_len = *out_aligned_end - *out_aligned_offset;
150 if (out_misalign)
151 *out_misalign = misalign;
154 /******************************************************************
155 * macho_map_range
157 * Maps a range (offset, length in bytes) from a Mach-O file into memory
159 static const char* macho_map_range(const struct macho_file_map* fmap, unsigned offset, unsigned len)
161 unsigned misalign, aligned_offset, aligned_map_end, map_size;
162 const void* aligned_ptr;
164 TRACE("(%p/%d, 0x%08x, 0x%08x)\n", fmap, fmap->fd, offset, len);
166 macho_calc_range(fmap, offset, len, &aligned_offset, &aligned_map_end,
167 &map_size, &misalign);
169 aligned_ptr = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, fmap->fd, aligned_offset);
171 TRACE("Mapped (0x%08x - 0x%08x) to %p\n", aligned_offset, aligned_map_end, aligned_ptr);
173 if (aligned_ptr == MAP_FAILED) return MACHO_NO_MAP;
174 return (const char*)aligned_ptr + misalign;
177 /******************************************************************
178 * macho_unmap_range
180 * Unmaps a range (offset, length in bytes) of a Mach-O file from memory
182 static void macho_unmap_range(const void** mapped, const struct macho_file_map* fmap,
183 unsigned offset, unsigned len)
185 TRACE("(%p, %p/%d, 0x%08x, 0x%08x)\n", mapped, fmap, fmap->fd, offset, len);
187 if (mapped && *mapped != MACHO_NO_MAP)
189 unsigned misalign, aligned_offset, aligned_map_end, map_size;
190 void* aligned_ptr;
192 macho_calc_range(fmap, offset, len, &aligned_offset, &aligned_map_end,
193 &map_size, &misalign);
195 aligned_ptr = (char*)*mapped - misalign;
196 if (munmap(aligned_ptr, map_size) < 0)
197 WARN("Couldn't unmap the range\n");
198 TRACE("Unmapped (0x%08x - 0x%08x) from %p - %p\n", aligned_offset, aligned_map_end, aligned_ptr, (char*)aligned_ptr + map_size);
199 *mapped = MACHO_NO_MAP;
203 /******************************************************************
204 * macho_map_ranges
206 * Maps two ranges (offset, length in bytes) from a Mach-O file
207 * into memory. If the two ranges overlap, use one mmap so that
208 * the munmap doesn't fragment the mapping.
210 static BOOL macho_map_ranges(const struct macho_file_map* fmap,
211 unsigned offset1, unsigned len1,
212 unsigned offset2, unsigned len2,
213 const void** mapped1, const void** mapped2)
215 unsigned aligned_offset1, aligned_map_end1;
216 unsigned aligned_offset2, aligned_map_end2;
218 TRACE("(%p/%d, 0x%08x, 0x%08x, 0x%08x, 0x%08x, %p, %p)\n", fmap, fmap->fd,
219 offset1, len1, offset2, len2, mapped1, mapped2);
221 macho_calc_range(fmap, offset1, len1, &aligned_offset1, &aligned_map_end1, NULL, NULL);
222 macho_calc_range(fmap, offset2, len2, &aligned_offset2, &aligned_map_end2, NULL, NULL);
224 if (aligned_map_end1 < aligned_offset2 || aligned_map_end2 < aligned_offset1)
226 *mapped1 = macho_map_range(fmap, offset1, len1);
227 if (*mapped1 != MACHO_NO_MAP)
229 *mapped2 = macho_map_range(fmap, offset2, len2);
230 if (*mapped2 == MACHO_NO_MAP)
231 macho_unmap_range(mapped1, fmap, offset1, len1);
234 else
236 if (offset1 < offset2)
238 *mapped1 = macho_map_range(fmap, offset1, offset2 + len2 - offset1);
239 if (*mapped1 != MACHO_NO_MAP)
240 *mapped2 = (const char*)*mapped1 + offset2 - offset1;
242 else
244 *mapped2 = macho_map_range(fmap, offset2, offset1 + len1 - offset2);
245 if (*mapped2 != MACHO_NO_MAP)
246 *mapped1 = (const char*)*mapped2 + offset1 - offset2;
250 TRACE(" => %p, %p\n", *mapped1, *mapped2);
252 return (*mapped1 != MACHO_NO_MAP) && (*mapped2 != MACHO_NO_MAP);
255 /******************************************************************
256 * macho_unmap_ranges
258 * Unmaps two ranges (offset, length in bytes) of a Mach-O file
259 * from memory. Use for ranges which were mapped by
260 * macho_map_ranges.
262 static void macho_unmap_ranges(const struct macho_file_map* fmap,
263 unsigned offset1, unsigned len1,
264 unsigned offset2, unsigned len2,
265 const void** mapped1, const void** mapped2)
267 unsigned aligned_offset1, aligned_map_end1;
268 unsigned aligned_offset2, aligned_map_end2;
270 TRACE("(%p/%d, 0x%08x, 0x%08x, 0x%08x, 0x%08x, %p/%p, %p/%p)\n", fmap, fmap->fd,
271 offset1, len1, offset2, len2, mapped1, *mapped1, mapped2, *mapped2);
273 macho_calc_range(fmap, offset1, len1, &aligned_offset1, &aligned_map_end1, NULL, NULL);
274 macho_calc_range(fmap, offset2, len2, &aligned_offset2, &aligned_map_end2, NULL, NULL);
276 if (aligned_map_end1 < aligned_offset2 || aligned_map_end2 < aligned_offset1)
278 macho_unmap_range(mapped1, fmap, offset1, len1);
279 macho_unmap_range(mapped2, fmap, offset2, len2);
281 else
283 if (offset1 < offset2)
285 macho_unmap_range(mapped1, fmap, offset1, offset2 + len2 - offset1);
286 *mapped2 = MACHO_NO_MAP;
288 else
290 macho_unmap_range(mapped2, fmap, offset2, offset1 + len1 - offset2);
291 *mapped1 = MACHO_NO_MAP;
296 /******************************************************************
297 * macho_map_load_commands
299 * Maps the load commands from a Mach-O file into memory
301 static const struct load_command* macho_map_load_commands(struct macho_file_map* fmap)
303 if (fmap->load_commands == MACHO_NO_MAP)
305 fmap->load_commands = (const struct load_command*) macho_map_range(
306 fmap, sizeof(fmap->mach_header), fmap->mach_header.sizeofcmds);
307 TRACE("Mapped load commands: %p\n", fmap->load_commands);
310 return fmap->load_commands;
313 /******************************************************************
314 * macho_unmap_load_commands
316 * Unmaps the load commands of a Mach-O file from memory
318 static void macho_unmap_load_commands(struct macho_file_map* fmap)
320 if (fmap->load_commands != MACHO_NO_MAP)
322 TRACE("Unmapping load commands: %p\n", fmap->load_commands);
323 macho_unmap_range((const void**)&fmap->load_commands, fmap,
324 sizeof(fmap->mach_header), fmap->mach_header.sizeofcmds);
328 /******************************************************************
329 * macho_next_load_command
331 * Advance to the next load command
333 static const struct load_command* macho_next_load_command(const struct load_command* lc)
335 return (const struct load_command*)((const char*)lc + lc->cmdsize);
338 /******************************************************************
339 * macho_enum_load_commands
341 * Enumerates the load commands for a Mach-O file, selecting by
342 * the command type, calling a callback for each. If the callback
343 * returns <0, that indicates an error. If it returns >0, that means
344 * it's not interested in getting any more load commands.
345 * If this function returns <0, that's an error produced by the
346 * callback. If >=0, that's the count of load commands successfully
347 * processed.
349 static int macho_enum_load_commands(struct macho_file_map* fmap, unsigned cmd,
350 int (*cb)(struct macho_file_map*, const struct load_command*, void*),
351 void* user)
353 const struct load_command* lc;
354 int i;
355 int count = 0;
357 TRACE("(%p/%d, %u, %p, %p)\n", fmap, fmap->fd, cmd, cb, user);
359 if ((lc = macho_map_load_commands(fmap)) == MACHO_NO_MAP) return -1;
361 TRACE("%d total commands\n", fmap->mach_header.ncmds);
363 for (i = 0; i < fmap->mach_header.ncmds; i++, lc = macho_next_load_command(lc))
365 int result;
367 if (cmd && cmd != lc->cmd) continue;
368 count++;
370 result = cb(fmap, lc, user);
371 TRACE("load_command[%d] (%p), cmd %u; callback => %d\n", i, lc, lc->cmd, result);
372 if (result) return (result < 0) ? result : count;
375 return count;
378 /******************************************************************
379 * macho_accum_segs_range
381 * Callback for macho_enum_load_commands. Accumulates the address
382 * range covered by the segments of a Mach-O file. All commands
383 * are expected to be of LC_SEGMENT type.
385 static int macho_accum_segs_range(struct macho_file_map* fmap,
386 const struct load_command* lc, void* user)
388 const struct segment_command* sc = (const struct segment_command*)lc;
389 unsigned tmp, page_mask = sysconf( _SC_PAGESIZE ) - 1;
391 TRACE("(%p/%d, %p, %p) before: 0x%08x - 0x%08x\n", fmap, fmap->fd, lc, user,
392 (unsigned)fmap->segs_start, (unsigned)fmap->segs_size);
393 TRACE("Segment command vm: 0x%08x - 0x%08x\n", (unsigned)sc->vmaddr,
394 (unsigned)sc->vmaddr + sc->vmsize);
396 if (!strncmp(sc->segname, "WINE_", 5))
398 TRACE("Ignoring special Wine segment %s\n", debugstr_an(sc->segname, sizeof(sc->segname)));
399 return 0;
401 if (!strncmp(sc->segname, "__PAGEZERO", 10))
403 TRACE("Ignoring __PAGEZERO segment\n");
404 return 0;
407 /* If this segment starts before previously-known earliest, record
408 * new earliest. */
409 if (sc->vmaddr < fmap->segs_start)
410 fmap->segs_start = sc->vmaddr;
412 /* If this segment extends beyond previously-known furthest, record
413 * new furthest. */
414 tmp = (sc->vmaddr + sc->vmsize + page_mask) & ~page_mask;
415 if (fmap->segs_size < tmp) fmap->segs_size = tmp;
417 TRACE("after: 0x%08x - 0x%08x\n", (unsigned)fmap->segs_start, (unsigned)fmap->segs_size);
419 return 0;
422 /******************************************************************
423 * macho_map_file
425 * Maps a Mach-O file into memory (and checks it's a real Mach-O file)
427 static BOOL macho_map_file(const WCHAR* filenameW, struct macho_file_map* fmap)
429 struct fat_header fat_header;
430 struct stat statbuf;
431 int i;
432 char* filename;
433 unsigned len;
434 BOOL ret = FALSE;
436 TRACE("(%s, %p)\n", debugstr_w(filenameW), fmap);
438 fmap->fd = -1;
439 fmap->load_commands = MACHO_NO_MAP;
440 RtlInitializeBitMap(&fmap->sect_is_code, fmap->sect_is_code_buff, MAX_SECT + 1);
442 len = WideCharToMultiByte(CP_UNIXCP, 0, filenameW, -1, NULL, 0, NULL, NULL);
443 if (!(filename = HeapAlloc(GetProcessHeap(), 0, len)))
445 WARN("failed to allocate filename buffer\n");
446 return FALSE;
448 WideCharToMultiByte(CP_UNIXCP, 0, filenameW, -1, filename, len, NULL, NULL);
450 /* check that the file exists */
451 if (stat(filename, &statbuf) == -1 || S_ISDIR(statbuf.st_mode))
453 TRACE("stat() failed or %s is directory: %s\n", debugstr_a(filename), strerror(errno));
454 goto done;
457 /* Now open the file, so that we can mmap() it. */
458 if ((fmap->fd = open(filename, O_RDONLY)) == -1)
460 TRACE("failed to open file %s: %d\n", debugstr_a(filename), errno);
461 goto done;
464 if (read(fmap->fd, &fat_header, sizeof(fat_header)) != sizeof(fat_header))
466 TRACE("failed to read fat header: %d\n", errno);
467 goto done;
469 TRACE("... got possible fat header\n");
471 /* Fat header is always in big-endian order. */
472 if (swap_ulong_be_to_host(fat_header.magic) == FAT_MAGIC)
474 int narch = swap_ulong_be_to_host(fat_header.nfat_arch);
475 for (i = 0; i < narch; i++)
477 struct fat_arch fat_arch;
478 if (read(fmap->fd, &fat_arch, sizeof(fat_arch)) != sizeof(fat_arch))
479 goto done;
480 if (swap_ulong_be_to_host(fat_arch.cputype) == CPU_TYPE_X86)
482 fmap->arch_offset = swap_ulong_be_to_host(fat_arch.offset);
483 fmap->arch_size = swap_ulong_be_to_host(fat_arch.size);
484 break;
487 if (i >= narch) goto done;
488 TRACE("... found x86 arch\n");
490 else
492 fmap->arch_offset = 0;
493 fmap->arch_size = statbuf.st_size;
494 TRACE("... not a fat header\n");
497 /* Individual architecture (standalone or within a fat file) is in its native byte order. */
498 lseek(fmap->fd, fmap->arch_offset, SEEK_SET);
499 if (read(fmap->fd, &fmap->mach_header, sizeof(fmap->mach_header)) != sizeof(fmap->mach_header))
500 goto done;
501 TRACE("... got possible Mach header\n");
502 /* and check for a Mach-O header */
503 if (fmap->mach_header.magic != MH_MAGIC ||
504 fmap->mach_header.cputype != CPU_TYPE_X86) goto done;
505 /* Make sure the file type is one of the ones we expect. */
506 switch (fmap->mach_header.filetype)
508 case MH_EXECUTE:
509 case MH_DYLIB:
510 case MH_DYLINKER:
511 case MH_BUNDLE:
512 break;
513 default:
514 goto done;
516 TRACE("... verified Mach x86 header\n");
518 fmap->segs_size = 0;
519 fmap->segs_start = ~0L;
521 if (macho_enum_load_commands(fmap, LC_SEGMENT, macho_accum_segs_range, NULL) < 0)
522 goto done;
524 fmap->segs_size -= fmap->segs_start;
525 TRACE("segs_start: 0x%08x, segs_size: 0x%08x\n", (unsigned)fmap->segs_start,
526 (unsigned)fmap->segs_size);
528 ret = TRUE;
529 done:
530 if (!ret)
531 macho_unmap_file(fmap);
532 HeapFree(GetProcessHeap(), 0, filename);
533 return ret;
536 /******************************************************************
537 * macho_unmap_file
539 * Unmaps a Mach-O file from memory (previously mapped with macho_map_file)
541 static void macho_unmap_file(struct macho_file_map* fmap)
543 TRACE("(%p/%d)\n", fmap, fmap->fd);
544 if (fmap->fd != -1)
546 macho_unmap_load_commands(fmap);
547 close(fmap->fd);
548 fmap->fd = -1;
552 /******************************************************************
553 * macho_fill_sect_is_code
555 * Callback for macho_enum_load_commands. Determines which segments
556 * of a Mach-O file contain code. All commands are expected to be
557 * of LC_SEGMENT type.
559 static int macho_fill_sect_is_code(struct macho_file_map* fmap,
560 const struct load_command* lc, void* user)
562 const struct segment_command* sc = (const struct segment_command*)lc;
563 const struct section* sections;
564 int* cursect = user;
565 int i;
567 TRACE("(%p/%d, %p, %p/%d) scanning %u sections\n", fmap, fmap->fd, lc,
568 cursect, *cursect, sc->nsects);
570 sections = (const struct section*)(sc + 1);
571 for (i = 0; i < sc->nsects; i++)
573 if (*cursect > MAX_SECT) return -1;
574 (*cursect)++;
576 if (!(sections[i].flags & SECTION_TYPE) &&
577 (sections[i].flags & (S_ATTR_PURE_INSTRUCTIONS|S_ATTR_SOME_INSTRUCTIONS)))
578 RtlSetBits(&fmap->sect_is_code, *cursect, 1);
579 else
580 RtlClearBits(&fmap->sect_is_code, *cursect, 1);
581 TRACE("Section %d (%d of this segment) is%s code\n", *cursect, i,
582 (RtlAreBitsSet(&fmap->sect_is_code, *cursect, 1) ? "" : " not"));
585 return 0;
588 /******************************************************************
589 * macho_sect_is_code
591 * Checks if a section, identified by sectidx which is a 1-based
592 * index into the sections of all segments, in order of load
593 * commands, contains code.
595 static BOOL macho_sect_is_code(struct macho_file_map* fmap, unsigned char sectidx)
597 TRACE("(%p/%d, %u)\n", fmap, fmap->fd, sectidx);
599 if (!RtlAreBitsSet(&fmap->sect_is_code, 0, 1))
601 int cursect = 0;
602 if (macho_enum_load_commands(fmap, LC_SEGMENT, macho_fill_sect_is_code, &cursect) < 0)
603 WARN("Couldn't load sect_is_code map\n");
604 RtlSetBits(&fmap->sect_is_code, 0, 1);
607 return RtlAreBitsSet(&fmap->sect_is_code, sectidx, 1);
610 struct symtab_elt
612 struct hash_table_elt ht_elt;
613 struct symt_compiland* compiland;
614 unsigned long addr;
615 unsigned char is_code:1,
616 is_public:1,
617 is_global:1,
618 used:1;
621 struct macho_debug_info
623 struct macho_file_map* fmap;
624 struct module* module;
625 struct pool pool;
626 struct hash_table ht_symtab;
629 /******************************************************************
630 * macho_stabs_def_cb
632 * Callback for stabs_parse. Collect symbol definitions.
634 static void macho_stabs_def_cb(struct module* module, unsigned long load_offset,
635 const char* name, unsigned long offset,
636 BOOL is_public, BOOL is_global, unsigned char sectidx,
637 struct symt_compiland* compiland, void* user)
639 struct macho_debug_info* mdi = user;
640 struct symtab_elt* ste;
642 TRACE("(%p, 0x%08lx, %s, 0x%08lx, %d, %d, %u, %p, %p/%p/%d)\n", module, load_offset,
643 debugstr_a(name), offset, is_public, is_global, sectidx,
644 compiland, mdi, mdi->fmap, mdi->fmap->fd);
646 /* Defer the creation of new non-debugging symbols until after we've
647 * finished parsing the stabs. */
648 ste = pool_alloc(&mdi->pool, sizeof(*ste));
649 ste->ht_elt.name = pool_strdup(&mdi->pool, name);
650 ste->compiland = compiland;
651 ste->addr = load_offset + offset;
652 ste->is_code = !!macho_sect_is_code(mdi->fmap, sectidx);
653 ste->is_public = !!is_public;
654 ste->is_global = !!is_global;
655 ste->used = 0;
656 hash_table_add(&mdi->ht_symtab, &ste->ht_elt);
659 /******************************************************************
660 * macho_parse_symtab
662 * Callback for macho_enum_load_commands. Processes the LC_SYMTAB
663 * load commands from the Mach-O file.
665 static int macho_parse_symtab(struct macho_file_map* fmap,
666 const struct load_command* lc, void* user)
668 const struct symtab_command* sc = (const struct symtab_command*)lc;
669 struct macho_debug_info* mdi = user;
670 const struct nlist* stab;
671 const char* stabstr;
672 int ret = 0;
674 TRACE("(%p/%d, %p, %p) %u syms at 0x%08x, strings 0x%08x - 0x%08x\n", fmap, fmap->fd, lc,
675 user, sc->nsyms, sc->symoff, sc->stroff, sc->stroff + sc->strsize);
677 if (!macho_map_ranges(fmap, sc->symoff, sc->nsyms * sizeof(struct nlist),
678 sc->stroff, sc->strsize, (const void**)&stab, (const void**)&stabstr))
679 return 0;
681 if (!stabs_parse(mdi->module,
682 mdi->module->format_info[DFI_MACHO]->u.macho_info->load_addr - fmap->segs_start,
683 stab, sc->nsyms * sizeof(struct nlist),
684 stabstr, sc->strsize, macho_stabs_def_cb, mdi))
685 ret = -1;
687 macho_unmap_ranges(fmap, sc->symoff, sc->nsyms * sizeof(struct nlist),
688 sc->stroff, sc->strsize, (const void**)&stab, (const void**)&stabstr);
690 return ret;
693 /******************************************************************
694 * macho_finish_stabs
696 * Integrate the non-debugging symbols we've gathered into the
697 * symbols that were generated during stabs parsing.
699 static void macho_finish_stabs(struct module* module, struct hash_table* ht_symtab)
701 struct hash_table_iter hti_ours;
702 struct symtab_elt* ste;
703 BOOL adjusted = FALSE;
705 TRACE("(%p, %p)\n", module, ht_symtab);
707 /* For each of our non-debugging symbols, see if it can provide some
708 * missing details to one of the module's known symbols. */
709 hash_table_iter_init(ht_symtab, &hti_ours, NULL);
710 while ((ste = hash_table_iter_up(&hti_ours)))
712 struct hash_table_iter hti_modules;
713 void* ptr;
714 struct symt_ht* sym;
715 struct symt_function* func;
716 struct symt_data* data;
718 hash_table_iter_init(&module->ht_symbols, &hti_modules, ste->ht_elt.name);
719 while ((ptr = hash_table_iter_up(&hti_modules)))
721 sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
723 if (strcmp(sym->hash_elt.name, ste->ht_elt.name))
724 continue;
726 switch (sym->symt.tag)
728 case SymTagFunction:
729 func = (struct symt_function*)sym;
730 if (func->address == module->format_info[DFI_MACHO]->u.macho_info->load_addr)
732 TRACE("Adjusting function %p/%s!%s from 0x%08lx to 0x%08lx\n", func,
733 debugstr_w(module->module.ModuleName), sym->hash_elt.name,
734 func->address, ste->addr);
735 func->address = ste->addr;
736 adjusted = TRUE;
738 if (func->address == ste->addr)
739 ste->used = 1;
740 break;
741 case SymTagData:
742 data = (struct symt_data*)sym;
743 switch (data->kind)
745 case DataIsGlobal:
746 case DataIsFileStatic:
747 if (data->u.var.offset == module->format_info[DFI_MACHO]->u.macho_info->load_addr)
749 TRACE("Adjusting data symbol %p/%s!%s from 0x%08lx to 0x%08lx\n",
750 data, debugstr_w(module->module.ModuleName), sym->hash_elt.name,
751 data->u.var.offset, ste->addr);
752 data->u.var.offset = ste->addr;
753 adjusted = TRUE;
755 if (data->u.var.offset == ste->addr)
757 enum DataKind new_kind;
759 new_kind = ste->is_global ? DataIsGlobal : DataIsFileStatic;
760 if (data->kind != new_kind)
762 WARN("Changing kind for %p/%s!%s from %d to %d\n", sym,
763 debugstr_w(module->module.ModuleName), sym->hash_elt.name,
764 (int)data->kind, (int)new_kind);
765 data->kind = new_kind;
766 adjusted = TRUE;
768 ste->used = 1;
770 break;
771 default:;
773 break;
774 default:
775 TRACE("Ignoring tag %u\n", sym->symt.tag);
776 break;
781 if (adjusted)
783 /* since we may have changed some addresses, mark the module to be resorted */
784 module->sortlist_valid = FALSE;
787 /* Mark any of our non-debugging symbols which fall on an already-used
788 * address as "used". This allows us to skip them in the next loop,
789 * below. We do this in separate loops because symt_new_* marks the
790 * list as needing sorting and symt_find_nearest sorts if needed,
791 * causing thrashing. */
792 if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY))
794 hash_table_iter_init(ht_symtab, &hti_ours, NULL);
795 while ((ste = hash_table_iter_up(&hti_ours)))
797 struct symt_ht* sym;
798 ULONG64 addr;
800 if (ste->used) continue;
802 sym = symt_find_nearest(module, ste->addr);
803 if (sym)
804 symt_get_address(&sym->symt, &addr);
805 if (sym && ste->addr == addr)
807 ULONG64 size = 0;
808 DWORD kind = -1;
810 ste->used = 1;
812 /* If neither symbol has a correct size (ours never does), we
813 * consider them both to be markers. No warning is needed in
814 * that case.
815 * Also, we check that we don't have two symbols, one local, the other
816 * global, which is legal.
818 symt_get_info(module, &sym->symt, TI_GET_LENGTH, &size);
819 symt_get_info(module, &sym->symt, TI_GET_DATAKIND, &kind);
820 if (size && kind == (ste->is_global ? DataIsGlobal : DataIsFileStatic))
821 FIXME("Duplicate in %s: %s<%08lx> %s<%s-%s>\n",
822 debugstr_w(module->module.ModuleName),
823 ste->ht_elt.name, ste->addr,
824 sym->hash_elt.name,
825 wine_dbgstr_longlong(addr), wine_dbgstr_longlong(size));
830 /* For any of our remaining non-debugging symbols which have no match
831 * among the module's known symbols, add them as new symbols. */
832 hash_table_iter_init(ht_symtab, &hti_ours, NULL);
833 while ((ste = hash_table_iter_up(&hti_ours)))
835 if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY) && !ste->used)
837 if (ste->is_code)
839 symt_new_function(module, ste->compiland, ste->ht_elt.name,
840 ste->addr, 0, NULL);
842 else
844 struct location loc;
846 loc.kind = loc_absolute;
847 loc.reg = 0;
848 loc.offset = ste->addr;
849 symt_new_global_variable(module, ste->compiland, ste->ht_elt.name,
850 !ste->is_global, loc, 0, NULL);
853 ste->used = 1;
856 if (ste->is_public && !(dbghelp_options & SYMOPT_NO_PUBLICS))
858 symt_new_public(module, ste->compiland, ste->ht_elt.name, ste->addr, 0);
863 /******************************************************************
864 * macho_load_debug_info_from_map
866 * Loads the symbolic information from a Mach-O module.
867 * Returns
868 * FALSE if the file doesn't contain symbolic info (or this info
869 * cannot be read or parsed)
870 * TRUE on success
872 static BOOL macho_load_debug_info_from_map(struct module* module,
873 struct macho_file_map* fmap)
875 BOOL ret = FALSE;
876 struct macho_debug_info mdi;
877 int result;
879 TRACE("(%p, %p/%d)\n", module, fmap, fmap->fd);
881 module->module.SymType = SymExport;
883 mdi.fmap = fmap;
884 mdi.module = module;
885 pool_init(&mdi.pool, 65536);
886 hash_table_init(&mdi.pool, &mdi.ht_symtab, 256);
887 result = macho_enum_load_commands(fmap, LC_SYMTAB, macho_parse_symtab, &mdi);
888 if (result > 0)
889 ret = TRUE;
890 else if (result < 0)
891 WARN("Couldn't correctly read stabs\n");
893 macho_finish_stabs(module, &mdi.ht_symtab);
895 pool_destroy(&mdi.pool);
896 return ret;
899 /******************************************************************
900 * macho_load_debug_info
902 * Loads Mach-O debugging information from the module image file.
904 BOOL macho_load_debug_info(struct module* module, struct macho_file_map* fmap)
906 BOOL ret = TRUE;
907 struct macho_file_map my_fmap;
909 TRACE("(%p, %p/%d)\n", module, fmap, fmap ? fmap->fd : -1);
911 if (module->type != DMT_MACHO || !module->format_info[DFI_MACHO]->u.macho_info)
913 ERR("Bad Mach-O module '%s'\n", debugstr_w(module->module.LoadedImageName));
914 return FALSE;
917 if (!fmap)
919 fmap = &my_fmap;
920 ret = macho_map_file(module->module.LoadedImageName, fmap);
922 if (ret)
923 ret = macho_load_debug_info_from_map(module, fmap);
925 if (fmap == &my_fmap) macho_unmap_file(fmap);
926 return ret;
929 /******************************************************************
930 * macho_fetch_file_info
932 * Gathers some more information for a Mach-O module from a given file
934 BOOL macho_fetch_file_info(const WCHAR* name, DWORD_PTR* base,
935 DWORD* size, DWORD* checksum)
937 struct macho_file_map fmap;
939 TRACE("(%s, %p, %p, %p)\n", debugstr_w(name), base, size, checksum);
941 if (!macho_map_file(name, &fmap)) return FALSE;
942 if (base) *base = fmap.segs_start;
943 *size = fmap.segs_size;
944 *checksum = calc_crc32(fmap.fd);
945 macho_unmap_file(&fmap);
946 return TRUE;
949 /******************************************************************
950 * macho_load_file
952 * Loads the information for Mach-O module stored in 'filename'.
953 * The module has been loaded at 'load_addr' address.
954 * returns
955 * FALSE if the file cannot be found/opened or if the file doesn't
956 * contain symbolic info (or this info cannot be read or parsed)
957 * TRUE on success
959 static BOOL macho_load_file(struct process* pcs, const WCHAR* filename,
960 unsigned long load_addr, struct macho_info* macho_info)
962 BOOL ret = TRUE;
963 struct macho_file_map fmap;
965 TRACE("(%p/%p, %s, 0x%08lx, %p/0x%08x)\n", pcs, pcs->handle, debugstr_w(filename),
966 load_addr, macho_info, macho_info->flags);
968 if (!macho_map_file(filename, &fmap)) return FALSE;
970 /* Find the dynamic loader's table of images loaded into the process.
972 if (macho_info->flags & MACHO_INFO_DEBUG_HEADER)
974 PROCESS_BASIC_INFORMATION pbi;
975 NTSTATUS status;
977 ret = FALSE;
979 /* Get address of PEB */
980 status = NtQueryInformationProcess(pcs->handle, ProcessBasicInformation,
981 &pbi, sizeof(pbi), NULL);
982 if (status == STATUS_SUCCESS)
984 ULONG dyld_image_info;
986 /* Read dyld image info address from PEB */
987 if (ReadProcessMemory(pcs->handle, &pbi.PebBaseAddress->Reserved,
988 &dyld_image_info, sizeof(dyld_image_info), NULL))
990 TRACE("got dyld_image_info 0x%08x from PEB %p MacDyldImageInfo %p\n",
991 dyld_image_info, pbi.PebBaseAddress, &pbi.PebBaseAddress->Reserved);
992 macho_info->dbg_hdr_addr = dyld_image_info;
993 ret = TRUE;
997 if (!ret)
999 static void* dyld_all_image_infos_addr;
1001 /* Our next best guess is that dyld was loaded at its base address
1002 and we can find the dyld image infos address by looking up its symbol. */
1003 if (!dyld_all_image_infos_addr)
1005 struct nlist nl[2];
1006 memset(nl, 0, sizeof(nl));
1007 nl[0].n_un.n_name = (char*)"_dyld_all_image_infos";
1008 if (!nlist("/usr/lib/dyld", nl))
1009 dyld_all_image_infos_addr = (void*)nl[0].n_value;
1012 if (dyld_all_image_infos_addr)
1014 TRACE("got dyld_image_info %p from /usr/lib/dyld symbol table\n",
1015 dyld_all_image_infos_addr);
1016 macho_info->dbg_hdr_addr = (unsigned long)dyld_all_image_infos_addr;
1017 ret = TRUE;
1022 if (macho_info->flags & MACHO_INFO_MODULE)
1024 struct macho_module_info *macho_module_info;
1025 struct module_format* modfmt =
1026 HeapAlloc(GetProcessHeap(), 0, sizeof(struct module_format) + sizeof(struct macho_module_info));
1027 if (!modfmt) goto leave;
1028 if (!load_addr)
1029 load_addr = fmap.segs_start;
1030 macho_info->module = module_new(pcs, filename, DMT_MACHO, FALSE, load_addr,
1031 fmap.segs_size, 0, calc_crc32(fmap.fd));
1032 if (!macho_info->module)
1034 HeapFree(GetProcessHeap(), 0, modfmt);
1035 goto leave;
1037 macho_module_info = (void*)(modfmt + 1);
1038 macho_info->module->format_info[DFI_MACHO] = modfmt;
1040 modfmt->module = macho_info->module;
1041 modfmt->remove = NULL;
1042 modfmt->loc_compute = NULL;
1043 modfmt->u.macho_info = macho_module_info;
1045 macho_module_info->load_addr = load_addr;
1047 if (dbghelp_options & SYMOPT_DEFERRED_LOADS)
1048 macho_info->module->module.SymType = SymDeferred;
1049 else if (!macho_load_debug_info(macho_info->module, &fmap))
1050 ret = FALSE;
1052 macho_info->module->format_info[DFI_MACHO]->u.macho_info->in_use = 1;
1053 macho_info->module->format_info[DFI_MACHO]->u.macho_info->is_loader = 0;
1054 TRACE("module = %p\n", macho_info->module);
1057 if (macho_info->flags & MACHO_INFO_NAME)
1059 WCHAR* ptr;
1060 ptr = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
1061 if (ptr)
1063 strcpyW(ptr, filename);
1064 macho_info->module_name = ptr;
1066 else ret = FALSE;
1067 TRACE("module_name = %p %s\n", macho_info->module_name, debugstr_w(macho_info->module_name));
1069 leave:
1070 macho_unmap_file(&fmap);
1072 TRACE(" => %d\n", ret);
1073 return ret;
1076 /******************************************************************
1077 * macho_load_file_from_path
1078 * Tries to load a Mach-O file from a set of paths (separated by ':')
1080 static BOOL macho_load_file_from_path(struct process* pcs,
1081 const WCHAR* filename,
1082 unsigned long load_addr,
1083 const char* path,
1084 struct macho_info* macho_info)
1086 BOOL ret = FALSE;
1087 WCHAR *s, *t, *fn;
1088 WCHAR* pathW = NULL;
1089 unsigned len;
1091 TRACE("(%p/%p, %s, 0x%08lx, %s, %p)\n", pcs, pcs->handle, debugstr_w(filename), load_addr,
1092 debugstr_a(path), macho_info);
1094 if (!path) return FALSE;
1096 len = MultiByteToWideChar(CP_UNIXCP, 0, path, -1, NULL, 0);
1097 pathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1098 if (!pathW) return FALSE;
1099 MultiByteToWideChar(CP_UNIXCP, 0, path, -1, pathW, len);
1101 for (s = pathW; s && *s; s = (t) ? (t+1) : NULL)
1103 t = strchrW(s, ':');
1104 if (t) *t = '\0';
1105 fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1 + lstrlenW(s) + 1) * sizeof(WCHAR));
1106 if (!fn) break;
1107 strcpyW(fn, s);
1108 strcatW(fn, S_SlashW);
1109 strcatW(fn, filename);
1110 ret = macho_load_file(pcs, fn, load_addr, macho_info);
1111 HeapFree(GetProcessHeap(), 0, fn);
1112 if (ret) break;
1113 s = (t) ? (t+1) : NULL;
1116 TRACE(" => %d\n", ret);
1117 HeapFree(GetProcessHeap(), 0, pathW);
1118 return ret;
1121 /******************************************************************
1122 * macho_load_file_from_dll_path
1124 * Tries to load a Mach-O file from the dll path
1126 static BOOL macho_load_file_from_dll_path(struct process* pcs,
1127 const WCHAR* filename,
1128 unsigned long load_addr,
1129 struct macho_info* macho_info)
1131 BOOL ret = FALSE;
1132 unsigned int index = 0;
1133 const char *path;
1135 TRACE("(%p/%p, %s, 0x%08lx, %p)\n", pcs, pcs->handle, debugstr_w(filename), load_addr,
1136 macho_info);
1138 while (!ret && (path = wine_dll_enum_load_path( index++ )))
1140 WCHAR *name;
1141 unsigned len;
1143 len = MultiByteToWideChar(CP_UNIXCP, 0, path, -1, NULL, 0);
1145 name = HeapAlloc( GetProcessHeap(), 0,
1146 (len + lstrlenW(filename) + 2) * sizeof(WCHAR) );
1148 if (!name) break;
1149 MultiByteToWideChar(CP_UNIXCP, 0, path, -1, name, len);
1150 strcatW( name, S_SlashW );
1151 strcatW( name, filename );
1152 ret = macho_load_file(pcs, name, load_addr, macho_info);
1153 HeapFree( GetProcessHeap(), 0, name );
1155 TRACE(" => %d\n", ret);
1156 return ret;
1159 /******************************************************************
1160 * macho_search_and_load_file
1162 * Lookup a file in standard Mach-O locations, and if found, load it
1164 static BOOL macho_search_and_load_file(struct process* pcs, const WCHAR* filename,
1165 unsigned long load_addr,
1166 struct macho_info* macho_info)
1168 BOOL ret = FALSE;
1169 struct module* module;
1170 static const WCHAR S_libstdcPPW[] = {'l','i','b','s','t','d','c','+','+','\0'};
1171 const WCHAR* p;
1173 TRACE("(%p/%p, %s, 0x%08lx, %p)\n", pcs, pcs->handle, debugstr_w(filename), load_addr,
1174 macho_info);
1176 if (filename == NULL || *filename == '\0') return FALSE;
1177 if ((module = module_is_already_loaded(pcs, filename)))
1179 macho_info->module = module;
1180 module->format_info[DFI_MACHO]->u.macho_info->in_use = 1;
1181 return module->module.SymType;
1184 if (strstrW(filename, S_libstdcPPW)) return FALSE; /* We know we can't do it */
1186 /* If has no directories, try LD_LIBRARY_PATH first. */
1187 if (!strchrW(filename, '/'))
1189 ret = macho_load_file_from_path(pcs, filename, load_addr,
1190 getenv("PATH"), macho_info);
1192 /* Try DYLD_LIBRARY_PATH, with just the filename (no directories). */
1193 if (!ret)
1195 if ((p = strrchrW(filename, '/'))) p++;
1196 else p = filename;
1197 ret = macho_load_file_from_path(pcs, p, load_addr,
1198 getenv("DYLD_LIBRARY_PATH"), macho_info);
1200 /* Try the path as given. */
1201 if (!ret)
1202 ret = macho_load_file(pcs, filename, load_addr, macho_info);
1203 /* Try DYLD_FALLBACK_LIBRARY_PATH, with just the filename (no directories). */
1204 if (!ret)
1206 ret = macho_load_file_from_path(pcs, p, load_addr,
1207 getenv("DYLD_FALLBACK_LIBRARY_PATH"), macho_info);
1209 if (!ret && !strchrW(filename, '/'))
1210 ret = macho_load_file_from_dll_path(pcs, filename, load_addr, macho_info);
1212 return ret;
1215 /******************************************************************
1216 * macho_enum_modules_internal
1218 * Enumerate Mach-O modules from a running process
1220 static BOOL macho_enum_modules_internal(const struct process* pcs,
1221 const WCHAR* main_name,
1222 enum_modules_cb cb, void* user)
1224 struct dyld_all_image_infos image_infos;
1225 struct dyld_image_info* info_array = NULL;
1226 unsigned long len;
1227 int i;
1228 char bufstr[256];
1229 WCHAR bufstrW[MAX_PATH];
1230 BOOL ret = FALSE;
1232 TRACE("(%p/%p, %s, %p, %p)\n", pcs, pcs->handle, debugstr_w(main_name), cb,
1233 user);
1235 if (!pcs->dbg_hdr_addr ||
1236 !ReadProcessMemory(pcs->handle, (void*)pcs->dbg_hdr_addr,
1237 &image_infos, sizeof(image_infos), NULL) ||
1238 !image_infos.infoArray)
1239 goto done;
1240 TRACE("Process has %u image infos at %p\n", image_infos.infoArrayCount, image_infos.infoArray);
1242 len = image_infos.infoArrayCount * sizeof(info_array[0]);
1243 info_array = HeapAlloc(GetProcessHeap(), 0, len);
1244 if (!info_array ||
1245 !ReadProcessMemory(pcs->handle, image_infos.infoArray,
1246 info_array, len, NULL))
1247 goto done;
1248 TRACE("... read image infos\n");
1250 for (i = 0; i < image_infos.infoArrayCount; i++)
1252 if (info_array[i].imageFilePath != NULL &&
1253 ReadProcessMemory(pcs->handle, info_array[i].imageFilePath, bufstr, sizeof(bufstr), NULL))
1255 bufstr[sizeof(bufstr) - 1] = '\0';
1256 TRACE("[%d] image file %s\n", i, debugstr_a(bufstr));
1257 MultiByteToWideChar(CP_UNIXCP, 0, bufstr, -1, bufstrW, sizeof(bufstrW) / sizeof(WCHAR));
1258 if (main_name && !bufstrW[0]) strcpyW(bufstrW, main_name);
1259 if (!cb(bufstrW, (unsigned long)info_array[i].imageLoadAddress, user)) break;
1263 ret = TRUE;
1264 done:
1265 HeapFree(GetProcessHeap(), 0, info_array);
1266 return ret;
1269 struct macho_sync
1271 struct process* pcs;
1272 struct macho_info macho_info;
1275 static BOOL macho_enum_sync_cb(const WCHAR* name, unsigned long addr, void* user)
1277 struct macho_sync* ms = user;
1279 TRACE("(%s, 0x%08lx, %p)\n", debugstr_w(name), addr, user);
1280 macho_search_and_load_file(ms->pcs, name, addr, &ms->macho_info);
1281 return TRUE;
1284 /******************************************************************
1285 * macho_synchronize_module_list
1287 * Rescans the debuggee's modules list and synchronizes it with
1288 * the one from 'pcs', ie:
1289 * - if a module is in debuggee and not in pcs, it's loaded into pcs
1290 * - if a module is in pcs and not in debuggee, it's unloaded from pcs
1292 BOOL macho_synchronize_module_list(struct process* pcs)
1294 struct module* module;
1295 struct macho_sync ms;
1297 TRACE("(%p/%p)\n", pcs, pcs->handle);
1299 for (module = pcs->lmodules; module; module = module->next)
1301 if (module->type == DMT_MACHO && !module->is_virtual)
1302 module->format_info[DFI_MACHO]->u.macho_info->in_use = 0;
1305 ms.pcs = pcs;
1306 ms.macho_info.flags = MACHO_INFO_MODULE;
1307 if (!macho_enum_modules_internal(pcs, NULL, macho_enum_sync_cb, &ms))
1308 return FALSE;
1310 module = pcs->lmodules;
1311 while (module)
1313 if (module->type == DMT_MACHO && !module->is_virtual &&
1314 !module->format_info[DFI_MACHO]->u.macho_info->in_use &&
1315 !module->format_info[DFI_MACHO]->u.macho_info->is_loader)
1317 module_remove(pcs, module);
1318 /* restart all over */
1319 module = pcs->lmodules;
1321 else module = module->next;
1323 return TRUE;
1326 /******************************************************************
1327 * macho_search_loader
1329 * Lookup in a running Mach-O process the loader, and sets its Mach-O link
1330 * address (for accessing the list of loaded images) in pcs.
1331 * If flags is MACHO_INFO_MODULE, the module for the loader is also
1332 * added as a module into pcs.
1334 static BOOL macho_search_loader(struct process* pcs, struct macho_info* macho_info)
1336 return macho_search_and_load_file(pcs, get_wine_loader_name(), 0, macho_info);
1339 /******************************************************************
1340 * macho_read_wine_loader_dbg_info
1342 * Try to find a decent wine executable which could have loaded the debuggee
1344 BOOL macho_read_wine_loader_dbg_info(struct process* pcs)
1346 struct macho_info macho_info;
1348 TRACE("(%p/%p)\n", pcs, pcs->handle);
1349 macho_info.flags = MACHO_INFO_DEBUG_HEADER | MACHO_INFO_MODULE;
1350 if (!macho_search_loader(pcs, &macho_info)) return FALSE;
1351 macho_info.module->format_info[DFI_MACHO]->u.macho_info->is_loader = 1;
1352 module_set_module(macho_info.module, S_WineLoaderW);
1353 return (pcs->dbg_hdr_addr = macho_info.dbg_hdr_addr) != 0;
1356 /******************************************************************
1357 * macho_enum_modules
1359 * Enumerates the Mach-O loaded modules from a running target (hProc)
1360 * This function doesn't require that someone has called SymInitialize
1361 * on this very process.
1363 BOOL macho_enum_modules(HANDLE hProc, enum_modules_cb cb, void* user)
1365 struct process pcs;
1366 struct macho_info macho_info;
1367 BOOL ret;
1369 TRACE("(%p, %p, %p)\n", hProc, cb, user);
1370 memset(&pcs, 0, sizeof(pcs));
1371 pcs.handle = hProc;
1372 macho_info.flags = MACHO_INFO_DEBUG_HEADER | MACHO_INFO_NAME;
1373 if (!macho_search_loader(&pcs, &macho_info)) return FALSE;
1374 pcs.dbg_hdr_addr = macho_info.dbg_hdr_addr;
1375 ret = macho_enum_modules_internal(&pcs, macho_info.module_name, cb, user);
1376 HeapFree(GetProcessHeap(), 0, (char*)macho_info.module_name);
1377 return ret;
1380 struct macho_load
1382 struct process* pcs;
1383 struct macho_info macho_info;
1384 const WCHAR* name;
1385 BOOL ret;
1388 /******************************************************************
1389 * macho_load_cb
1391 * Callback for macho_load_module, used to walk the list of loaded
1392 * modules.
1394 static BOOL macho_load_cb(const WCHAR* name, unsigned long addr, void* user)
1396 struct macho_load* ml = user;
1397 const WCHAR* p;
1399 TRACE("(%s, 0x%08lx, %p)\n", debugstr_w(name), addr, user);
1401 /* memcmp is needed for matches when bufstr contains also version information
1402 * ml->name: libc.so, name: libc.so.6.0
1404 p = strrchrW(name, '/');
1405 if (!p++) p = name;
1406 if (!memcmp(p, ml->name, lstrlenW(ml->name) * sizeof(WCHAR)))
1408 ml->ret = macho_search_and_load_file(ml->pcs, name, addr, &ml->macho_info);
1409 return FALSE;
1411 return TRUE;
1414 /******************************************************************
1415 * macho_load_module
1417 * Loads a Mach-O module and stores it in process' module list.
1418 * Also, find module real name and load address from
1419 * the real loaded modules list in pcs address space.
1421 struct module* macho_load_module(struct process* pcs, const WCHAR* name, unsigned long addr)
1423 struct macho_load ml;
1425 TRACE("(%p/%p, %s, 0x%08lx)\n", pcs, pcs->handle, debugstr_w(name), addr);
1427 ml.macho_info.flags = MACHO_INFO_MODULE;
1428 ml.ret = FALSE;
1430 if (pcs->dbg_hdr_addr) /* we're debugging a live target */
1432 ml.pcs = pcs;
1433 /* do only the lookup from the filename, not the path (as we lookup module
1434 * name in the process' loaded module list)
1436 ml.name = strrchrW(name, '/');
1437 if (!ml.name++) ml.name = name;
1438 ml.ret = FALSE;
1440 if (!macho_enum_modules_internal(pcs, NULL, macho_load_cb, &ml))
1441 return NULL;
1443 else if (addr)
1445 ml.name = name;
1446 ml.ret = macho_search_and_load_file(pcs, ml.name, addr, &ml.macho_info);
1448 if (!ml.ret) return NULL;
1449 assert(ml.macho_info.module);
1450 return ml.macho_info.module;
1453 #else /* HAVE_MACH_O_LOADER_H */
1455 BOOL macho_synchronize_module_list(struct process* pcs)
1457 return FALSE;
1460 BOOL macho_fetch_file_info(const WCHAR* name, DWORD_PTR* base,
1461 DWORD* size, DWORD* checksum)
1463 return FALSE;
1466 BOOL macho_read_wine_loader_dbg_info(struct process* pcs)
1468 return FALSE;
1471 BOOL macho_enum_modules(HANDLE hProc, enum_modules_cb cb, void* user)
1473 return FALSE;
1476 struct module* macho_load_module(struct process* pcs, const WCHAR* name, unsigned long addr)
1478 return NULL;
1481 BOOL macho_load_debug_info(struct module* module, struct macho_file_map* fmap)
1483 return FALSE;
1485 #endif /* HAVE_MACH_O_LOADER_H */