Split DWARF is defined in sections 3.1.2, 3.1.3, 7.3.2 and 7.5.1 of the
DWARF Version 5 standard. The
compiler divides the debug information into two partitions: a small one that the linker
still processes and copies into the program, and the bulk, which goes into the
.dwo file and is never linked. All numbers are in the target’s byte order.
The tag, attribute and form codes are listed on the
tags, attributes and forms page.
Sections
A .dwo file is an object file in the same format as the program
(ELF on Linux) that holds only debug sections. Because
it is never linked, it has no relocations, and nothing in it may refer to a symbol:
every address it needs is fetched by index from the program’s
.debug_addr table.
First partition: stays in the object file and the program
| Section | Holds |
|---|---|
.debug_info | One skeleton unit per compilation unit (below). |
.debug_abbrev | The skeleton units’ abbreviations. |
.debug_addr | The address table: every address and relocated constant the split unit needs, referred to by index. |
.debug_line, .debug_line_str | The line-number tables, as usual. |
.debug_frame | Call frame information, as usual. |
.debug_names, .debug_aranges | The name index and address ranges, as usual. |
.debug_str | Strings, needed only if the skeleton uses an indirect string form. |
.debug_str_offsets | String offsets, needed only if the skeleton uses DW_FORM_strx forms. |
.debug_rnglists | Range lists for the skeleton’s own DW_AT_ranges, when it has one (not in the standard’s list of first-partition sections, but where that attribute points). |
Second partition: the .dwo file
| Section | Holds |
|---|---|
.debug_info.dwo | The full split compilation unit, and any split type units. |
.debug_abbrev.dwo | Abbreviation tables for those units. |
.debug_line.dwo | A cut-down line table for the type units: only its directory and file lists, used by DW_AT_decl_file. |
.debug_loclists.dwo | Location lists. |
.debug_rnglists.dwo | Range lists. |
.debug_macro.dwo | Macro information. |
.debug_str.dwo | The strings. |
.debug_str_offsets.dwo | Offsets into .debug_str.dwo, indexed by the DW_FORM_strx forms. |
The standard also allows the second partition to stay in the object file itself, in
the same .dwo-named sections, for the linker to leave out of the
program.
Unit headers
Every unit in .debug_info or .debug_info.dwo starts with a
header. In the 32-bit DWARF format unit_length is 4 bytes and offsets are 4
bytes; in the 64-bit format unit_length is 0xFFFFFFFF followed
by an 8-byte length, and offsets are 8 bytes. Offsets below are for the 32-bit
format.
Skeleton and split compilation units
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 4 | unit_length | Bytes in the unit after this field. Must be below 0xFFFFFFF0. |
| 4 | 2 | version | 5. |
| 6 | 1 | unit_type | 0x04 DW_UT_skeleton in the program; 0x05 DW_UT_split_compile in the .dwo. There is no split partial unit. |
| 7 | 1 | address_size | Bytes in an address on the target (4 or 8). |
| 8 | 4 | debug_abbrev_offset | Offset of the unit’s abbreviations in .debug_abbrev or .debug_abbrev.dwo. |
| 12 | 8 | dwo_id | The compilation unit ID: the same 8-byte value in the skeleton and its split unit, so a debugger can check that the files belong together and find the unit in a .dwp. How it is computed is up to the compiler; it should be suitable for detecting mismatched files. |
| 20 | The first debugging information entry. |
Split type units
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 4 | unit_length | As above. |
| 4 | 2 | version | 5. |
| 6 | 1 | unit_type | 0x06 DW_UT_split_type. |
| 7 | 1 | address_size | As above. |
| 8 | 4 | debug_abbrev_offset | Offset of its abbreviations in .debug_abbrev.dwo. |
| 12 | 8 | type_signature | The 8-byte signature of the type, which DW_FORM_ref_sig8 references use. |
| 20 | 4 | type_offset | Offset, from the start of this header, of the DIE that describes the type. |
The unit then holds a DW_TAG_type_unit entry; in a split type unit its
DW_AT_stmt_list points into .debug_line.dwo, and
DW_AT_str_offsets_base is not used.
The skeleton unit
The skeleton is a single DW_TAG_skeleton_unit entry with no children.
It holds what a debugger needs to find the .dwo file and to turn indexes
into addresses:
| Attribute | Meaning |
|---|---|
DW_AT_dwo_name | Required. Path of the file holding the split unit, absolute or relative to DW_AT_comp_dir. |
DW_AT_low_pc and DW_AT_high_pc, or DW_AT_ranges | The code the unit covers. |
DW_AT_stmt_list | Offset of its line table in .debug_line. |
DW_AT_comp_dir | The compilation directory. |
DW_AT_use_UTF8 | The skeleton’s own strings and its line table are UTF-8. |
DW_AT_str_offsets_base | Start of the skeleton’s entries in .debug_str_offsets. |
DW_AT_addr_base | Start of the unit’s entries in .debug_addr; the split unit’s DW_FORM_addrx forms and DW_OP_addrx/DW_OP_constx operations index from here. |
DW_AT_base_types is not allowed in a skeleton.
The split compilation unit
In .debug_info.dwo, a DW_TAG_compile_unit entry and all its
children: the whole description of the unit’s types, functions and variables. Its own
attributes may be:
| Attribute | Meaning |
|---|---|
DW_AT_name | The primary source file. |
DW_AT_language | The source language code. |
DW_AT_macros | Offset of its macro information in .debug_macro.dwo. |
DW_AT_producer | The compiler that made it. |
DW_AT_identifier_case | How identifiers’ case is treated. |
DW_AT_main_subprogram | The unit holds the program’s main subprogram. |
DW_AT_entry_pc | The unit’s entry address. |
DW_AT_use_UTF8 | Strings in the .dwo are UTF-8. |
These are not written in the split unit but taken from its skeleton:
DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges,
DW_AT_stmt_list, DW_AT_comp_dir,
DW_AT_str_offsets_base, DW_AT_addr_base and
DW_AT_rnglists_base. DW_AT_base_types is not allowed.
To find a split unit a debugger reads the skeleton, opens
DW_AT_dwo_name (relative to DW_AT_comp_dir), and checks that
the dwo_id fields match; if there is a .dwp
package, it looks the dwo_id up in its .debug_cu_index
instead.
Indexed forms
Since a .dwo cannot be relocated, it refers to addresses, strings and
lists by index:
| Form or operation | Operand | Refers to |
|---|---|---|
DW_FORM_addrx, addrx1–addrx4 | ULEB128, or 1 to 4 bytes | Entry n of the program’s .debug_addr, counted from the skeleton’s DW_AT_addr_base. |
DW_OP_addrx (0xA1), DW_OP_constx (0xA2) | ULEB128 | The same table, inside location expressions; constx is for constants that need link-time relocation. |
DW_FORM_strx, strx1–strx4 | ULEB128, or 1 to 4 bytes | Entry n of the unit’s offsets in .debug_str_offsets.dwo, which gives an offset into .debug_str.dwo. |
DW_FORM_loclistx | ULEB128 | Entry n of the offset array in .debug_loclists.dwo. Split units must use this rather than DW_FORM_sec_offset. |
DW_FORM_rnglistx | ULEB128 | Entry n of the offset array in .debug_rnglists.dwo; likewise required in split units. |
Offset and address tables
.debug_str_offsets and .debug_str_offsets.dwo
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 4 | unit_length | Bytes after this field (12 bytes in the 64-bit format). |
| 4 | 2 | version | 5. |
| 6 | 2 | padding | 0. |
| 8 | 4 each | offsets | String offsets, 4 bytes each (8 in the 64-bit format). DW_AT_str_offsets_base points at the first; entries count from 0. |
.debug_addr
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 4 | unit_length | Bytes after this field. |
| 4 | 2 | version | 5. |
| 6 | 1 | address_size | Bytes per address. |
| 7 | 1 | segment_selector_size | Bytes per segment selector; usually 0, and then entries are addresses only. |
| 8 | – | entries | Segment and address pairs. DW_AT_addr_base points at the first; entries count from 0. |
.debug_loclists(.dwo) and .debug_rnglists(.dwo)
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 4 | unit_length | Bytes after this field. |
| 4 | 2 | version | 5. |
| 6 | 1 | address_size | Bytes per address. |
| 7 | 1 | segment_selector_size | Bytes per segment selector, usually 0. |
| 8 | 4 | offset_entry_count | Number of offsets that follow; may be 0, in which case the x forms cannot be used. |
| 12 | 4 each | offsets | For list i, its offset from the start of this array (8 bytes each in the 64-bit format). DW_AT_loclists_base / DW_AT_rnglists_base point at the first. |
| – | – | lists | The lists, made of the entries below. |
Location and range list entries
Each entry starts with a 1-byte kind. In a location list, every entry that gives a
range (all but end-of-list and the two base-address kinds) is followed by a counted
location description: a ULEB128 length and that many bytes of DWARF expression. The
standard allows the kinds that take a target address (location codes 6 to 8, range
codes 5 to 7) only in non-split units, so a .dwo uses the index kinds
(x), offset pairs and the default location.
| Code | Location list (DW_LLE_) | Range list (DW_RLE_) | Operands |
|---|---|---|---|
0x00 | end_of_list | end_of_list | None; ends the list. |
0x01 | base_addressx | base_addressx | ULEB128 index into .debug_addr: the new base address. |
0x02 | startx_endx | startx_endx | Two ULEB128 indexes: start and end address. |
0x03 | startx_length | startx_length | ULEB128 index of the start, then a ULEB128 length. |
0x04 | offset_pair | offset_pair | Two ULEB128 offsets from the current base address: start and end. |
0x05 | default_location | base_address | Location: no range; the description applies wherever no other entry does. Range: an address, the new base. |
0x06 | base_address | start_end | Location: an address, the new base. Range: two addresses. |
0x07 | start_end | start_length | Location: two addresses. Range: an address and a ULEB128 length. |
0x08 | start_length | – | Location: an address and a ULEB128 length. |
Before DWARF 5: the GNU extension
Split DWARF started as a GNU extension to DWARF 4, before it was standardised. Its
package index (version 2) shows its
sections: type units in .debug_types.dwo, location lists in
.debug_loc.dwo, and macros in .debug_macinfo.dwo or
.debug_macro.dwo, besides the sections above. Its codes are vendor codes;
LLVM lists them as follows (the right-hand column is the nearest DWARF 5 item, by
name):
| Code | Name | Nearest DWARF 5 item |
|---|---|---|
0x2130 | DW_AT_GNU_dwo_name | DW_AT_dwo_name |
0x2131 | DW_AT_GNU_dwo_id | the dwo_id header field |
0x2132 | DW_AT_GNU_ranges_base | DW_AT_rnglists_base |
0x2133 | DW_AT_GNU_addr_base | DW_AT_addr_base |
0x2134 | DW_AT_GNU_pubnames | – |
0x2135 | DW_AT_GNU_pubtypes | – |
0x1F01 | DW_FORM_GNU_addr_index | DW_FORM_addrx |
0x1F02 | DW_FORM_GNU_str_index | DW_FORM_strx |
0xFB | DW_OP_GNU_addr_index | DW_OP_addrx |
0xFC | DW_OP_GNU_const_index | DW_OP_constx |
The unit header layouts and other details of the GNU form are described on the GCC wiki’s DebugFission pages, which could not be read for this page; they are not covered here.
Sources
DWARF Debugging Information Format,
Version 5 (DWARF Standards Committee, 2017): sections 3.1.2 and 3.1.3 (skeleton
and split units), 3.1.4 (type units), 7.3.2 (split DWARF object files), 7.5.1 (unit
headers), 7.5.5 (classes and forms), 7.26 to 7.29 (string offsets, address, range
list and location list tables), 2.6.2 and 2.17.3 (location and range list entries),
tables 7.2, 7.10 and 7.30. LLVM source BinaryFormat/Dwarf.def (the GNU
codes, DW_OP_addrx and DW_OP_constx) and
DWARFUnitIndex.cpp (the GNU package index sections).