dbghelp/pdb: Handle S_LOCAL codeview entries.

Signed-off-by: Eric Pouech <eric.pouech@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Eric Pouech 2021-11-08 14:58:39 +01:00 committed by Alexandre Julliard
parent de5fe89357
commit 7ad847bc34
2 changed files with 181 additions and 5 deletions

View File

@ -88,6 +88,8 @@ struct pdb_module_info
struct pdb_file_info pdb_files[CV_MAX_MODULES];
};
#define loc_cv_local_range (loc_user + 0) /* loc.offset contain the copy of all defrange* Codeview records following S_LOCAL */
/*========================================================================
* Debug file access helper routines
*/
@ -1581,6 +1583,24 @@ static ULONG_PTR codeview_get_address(const struct msc_debug_info* msc_dbg,
codeview_map_offset(msc_dbg, sectp[seg-1].VirtualAddress + offset);
}
static BOOL func_has_local(struct symt_function* func, const char* name)
{
int i;
for (i = 0; i < func->vchildren.num_elts; ++i)
{
struct symt* p = *(struct symt**)vector_at(&func->vchildren, i);
if (symt_check_tag(p, SymTagData) && !strcmp(((struct symt_data*)p)->hash_elt.name, name))
return TRUE;
}
return FALSE;
}
static const union codeview_symbol* get_next_sym(const union codeview_symbol* sym)
{
return (const union codeview_symbol*)((const char*)sym + sym->generic.len + 2);
}
static inline void codeview_add_variable(const struct msc_debug_info* msc_dbg,
struct symt_compiland* compiland,
const char* name,
@ -1603,6 +1623,135 @@ static inline void codeview_add_variable(const struct msc_debug_info* msc_dbg,
}
}
struct cv_local_info
{
unsigned short kind; /* the S_DEFRANGE* */
unsigned short ngaps; /* number of gaps */
unsigned short reg;
unsigned short rangelen; /* after start */
short offset;
DWORD_PTR start;
struct cv_addr_gap gaps[0];
};
static const struct cv_addr_gap* codeview_get_gaps(const union codeview_symbol* symrange)
{
const struct cv_addr_gap* gap;
switch (symrange->generic.id)
{
case S_DEFRANGE: gap = symrange->defrange_v3.gaps; break;
case S_DEFRANGE_SUBFIELD: gap = symrange->defrange_subfield_v3.gaps; break;
case S_DEFRANGE_REGISTER: gap = symrange->defrange_register_v3.gaps; break;
case S_DEFRANGE_FRAMEPOINTER_REL: gap = symrange->defrange_frameptrrel_v3.gaps; break;
case S_DEFRANGE_SUBFIELD_REGISTER: gap = symrange->defrange_subfield_register_v3.gaps; break;
case S_DEFRANGE_REGISTER_REL: gap = symrange->defrange_registerrel_v3.gaps; break;
/* no gaps for that one */
case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE:
default: return NULL;
}
return gap != (const struct cv_addr_gap*)get_next_sym(symrange) ? gap : NULL;
}
static void codeview_xform_range(const struct msc_debug_info* msc_dbg,
struct cv_local_info* locinfo,
const struct cv_addr_range* adrange)
{
locinfo->start = codeview_get_address(msc_dbg, adrange->isectStart, adrange->offStart);
locinfo->rangelen = adrange->cbRange;
}
static unsigned codeview_transform_defrange(const struct msc_debug_info* msc_dbg,
struct symt_function* curr_func,
const union codeview_symbol* sym,
struct location* loc)
{
const union codeview_symbol* first_symrange = get_next_sym(sym);
const union codeview_symbol* symrange;
const struct cv_addr_gap* gap;
unsigned len, alloc = sizeof(DWORD); /* for terminating kind = 0 */
unsigned char* ptr;
/* we need to transform the cv_addr_range into cv_local_info */
for (symrange = first_symrange;
symrange->generic.id >= S_DEFRANGE && symrange->generic.id <= S_DEFRANGE_REGISTER_REL;
symrange = get_next_sym(symrange))
{
gap = codeview_get_gaps(symrange);
alloc += sizeof(struct cv_local_info) +
(gap ? (const char*)get_next_sym(symrange) - (const char*)gap : 0);
}
/* total length of all S_DEFRANGE* records (in bytes) following S_LOCAL */
len = (const char*)symrange - (const char*)first_symrange;
ptr = pool_alloc(&msc_dbg->module->pool, alloc);
if (ptr)
{
struct cv_local_info* locinfo = (struct cv_local_info*)ptr;
loc->kind = loc_cv_local_range;
loc->offset = (DWORD_PTR)ptr;
/* transform the cv_addr_range into cv_local_info */
for (symrange = first_symrange;
symrange->generic.id >= S_DEFRANGE && symrange->generic.id <= S_DEFRANGE_REGISTER_REL;
symrange = get_next_sym(symrange))
{
locinfo->kind = symrange->generic.id;
switch (symrange->generic.id)
{
case S_DEFRANGE:
case S_DEFRANGE_SUBFIELD:
/* FIXME: transformation unsupported; let loc_compute bark if actually needed */
break;
case S_DEFRANGE_REGISTER:
locinfo->reg = symrange->defrange_register_v3.reg;
codeview_xform_range(msc_dbg, locinfo, &symrange->defrange_register_v3.range);
break;
case S_DEFRANGE_FRAMEPOINTER_REL:
locinfo->offset = symrange->defrange_frameptrrel_v3.offFramePointer;
codeview_xform_range(msc_dbg, locinfo, &symrange->defrange_frameptrrel_v3.range);
break;
case S_DEFRANGE_SUBFIELD_REGISTER:
locinfo->reg = symrange->defrange_subfield_register_v3.reg;
locinfo->offset = symrange->defrange_subfield_register_v3.offParent;
codeview_xform_range(msc_dbg, locinfo, &symrange->defrange_subfield_register_v3.range);
break;
case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE:
locinfo->offset = symrange->defrange_frameptr_relfullscope_v3.offFramePointer;
locinfo->start = curr_func->address;
locinfo->rangelen = curr_func->size;
break;
case S_DEFRANGE_REGISTER_REL:
locinfo->reg = symrange->defrange_registerrel_v3.baseReg;
locinfo->offset = symrange->defrange_registerrel_v3.offBasePointer;
codeview_xform_range(msc_dbg, locinfo, &symrange->defrange_registerrel_v3.range);
break;
default:
assert(0);
}
gap = codeview_get_gaps(symrange);
if (gap)
{
unsigned gaplen = (const char*)get_next_sym(symrange) - (const char*)gap;
locinfo->ngaps = gaplen / sizeof(*gap);
memcpy(locinfo->gaps, gap, gaplen);
locinfo = (struct cv_local_info*)((unsigned char*)(locinfo + 1) + gaplen);
}
else
{
locinfo->ngaps = 0;
locinfo++;
}
}
*(DWORD*)locinfo = 0; /* store terminating kind == 0 */
}
else
{
loc->kind = loc_error;
loc->reg = loc_err_internal;
}
return len;
}
static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, const BYTE* root,
int offset, int size, BOOL do_globals)
{
@ -1774,6 +1923,8 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, const BYTE* roo
terminate_string(&sym->stack_v2.p_name));
break;
case S_BPREL32:
/* S_BPREL32 can be present after S_LOCAL; prefer S_LOCAL when present */
if (func_has_local(curr_func, sym->stack_v3.name)) break;
loc.kind = loc_regrel;
/* Yes, it's i386 dependent, but that's the symbol purpose. S_REGREL is used on other CPUs */
loc.reg = CV_REG_EBP;
@ -1785,6 +1936,8 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, const BYTE* roo
sym->stack_v3.name);
break;
case S_REGREL32:
/* S_REGREL32 can be present after S_LOCAL; prefer S_LOCAL when present */
if (func_has_local(curr_func, sym->regrel_v3.name)) break;
loc.kind = loc_regrel;
loc.reg = sym->regrel_v3.reg;
loc.offset = sym->regrel_v3.offset;
@ -1815,6 +1968,8 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, const BYTE* roo
terminate_string(&sym->register_v2.p_name));
break;
case S_REGISTER:
/* S_REGISTER can be present after S_LOCAL; prefer S_LOCAL when present */
if (func_has_local(curr_func, sym->register_v3.name)) break;
loc.kind = loc_register;
loc.reg = sym->register_v3.reg;
loc.offset = 0;
@ -1989,6 +2144,14 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, const BYTE* roo
sym->udt_v3.name, sym->udt_v3.type);
}
break;
case S_LOCAL:
length += codeview_transform_defrange(msc_dbg, curr_func, sym, &loc);
symt_add_func_local(msc_dbg->module, curr_func,
sym->local_v3.varflags & 0x0001 ? DataIsParam : DataIsLocal,
&loc, block,
codeview_get_type(sym->local_v3.symtype, FALSE),
sym->local_v3.name);
break;
/*
* These are special, in that they are always followed by an
@ -2027,8 +2190,11 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, const BYTE* roo
case S_SECTION:
case S_COFFGROUP:
case S_EXPORT:
case S_LOCAL:
case S_CALLSITEINFO:
/* even if S_LOCAL groks all the S_DEFRANGE* records following itself,
* those kinds of records can also be present after a S_FILESTATIC record
* so silence them until (at least) S_FILESTATIC is supported
*/
case S_DEFRANGE_REGISTER:
case S_DEFRANGE_FRAMEPOINTER_REL:
case S_DEFRANGE_SUBFIELD_REGISTER:
@ -2054,6 +2220,16 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, const BYTE* roo
return TRUE;
}
static void pdb_location_compute(struct process* pcs,
const struct module_format* modfmt,
const struct symt_function* func,
struct location* loc)
{
loc->kind = loc_register;
loc->reg = loc_err_internal;
}
static BOOL codeview_snarf_public(const struct msc_debug_info* msc_dbg, const BYTE* root,
int offset, int size)
@ -2886,7 +3062,7 @@ static BOOL pdb_process_file(const struct process* pcs,
msc_dbg->module->format_info[DFI_PDB] = modfmt;
modfmt->module = msc_dbg->module;
modfmt->remove = pdb_module_remove;
modfmt->loc_compute = NULL;
modfmt->loc_compute = pdb_location_compute;
modfmt->u.pdb_info = pdb_module_info;
memset(cv_zmodules, 0, sizeof(cv_zmodules));

View File

@ -1879,7 +1879,7 @@ union codeview_symbol
{
unsigned short int len;
unsigned short int id;
unsigned int offFramePointer;
int offFramePointer;
struct cv_addr_range range;
struct cv_addr_gap gaps[0];
} defrange_frameptrrel_v3;
@ -1888,7 +1888,7 @@ union codeview_symbol
{
unsigned short int len;
unsigned short int id;
unsigned int offFramePointer;
int offFramePointer;
} defrange_frameptr_relfullscope_v3;
struct
@ -1911,7 +1911,7 @@ union codeview_symbol
unsigned short spilledUdtMember : 1;
unsigned short padding : 3;
unsigned short offsetParent : 12;
unsigned int offBasePointer;
int offBasePointer;
struct cv_addr_range range;
struct cv_addr_gap gaps[0];
} defrange_registerrel_v3;