When multiple source files define same-name struct/union tags, default
lookup may resolve the wrong candidate depending on lookup order/context.
Add `-F <file_name>` support to `struct`/`union` so datatype lookup is
constrained to the compilation unit scope matched by the requested file.
The gdb-side logic (in gdb-16.2.patch) resolves a unique block from
objfile->compunit->filetabs, applies a temporary scope guard for the
request, and restores previous scope after completion. If file matching
is missing or ambiguous, the request fails explicitly.
Keep existing behavior unchanged when `-F` is not used.
Expose two helper APIs for external/plugin callers to reuse file-scoped
resolution with existing STRUCT_SIZE/MEMBER_OFFSET call paths:
- char *set_temporary_datatype_filename(const char *source_file);
- void restore_datatype_filename(char *saved_source_file);
Signed-off-by: chahuan <chahuan(a)qti.qualcomm.com>
---
cmdline.c | 1 +
defs.h | 4 ++
gdb-16.2.patch | 166 ++++++++++++++++++++++++++++++++++++++++++++++--
gdb_interface.c | 6 ++
help.c | 8 ++-
symbols.c | 31 ++++++++-
6 files changed, 206 insertions(+), 10 deletions(-)
diff --git a/cmdline.c b/cmdline.c
index 7e7b81d..c4c2eed 100644
--- a/cmdline.c
+++ b/cmdline.c
@@ -1210,6 +1210,7 @@ restore_sanity(void)
" encoded bytes to skip after a ud2a (BUG)
instruction.\n");
pc->curcmd_flags = 0;
pc->curcmd_private = 0;
+ pc->datatype_filename = NULL;
restore_gdb_sanity();
diff --git a/defs.h b/defs.h
index a6f4372..3f727e7 100644
--- a/defs.h
+++ b/defs.h
@@ -527,6 +527,7 @@ struct program_context {
#define CPUMASK (0x20000)
#define PARTIAL_READ_OK (0x40000)
ulonglong curcmd_private; /* general purpose per-command info */
+ char *datatype_filename; /* optional datatype source file filter */
int cur_gdb_cmd; /* current gdb command */
int last_gdb_cmd; /* previously-executed gdb command */
int sigint_cnt; /* number of ignored SIGINTs */
@@ -5234,6 +5235,7 @@ struct gnu_request {
ulong target_length;
int target_typecode;
int is_typedef;
+ char *source_file;
char *member;
long member_offset;
long member_length;
@@ -5853,6 +5855,8 @@ char *load_module_filter(char *, int);
#define LM_P_FILTER (1)
#define LM_DIS_FILTER (2)
long datatype_info(char *, char *, struct datatype_member *);
+char *set_temporary_datatype_filename(const char *);
+void restore_datatype_filename(char *);
int get_symbol_type(char *, char *, struct gnu_request *);
int get_symbol_length(char *);
void dump_numargs_cache(void);
diff --git a/gdb-16.2.patch b/gdb-16.2.patch
index 78cf605..0d0e0b9 100644
--- a/gdb-16.2.patch
+++ b/gdb-16.2.patch
@@ -813,7 +813,7 @@ exit 0
if (result != NULL)
return result;
}
-@@ -7320,3 +7339,914 @@ the use of prologue scanners."),
+@@ -7320,3 +7339,1068 @@ the use of prologue scanners."),
"symtab");
gdb::observers::free_objfile.attach (symtab_free_objfile_observer,
"symtab");
}
@@ -843,6 +843,11 @@ exit 0
+void gdb_command_funnel_1(struct gnu_request *);
+static long lookup_struct_contents(struct gnu_request *);
+static void iterate_datatypes(struct gnu_request *);
++static int source_file_matches(struct symtab *, const char *);
++static const struct block *gdb_scope_block_for_source_file(struct gnu_request *);
++static int gdb_push_source_file_scope(struct gnu_request *, CORE_ADDR *, const struct
block **);
++static void gdb_pop_source_file_scope(CORE_ADDR, const struct block *);
++static void execute_command_with_source_scope(struct gnu_request *);
+extern void execute_command (const char *, int);
+
+struct objfile *gdb_kernel_objfile = { 0 };
@@ -857,6 +862,36 @@ exit 0
+#define TYPE_NFIELDS(t) (t->num_fields ())
+#define TYPE_NAME(t) (t->name ())
+
++class source_file_scope_guard {
++public:
++ explicit source_file_scope_guard(struct gnu_request *req)
++ : m_active(0), m_failed(0), m_saved_text_scope(0),
++ m_saved_scope_block(NULL)
++ {
++ m_active = gdb_push_source_file_scope(req, &m_saved_text_scope,
++ &m_saved_scope_block);
++ m_failed = req->flags & GNU_COMMAND_FAILED;
++ }
++
++ ~source_file_scope_guard()
++ {
++ if (m_active)
++ gdb_pop_source_file_scope(m_saved_text_scope,
++ m_saved_scope_block);
++ }
++
++ int failed(void) const
++ {
++ return m_failed;
++ }
++
++private:
++ int m_active;
++ int m_failed;
++ CORE_ADDR m_saved_text_scope;
++ const struct block *m_saved_scope_block;
++};
++
+/*
+ * All commands from above come through here.
+ */
@@ -894,8 +929,7 @@ exit 0
+ break;
+
+ case GNU_PASS_THROUGH:
-+ execute_command(req->buf,
-+ req->flags & GNU_FROM_TTY_OFF ? FALSE : TRUE);
++ execute_command_with_source_scope(req);
+ break;
+
+ case GNU_USER_PRINT_OPTION:
@@ -1130,6 +1164,10 @@ exit 0
+ expression_up expr;
+ struct symbol *sym;
+ struct value *val;
++ source_file_scope_guard scope(req);
++
++ if (scope.failed())
++ return;
+
+ if (gdb_CRASHDEBUG(2))
+ console("gdb_get_datatype [%s] (a)\n", req->name);
@@ -1522,6 +1560,10 @@ exit 0
+ struct value *val;
+ struct type *type;
+ struct type *target_type;
++ source_file_scope_guard scope(req);
++
++ if (scope.failed())
++ return;
+
+ req->typecode = TYPE_CODE_UNDEF;
+
@@ -1599,19 +1641,129 @@ exit 0
+}
+
+CORE_ADDR crash_text_scope;
++static const struct block *crash_scope_block;
++
++static int
++source_file_matches(struct symtab *s, const char *source_file)
++{
++ const char *fullname;
++
++ if (!s || !source_file || !*source_file)
++ return FALSE;
++
++ if (s->filename && compare_filenames_for_search(s->filename,
source_file))
++ return TRUE;
++
++ fullname = symtab_to_fullname(s);
++ if (fullname && compare_filenames_for_search(fullname, source_file))
++ return TRUE;
++
++ return FALSE;
++}
++
++static const struct block *
++gdb_scope_block_for_source_file(struct gnu_request *req)
++{
++ const struct block *scope = NULL;
++ const struct blockvector *bv;
++ const char *source_file;
++
++ if (!req || !(source_file = req->source_file) || !*source_file)
++ return NULL;
++
++ for (objfile *objfile : current_program_space->objfiles ()) {
++ if (objfile->sf) {
++ objfile->expand_all_symtabs();
++ objfile->all_symtabs_expanded = true;
++ }
++
++ for (compunit_symtab *cust : objfile->compunits ()) {
++ bv = cust->blockvector();
++ if (!bv)
++ continue;
++
++ for (symtab *s : cust->filetabs()) {
++ const struct block *candidate;
++
++ if (!source_file_matches(s, source_file))
++ continue;
++
++ candidate = bv->static_block();
++ if (!candidate)
++ candidate = bv->global_block();
++ if (!candidate)
++ continue;
++
++ if (scope && (scope != candidate))
++ return NULL;
++
++ scope = candidate;
++ break;
++ }
++ }
++ }
++
++ return scope;
++}
++
++static int
++gdb_push_source_file_scope(struct gnu_request *req, CORE_ADDR *saved_text_scope,
++ const struct block **saved_scope_block)
++{
++ const struct block *scope;
++
++ if (!req || !req->source_file || !*req->source_file)
++ return FALSE;
++
++ scope = gdb_scope_block_for_source_file(req);
++ if (!scope) {
++ req->flags |= GNU_COMMAND_FAILED;
++ return FALSE;
++ }
++
++ *saved_text_scope = crash_text_scope;
++ *saved_scope_block = crash_scope_block;
++ crash_text_scope = 0;
++ crash_scope_block = scope;
++
++ return TRUE;
++}
++
++static void
++gdb_pop_source_file_scope(CORE_ADDR saved_text_scope,
++ const struct block *saved_scope_block)
++{
++ crash_text_scope = saved_text_scope;
++ crash_scope_block = saved_scope_block;
++}
++
++static void
++execute_command_with_source_scope(struct gnu_request *req)
++{
++ source_file_scope_guard scope(req);
++
++ if (scope.failed())
++ return;
++
++ execute_command(req->buf,
++ req->flags & GNU_FROM_TTY_OFF ? FALSE : TRUE);
++}
+
+static void
+gdb_set_crash_block(struct gnu_request *req)
+{
+ if (!req->addr) { /* debug */
+ crash_text_scope = 0;
++ crash_scope_block = NULL;
+ return;
+ }
+
-+ if ((req->addr2 = (ulong)block_for_pc(req->addr)))
++ if ((req->addr2 = (ulong)block_for_pc(req->addr))) {
+ crash_text_scope = req->addr;
-+ else {
++ crash_scope_block = NULL;
++ } else {
+ crash_text_scope = 0;
++ crash_scope_block = NULL;
+ req->flags |= GNU_COMMAND_FAILED;
+ }
+}
@@ -1619,7 +1771,9 @@ exit 0
+static const struct block *
+gdb_get_crash_block(void)
+{
-+ if (crash_text_scope)
++ if (crash_scope_block)
++ return crash_scope_block;
++ else if (crash_text_scope)
+ return block_for_pc(crash_text_scope);
+ else
+ return NULL;
diff --git a/gdb_interface.c b/gdb_interface.c
index 9f76f85..542ee42 100644
--- a/gdb_interface.c
+++ b/gdb_interface.c
@@ -388,6 +388,11 @@ gdb_interface(struct gnu_request *req)
fp : CRASHDEBUG(1) ? fp : pc->nullfp;
}
+ if (req->command == GNU_GET_DATATYPE ||
+ req->command == GNU_GET_SYMBOL_TYPE ||
+ req->command == GNU_PASS_THROUGH)
+ req->source_file = pc->datatype_filename;
+
pc->cur_req = req;
pc->cur_gdb_cmd = req->command;
@@ -524,6 +529,7 @@ dump_gnu_request(struct gnu_request *req, int in_gdb)
console("target_length: %ld ", req->target_length);
console("target_typecode: %d ", req->target_typecode);
console("is_typedef: %d ", req->is_typedef);
+ console("source_file: \"%s\" ", req->source_file);
console("member: \"%s\" ", req->member);
console("member_offset: %ld\n", req->member_offset);
console("member_length: %ld\n", req->member_length);
diff --git a/help.c b/help.c
index 91d1550..218c1e4 100644
--- a/help.c
+++ b/help.c
@@ -5243,7 +5243,7 @@ NULL
char *help_struct[] = {
"struct",
"structure contents",
-"struct_name[.member[,member]][-o][-l offset][-rfuxdp]\n"
+"struct_name[.member[,member]][-o][-l offset][-F file_name][-rfuxdp]\n"
" [address | symbol][:cpuspec] [count | -c count]",
" This command displays either a structure definition, or a formatted
display",
" of the contents of a structure at a specified address. When no address
is",
@@ -5269,6 +5269,8 @@ char *help_struct[] = {
" be entered in either of the following manners:",
" 1. in \"structure.member\" format.",
" 2. a number of bytes. ",
+" -F file_name restrict type lookup to a source file when duplicate",
+" structure names exist in vmlinux debug data.",
" -r raw dump of structure data.",
" -f address argument is a dumpfile offset.",
" -u address argument is a user virtual address in the current",
@@ -5610,7 +5612,7 @@ NULL
char *help_union[] = {
"union",
"union contents",
-"union_name[.member[,member]] [-o][-l offset][-rfuxdp]\n"
+"union_name[.member[,member]] [-o][-l offset][-F file_name][-rfuxdp]\n"
" [address | symbol][:cpuspec] [count | -c count]",
" This command displays either a union definition, or a formatted display",
" of the contents of a union at a specified address. When no address is",
@@ -5637,6 +5639,8 @@ char *help_union[] = {
" following manners:",
" 1. in \"structure.member\" format.",
" 2. a number of bytes. ",
+" -F file_name restrict type lookup to a source file when duplicate",
+" union names exist in vmlinux debug data.",
" -r raw dump of union data.",
" -f address argument is a dumpfile offset.",
" -x override default output format with hexadecimal format.",
diff --git a/symbols.c b/symbols.c
index e6865ca..fa92ead 100644
--- a/symbols.c
+++ b/symbols.c
@@ -6947,7 +6947,25 @@ datatype_info(char *name, char *member, struct datatype_member
*dm)
else
return -1;
} else
- return size;
+ return size;
+}
+
+char *
+set_temporary_datatype_filename(const char *source_file)
+{
+ char *saved_source_file;
+
+ saved_source_file = pc->datatype_filename;
+ pc->datatype_filename = (source_file && *source_file) ?
+ (char *)source_file : NULL;
+
+ return saved_source_file;
+}
+
+void
+restore_datatype_filename(char *saved_source_file)
+{
+ pc->datatype_filename = saved_source_file;
}
/*
@@ -7542,8 +7560,9 @@ cmd_datatype_common(ulong flags)
separator = members = NULL;
cpuspec = NULL;
cpus = NULL;
+ pc->datatype_filename = NULL;
- while ((c = getopt(argcnt, args, "pxdhfuc:rvol:")) != EOF) {
+ while ((c = getopt(argcnt, args, "pxdhfuc:rvol:F:")) != EOF) {
switch (c)
{
case 'p':
@@ -7604,6 +7623,12 @@ cmd_datatype_common(ulong flags)
pc->curcmd_flags |= MEMTYPE_UVADDR;
break;
+ case 'F':
+ if (!optarg || !*optarg)
+ error(FATAL, "invalid -F option\n");
+ pc->datatype_filename = optarg;
+ break;
+
default:
argerrs++;
break;
@@ -7786,6 +7811,8 @@ cmd_datatype_common(ulong flags)
restore_current_radix(restore_radix);
freebuf:
+ pc->datatype_filename = NULL;
+
if (argc_members) {
FREEBUF(structname);
FREEBUF(members);
--
2.43.0