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:
Ken Thomases 2015-08-04 02:40:38 -05:00 committed by Alexandre Julliard
parent be68890cac
commit 49f0bf6f0b
5 changed files with 83 additions and 20 deletions

View File

@ -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;

View File

@ -126,6 +126,7 @@ struct image_file_map
{
const macho_section* section;
const char* mapped;
unsigned int ignored : 1;
}* sect;
#endif
} macho;

View File

@ -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 = &section[i];
fmap->sect[*section_index].mapped = IMAGE_NO_MAP;
(*section_index)++;
fmap->sect[info->section_index].section = &section[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;

View File

@ -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;

View File

@ -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++;