dbghelp: Ignore Mach-O segments other than __TEXT for images in dyld's shared cache.
Images in the shared cache have their segments mapped non-contiguously. We don't know how to find where the non-__TEXT segments actually are. Also, the code assumes that the image is mapped contiguously in various places. So, we just ignore those segments. Treating the non-contiguous segments as though they were contiguous caused their apparent memory ranges to overlap, leading to addresses being attributed to the wrong module, let alone symbol.
This commit is contained in:
parent
be68890cac
commit
49f0bf6f0b
|
@ -570,7 +570,7 @@ extern int elf_is_in_thunk_area(unsigned long addr, const struct elf_th
|
|||
|
||||
/* macho_module.c */
|
||||
extern BOOL macho_enum_modules(HANDLE hProc, enum_modules_cb, void*) DECLSPEC_HIDDEN;
|
||||
extern BOOL macho_fetch_file_info(const WCHAR* name, DWORD_PTR* base, DWORD* size, DWORD* checksum) DECLSPEC_HIDDEN;
|
||||
extern BOOL macho_fetch_file_info(HANDLE process, const WCHAR* name, unsigned long load_addr, DWORD_PTR* base, DWORD* size, DWORD* checksum) DECLSPEC_HIDDEN;
|
||||
extern BOOL macho_load_debug_info(struct module* module) DECLSPEC_HIDDEN;
|
||||
extern struct module*
|
||||
macho_load_module(struct process* pcs, const WCHAR* name, unsigned long) DECLSPEC_HIDDEN;
|
||||
|
|
|
@ -126,6 +126,7 @@ struct image_file_map
|
|||
{
|
||||
const macho_section* section;
|
||||
const char* mapped;
|
||||
unsigned int ignored : 1;
|
||||
}* sect;
|
||||
#endif
|
||||
} macho;
|
||||
|
|
|
@ -103,6 +103,16 @@ typedef struct nlist macho_nlist;
|
|||
#endif
|
||||
|
||||
|
||||
/* Bitmask for Mach-O image header flags indicating that the image is in dyld's
|
||||
shared cached. That implies that its segments are mapped non-contiguously.
|
||||
This value isn't defined anywhere in headers. It's used in dyld and in
|
||||
debuggers which support OS X as a magic number.
|
||||
|
||||
The flag also isn't set in the on-disk image file. It's only set in
|
||||
memory by dyld. */
|
||||
#define MACHO_DYLD_IN_SHARED_CACHE 0x80000000
|
||||
|
||||
|
||||
#define UUID_STRING_LEN 37 /* 16 bytes at 2 hex digits apiece, 4 dashes, and the null terminator */
|
||||
|
||||
|
||||
|
@ -114,6 +124,12 @@ struct macho_module_info
|
|||
is_loader : 1;
|
||||
};
|
||||
|
||||
struct section_info
|
||||
{
|
||||
BOOL split_segs;
|
||||
unsigned int section_index;
|
||||
};
|
||||
|
||||
#define MACHO_INFO_DEBUG_HEADER 0x0001
|
||||
#define MACHO_INFO_MODULE 0x0002
|
||||
#define MACHO_INFO_NAME 0x0004
|
||||
|
@ -336,7 +352,8 @@ BOOL macho_find_section(struct image_file_map* ifm, const char* segname, const c
|
|||
fmap = &ifm->u.macho;
|
||||
for (i = 0; i < fmap->num_sections; i++)
|
||||
{
|
||||
if (strcmp(fmap->sect[i].section->sectname, sectname) == 0 &&
|
||||
if (!fmap->sect[i].ignored &&
|
||||
strcmp(fmap->sect[i].section->sectname, sectname) == 0 &&
|
||||
(!segname || strcmp(fmap->sect[i].section->segname, segname) == 0))
|
||||
{
|
||||
ism->fmap = ifm;
|
||||
|
@ -360,7 +377,7 @@ const char* macho_map_section(struct image_section_map* ism)
|
|||
struct macho_file_map* fmap = &ism->fmap->u.macho;
|
||||
|
||||
assert(ism->fmap->modtype == DMT_MACHO);
|
||||
if (ism->sidx < 0 || ism->sidx >= ism->fmap->u.macho.num_sections)
|
||||
if (ism->sidx < 0 || ism->sidx >= ism->fmap->u.macho.num_sections || fmap->sect[ism->sidx].ignored)
|
||||
return IMAGE_NO_MAP;
|
||||
|
||||
return macho_map_range(fmap, fmap->sect[ism->sidx].section->offset, fmap->sect[ism->sidx].section->size,
|
||||
|
@ -386,7 +403,8 @@ void macho_unmap_section(struct image_section_map* ism)
|
|||
*/
|
||||
DWORD_PTR macho_get_map_rva(const struct image_section_map* ism)
|
||||
{
|
||||
if (ism->sidx < 0 || ism->sidx >= ism->fmap->u.macho.num_sections)
|
||||
if (ism->sidx < 0 || ism->sidx >= ism->fmap->u.macho.num_sections ||
|
||||
ism->fmap->u.macho.sect[ism->sidx].ignored)
|
||||
return 0;
|
||||
return ism->fmap->u.macho.sect[ism->sidx].section->addr - ism->fmap->u.macho.segs_start;
|
||||
}
|
||||
|
@ -396,7 +414,8 @@ DWORD_PTR macho_get_map_rva(const struct image_section_map* ism)
|
|||
*/
|
||||
unsigned macho_get_map_size(const struct image_section_map* ism)
|
||||
{
|
||||
if (ism->sidx < 0 || ism->sidx >= ism->fmap->u.macho.num_sections)
|
||||
if (ism->sidx < 0 || ism->sidx >= ism->fmap->u.macho.num_sections ||
|
||||
ism->fmap->u.macho.sect[ism->sidx].ignored)
|
||||
return 0;
|
||||
return ism->fmap->u.macho.sect[ism->sidx].section->size;
|
||||
}
|
||||
|
@ -510,7 +529,8 @@ static int macho_count_sections(struct macho_file_map* fmap, const struct load_c
|
|||
static int macho_load_section_info(struct macho_file_map* fmap, const struct load_command* lc, void* user)
|
||||
{
|
||||
const macho_segment_command* sc = (const macho_segment_command*)lc;
|
||||
int* section_index = (int*)user;
|
||||
struct section_info* info = user;
|
||||
BOOL ignore;
|
||||
const macho_section* section;
|
||||
int i;
|
||||
unsigned long tmp, page_mask = sysconf( _SC_PAGESIZE ) - 1;
|
||||
|
@ -520,10 +540,17 @@ static int macho_load_section_info(struct macho_file_map* fmap, const struct loa
|
|||
TRACE("Segment command vm: 0x%08lx - 0x%08lx\n", (unsigned long)sc->vmaddr,
|
||||
(unsigned long)(sc->vmaddr + sc->vmsize));
|
||||
|
||||
/* Images in the dyld shared cache have their segments mapped non-contiguously.
|
||||
We don't know how to properly locate any of the segments other than __TEXT,
|
||||
so ignore them. */
|
||||
ignore = (info->split_segs && strcmp(sc->segname, SEG_TEXT));
|
||||
|
||||
if (!strncmp(sc->segname, "WINE_", 5))
|
||||
TRACE("Ignoring special Wine segment %s\n", debugstr_an(sc->segname, sizeof(sc->segname)));
|
||||
else if (!strncmp(sc->segname, "__PAGEZERO", 10))
|
||||
TRACE("Ignoring __PAGEZERO segment\n");
|
||||
else if (ignore)
|
||||
TRACE("Ignoring %s segment because image has split segments\n", sc->segname);
|
||||
else
|
||||
{
|
||||
/* If this segment starts before previously-known earliest, record new earliest. */
|
||||
|
@ -540,9 +567,10 @@ static int macho_load_section_info(struct macho_file_map* fmap, const struct loa
|
|||
section = (const macho_section*)(sc + 1);
|
||||
for (i = 0; i < sc->nsects; i++)
|
||||
{
|
||||
fmap->sect[*section_index].section = §ion[i];
|
||||
fmap->sect[*section_index].mapped = IMAGE_NO_MAP;
|
||||
(*section_index)++;
|
||||
fmap->sect[info->section_index].section = §ion[i];
|
||||
fmap->sect[info->section_index].mapped = IMAGE_NO_MAP;
|
||||
fmap->sect[info->section_index].ignored = ignore;
|
||||
info->section_index++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -580,7 +608,7 @@ static inline void reset_file_map(struct image_file_map* ifm)
|
|||
*
|
||||
* Maps a Mach-O file into memory (and checks it's a real Mach-O file)
|
||||
*/
|
||||
static BOOL macho_map_file(const WCHAR* filenameW, struct image_file_map* ifm)
|
||||
static BOOL macho_map_file(const WCHAR* filenameW, BOOL split_segs, struct image_file_map* ifm)
|
||||
{
|
||||
struct macho_file_map* fmap = &ifm->u.macho;
|
||||
struct fat_header fat_header;
|
||||
|
@ -588,6 +616,7 @@ static BOOL macho_map_file(const WCHAR* filenameW, struct image_file_map* ifm)
|
|||
int i;
|
||||
char* filename;
|
||||
unsigned len;
|
||||
struct section_info info;
|
||||
BOOL ret = FALSE;
|
||||
|
||||
TRACE("(%s, %p)\n", debugstr_w(filenameW), fmap);
|
||||
|
@ -688,8 +717,9 @@ static BOOL macho_map_file(const WCHAR* filenameW, struct image_file_map* ifm)
|
|||
fmap->segs_size = 0;
|
||||
fmap->segs_start = ~0L;
|
||||
|
||||
i = 0;
|
||||
if (macho_enum_load_commands(fmap, TARGET_SEGMENT_COMMAND, macho_load_section_info, &i) < 0)
|
||||
info.split_segs = split_segs;
|
||||
info.section_index = 0;
|
||||
if (macho_enum_load_commands(fmap, TARGET_SEGMENT_COMMAND, macho_load_section_info, &info) < 0)
|
||||
{
|
||||
fmap->num_sections = 0;
|
||||
goto done;
|
||||
|
@ -770,7 +800,7 @@ static BOOL macho_sect_is_code(struct macho_file_map* fmap, unsigned char sectid
|
|||
if (!sectidx) return FALSE;
|
||||
|
||||
sectidx--; /* convert from 1-based to 0-based */
|
||||
if (sectidx >= fmap->num_sections) return FALSE;
|
||||
if (sectidx >= fmap->num_sections || fmap->sect[sectidx].ignored) return FALSE;
|
||||
|
||||
ret = (!(fmap->sect[sectidx].section->flags & SECTION_TYPE) &&
|
||||
(fmap->sect[sectidx].section->flags & (S_ATTR_PURE_INSTRUCTIONS|S_ATTR_SOME_INSTRUCTIONS)));
|
||||
|
@ -1044,7 +1074,7 @@ static BOOL try_dsym(const WCHAR* path, struct macho_file_map* fmap)
|
|||
{
|
||||
struct image_file_map dsym_ifm;
|
||||
|
||||
if (macho_map_file(path, &dsym_ifm))
|
||||
if (macho_map_file(path, FALSE, &dsym_ifm))
|
||||
{
|
||||
char uuid_string[UUID_STRING_LEN];
|
||||
|
||||
|
@ -1153,6 +1183,34 @@ found:
|
|||
if (query) CFRelease(query);
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* image_uses_split_segs
|
||||
*
|
||||
* Determine if the Mach-O image loaded at a particular address in
|
||||
* the given process is in the dyld shared cache and therefore has
|
||||
* its segments mapped non-contiguously.
|
||||
*
|
||||
* The image header has to be loaded from the process's memory
|
||||
* because the relevant flag is only set in memory, not in the file.
|
||||
*/
|
||||
static BOOL image_uses_split_segs(HANDLE process, unsigned long load_addr)
|
||||
{
|
||||
BOOL split_segs = FALSE;
|
||||
|
||||
if (process && load_addr)
|
||||
{
|
||||
macho_mach_header header;
|
||||
if (ReadProcessMemory(process, (void*)load_addr, &header, sizeof(header), NULL) &&
|
||||
header.magic == TARGET_MH_MAGIC && header.cputype == TARGET_CPU_TYPE &&
|
||||
header.flags & MACHO_DYLD_IN_SHARED_CACHE)
|
||||
{
|
||||
split_segs = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return split_segs;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* macho_load_debug_info
|
||||
*
|
||||
|
@ -1217,14 +1275,16 @@ BOOL macho_load_debug_info(struct module* module)
|
|||
*
|
||||
* Gathers some more information for a Mach-O module from a given file
|
||||
*/
|
||||
BOOL macho_fetch_file_info(const WCHAR* name, DWORD_PTR* base,
|
||||
BOOL macho_fetch_file_info(HANDLE process, const WCHAR* name, unsigned long load_addr, DWORD_PTR* base,
|
||||
DWORD* size, DWORD* checksum)
|
||||
{
|
||||
struct image_file_map fmap;
|
||||
BOOL split_segs;
|
||||
|
||||
TRACE("(%s, %p, %p, %p)\n", debugstr_w(name), base, size, checksum);
|
||||
|
||||
if (!macho_map_file(name, &fmap)) return FALSE;
|
||||
split_segs = image_uses_split_segs(process, load_addr);
|
||||
if (!macho_map_file(name, split_segs, &fmap)) return FALSE;
|
||||
if (base) *base = fmap.u.macho.segs_start;
|
||||
*size = fmap.u.macho.segs_size;
|
||||
*checksum = calc_crc32(fmap.u.macho.fd);
|
||||
|
@ -1255,12 +1315,14 @@ static BOOL macho_load_file(struct process* pcs, const WCHAR* filename,
|
|||
unsigned long load_addr, struct macho_info* macho_info)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
BOOL split_segs;
|
||||
struct image_file_map fmap;
|
||||
|
||||
TRACE("(%p/%p, %s, 0x%08lx, %p/0x%08x)\n", pcs, pcs->handle, debugstr_w(filename),
|
||||
load_addr, macho_info, macho_info->flags);
|
||||
|
||||
if (!macho_map_file(filename, &fmap)) return FALSE;
|
||||
split_segs = image_uses_split_segs(pcs->handle, load_addr);
|
||||
if (!macho_map_file(filename, split_segs, &fmap)) return FALSE;
|
||||
|
||||
/* Find the dynamic loader's table of images loaded into the process.
|
||||
*/
|
||||
|
@ -1781,7 +1843,7 @@ BOOL macho_synchronize_module_list(struct process* pcs)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL macho_fetch_file_info(const WCHAR* name, DWORD_PTR* base,
|
||||
BOOL macho_fetch_file_info(HANDLE process, const WCHAR* name, unsigned long load_addr, DWORD_PTR* base,
|
||||
DWORD* size, DWORD* checksum)
|
||||
{
|
||||
return FALSE;
|
||||
|
|
|
@ -287,7 +287,7 @@ static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base,
|
|||
/* NB: if we have a non-null base from the live-target use it. If we have
|
||||
* a null base, then grab its base address from Mach-O file.
|
||||
*/
|
||||
if (!macho_fetch_file_info(name, &rbase, &size, &checksum))
|
||||
if (!macho_fetch_file_info(dc->hProcess, name, base, &rbase, &size, &checksum))
|
||||
size = checksum = 0;
|
||||
add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
|
||||
return TRUE;
|
||||
|
|
|
@ -522,7 +522,7 @@ static BOOL CALLBACK module_find_cb(PCWSTR buffer, PVOID user)
|
|||
}
|
||||
break;
|
||||
case DMT_MACHO:
|
||||
if (macho_fetch_file_info(buffer, 0, &size, &checksum))
|
||||
if (macho_fetch_file_info(NULL, buffer, 0, 0, &size, &checksum))
|
||||
{
|
||||
matched++;
|
||||
if (checksum == mf->dw1) matched++;
|
||||
|
|
Loading…
Reference in New Issue