├── README.md ├── a2lUpdater.py └── objdump.exe /README.md: -------------------------------------------------------------------------------- 1 | # a2lupdater 2 | Python Script that updates addresses in A2L-Files out of the DWARF-Info of an elf-file - Automotive - XCP 3 | 4 | example: a2lUpdater.py elf-file.elf input.a2l output.a2l 5 | 6 | **the nice thing:** 7 | Supports strucure elements (this is why DWARF-Info is used) 8 | 9 | * currently this script needs gnu objdump.exe - therefore objdump.exe ist in git repo 10 | * currently this script only runs under windows (easy to adopt) 11 | * currently only *MEASUREMENT* and *CALIBRATION* labels are supportet 12 | * currently NO SUPPORT for "*SYMBOL_LINK*" 13 | * currently NO SUPPORT for "*LINK_MAP*" 14 | 15 | any comments are welcome 16 | 17 | Eduard 18 | -------------------------------------------------------------------------------- /a2lUpdater.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import re 3 | import subprocess 4 | import os 5 | import sys 6 | 7 | ################################################# 8 | ## Dwarf parsing... 9 | ################################################# 10 | 11 | dwarfArray = [] 12 | symTab = {} 13 | 14 | def parseSymbolTable(elfFileName): 15 | global symTab 16 | path = os.path.dirname(os.path.abspath(__file__)) 17 | ps = subprocess.Popen(path + "\\objdump -t " + elfFileName, stdout=subprocess.PIPE,stderr=subprocess.STDOUT) 18 | 19 | symTabStream = ps.communicate()[0] 20 | 21 | # Load symbols out of symfile generated by objdump 22 | lengths = {"":""} 23 | for line in symTabStream.split('\n'): 24 | match = re.match('(.*?)\ .*\t(.*?)\ (.*)', line) 25 | if hasattr(match,'group'): 26 | symTab[match.group(3).strip()] = match.group(1) 27 | lengths[match.group(3).strip()] = match.group(2) 28 | 29 | def parseDwarfOutput(elfFileName): 30 | path = os.path.dirname(os.path.abspath(__file__)) 31 | ps = subprocess.Popen(path + "\\objdump --dwarf=info " + elfFileName, stdout=subprocess.PIPE,stderr=subprocess.STDOUT) 32 | dwarf = ps.communicate()[0] 33 | 34 | deepth = 0 35 | addr = 0 36 | for line in dwarf.split('\n'): 37 | if line.strip().startswith("<"): 38 | new = 0 39 | first = re.match('.*<([0-9a-f]+)>(.*)',line) 40 | if first is None: 41 | continue 42 | addr = first.group(1) 43 | tuple = first.group(2) 44 | second = re.match('.*<([0-9a-f]+)><([0-9a-f]+)>:(.*)',line) 45 | if second is not None: 46 | new = 1 47 | deepth = second.group(1) 48 | addr = second.group(2) 49 | tuple = second.group(3) 50 | tupleArray = tuple.split(':',1) 51 | dwarfArray.append({"address" : int(addr, 16), "deepth" : deepth, "name" : tupleArray[0].strip(), "value" : tupleArray[1].strip(), "new" : new}) 52 | 53 | 54 | def getDwarfType(typeAddress): 55 | # find type at DIE-Address 56 | typeFound = 0 57 | retVal = {} 58 | for i in range(len(dwarfArray)): 59 | if (dwarfArray[i]["address"] == typeAddress): 60 | typeFound = i 61 | while(i < len(dwarfArray)): 62 | if 'DW_TAG_array_type' in dwarfArray[i]["value"]: 63 | #array type found 64 | retVal["array"] = "1" 65 | elif dwarfArray[i]["name"] == "DW_AT_name": 66 | #name of type 67 | retVal["name"] = dwarfArray[i]["value"] 68 | elif dwarfArray[i]["name"] == "DW_AT_type": 69 | #type of type (this is done, until basetype is referenced) 70 | dummy = getDwarfType(int(dwarfArray[i]["value"][1:-1],0)) 71 | retVal["type"] = dummy[1] 72 | elif dwarfArray[i]["name"] == "DW_AT_byte_size": 73 | #size of type 74 | retVal["size"] = dwarfArray[i]["value"] 75 | elif dwarfArray[i]["name"] == "DW_AT_data_member_location": 76 | #structre element found at location: 77 | match = re.match('.*DW_OP_plus_uconst:(.*)\)',dwarfArray[i]["value"]) 78 | if match is not None: 79 | retVal["offset"] = (match.group(1)) 80 | else: 81 | print ("") 82 | i = i+1; 83 | countElements = 0 84 | # search for sub-DIEs (structure elements) 85 | while i < len(dwarfArray) and dwarfArray[i]["new"] == 1 and dwarfArray[i]["deepth"] > dwarfArray[typeFound]["deepth"]: 86 | dummy = getDwarfType(dwarfArray[i]["address"]) 87 | i = dummy[0] 88 | if "name" in dummy[1]: 89 | #name of structure element 90 | retVal["%d" % (countElements)] = dummy[1] 91 | countElements += 1 92 | if i < len(dwarfArray) and dwarfArray[i]["new"] == 1 and dwarfArray[i]["deepth"] <= dwarfArray[typeFound]["deepth"]: 93 | #probably all subelements found, so save the count 94 | retVal["countElements"] = countElements 95 | break 96 | break 97 | return i, retVal 98 | 99 | def getDwarfVar(name): 100 | foundArray = None 101 | address = "" 102 | struct = 0 103 | for i in range(len(dwarfArray)): 104 | # Array or variablename? 105 | if dwarfArray[i]["name"] == "DW_AT_name" and dwarfArray[i]["value"] == name: 106 | foundArray = i 107 | #goto top of this DIE 108 | while i > 0 and dwarfArray[i]["new"] != 1: 109 | i -= 1 110 | i += 1 111 | #walk through the whole DIE 112 | while i < len(dwarfArray) and dwarfArray[i]["new"] != 1: 113 | if dwarfArray[i]["name"] == "DW_AT_location": 114 | struct = 1 115 | match = re.match(".*\(DW_OP_addr\:\ *([0-9a-f]+)\).*", dwarfArray[i]["value"]) 116 | if match is not None: 117 | address = match.group(1) 118 | break 119 | if dwarfArray[i]["name"] == "DW_AT_declaration" and dwarfArray[i]["value"] == "1": 120 | #skip deklaration 121 | foundArray = None 122 | break 123 | i += 1 124 | #print name + " ", 125 | if foundArray is not None: 126 | break 127 | #Variablename found, get type 128 | if foundArray is not None: 129 | currentDepth = dwarfArray[foundArray]["deepth"] 130 | i = foundArray 131 | #goto top of this DIE 132 | while i > 0 and dwarfArray[i]["new"] != 1: 133 | i -= 1 134 | i += 1 135 | #search for Type 136 | while dwarfArray[i]["deepth"] == currentDepth and dwarfArray[i]["new"] != 1 and dwarfArray[i]["name"] != "DW_AT_type": 137 | i+=1 138 | #get type DIE-address 139 | typeAddr = int(dwarfArray[i]["value"][1:-1],0) 140 | #get type at DIE-address 141 | type = getDwarfType(typeAddr) 142 | if struct == 1: 143 | type[1]["struct"] = 1 144 | type[1]["address"] = address 145 | return type[1] 146 | 147 | 148 | def printDwarfVar(FoundType, baseAddr, name): 149 | if "name" in FoundType: 150 | name = FoundType["name"] 151 | if "type" in FoundType: 152 | print(FoundType["type"]["name"] + " " + name + ";\t/* " + FoundType["type"]["size"] + ", at 0x") 153 | if "address" in FoundType: 154 | print(FoundType["address"] + " */") 155 | else: 156 | print(baseAddr + " + " + FoundType["offset"] + " */") 157 | elif "struct" in FoundType: 158 | print( "struct " + name + " /* " + FoundType["size"] + ", at 0x" + FoundType["address"] + " */") 159 | print( "{") 160 | for i in range(0, FoundType["countElements"]): 161 | printDwarfVar(FoundType["%d" % (i)], FoundType["address"], name) 162 | print( "}") 163 | 164 | 165 | def findAddress(name, useSymbolTable=False): 166 | 167 | if useSymbolTable == True: 168 | global symTab 169 | if name in symTab: 170 | return "0x%x" % int(symTab[name], 16) 171 | else: 172 | print ("name: " + name + " not found ") 173 | return "0" 174 | 175 | else: 176 | if "." in name: 177 | #structure variable or array 178 | structPath = name.split('.') 179 | FoundVar = getDwarfVar(structPath[0]) 180 | address = int(FoundVar["address"], 16) 181 | for subLevel in structPath[1:]: 182 | if str.isdigit(subLevel[1:-1]) and subLevel[0] == "_" and subLevel[-1] == "_": 183 | #search for array index 184 | arrayIndex = int(subLevel[1:-1]) 185 | if FoundVar.has_key("array"): 186 | address += int(FoundVar["type"]["size"])*int(arrayIndex) 187 | FoundVar = FoundVar["type"] 188 | continue 189 | 190 | for i in range(0, FoundVar["countElements"]): 191 | # search for structure element 192 | if subLevel == FoundVar["%d" % (i)]["name"]: 193 | address += int(FoundVar["%d" % (i)]["offset"], 16) 194 | break 195 | return "0x%x" % (address) 196 | else: 197 | #normal variable 198 | FoundVar = getDwarfVar(name) 199 | if "address" in FoundVar: 200 | return "0x%x" % int(FoundVar["address"], 16) 201 | else: 202 | print ("name: " + name + " not found ") 203 | return "0" 204 | 205 | 206 | ################################################# 207 | ## A2L parsing... 208 | ################################################# 209 | 210 | 211 | a2lInput = "" 212 | trenner = [' ','[',']','(',')',',',';','=','\r','\n'] 213 | def getNextToken(pos, length): 214 | global a2lInput 215 | while pos < length: 216 | if a2lInput[pos:pos+2] == "/*": 217 | pos = pos + 2 218 | startpos = pos 219 | while pos < length and not a2lInput[pos:pos+2] == '*/': 220 | pos = pos + 1 221 | pos = pos+2 222 | elif a2lInput[pos] == '"': 223 | pos = pos + 1 224 | startpos = pos 225 | while pos < length and not a2lInput[pos] == '"': 226 | pos = pos + 1 227 | return [pos+1, "STRING", a2lInput[startpos:pos]] 228 | elif a2lInput[pos:pos+6] == "/begin": 229 | return [pos+6, "BEGIN", ""] 230 | elif a2lInput[pos:pos+4] == "/end": 231 | return [pos+4, "END", ""] 232 | elif a2lInput[pos].isspace() or a2lInput[pos] in trenner: 233 | pos = pos + 1 234 | else: 235 | startpos = pos 236 | while pos < length and not a2lInput[pos] in trenner: 237 | pos = pos + 1 238 | return [pos, "OUTLINE", a2lInput[startpos:pos]] 239 | return [pos,"END", ""] 240 | 241 | def updateA2L(fileName, useSymbolTable=False): 242 | a2lInputFile = open(fileName, "r") 243 | global a2lInput 244 | a2lInput = a2lInputFile.read() 245 | length = len(a2lInput) 246 | output = "" 247 | 248 | pos = 0 249 | lastPos = 0 250 | ignore = 0 251 | while pos < length: 252 | [pos, Token, outline] = getNextToken(pos, length) 253 | if pos < length: 254 | [pos2, Token2, outline2] = getNextToken(pos, length) 255 | else: 256 | pos2 = length 257 | Token2 = "" 258 | outline2 = "" 259 | if Token == "BEGIN": 260 | [pos, tt, blockname] = getNextToken(pos, length) 261 | if blockname == "CHARACTERISTIC": 262 | output += a2lInput[lastPos:pos] 263 | [pos, tt, blockname] = getNextToken(pos, length) 264 | name = blockname 265 | [pos, tt, blockname] = getNextToken(pos, length) 266 | Long = blockname 267 | [pos, tt, blockname] = getNextToken(pos, length) 268 | Type = blockname 269 | [pos, tt, blockname] = getNextToken(pos, length) 270 | Addr = blockname 271 | lastPos = pos 272 | output += "\n" + name + "\n\"" + Long + "\"\n" + Type + "\n " + findAddress(name, useSymbolTable) + "\n" 273 | elif blockname == "MEASUREMENT": 274 | output += a2lInput[lastPos:pos] 275 | [pos, tt, blockname] = getNextToken(pos, length) 276 | name = blockname 277 | [pos, tt, blockname] = getNextToken(pos, length) 278 | Long = blockname 279 | output += "\n" + name + "\n\"" + Long + "\"\n" 280 | lastPos = pos 281 | while blockname != "ECU_ADDRESS": 282 | [pos, tt, blockname] = getNextToken(pos, length) 283 | output += a2lInput[lastPos:pos] 284 | lastPos = pos 285 | [pos, tt, blockname] = getNextToken(pos, length) 286 | lastPos = pos 287 | output += "\t " + findAddress(name, useSymbolTable) + "\n" 288 | else: 289 | pass 290 | output += a2lInput[lastPos:pos] 291 | lastPos = pos 292 | return output 293 | 294 | 295 | 296 | from optparse import OptionParser 297 | 298 | usage = """ 299 | 300 | %prog [options] elf-file a2l-input a2l-output 301 | """ 302 | 303 | parser = OptionParser(usage=usage) 304 | parser.add_option("-s", "--useSymbolTable", action="store_true", 305 | dest="useSymbolTable", default=False, 306 | help="use symboltable of elf-file (much faster, but without support for structs)") 307 | (cmdlineOptions, args) = parser.parse_args() 308 | 309 | 310 | if len(args) < 2: 311 | parser.print_help() 312 | sys.exit(1) 313 | 314 | print ("parsing elf file ... ") 315 | if cmdlineOptions.useSymbolTable == True: 316 | parseSymbolTable(args[0]) 317 | else: 318 | parseDwarfOutput(args[0]) 319 | print ("done") 320 | newA2l = updateA2L(args[1], cmdlineOptions.useSymbolTable) 321 | newA2lFile = open(args[2], "w") 322 | newA2lFile.write(newA2l) 323 | newA2lFile.close() 324 | -------------------------------------------------------------------------------- /objdump.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ebroecker/a2lupdater/5af8b136e9fc2f0c1ad23f95d08d56e6a58a1fd2/objdump.exe --------------------------------------------------------------------------------