Skip to content

[DWARF Import] Reconstruct clone signatures from parameter entry locations - #8716

Open
bpotchik wants to merge 1 commit into
devfrom
fix_dwarf_clone_signatures
Open

bpotchik wants to merge 1 commit into
devfrom
fix_dwarf_clone_signatures

Conversation

@bpotchik

@bpotchik bpotchik commented Nov 9, 2026

Copy link
Copy Markdown
Member

GCC's .isra, .constprop and .part clones can drop, reorder or replace parameters, but their DWARF entries point back (DW_AT_abstract_origin) to the original declaration. We used that declaration's parameter list, so every parameter after a dropped one was assigned to the wrong register.

This change rebuilds a clone's signature from where each parameter actually arrives at the function's entry point. The DWARF register numbers are mapped to the platform's argument registers. In the result:

  • a parameter GCC removed (DW_OP_GNU_parameter_ref) takes no slot;
  • a slot that is passed but has no DWARF entry becomes an unnamed gap;
  • when a parameter was removed, unnamed zero-confidence placeholders pad the signature back to the source parameter count, because DWARF only gives a lower bound on the clone's real arity.

Anything that can't be placed with confidence keeps the current behavior:

  • stack arguments, pieces and constants;
  • a parameter with no location at entry;
  • a return type that may be an aggregate, resolved through DW_AT_abstract_origin and DW_AT_specification;
  • split functions (DW_AT_ranges or DW_AT_entry_pc);
  • attributes or expressions that can't be read;
  • any architecture other than x86-64.

Example: _set_status_v2.isra.0 in Xen 4.20 (xen-syms, 0xffff82d0402212d0). GCC dropped rd, the third parameter, so everything after it moved up one register.

before: int(shah, status, struct domain* rd, struct active_grant_entry* act, int readonly, int mapflag, domid_t ldomid)
rdi rsi rdx ✗ rcx ✗ r8 ✗ r9 ✗ stack ✗
after: int(shah, status, struct active_grant_entry* act, int readonly, int mapflag, domid_t ldomid, uint64_t arg7)
rdi rsi rdx ✓ rcx ✓ r8 ✓ r9 ✓ placeholder

The body now reads act->pin from rdx as a struct active_grant_entry. Before, that register was named rd and typed as a struct domain*.

…tions

A concrete function instance (DW_AT_abstract_origin) can take different
arguments than its source declaration: GCC's .isra, .constprop, and .part
clones remove, reorder, or replace parameters. Rebuild the signature of such
an instance from where each parameter is at its entry PC instead of from the
declaration, mapping DWARF registers to the platform's argument slots.

A register-sized scalar on the stack (DW_OP_fbreg from a CFA frame base, or
the stack pointer at entry) takes the stack argument slot after the argument
registers, past any space reserved for them (Win64).

A passed slot with no DIE becomes an unnamed gap, and a parameter GCC
removed (DW_OP_GNU_parameter_ref, including a location list made only of
such entries that does not cover the entry PC) takes no slot. When a
parameter was removed, unnamed placeholders pad the signature to the source
parameter count, since DWARF only gives a lower bound on the clone's arity:
a trailing replacement piece can be passed with no DIE and no call-site
parameter describing it.

Anything that cannot be placed with confidence keeps the existing behavior:
aggregates or misaligned values on the stack, pieces, constants, a parameter
without a location at entry, a possibly aggregate return (resolved through
abstract origins and specifications), split functions, unreadable attributes
or expressions, and architectures other than x86-64.
@bpotchik bpotchik added this to the Langara milestone Nov 9, 2026
@bpotchik
bpotchik requested a review from ElykDeer November 9, 2026 19:26
@bpotchik bpotchik self-assigned this Nov 9, 2026

@ElykDeer ElykDeer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general shape here is good, but it's got some rough edges.

  • You've hardcoded X86_64_DWARF_REGISTERS, when gimli has gimli::X86_64::name_to_register() / register_name() that'll let you resolve that for all(?) architectures we care about, and be resilient to spec changes. This is probably the most important to address.
  • The raw stack offset can flow to this index iteration here and potentially cause a very large allocation. Add a setting to filter very large offsets? Or just choose something reasonable and document it?
  • You added a member variable to DebugInfoBuilderContext which should probably be initialized in the constructor rather than an optional extra call which could get missed and silently disable reconstruction some time in the future.
  • This is mostly a style note, caused me to raise an eyebrow so figured I'd mention it: You made a has_attribute which translates result values rather than using them directly. See how we handle similar patterns here and here. Clippy doesn't catch this one, so I don't think it needs to change. That said, clippy pedantic+nursery find a few other things you might want to consider.
  • One last finding from my bot that I wasn't able to verify: it seems convinced that with enough normal parameters followed by a floating point value, this code will place it on the stack rather than through the correct register. You may need to check the type's encoding to see if it can be passed on the stack.

Another design question: is it worth retaining the original arguments, or should this run and overwrite those so we only have one list to query/maintain?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants