elf_mem_map 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. #!/usr/bin/env python3
  2. import argparse
  3. import elftools.elf.elffile
  4. import elftools.dwarf.descriptions
  5. from struct import unpack
  6. SRAM_OFFSET = 0x800000
  7. EEPROM_OFFSET = 0x810000
  8. FILL_BYTE = b'\0'
  9. def get_elf_globals(path):
  10. fd = open(path, "rb")
  11. if fd is None:
  12. return
  13. elffile = elftools.elf.elffile.ELFFile(fd)
  14. if elffile is None or not elffile.has_dwarf_info():
  15. return
  16. # probably not needed, since we're decoding expressions manually
  17. elftools.dwarf.descriptions.set_global_machine_arch(elffile.get_machine_arch())
  18. dwarfinfo = elffile.get_dwarf_info()
  19. grefs = []
  20. for CU in dwarfinfo.iter_CUs():
  21. for DIE in CU.iter_DIEs():
  22. # handle only variable types
  23. if DIE.tag != 'DW_TAG_variable':
  24. continue
  25. if 'DW_AT_name' not in DIE.attributes:
  26. continue
  27. if 'DW_AT_location' not in DIE.attributes:
  28. continue
  29. if 'DW_AT_type' not in DIE.attributes:
  30. continue
  31. # handle locations encoded directly as DW_OP_addr (leaf globals)
  32. at_loc = DIE.attributes['DW_AT_location']
  33. if at_loc.form != 'DW_FORM_block1' or at_loc.value[0] != 3:
  34. continue
  35. loc = (at_loc.value[1]) + (at_loc.value[2] << 8) \
  36. + (at_loc.value[3] << 16) + (at_loc.value[4] << 24)
  37. if loc < SRAM_OFFSET or loc >= EEPROM_OFFSET:
  38. continue
  39. loc -= SRAM_OFFSET
  40. # variable name
  41. name = DIE.attributes['DW_AT_name'].value.decode('ascii')
  42. # recurse on type to find the leaf definition
  43. type_DIE = DIE
  44. while 'DW_AT_type' in type_DIE.attributes:
  45. type_DIE = type_DIE.get_DIE_from_attribute('DW_AT_type')
  46. byte_size = type_DIE.attributes.get('DW_AT_byte_size')
  47. if byte_size is None:
  48. continue
  49. size = byte_size.value
  50. grefs.append([name, loc, size])
  51. return grefs
  52. def decode_dump(path):
  53. fd = open(path, 'r')
  54. if fd is None:
  55. return None
  56. buf_addr = None # starting address
  57. buf_data = None # data
  58. for line in fd:
  59. tokens = line.split(maxsplit=1)
  60. if len(tokens) == 0 or tokens[0] == 'ok':
  61. break
  62. elif len(tokens) < 2 or tokens[0] == 'D2':
  63. continue
  64. addr = int.from_bytes(bytes.fromhex(tokens[0]), 'big')
  65. data = bytes.fromhex(tokens[1])
  66. if buf_addr is None:
  67. buf_addr = addr
  68. buf_data = data
  69. else:
  70. # grow buffer as needed
  71. if addr < buf_addr:
  72. buf_data = FILL_BYTE * (buf_addr - addr)
  73. buf_addr = addr
  74. addr_end = addr + len(data)
  75. buf_end = buf_addr + len(buf_data)
  76. if addr_end > buf_end:
  77. buf_data += FILL_BYTE * (addr_end - buf_end)
  78. # replace new part
  79. rep_start = addr - buf_addr
  80. rep_end = rep_start + len(data)
  81. buf_data = buf_data[:rep_start] + data + buf_data[rep_end:]
  82. return (buf_addr, buf_data)
  83. def annotate_refs(grefs, addr, data, width=45):
  84. for name, loc, size in grefs:
  85. if loc < addr:
  86. continue
  87. if loc + size > addr + len(data):
  88. continue
  89. pos = loc-addr
  90. buf = data[pos:pos+size]
  91. buf_repr = ''
  92. if len(buf) in [1, 2, 4]:
  93. # attempt to decode as integers
  94. buf_repr += ' I:' + str(int.from_bytes(buf, 'big')).rjust(10)
  95. if len(buf) in [4, 8]:
  96. # attempt to decode as floats
  97. buf_repr += ' F:' + '{:10.3f}'.format(unpack('f', buf)[0])
  98. print('{:04x} {} {:4}{} R:{}'.format(loc, name.ljust(width), size,
  99. buf_repr, buf.hex()))
  100. def print_map(grefs):
  101. print('OFFSET\tSIZE\tNAME')
  102. for name, loc, size in grefs:
  103. print('{:x}\t{}\t{}'.format(loc, size, name))
  104. def main():
  105. ap = argparse.ArgumentParser(description="""
  106. Generate a symbol table map starting directly from an ELF
  107. firmware with DWARF2 debugging information.
  108. When used along with a memory dump obtained from the D2 g-code,
  109. show the value of each symbol which is within the address range.
  110. """)
  111. ap.add_argument('elf', help='ELF file containing DWARF2 debugging information')
  112. g = ap.add_mutually_exclusive_group(required=True)
  113. g.add_argument('dump', nargs='?', help='RAM dump obtained from D2 g-code')
  114. g.add_argument('--map', action='store_true', help='dump global memory map')
  115. args = ap.parse_args()
  116. grefs = get_elf_globals(args.elf)
  117. grefs = list(sorted(grefs, key=lambda x: x[1]))
  118. if args.dump is None:
  119. print_map(grefs)
  120. else:
  121. addr, data = decode_dump(args.dump)
  122. annotate_refs(grefs, addr, data)
  123. if __name__ == '__main__':
  124. exit(main())