BTW, I encountered some problems when testing your patch against arm64
vmcores. Please see the following output:
crash> struct arm_smmu_device
struct arm_smmu_device {
struct device *dev;
void *base;
phys_addr_t ioaddr;
unsigned int numpage;
unsigned int pgshift;
u32 features;
enum arm_smmu_arch_version version;
enum arm_smmu_implementation model;
const struct arm_smmu_impl *impl;
u32 num_context_banks;
u32 num_s2_context_banks;
unsigned long context_map[2];
struct arm_smmu_cb *cbs;
atomic_t irptndx;
u32 num_mapping_groups;
u16 streamid_mask;
u16 smr_mask_mask;
struct arm_smmu_smr *smrs;
struct arm_smmu_s2cr *s2crs;
struct mutex stream_map_mutex;
unsigned long va_size;
unsigned long ipa_size;
unsigned long pa_size;
unsigned long pgsize_bitmap;
int num_context_irqs;
int num_clks;
unsigned int *irqs;
struct clk_bulk_data *clks;
spinlock_t global_sync_lock;
struct iommu_device iommu;
}
SIZE: 272
crash> struct -o arm_smmu_device -F arm-smmu.c
struct arm_smmu_device {
[0] struct device *dev;
[8] void *base;
[16] phys_addr_t ioaddr;
[24] unsigned int numpage;
[28] unsigned int pgshift;
[32] u32 features;
[36] enum arm_smmu_arch_version version;
[40] enum arm_smmu_implementation model;
[48] const struct arm_smmu_impl *impl;
[56] u32 num_context_banks;
[60] u32 num_s2_context_banks;
[64] unsigned long context_map[2];
[80] struct arm_smmu_cb *cbs;
[88] atomic_t irptndx;
[92] u32 num_mapping_groups;
[96] u16 streamid_mask;
[98] u16 smr_mask_mask;
[104] struct arm_smmu_smr *smrs;
[112] struct arm_smmu_s2cr *s2crs;
[120] struct mutex stream_map_mutex;
[152] unsigned long va_size;
[160] unsigned long ipa_size;
[168] unsigned long pa_size;
[176] unsigned long pgsize_bitmap;
[184] int num_context_irqs;
[188] int num_clks;
[192] unsigned int *irqs;
[200] struct clk_bulk_data *clks;
[208] spinlock_t global_sync_lock;
[216] struct iommu_device iommu;
}
SIZE: 272 <<----- correct output
crash> struct -o arm_smmu_device -F arm-smmu-v3.c
struct arm_smmu_device {
[0] struct device *dev;
[24] void *base;
[16] phys_addr_t ioaddr;
[24] unsigned int numpage;
[28] unsigned int pgshift;
[40] u32 features;
[36] enum arm_smmu_arch_version version;
[40] enum arm_smmu_implementation model;
[48] const struct arm_smmu_impl *impl;
[56] u32 num_context_banks;
[60] u32 num_s2_context_banks;
[64] unsigned long context_map[2];
[80] struct arm_smmu_cb *cbs;
[88] atomic_t irptndx;
[92] u32 num_mapping_groups;
[96] u16 streamid_mask;
u16 smr_mask_mask; <<----- offset is missing
struct arm_smmu_smr *smrs;
struct arm_smmu_s2cr *s2crs;
struct mutex stream_map_mutex;
unsigned long va_size;
unsigned long ipa_size;
unsigned long pa_size;
[792] unsigned long pgsize_bitmap;
int num_context_irqs;
int num_clks;
unsigned int *irqs;
struct clk_bulk_data *clks;
spinlock_t global_sync_lock;
[864] struct iommu_device iommu;
} <<------ the struct is incorrect, arm_smmu_device isn't like this
for arm-smmu-v3.c.
SIZE: 960
The correct arm-smmu-v3.c arm_smmu_device is:
crash> struct -o arm_smmu_device -F arm-smmu-v3.c
struct arm_smmu_device {
[0] struct device *dev;
[8] struct device *impl_dev;
[16] const struct arm_smmu_impl_ops *impl_ops;
[24] void *base;
[32] void *page1;
[40] u32 features;
[44] u32 options;
[64] struct arm_smmu_cmdq cmdq;
[320] struct arm_smmu_evtq evtq;
[576] struct arm_smmu_priq priq;
[768] int gerr_irq;
[772] int combined_irq;
[776] unsigned long ias;
[784] unsigned long oas;
[792] unsigned long pgsize_bitmap;
[800] unsigned int asid_bits;
[804] unsigned int vmid_bits;
[808] struct ida vmid_map;
[824] unsigned int ssid_bits;
[828] unsigned int sid_bits;
[832] struct arm_smmu_strtab_cfg strtab_cfg;
[864] struct iommu_device iommu;
[920] struct rb_root streams;
[928] struct mutex streams_mutex;
}
SIZE: 960
On Tue, May 5, 2026 at 7:28 PM Tao Liu <ltao(a)redhat.com> wrote:
On Wed, Mar 25, 2026 at 2:52 PM chahuan <chahuan(a)qti.qualcomm.com> wrote:
>
> 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;
For program_context & gnu_request, please append your new members at
the end of the struct, rather than in the middle.
> @@ -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 *);
For code modification on gdb-16.2.patch, please not modify the
already-exist lines, but append your patch at the end of the
gdb-16.2.patch file. Please refer to the developing guide in
https://github.com/crash-utility/crash/wiki#writing-patches.
> +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;
> }
Didn't see any user of the 2 functions, why keep those?
>
> /*
> @@ -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
> --
> Crash-utility mailing list -- devel(a)lists.crash-utility.osci.io
> To unsubscribe send an email to devel-leave(a)lists.crash-utility.osci.io
> https://${domain_name}/admin/lists/devel.lists.crash-utility.osci.io/
> Contribution Guidelines:
https://github.com/crash-utility/crash/wiki