The toolbox
Some work is done once rather than in every build: bringing what a project already has into
DDD, or taking DDD’s descriptions somewhere else. That is what the toolbox is for. Its tools
are commands under ddd tool, and ddd tool from-elf is the first.
From an ELF image
A project that adopts DDD rarely starts from nothing: its variables already exist, in C,
compiled into an image. ddd tool from-elf reads a linked ELF image and its DWARF debug
information, and prints the DDD declaration of every C variable it is asked for - as far as
the image states it, and checked by DDD itself before it is printed, the way
ddd check --standalone checks a component. It needs an image built with debug information
(-g); pyelftools, which reads it, is installed with DDD.
A variable is named as it is in C, or matched with a glob such as 'Cal_*', and a
static that several compilation units define is taken from one of them by naming its unit
first, cal.c:Gain. What the tool prints is the interface of a component, ready to paste
into one; examples/firmware/firmware.elf is an image of the tests’ fixture, built for a
Cortex-M4:
$ ddd tool from-elf examples/firmware/firmware.elf Cal_Gain Enum_State
[
{
"scope": "output",
"definition": {
"name": "Cal_Gain",
"kind": "parameter",
"datatype": "uint16",
"conversion": {
"kind": "identity"
},
"init": 300,
"volatile": false
}
},
{
"scope": "output",
"definition": {
"name": "Enum_State",
"kind": "measurement",
"datatype": "uint8",
"conversion": {
"kind": "enum",
"name": "State_t",
"enumerators": {
"STATE_OFF": 0,
"STATE_ON": 1,
"STATE_FAULT": 200
}
},
"init": 1,
"volatile": false
}
}
]
examples/firmware/firmware.elf: info[elf-not-inferred]: an image states no unit, description, limits, scaling or id, so the output states none: every conversion but an enum's is the identity, the limits are the ones DDD derives, and 'ddd id --assign' writes the ids
1 info
$ echo $?
0
Enum_State is a uint8 because this target makes an enum as small as its values: the
datatype is the one the image states, never the one its C spelling suggests elsewhere.
--scope input prints the same entries as a consumer states them, without the initial value
and the section; -o FILE writes them into a file instead of standard output.
What an image states, and what it does not
key |
where it comes from |
|---|---|
|
the variable’s |
|
the encoding and size DWARF states: |
|
the identity, or an |
|
the array’s extents, in C order. |
|
the image’s own bytes, in its byte order; an array of one value as that value, a
|
|
stated only where the variable’s section is not one of the toolchain’s defaults. It is the image’s output section, which is the name the source used when the linker script kept it, and the project has to declare it in a sections file. |
|
the qualifier. |
|
a structure, its members, bitfields and nested structures, one |
|
nothing in an image states them. They are left out, and the run says so once:
|
Structures
A variable of a structure names it as its typename, and --component NAME prints a whole
component file, with a types entry for every structure the variables reach:
$ ddd tool from-elf examples/firmware/firmware.elf Struct_Inlet --component Inlet
{
"component": {
"name": "Inlet",
"types": [
...
"interface": [
{
"scope": "output",
"definition": {
"name": "Struct_Inlet",
"kind": "measurement",
"typename": "Inlet_t",
"volatile": false
}
}
]
}
}
main.c:57: warning[elf-name-synthesized]: an anonymous structure is named 'Inlet_t_pair_t', after the first thing that reaches it; rename it if the source has a better name
examples/firmware/firmware.elf: info[elf-not-inferred]: an image states no unit, description, limits, scaling or id, so the output states none: every conversion but an enum's is the identity, the limits are the ones DDD derives, and 'ddd id --assign' writes the ids
main.c:63: info[elf-boolean-bitfield]: 'Inlet_t.ready' is a _Bool bitfield, described as a uint8 one of the same width: DDD refuses a boolean bitfield
1 warning, 2 infos
The list output has nowhere to put a types entry, and says so (elf-types-omitted). A
structure’s entry belongs once in a project: where two components need it, move it into a
shared types file rather than pasting it into both.
A structure is named by the typedef closest to it, else by its tag, and an anonymous one after
the first thing that reaches it (elf-name-synthesized). Two structures of one name that
differ - one per compilation unit, each unit’s own - are elf-type-conflict, and no
variable reaching either is printed. A _Bool bitfield is described as a uint8 one
(elf-boolean-bitfield), since DDD refuses a boolean bitfield, and const or volatile
on a member is dropped (elf-qualifier-dropped): DDD qualifies whole objects. A structured
object states no initial value, as DDD requires; values the image holds for one are dropped
and said to be (elf-init-dropped).
The layout of a structure
DDD states member order, datatypes and widths, never offsets, and the compiler lays out the
structure it generates. Nothing in the a2l depends on that, but the structure DDD generates can
lay out differently from the source’s where the source asked for a layout its members do not
imply - and DWARF records none of those requests directly. The tool warns about the two traces
it can read: bits skipped where the next bitfield would have fit, which an unnamed or zero
width bitfield leaves (elf-bitfield-gap), and an alignment the source states
(elf-alignment). Packing - #pragma pack, packed - is not detected: whether an
offset is packed or merely the target’s own alignment depends on the ABI.
What the tool cannot describe
A variable that cannot be described is not printed, and a run with one exits 1 and writes
nothing, unless --force asks for what could be described:
$ ddd tool from-elf examples/firmware/firmware.elf Type_Pointer Tls_Counter Cal_Gain
main.c:106: error[elf-no-storage]: 'Tls_Counter' has no address in the image: it is thread-local, with an address of its own in every thread
main.c:38: error[elf-type-unsupported]: 'Type_Pointer' is a pointer, which DDD cannot state
examples/firmware/firmware.elf: info[elf-not-inferred]: an image states no unit, description, limits, scaling or id, so the output states none: every conversion but an enum's is the identity, the limits are the ones DDD derives, and 'ddd id --assign' writes the ids
2 errors, 1 info
$ echo $?
1
A finding about a variable is shown at its declaration in the C source, as the image recorded
the path. An image that cannot be used at all - not ELF, without DWARF, a relocatable object
rather than a linked image, one whose types sit in DWARF type units (-fdebug-types-section),
one built with gcc’s link-time optimisation (-flto), one whose debug information is
compressed with zstd rather than zlib (-gz=zlib), or a damaged file - is a usage error,
exit 2, and the message names the flag to build it with or without where one would do. So
are a malformed argument and an -o naming the image, refused before the image is read.
The findings
They are the tool’s own rather than checks of the catalogue: ddd checks does not list them
and -W does not take them. DDD’s own findings on the declarations are reported beside them
under DDD’s identifiers.
finding |
severity |
when |
|---|---|---|
|
error |
a name or a pattern matches no variable of the image’s debug information; where the
symbol table holds the name and no unit’s debug information does, the unit defining it
was built without |
|
error |
several compilation units define the name, other than as one variable at one
address; |
|
error |
the variable has no address: only declared, folded into a constant or removed by the compiler, thread-local, at a location that is not a fixed address, discarded by the linker - which leaves its address at 0, or at all ones, where no symbol of its name sits - or at an address no section of the image holds |
|
error |
a type DDD cannot state - a pointer, a union, a |
|
error |
one name stands for two different structures or enums |
|
error |
an initial value DDD has no spelling for, such as NaN, or one whose bytes run past the end of the section holding it |
|
warning |
a structured object whose values the image holds |
|
warning |
bits skipped where the next bitfield would have fit |
|
warning |
an alignment the source states |
|
warning |
|
|
warning |
a section the output states, once per name |
|
warning |
an anonymous structure or enum given a name |
|
warning |
the list output holds a structured object, whose type only |
|
info |
a |
|
info |
once per run: what an image never states |