What should a runtime DW_AT_lower_bound be reported as?

Open
#2,420 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
30/100
Issue type
Feature
Clarity
Clearly specified
Activity status
Active
Tech stack
c
Domain
compilers

Research direction

Start with the two alternatives in the issue: dwarf_subrange_bounds() in dwarf/src/dwarf_subrange.cpp and the parseSubrange() fallback in symtabAPI/src/dwarfWalker.C. Read the linked regression-test context and reproduce the runtime-bound cases under test_binaries/symtabAPI/DWARF/subrange_bounds. Done means the project decides how runtime lower bounds—and the related DW_AT_count cases—are represented without reporting a misleading value; this issue is design work, not a scoped implementation task.

Written by the indexing model from the issue text.

Description

Summary

When a subrange's DW_AT_lower_bound is a runtime value (a DWARF expression or a reference to a DIE, not a constant), it isn't clear what typeSubrange/typeArray should report as the lower bound. This issue is to decide that.

On master this doesn't show up yet, because any non-constant bound makes dwarf_subrange_bounds() return an error and the whole array type is dropped. Once the non-constant forms are parsed (#2418, and a follow-up that also covers the ref* forms and DW_AT_count), the question is live. Both of those currently give a runtime lower bound the language default (1 for Fortran), the same as a missing attribute.

Why the default is questionable

DWARF 5 §5.13 applies the language default only when DW_AT_lower_bound is absent. A present-but-runtime lower bound is a real value that just can't be evaluated statically. Reporting the default gives a wrong value that looks like a known one:

subroutine explicit_dyn(y, lo, hi)
  integer :: lo, hi
  real :: y(lo:hi)          ! DW_AT_lower_bound: ref4 -> artificial DW_TAG_variable
end subroutine

program p
  real, allocatable :: z(:)
  allocate(z(-2:5))         ! DW_AT_lower_bound: exprloc (DW_OP_push_object_address; DW_OP_plus_uconst 48; DW_OP_deref)
  call explicit_dyn(z, -2, 5)
end program

With gfortran -g -O0 (gcc 15.2), both y and z would be reported with lower bound 1; the actual lower bound is -2.

When gfortran knows the lower bound is 1 (e.g. assumed-shape x(:)), it leaves DW_AT_lower_bound out, so the default is correct there.

Options

  1. Keep the language default. No change from #2418. The value is wrong for allocatables and explicit-shape dummies.
  2. Report it as unknown, with LONG_MIN as the lower bound, matching parseSubrange()'s existing lower_bound.value_or(LONG_MIN) and the LONG_MAX used for an unknown upper bound. The only change needed is to apply the default only when !dwarf_hasattr_integrate(die, DW_AT_lower_bound), in both dwarf_subrange_bounds() and the parseSubrange() fallback. Caveats:
    • the bounds are stored as unsigned long;
    • typeArray::updateSize() computes hi_ - low_ + 1, which wraps to 0 for [LONG_MIN:LONG_MAX];
    • BPatch_type::getLow() users would see LONG_MIN where they used to see a small number.
  3. Keep the runtime description. Store the location expression or referenced DIE (e.g. as a VariableLocation list, the way variables are handled) so a consumer with a live process can evaluate it. This is the most complete option but needs an API addition on typeSubrange/typeArray.

The same question applies to a runtime DW_AT_count with a constant lower bound, and to a constant DW_AT_count counted from a runtime lower bound. Both currently end up as an unknown upper bound (LONG_MAX).

Reproducers

Test binaries covering the exprloc, block1 and ref4 forms of DW_AT_lower_bound, DW_AT_upper_bound and DW_AT_count (gcc/clang C99 VLAs, gfortran assumed-shape/allocatable/explicit-shape), with sources, are being added to test_binaries/symtabAPI/DWARF/subrange_bounds. The accompanying regression test deliberately doesn't check runtime lower bounds until this is settled.

Dominant language
C
Stars
836
Forks
177
Avg merge
3d 20h
Merged PRs (30d)
61

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from dyninst/dyninst

All issues in dyninst/dyninst

Similar issues

More C issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.