[PATCH 07/13] dbghelp/pdb: properly handle S_LOCAL codeview entries
Eric Pouech
eric.pouech at gmail.com
Mon Nov 8 07:58:39 CST 2021
Signed-off-by: Eric Pouech <eric.pouech at gmail.com>
---
dlls/dbghelp/msc.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++-
include/wine/mscvpdb.h | 6 +-
2 files changed, 181 insertions(+), 5 deletions(-)
diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c
index 3a62736e7ed..0c58e5fc14f 100644
--- a/dlls/dbghelp/msc.c
+++ b/dlls/dbghelp/msc.c
@@ -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));
diff --git a/include/wine/mscvpdb.h b/include/wine/mscvpdb.h
index 1a173c097d7..9a733333837 100644
--- a/include/wine/mscvpdb.h
+++ b/include/wine/mscvpdb.h
@@ -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;
More information about the wine-devel
mailing list