llvm::DWARFUnit::getDIEForOffset returns invalid DWARFDie for TI's armcl produced elf

This post is about the llvm/DebugInfo/DWARF library. I want to write a tool to get the type information of a specific C variable in an elf. For example:

// test.c
int myVariable = 3;
int main() { return myVariable; }
some_compiler -g test.c -o test.out
mytool test.out myVariable
# should print that myVariable has type int, the size of int, etc

I read the llvm-dwarfdump source code and made this tool that worked on gcc DWARF32 version 5 elf, but it failed to work on elf produced by the armcl compiler embedded in TI’s CCS IDE. The debug info has format DWARF32 version 3. Here is the elf produced by armcl with the above test.c: test.out

The code of my tool:

// die must have attribute DW_AT_type, return that type's DWARFDie
llvm::DWARFDie GetDW_AT_type(llvm::DWARFDie die);

void FindVariableType() {
  lvm::DWARFDie variable = FindVariableByName("myVariable");
  // variable.dump(); // OK, variable.dump() does print the DW_TAG_variable's info
  auto typeDie = GetDW_AT_type(variable);
  assert(typeDie.isValid()); // fail
}
llvm::DWARFDie GetDW_AT_type(llvm::DWARFDie die) {
  // from llvm::DWARFFormValue::dump
  auto offset = die.find(llvm::dwarf::DW_AT_type)->getRawValue(); // 0x1e4 for this case
  auto unit = die.getDwarfUnit();
  auto unitOffset = unit->getOffset(); // 0 for this case
  return unit->getDIEForOffset(offset + unitOffset);
}

If variable.dump() is uncommented, the following lines are printed:

0x00000099: DW_TAG_variable
              DW_AT_location    (DW_OP_addr 0x208)
              DW_AT_name        ("myVariable")
              DW_AT_decl_column (0x05)
              DW_AT_decl_file   ("some_path/test.c")
              DW_AT_decl_line   (2)
              DW_AT_external    (0x01)
              DW_AT_type        (0x00000000000001e4 "int")
              DW_AT_MIPS_fde    ("myVariable")

and here is the section of int that llvm-dwarfdump prints for the same elf:

0x000001e4:   DW_TAG_base_type
                DW_AT_name	("int")
                DW_AT_byte_size	(0x04)
                DW_AT_encoding	(DW_ATE_signed)

The problem is llvm::DWARFUnit::getDIEForOffset() returns an invalid DWARFDie although that DIE should exist. Is my GetDW_AT_type implementation correct or is it a llvm library bug? The llvm libraries I’m using has version 17.0.6.

I used llvm-dwardump to dump the test.out you provided. It looks like myVariable is in the first CU, but its DW_AT_type (offset 0x1e4) is in a different unit. That’s probably why unit->getDIEForOffset() is returning an invalid DIE; there is no such DIE in that unit.

If you use --verbose to dump test.out, you’ll see that the DW_AT_type has form DW_FORM_ref_addr, meaning it is a reference that is relative to the section, not the unit, and probably contained in a different unit. You’ll need to use a different API to fetch that DIE. (I’m not super familiar with these APIs so I can’t point you to exactly what to use, but it shouldn’t be all that hard to figure out.)

1 Like

Thanks. You are right. There is a DWARFDie::getAttributeValueAsReferencedDie that can find the attribute value even if it’s in another DWARFUnit.

llvm::DWARFDie GetDW_AT_type(llvm::DWARFDie die) {
  return die.getAttributeValueAsReferencedDie(llvm::dwarf::DW_AT_type);
}