DanielT / a2ltool

A tool to edit, merge and update a2l files
Apache License 2.0
46 stars 15 forks source link

Q: Array support #15

Closed oleid closed 8 months ago

oleid commented 8 months ago

Thanks for writing this tool! Looks great, so far!

I was trying to update the addresses of an existing file, which somebody produced with a tool called CANape. Apparently, the person mapped the individual elements of an array to individual measurements:

   /begin MEASUREMENT SomeVoltage._0_ ""
      UWORD NO_COMPU_METHOD 0 0 0 65535
      ECU_ADDRESS 0x4001B852
      ECU_ADDRESS_EXTENSION 0x0
      FORMAT "%.15"
      /begin IF_DATA CANAPE_EXT
        100
        LINK_MAP "SomeVoltage._0_" 0x4001B852 0x0 0 0x0 1 0x8F 0x0
        DISPLAY 0 0 65535
      /end IF_DATA
      SYMBOL_LINK "SomeVoltage._0_" 0
      PHYS_UNIT "mV"
    /end MEASUREMENT

     /begin MEASUREMENT SomeVoltage._1_ ""
      UWORD NO_COMPU_METHOD 0 0 0 65535
      ECU_ADDRESS 0x4001B854
      ECU_ADDRESS_EXTENSION 0x0
      FORMAT "%.15"
      /begin IF_DATA CANAPE_EXT
        100
        LINK_MAP "SomeVoltage._1_" 0x4001B854 0x0 0 0x0 1 0x8F 0x0
        DISPLAY 0 0 65535
      /end IF_DATA
      SYMBOL_LINK "SomeVoltage._1_" 0
      PHYS_UNIT "mV"
    /end MEASUREMENT

and so on. The elf file lists SomeVoltage (dumped with the objdump example of the objectcrate):

 Symbol { name: "SomeVoltage", address: 40018f0a, size: a0, kind: Data, section: Section(SectionIndex(b)), scope: Compilation, weak: false, flags: Elf { st_info: 1, st_other: 0 } }

But apparently, the entries get removed when running --update.

From the output of --help I figured the format of the entries is possibly wrong, thus, I tried adding an array value as follows:

$ a2ltool Comboard2.a2l --elffile a.out --measurement SomeVoltage[0] --output Comboard3.a2l --verbose
[...]
Insert skipped: Symbol SomeVoltage[0] could not be added: Remaining portion "[0]" of "SomeVoltage.[0]" could not be matched

So I keep wondering what the correct way of arrays is. Any help would be appreciated.

DanielT commented 8 months ago

You're welcome.

I'm familiar with Canape. I would assume that most people who are interested in a2ltool will be using Vector tools (Canape or Canoe.XCP) at some point.

Writing arrays as "VarName._0_" instead of "VarName[0]" is the old, backward compatible notation, because older file versions (<1.6) don't accept "[" and "]" as valid components of the variable name. Since then this has been added, so "VarName[0]" is allowed. Version 1.6 was standardized 13 years ago, so basically everyone should be using software that can handle both forms.

a2ltool also understands both notations, so I think your variable is not an array, or it was not recognized as an array when loading the debug data from the elf file. You can pass the "--debug-print" parameter to dump the dictionary of variables and types. You'll want to redirect the output to a text file if you do that, e.g. "a2ltool [...] --debug-print > output.txt" You can search for the name of the variable in the output, where you'll find something like this:

"MyGlobalVariable": VarInfo {
    address: 123456789,
    typeref: 11111111,
},

The typeref is a unique number identifying the type, which you can also search for, and then you might find something like this:

11111111: Array {
    size: 212,
    dim: [
        53,
    ],
    stride: 4,
    arraytype: Float,
}

This way you can check if the data type of your variable is an array, rather than a struct or a plain data type.

oleid commented 8 months ago

Thanks for your answer!

Indeed I found the VarInfo:

        "SomeVoltage": VarInfo {
            address: 1073843978,
            typeref: 300800,
        },

But I cannot find 300800 anywhere else in the output.

Digging through the generated type information, I find various types: different POD, enums, structs and so on, but no array at all.

The arrays are defined as follows in the C code:

static unsigned short SomeVoltage[NUM_OF_VOLTAGES];

Using llvm-dwarfdump-15 -a I can indeed see the type information:

0x000466d6:   DW_TAG_variable
                DW_AT_decl_file ("path/to/file.c")
                DW_AT_decl_line (32)
                DW_AT_decl_column   (23)
                DW_AT_name  ("SomeVoltage")
                DW_AT_type  (0x0000000000049700 "unsigned short[80]")
                DW_AT_location  (DW_OP_addr 0x40018f0a)

[...]

0x00049700:   DW_TAG_array_type
                DW_AT_sibling   (0x0004970c)
                DW_AT_type  (0x000000000004957c "unsigned short")

So I presume a2ltool or gimli for that matter should be able to identify the array correctly. I'll dig into the source code to check if I can find the issue.

DanielT commented 8 months ago

The code in typereader.rs reads the types from the DWARF info, the relevant code for arrays is on line 68 and following. For this code to work correctly, every array needs an array type, which I see in your dumped info, and a size attribute (DW_AT_byte_size), which I do not see. The byte size is needed to calculate the array length (array size / element size = length).

If you enable verbose output (-v) do you get any messages like this: "Error loading type info for variable SomeVoltage: error decoding array info: missing size attribute"?

oleid commented 8 months ago

If you enable verbose output (-v) do you get any messages like this: "Error loading type info for variable SomeVoltage: error decoding array info: missing size attribute"?

Indeed, I see lots of those messages including the SomeVoltage variable I mentioned before.

For this code to work correctly, every array needs an array type, which I see in your dumped info, and a size attribute (DW_AT_byte_size), which I do not see.

So the question boils down to why the llvm based tools ( I meanwhile checked llvm-symbolizer as well) manage to get a proper array length and gimli does not.

DanielT commented 8 months ago

The problem is not with gimli, which just provides the mechanism to read the debug info entries.

Rather, it looks like your compiler is emitting a different combination of attributes than the ones I have seen so far. Therefore the code in typereader.rs, which was written by me, is not handling your compilers output correctly.

Clearly the info about the array length must be in there somehow, since llvm-dwarfdump was able to display "unsigned short[80]"

oleid commented 8 months ago

The problem is not with gimli, which just provides the mechanism to read the debug info entries.

You're right, I got confused with gimli's dwarfdump example not printing the type info. Readelf was able to give me an idea how the information is encoded:

 <1><466d6>: Abbrev Number: 4 (DW_TAG_variable)
    <466d7>   DW_AT_decl_file   : 0x1
    <466db>   DW_AT_decl_line   : 32
    <466dc>   DW_AT_decl_column : 23
    <466dd>   DW_AT_name        : SomeVoltage
    <466ed>   DW_AT_type        : <0x49700>
    <466f1>   DW_AT_location    : 5 byte block: 3 40 1 8f a     (DW_OP_addr: 40018f0a)

[...]

 <1><49700>: Abbrev Number: 26 (DW_TAG_array_type)
    <49701>   DW_AT_sibling     : <0x4970c>
    <49705>   DW_AT_type        : <0x4957c>
 <2><49709>: Abbrev Number: 27 (DW_TAG_subrange_type)
    <4970a>   DW_AT_upper_bound : 79
 <2><4970b>: Abbrev Number: 0

Apparently, the length is encoded as child of the array type entry. The array is 80 elements long. According to https://developer.ibm.com/articles/au-dwarf-debug-format/, one can extract the array size as follows:

Extract array size

The immediate child of DW_TAG_array_type is DW_TAG_subrange_type, which has the array size. Array size is calculated as (DW_AT_upper_bound- DW_AT_lower_bound) +1. If it is a two-dimensional array, there will be an immediate sibling of type DW_TAG_subrange_type again. In this case, the array size is 8 (7+1).

EDIT:

Apparrently, there is already code in place to read the subrange type. As-is the code just assumes size is additionally set as an attribute.

EDIT2:

Okay, I've a local change that appears to work. Will send a PR soon.