scripts: ctx.py/structs.py: Worked around incomplete structs/unions

Found when trying to measure ctx of yaffs2, which relies on incomplete
structs to hide some internal state (yaffs_summary_tags, yaffs_DIR).
This is less common in microcontroller filesystems since almost all
structs end up statically/stack allocated, and you can't statically
allocate incomplete structs.

It's not too surprising, but incomplete structs have no associated
DW_AT_byte_size in the relevant dwarf info, which broke ctx.py and
structs.py...

As a workaround, I'm now defaulting to size=0 if DW_AT_byte_size is
missing.

---

With this fix, at least structs.py is able to pick up the later internal
definition of yaffs_summary_tags. ctx.py doesn't because it only looks
at the unique dwarf offset referenced by the function definition, but
I'm hesitant to try anything more clever here.

yaffs_DIR is noteworthy in that there is simply no complete definition.
Internally, yaffs_DIR pointers alias yaffsfs_DirSearchContext structs.
In this case I think returning size=0 is the only reasonable option.
This commit is contained in:
Christopher Haster
2025-08-10 23:39:16 -05:00
parent c9691503bc
commit 3e8f304138
2 changed files with 23 additions and 11 deletions
+10 -8
View File
@@ -522,9 +522,16 @@ def collect_ctx(obj_paths, *,
if 'DW_AT_type' in entry:
type = info[int(entry['DW_AT_type'].strip('<>'), 0)]
size += sizeof(type, seen | {entry.off})
# base type?
elif entry.tag == 'DW_TAG_base_type':
size = int(entry['DW_AT_byte_size'])
# function pointer?
elif entry.tag == 'DW_TAG_subroutine_type':
size = 0
# struct? include any nested pointers
elif entry.tag == 'DW_TAG_structure_type':
size = int(entry['DW_AT_byte_size'])
# note structs/unions can be incomplete
size = int(entry.get('DW_AT_byte_size', 0))
for child in entry.children:
if child.tag != 'DW_TAG_member':
continue
@@ -536,7 +543,8 @@ def collect_ctx(obj_paths, *,
size += sizeof(type__, seen | {entry.off})
# union? include any nested pointers
elif entry.tag == 'DW_TAG_union_type':
size = int(entry['DW_AT_byte_size'])
# note structs/unions can be incomplete
size = int(entry.get('DW_AT_byte_size', 0))
size_ = 0
for child in entry.children:
if child.tag != 'DW_TAG_member':
@@ -555,12 +563,6 @@ def collect_ctx(obj_paths, *,
for child in entry.children:
if child.tag == 'DW_TAG_subrange_type':
size *= int(child['DW_AT_upper_bound']) + 1
# base type?
elif entry.tag == 'DW_TAG_base_type':
size = int(entry['DW_AT_byte_size'])
# function pointer?
elif entry.tag == 'DW_TAG_subroutine_type':
size = 0
# a modifier?
elif (entry.tag in {
'DW_TAG_typedef',
+13 -3
View File
@@ -422,9 +422,17 @@ def collect_structs(obj_paths, *,
if entry.off in sizeof.cache:
return sizeof.cache[entry.off]
# explicit size?
if 'DW_AT_byte_size' in entry:
# pointer? base type?
if entry.tag in {
'DW_TAG_pointer_type',
'DW_TAG_base_type'}:
size = int(entry['DW_AT_byte_size'])
# struct? union?
elif entry.tag in {
'DW_TAG_structure_type',
'DW_TAG_union_type'}:
# note structs/unions can be incomplete
size = int(entry.get('DW_AT_byte_size', 0))
# array? multiply by size
elif entry.tag == 'DW_TAG_array_type':
type = info[int(entry['DW_AT_type'].strip('<>'), 0)]
@@ -469,7 +477,9 @@ def collect_structs(obj_paths, *,
elif entry.tag in {
'DW_TAG_structure_type',
'DW_TAG_union_type'}:
align = max(alignof(child) for child in entry.children)
align = max(
(alignof(child) for child in entry.children),
default=0)
# indirect type?
elif entry.tag in {
'DW_TAG_typedef',