espcoredump.py 78 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813
  1. #!/usr/bin/env python
  2. #
  3. # ESP32 core dump Utility
  4. from __future__ import print_function
  5. from __future__ import unicode_literals
  6. from __future__ import division
  7. from hashlib import sha256
  8. import sys
  9. try:
  10. from builtins import zip
  11. from builtins import str
  12. from builtins import range
  13. from past.utils import old_div
  14. from builtins import object
  15. except ImportError:
  16. print('Import has failed probably because of the missing "future" package. Please install all the packages for '
  17. 'interpreter {} from the $IDF_PATH/requirements.txt file.'.format(sys.executable))
  18. sys.exit(1)
  19. import os
  20. import argparse
  21. import subprocess
  22. import tempfile
  23. import struct
  24. import errno
  25. import base64
  26. import binascii
  27. import logging
  28. import re
  29. idf_path = os.getenv('IDF_PATH')
  30. if idf_path:
  31. sys.path.insert(0, os.path.join(idf_path, 'components', 'esptool_py', 'esptool'))
  32. try:
  33. import esptool
  34. except ImportError:
  35. print("esptool is not found! Set proper $IDF_PATH in environment.")
  36. sys.exit(2)
  37. __version__ = "0.4-dev"
  38. if os.name == 'nt':
  39. CLOSE_FDS = False
  40. else:
  41. CLOSE_FDS = True
  42. INVALID_CAUSE_VALUE = 0xFFFF
  43. # Exception cause dictionary to get translation of exccause register
  44. # From 4.4.1.5 table 4-64 Exception Causes of Xtensa
  45. # Instruction Set Architecture (ISA) Reference Manual
  46. xtensa_exception_cause_dict = {
  47. 0: ("IllegalInstructionCause", "Illegal instruction"),
  48. 1: ("SyscallCause", "SYSCALL instruction"),
  49. 2: ("InstructionFetchErrorCause", "Processor internal physical address or data error during instruction fetch. (See EXCVADDR for more information)"),
  50. 3: ("LoadStoreErrorCause", "Processor internal physical address or data error during load or store. (See EXCVADDR for more information)"),
  51. 4: ("Level1InterruptCause", "Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register"),
  52. 5: ("AllocaCause", "MOVSP instruction, if caller`s registers are not in the register file"),
  53. 6: ("IntegerDivideByZeroCause", "QUOS: QUOU, REMS: or REMU divisor operand is zero"),
  54. 8: ("PrivilegedCause", "Attempt to execute a privileged operation when CRING ? 0"),
  55. 9: ("LoadStoreAlignmentCause", "Load or store to an unaligned address. (See EXCVADDR for more information)"),
  56. 12: ("InstrPIFDataErrorCause", "PIF data error during instruction fetch. (See EXCVADDR for more information)"),
  57. 13: ("LoadStorePIFDataErrorCause", "Synchronous PIF data error during LoadStore access. (See EXCVADDR for more information)"),
  58. 14: ("InstrPIFAddrErrorCause", "PIF address error during instruction fetch. (See EXCVADDR for more information)"),
  59. 15: ("LoadStorePIFAddrErrorCause", "Synchronous PIF address error during LoadStore access. (See EXCVADDR for more information)"),
  60. 16: ("InstTLBMissCause", "Error during Instruction TLB refill. (See EXCVADDR for more information)"),
  61. 17: ("InstTLBMultiHitCause", "Multiple instruction TLB entries matched. (See EXCVADDR for more information)"),
  62. 18: ("InstFetchPrivilegeCause", "An instruction fetch referenced a virtual address at a ring level less than CRING. (See EXCVADDR for more information)"),
  63. 20: ("InstFetchProhibitedCause", "An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch (EXCVADDR)."),
  64. 24: ("LoadStoreTLBMissCause", "Error during TLB refill for a load or store. (See EXCVADDR for more information)"),
  65. 25: ("LoadStoreTLBMultiHitCause", "Multiple TLB entries matched for a load or store. (See EXCVADDR for more information)"),
  66. 26: ("LoadStorePrivilegeCause", "A load or store referenced a virtual address at a ring level less than CRING. (See EXCVADDR for more information)"),
  67. 28: ("LoadProhibitedCause", "A load referenced a page mapped with an attribute that does not permit loads. (See EXCVADDR for more information)"),
  68. 29: ("StoreProhibitedCause", "A store referenced a page mapped with an attribute that does not permit stores [Region Protection Option or MMU Option]."),
  69. 32: ("Coprocessor0Disabled", "Coprocessor 0 instruction when cp0 disabled"),
  70. 33: ("Coprocessor1Disabled", "Coprocessor 1 instruction when cp1 disabled"),
  71. 34: ("Coprocessor2Disabled", "Coprocessor 2 instruction when cp2 disabled"),
  72. 35: ("Coprocessor3Disabled", "Coprocessor 3 instruction when cp3 disabled"),
  73. 36: ("Coprocessor4Disabled", "Coprocessor 4 instruction when cp4 disabled"),
  74. 37: ("Coprocessor5Disabled", "Coprocessor 5 instruction when cp5 disabled"),
  75. 38: ("Coprocessor6Disabled", "Coprocessor 6 instruction when cp6 disabled"),
  76. 39: ("Coprocessor7Disabled", "Coprocessor 7 instruction when cp7 disabled"),
  77. INVALID_CAUSE_VALUE: ("InvalidCauseRegister", "Invalid EXCCAUSE register value or current task is broken and was skipped")}
  78. class ESPCoreDumpError(RuntimeError):
  79. """Core dump runtime error class
  80. """
  81. def __init__(self, message):
  82. """Constructor for core dump error
  83. """
  84. super(ESPCoreDumpError, self).__init__(message)
  85. class BinStruct(object):
  86. """Binary structure representation
  87. Subclasses must specify actual structure layout using 'fields' and 'format' members.
  88. For example, the following subclass represents structure with two fields:
  89. f1 of size 2 bytes and 4 bytes f2. Little endian.
  90. class SomeStruct(BinStruct):
  91. fields = ("f1",
  92. "f2")
  93. format = "<HL"
  94. Then subclass can be used to initialize fields of underlaying structure and convert it to binary representation:
  95. f = open('some_struct.bin', 'wb')
  96. s = SomeStruct()
  97. s.f1 = 1
  98. s.f2 = 10
  99. f.write(s.dump())
  100. f.close()
  101. """
  102. def __init__(self, buf=None):
  103. """Base constructor for binary structure objects
  104. """
  105. if buf is None:
  106. buf = b'\0' * self.sizeof()
  107. fields = struct.unpack(self.__class__.format, buf[:self.sizeof()])
  108. self.__dict__.update(zip(self.__class__.fields, fields))
  109. def sizeof(self):
  110. """Returns the size of the structure represented by specific subclass
  111. """
  112. return struct.calcsize(self.__class__.format)
  113. def dump(self):
  114. """Returns binary representation of structure
  115. """
  116. keys = self.__class__.fields
  117. return struct.pack(self.__class__.format, *(self.__dict__[k] for k in keys))
  118. class Elf32FileHeader(BinStruct):
  119. """ELF32 file header
  120. """
  121. fields = ("e_ident",
  122. "e_type",
  123. "e_machine",
  124. "e_version",
  125. "e_entry",
  126. "e_phoff",
  127. "e_shoff",
  128. "e_flags",
  129. "e_ehsize",
  130. "e_phentsize",
  131. "e_phnum",
  132. "e_shentsize",
  133. "e_shnum",
  134. "e_shstrndx")
  135. format = "<16sHHLLLLLHHHHHH"
  136. def __init__(self, buf=None):
  137. """Constructor for ELF32 file header structure
  138. """
  139. super(Elf32FileHeader, self).__init__(buf)
  140. if buf is None:
  141. # Fill in sane ELF header for LSB32
  142. self.e_ident = b"\x7fELF\1\1\1\0\0\0\0\0\0\0\0\0"
  143. self.e_version = ESPCoreDumpElfFile.EV_CURRENT
  144. self.e_ehsize = self.sizeof()
  145. class Elf32ProgramHeader(BinStruct):
  146. """ELF32 program header
  147. """
  148. fields = ("p_type",
  149. "p_offset",
  150. "p_vaddr",
  151. "p_paddr",
  152. "p_filesz",
  153. "p_memsz",
  154. "p_flags",
  155. "p_align")
  156. format = "<LLLLLLLL"
  157. class Elf32NoteDesc(object):
  158. """ELF32 note descriptor
  159. """
  160. def __init__(self, name, type, desc):
  161. """Constructor for ELF32 note descriptor
  162. """
  163. self.name = name
  164. self.type = type
  165. self.desc = desc
  166. def dump(self):
  167. """Returns binary representation of ELF32 note descriptor
  168. """
  169. nm_buf = bytearray(self.name, encoding='ascii') + b'\0'
  170. hdr = struct.pack("<LLL", len(nm_buf), len(self.desc), self.type)
  171. # pad for 4 byte alignment
  172. name = nm_buf + ((4 - len(nm_buf)) % 4) * b'\0'
  173. desc = self.desc + ((4 - len(self.desc)) % 4) * b'\0'
  174. return hdr + name + desc
  175. def read(self, data):
  176. """Reads ELF32 note descriptor
  177. """
  178. hdr_sz = struct.calcsize("<LLL")
  179. nm_len,desc_len,self.type = struct.unpack("<LLL", data[:hdr_sz])
  180. nm_len_a = nm_len + ((4 - nm_len) % 4)
  181. self.name = struct.unpack("<%ds" % (nm_len - 1), data[hdr_sz:hdr_sz + nm_len - 1])[0].decode('ascii')
  182. self.desc = data[hdr_sz + nm_len_a:hdr_sz + nm_len_a + desc_len]
  183. desc_len_a = desc_len + ((4 - desc_len) % 4)
  184. return hdr_sz + nm_len_a + desc_len_a
  185. class XtensaPrStatus(BinStruct):
  186. """Xtensa program status structure"""
  187. fields = ("si_signo", "si_code", "si_errno",
  188. "pr_cursig",
  189. "pr_pad0",
  190. "pr_sigpend",
  191. "pr_sighold",
  192. "pr_pid",
  193. "pr_ppid",
  194. "pr_pgrp",
  195. "pr_sid",
  196. "pr_utime",
  197. "pr_stime",
  198. "pr_cutime",
  199. "pr_cstime")
  200. format = "<3LHHLLLLLLQQQQ"
  201. class EspCoreDumpTaskStatus(BinStruct):
  202. """Core dump status structure"""
  203. # task status flags for note
  204. TASK_STATUS_CORRECT = 0x00
  205. TASK_STATUS_TCB_CORRUPTED = 0x01
  206. TASK_STATUS_STACK_CORRUPTED = 0x02
  207. fields = ("task_index",
  208. "task_flags",
  209. "task_tcb_addr",
  210. "task_stack_start",
  211. "task_stack_len",
  212. "task_name")
  213. format = "<LLLLL16s"
  214. class ESPCoreDumpSegment(esptool.ImageSegment):
  215. """ Wrapper class for a program segment in core ELF file, has a segment
  216. type and flags as well as the common properties of an ImageSegment.
  217. """
  218. # segment flags
  219. PF_X = 0x1 # Execute
  220. PF_W = 0x2 # Write
  221. PF_R = 0x4 # Read
  222. def __init__(self, addr, data, type, flags):
  223. """Constructor for program segment
  224. """
  225. super(ESPCoreDumpSegment, self).__init__(addr, data)
  226. self.flags = flags
  227. self.type = type
  228. def __repr__(self):
  229. """Returns string representation of program segment
  230. """
  231. return "%s %s %s" % (self.type, self.attr_str(), super(ESPCoreDumpSegment, self).__repr__())
  232. def attr_str(self):
  233. """Returns string representation of program segment attributes
  234. """
  235. str = ''
  236. if self.flags & self.PF_R:
  237. str += 'R'
  238. else:
  239. str += ' '
  240. if self.flags & self.PF_W:
  241. str += 'W'
  242. else:
  243. str += ' '
  244. if self.flags & self.PF_X:
  245. str += 'X'
  246. else:
  247. str += ' '
  248. return str
  249. class ESPCoreDumpSection(esptool.ELFSection):
  250. """ Wrapper class for a section in core ELF file, has a section
  251. flags as well as the common properties of an esptool.ELFSection.
  252. """
  253. # section flags
  254. SHF_WRITE = 0x1
  255. SHF_ALLOC = 0x2
  256. SHF_EXECINSTR = 0x4
  257. def __init__(self, name, addr, data, flags):
  258. """Constructor for section
  259. """
  260. super(ESPCoreDumpSection, self).__init__(name, addr, data)
  261. self.flags = flags
  262. def __repr__(self):
  263. """Returns string representation of section
  264. """
  265. return "%s %s" % (super(ESPCoreDumpSection, self).__repr__(), self.attr_str())
  266. def attr_str(self):
  267. """Returns string representation of section attributes
  268. """
  269. str = "R"
  270. if self.flags & self.SHF_WRITE:
  271. str += 'W'
  272. else:
  273. str += ' '
  274. if self.flags & self.SHF_EXECINSTR:
  275. str += 'X'
  276. else:
  277. str += ' '
  278. if self.flags & self.SHF_ALLOC:
  279. str += 'A'
  280. else:
  281. str += ' '
  282. return str
  283. class ESPCoreDumpElfFile(esptool.ELFFile):
  284. """ Wrapper class for core dump ELF file
  285. """
  286. # extra regs IDs used in EXTRA_INFO note
  287. REG_EXCCAUSE_IDX = 0
  288. REG_EXCVADDR_IDX = 1
  289. REG_EPS2_IDX = 2
  290. REG_EPS3_IDX = 3
  291. REG_EPS4_IDX = 4
  292. REG_EPS5_IDX = 5
  293. REG_EPS6_IDX = 6
  294. REG_EPS7_IDX = 7
  295. REG_EPC1_IDX = 8
  296. REG_EPC2_IDX = 9
  297. REG_EPC3_IDX = 10
  298. REG_EPC4_IDX = 11
  299. REG_EPC5_IDX = 12
  300. REG_EPC6_IDX = 13
  301. REG_EPC7_IDX = 14
  302. # ELF file type
  303. ET_NONE = 0x0 # No file type
  304. ET_REL = 0x1 # Relocatable file
  305. ET_EXEC = 0x2 # Executable file
  306. ET_DYN = 0x3 # Shared object file
  307. ET_CORE = 0x4 # Core file
  308. # ELF file version
  309. EV_NONE = 0x0
  310. EV_CURRENT = 0x1
  311. # ELF file machine type
  312. EM_NONE = 0x0
  313. EM_XTENSA = 0x5E
  314. # section types
  315. SEC_TYPE_PROGBITS = 0x01
  316. SEC_TYPE_STRTAB = 0x03
  317. # special section index
  318. SHN_UNDEF = 0x0
  319. # program segment types
  320. PT_NULL = 0x0
  321. PT_LOAD = 0x1
  322. PT_DYNAMIC = 0x2
  323. PT_INTERP = 0x3
  324. PT_NOTE = 0x4
  325. PT_SHLIB = 0x5
  326. PT_PHDR = 0x6
  327. def __init__(self, name=None):
  328. """Constructor for core dump ELF file
  329. """
  330. if name:
  331. super(ESPCoreDumpElfFile, self).__init__(name)
  332. else:
  333. self.sections = []
  334. self.program_segments = []
  335. self.aux_segments = []
  336. self.e_type = self.ET_NONE
  337. self.e_machine = self.EM_NONE
  338. def _read_elf_file(self, f):
  339. """Reads core dump from ELF file
  340. """
  341. # read the ELF file header
  342. LEN_FILE_HEADER = 0x34
  343. try:
  344. header = f.read(LEN_FILE_HEADER)
  345. (ident,type,machine,_version,
  346. self.entrypoint,phoff,shoff,_flags,
  347. _ehsize, phentsize,phnum,_shentsize,
  348. shnum,shstrndx) = struct.unpack("<16sHHLLLLLHHHHHH", header)
  349. except struct.error as e:
  350. raise ESPCoreDumpError("Failed to read a valid ELF header from %s: %s" % (f.name, e))
  351. if bytearray([ident[0]]) != b'\x7f' or ident[1:4] != b'ELF':
  352. raise ESPCoreDumpError("%s has invalid ELF magic header" % f.name)
  353. if machine != self.EM_XTENSA:
  354. raise ESPCoreDumpError("%s does not appear to be an Xtensa ELF file. e_machine=%04x" % (f.name, machine))
  355. self.e_type = type
  356. self.e_machine = machine
  357. self.sections = []
  358. self.program_segments = []
  359. self.aux_segments = []
  360. if shnum > 0:
  361. self._read_sections(f, shoff, shstrndx)
  362. if phnum > 0:
  363. self._read_program_segments(f, phoff, phentsize, phnum)
  364. def _read_sections(self, f, section_header_offs, shstrndx):
  365. """Reads core dump sections from ELF file
  366. """
  367. f.seek(section_header_offs)
  368. section_header = f.read()
  369. LEN_SEC_HEADER = 0x28
  370. if len(section_header) == 0:
  371. raise ESPCoreDumpError("No section header found at offset %04x in ELF file." % section_header_offs)
  372. if len(section_header) % LEN_SEC_HEADER != 0:
  373. logging.warning('Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER))
  374. # walk through the section header and extract all sections
  375. section_header_offsets = range(0, len(section_header), LEN_SEC_HEADER)
  376. def read_section_header(offs):
  377. name_offs,sec_type,flags,lma,sec_offs,size = struct.unpack_from("<LLLLLL", section_header[offs:])
  378. return (name_offs, sec_type, flags, lma, size, sec_offs)
  379. all_sections = [read_section_header(offs) for offs in section_header_offsets]
  380. prog_sections = [s for s in all_sections if s[1] == esptool.ELFFile.SEC_TYPE_PROGBITS]
  381. # search for the string table section
  382. if not shstrndx * LEN_SEC_HEADER in section_header_offsets:
  383. raise ESPCoreDumpError("ELF file has no STRTAB section at shstrndx %d" % shstrndx)
  384. _,sec_type,_,_,sec_size,sec_offs = read_section_header(shstrndx * LEN_SEC_HEADER)
  385. if sec_type != esptool.ELFFile.SEC_TYPE_STRTAB:
  386. logging.warning('ELF file has incorrect STRTAB section type 0x%02x' % sec_type)
  387. f.seek(sec_offs)
  388. string_table = f.read(sec_size)
  389. # build the real list of ELFSections by reading the actual section names from the
  390. # string table section, and actual data for each section from the ELF file itself
  391. def lookup_string(offs):
  392. raw = string_table[offs:]
  393. return raw[:raw.index(b'\x00')]
  394. def read_data(offs,size):
  395. f.seek(offs)
  396. return f.read(size)
  397. prog_sections = [ESPCoreDumpSection(lookup_string(n_offs), lma, read_data(offs, size), flags)
  398. for (n_offs, _type, flags, lma, size, offs) in prog_sections if lma != 0]
  399. self.sections = prog_sections
  400. def _read_program_segments(self, f, seg_table_offs, entsz, num):
  401. """Reads core dump program segments from ELF file
  402. """
  403. f.seek(seg_table_offs)
  404. seg_table = f.read(entsz * num)
  405. LEN_SEG_HEADER = 0x20
  406. if len(seg_table) == 0:
  407. raise ESPCoreDumpError("No program header table found at offset %04x in ELF file." % seg_table_offs)
  408. if len(seg_table) % LEN_SEG_HEADER != 0:
  409. logging.warning('Unexpected ELF program header table length %04x is not mod-%02x' % (len(seg_table),LEN_SEG_HEADER))
  410. # walk through the program segment table and extract all segments
  411. seg_table_offs = range(0, len(seg_table), LEN_SEG_HEADER)
  412. def read_program_header(offs):
  413. type,offset,vaddr,_paddr,filesz,_memsz,flags,_align = struct.unpack_from("<LLLLLLLL", seg_table[offs:])
  414. return (type,offset,vaddr,filesz,flags)
  415. prog_segments = [read_program_header(offs) for offs in seg_table_offs]
  416. # build the real list of ImageSegment by reading actual data for each segment from the ELF file itself
  417. def read_data(offs,size):
  418. f.seek(offs)
  419. return f.read(size)
  420. # read loadable segments
  421. self.program_segments = [ESPCoreDumpSegment(vaddr, read_data(offset, filesz), type, flags)
  422. for (type, offset, vaddr, filesz,flags) in prog_segments if vaddr != 0]
  423. self.aux_segments = [ESPCoreDumpSegment(vaddr, read_data(offset, filesz), type, flags)
  424. for (type, offset, vaddr, filesz, flags) in prog_segments if type == ESPCoreDumpElfFile.PT_NOTE and vaddr == 0]
  425. def add_program_segment(self, addr, data, type, flags):
  426. """Adds new program segment
  427. """
  428. # TODO: currently merging with existing segments is not supported
  429. data_sz = len(data)
  430. # check for overlapping and merge if needed
  431. if addr != 0 and data_sz != 0:
  432. for ps in self.program_segments:
  433. seg_len = len(ps.data)
  434. if addr >= ps.addr and addr < (ps.addr + seg_len):
  435. raise ESPCoreDumpError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." %
  436. (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1))
  437. if (addr + data_sz) > ps.addr and (addr + data_sz) <= (ps.addr + seg_len):
  438. raise ESPCoreDumpError("Can not add overlapping region [%x..%x] to ELF file. Conflict with existing [%x..%x]." %
  439. (addr, addr + data_sz - 1, ps.addr, ps.addr + seg_len - 1))
  440. # append
  441. self.program_segments.append(ESPCoreDumpSegment(addr, data, type, flags))
  442. def add_aux_segment(self, data, type, flags):
  443. """Adds new note segment
  444. """
  445. self.aux_segments.append(ESPCoreDumpSegment(0, data, type, flags))
  446. def write_program_headers(self, f, off, segs):
  447. for seg in segs:
  448. phdr = Elf32ProgramHeader()
  449. phdr.p_type = seg.type
  450. phdr.p_offset = off
  451. phdr.p_vaddr = seg.addr
  452. phdr.p_paddr = phdr.p_vaddr # TODO
  453. phdr.p_filesz = len(seg.data)
  454. phdr.p_memsz = phdr.p_filesz # TODO
  455. phdr.p_flags = seg.flags
  456. phdr.p_align = 0 # TODO
  457. f.write(phdr.dump())
  458. off += phdr.p_filesz
  459. return off
  460. def dump(self, f):
  461. """Write core dump contents to file
  462. """
  463. # TODO: currently dumps only program segments.
  464. # dumping sections is not supported yet
  465. # write ELF header
  466. ehdr = Elf32FileHeader()
  467. ehdr.e_type = self.e_type
  468. ehdr.e_machine = self.e_machine
  469. ehdr.e_entry = 0
  470. ehdr.e_phoff = ehdr.sizeof()
  471. ehdr.e_shoff = 0
  472. ehdr.e_flags = 0
  473. ehdr.e_phentsize = Elf32ProgramHeader().sizeof()
  474. ehdr.e_phnum = len(self.program_segments) + len(self.aux_segments)
  475. ehdr.e_shentsize = 0
  476. ehdr.e_shnum = 0
  477. ehdr.e_shstrndx = self.SHN_UNDEF
  478. f.write(ehdr.dump())
  479. # write program header table
  480. cur_off = ehdr.e_ehsize + ehdr.e_phnum * ehdr.e_phentsize
  481. cur_off = self.write_program_headers(f, cur_off, self.program_segments)
  482. cur_off = self.write_program_headers(f, cur_off, self.aux_segments)
  483. # write program segments
  484. for segment in self.program_segments:
  485. f.write(segment.data)
  486. # write aux program segments
  487. for segment in self.aux_segments:
  488. f.write(segment.data)
  489. class ESPCoreDumpLoaderError(ESPCoreDumpError):
  490. """Core dump loader error class
  491. """
  492. def __init__(self, message):
  493. """Constructor for core dump loader error
  494. """
  495. super(ESPCoreDumpLoaderError, self).__init__(message)
  496. class ESPCoreDumpVersion(object):
  497. """Core dump version class
  498. """
  499. # This class contains all version-dependent params
  500. ESP_CORE_DUMP_CHIP_ESP32 = 0
  501. ESP_CORE_DUMP_CHIP_ESP32S2 = 2
  502. def __init__(self, version=None):
  503. """Constructor for core dump version
  504. """
  505. super(ESPCoreDumpVersion, self).__init__()
  506. if version is None:
  507. self.version = 0
  508. else:
  509. self.set_version(version)
  510. @staticmethod
  511. def make_dump_ver(maj, min):
  512. return (((maj & 0xFF) << 8) | ((min & 0xFF) << 0))
  513. def set_version(self, version):
  514. self.version = version
  515. @property
  516. def chip_ver(self):
  517. return ((self.version & 0xFFFF0000) >> 16)
  518. @property
  519. def dump_ver(self):
  520. return (self.version & 0x0000FFFF)
  521. @property
  522. def major(self):
  523. return ((self.version & 0x0000FF00) >> 8)
  524. @property
  525. def minor(self):
  526. return (self.version & 0x000000FF)
  527. class ESPCoreDumpLoader(ESPCoreDumpVersion):
  528. """Core dump loader base class
  529. """
  530. # "legacy" stands for core dumps v0.1 (before IDF v4.1)
  531. ESP_COREDUMP_VERSION_BIN_V1 = ESPCoreDumpVersion.make_dump_ver(0, 1)
  532. ESP_COREDUMP_VERSION_BIN_V2 = ESPCoreDumpVersion.make_dump_ver(0, 2)
  533. ESP_COREDUMP_VERSION_ELF_CRC32 = ESPCoreDumpVersion.make_dump_ver(1, 0)
  534. ESP_COREDUMP_VERSION_ELF_SHA256 = ESPCoreDumpVersion.make_dump_ver(1, 1)
  535. ESP_CORE_DUMP_INFO_TYPE = 8266
  536. ESP_CORE_DUMP_TASK_INFO_TYPE = 678
  537. ESP_CORE_DUMP_EXTRA_INFO_TYPE = 677
  538. ESP_COREDUMP_CURR_TASK_MARKER = 0xdeadbeef
  539. ESP_COREDUMP_BIN_V1_HDR_FMT = '<4L'
  540. ESP_COREDUMP_BIN_V1_HDR_SZ = struct.calcsize(ESP_COREDUMP_BIN_V1_HDR_FMT)
  541. ESP_COREDUMP_HDR_FMT = '<5L'
  542. ESP_COREDUMP_HDR_SZ = struct.calcsize(ESP_COREDUMP_HDR_FMT)
  543. ESP_COREDUMP_TSK_HDR_FMT = '<3L'
  544. ESP_COREDUMP_TSK_HDR_SZ = struct.calcsize(ESP_COREDUMP_TSK_HDR_FMT)
  545. ESP_COREDUMP_MEM_SEG_HDR_FMT = '<2L'
  546. ESP_COREDUMP_MEM_SEG_HDR_SZ = struct.calcsize(ESP_COREDUMP_MEM_SEG_HDR_FMT)
  547. ESP_COREDUMP_NOTE_HDR_FMT = '<3L'
  548. ESP_COREDUMP_NOTE_HDR_SZ = struct.calcsize(ESP_COREDUMP_NOTE_HDR_FMT)
  549. ESP_COREDUMP_CRC_FMT = '<L'
  550. ESP_COREDUMP_CRC_SZ = struct.calcsize(ESP_COREDUMP_CRC_FMT)
  551. ESP_COREDUMP_SHA256_FMT = '32c'
  552. ESP_COREDUMP_SHA256_SZ = struct.calcsize(ESP_COREDUMP_SHA256_FMT)
  553. def __init__(self):
  554. """Base constructor for core dump loader
  555. """
  556. super(ESPCoreDumpLoader, self).__init__()
  557. self.fcore = None
  558. self.hdr = {}
  559. def _get_registers_from_stack(self, data, grows_down):
  560. """Returns list of registers (in GDB format) from xtensa stack frame
  561. """
  562. # from "gdb/xtensa-tdep.h"
  563. # typedef struct
  564. # {
  565. # 0 xtensa_elf_greg_t pc;
  566. # 1 xtensa_elf_greg_t ps;
  567. # 2 xtensa_elf_greg_t lbeg;
  568. # 3 xtensa_elf_greg_t lend;
  569. # 4 xtensa_elf_greg_t lcount;
  570. # 5 xtensa_elf_greg_t sar;
  571. # 6 xtensa_elf_greg_t windowstart;
  572. # 7 xtensa_elf_greg_t windowbase;
  573. # 8..63 xtensa_elf_greg_t reserved[8+48];
  574. # 64 xtensa_elf_greg_t ar[64];
  575. # } xtensa_elf_gregset_t;
  576. REG_PC_IDX = 0
  577. REG_PS_IDX = 1
  578. REG_LB_IDX = 2
  579. REG_LE_IDX = 3
  580. REG_LC_IDX = 4
  581. REG_SAR_IDX = 5
  582. # REG_WS_IDX = 6
  583. # REG_WB_IDX = 7
  584. REG_AR_START_IDX = 64
  585. # REG_AR_NUM = 64
  586. # FIXME: acc to xtensa_elf_gregset_t number of regs must be 128,
  587. # but gdb complanis when it less then 129
  588. REG_NUM = 129
  589. # XT_SOL_EXIT = 0
  590. XT_SOL_PC = 1
  591. XT_SOL_PS = 2
  592. # XT_SOL_NEXT = 3
  593. XT_SOL_AR_START = 4
  594. XT_SOL_AR_NUM = 4
  595. # XT_SOL_FRMSZ = 8
  596. XT_STK_EXIT = 0
  597. XT_STK_PC = 1
  598. XT_STK_PS = 2
  599. XT_STK_AR_START = 3
  600. XT_STK_AR_NUM = 16
  601. XT_STK_SAR = 19
  602. XT_STK_EXCCAUSE = 20
  603. XT_STK_EXCVADDR = 21
  604. XT_STK_LBEG = 22
  605. XT_STK_LEND = 23
  606. XT_STK_LCOUNT = 24
  607. XT_STK_FRMSZ = 25
  608. extra_regs = {ESPCoreDumpElfFile.REG_EPS2_IDX: 0, ESPCoreDumpElfFile.REG_EPS3_IDX: 0,
  609. ESPCoreDumpElfFile.REG_EPS4_IDX: 0, ESPCoreDumpElfFile.REG_EPS5_IDX: 0,
  610. ESPCoreDumpElfFile.REG_EPS6_IDX: 0, ESPCoreDumpElfFile.REG_EPS7_IDX: 0,
  611. ESPCoreDumpElfFile.REG_EPC1_IDX: 0, ESPCoreDumpElfFile.REG_EPC2_IDX: 0,
  612. ESPCoreDumpElfFile.REG_EPC3_IDX: 0, ESPCoreDumpElfFile.REG_EPC4_IDX: 0,
  613. ESPCoreDumpElfFile.REG_EPC5_IDX: 0, ESPCoreDumpElfFile.REG_EPC6_IDX: 0,
  614. ESPCoreDumpElfFile.REG_EPC7_IDX: 0}
  615. regs = [0] * REG_NUM
  616. # TODO: support for growing up stacks
  617. if not grows_down:
  618. raise ESPCoreDumpLoaderError("Growing up stacks are not supported for now!")
  619. ex_struct = "<%dL" % XT_STK_FRMSZ
  620. if len(data) < struct.calcsize(ex_struct):
  621. raise ESPCoreDumpLoaderError("Too small stack to keep frame: %d bytes!" % len(data))
  622. stack = struct.unpack(ex_struct, data[:struct.calcsize(ex_struct)])
  623. # Stack frame type indicator is always the first item
  624. rc = stack[XT_STK_EXIT]
  625. if rc != 0:
  626. regs[REG_PC_IDX] = stack[XT_STK_PC]
  627. regs[REG_PS_IDX] = stack[XT_STK_PS]
  628. for i in range(XT_STK_AR_NUM):
  629. regs[REG_AR_START_IDX + i] = stack[XT_STK_AR_START + i]
  630. regs[REG_SAR_IDX] = stack[XT_STK_SAR]
  631. regs[REG_LB_IDX] = stack[XT_STK_LBEG]
  632. regs[REG_LE_IDX] = stack[XT_STK_LEND]
  633. regs[REG_LC_IDX] = stack[XT_STK_LCOUNT]
  634. # FIXME: crashed and some running tasks (e.g. prvIdleTask) have EXCM bit set
  635. # and GDB can not unwind callstack properly (it implies not windowed call0)
  636. if regs[REG_PS_IDX] & (1 << 5):
  637. regs[REG_PS_IDX] &= ~(1 << 4)
  638. if stack[XT_STK_EXCCAUSE] in xtensa_exception_cause_dict:
  639. extra_regs[ESPCoreDumpElfFile.REG_EXCCAUSE_IDX] = stack[XT_STK_EXCCAUSE]
  640. else:
  641. extra_regs[ESPCoreDumpElfFile.REG_EXCCAUSE_IDX] = INVALID_CAUSE_VALUE
  642. extra_regs[ESPCoreDumpElfFile.REG_EXCVADDR_IDX] = stack[XT_STK_EXCVADDR]
  643. else:
  644. regs[REG_PC_IDX] = stack[XT_SOL_PC]
  645. regs[REG_PS_IDX] = stack[XT_SOL_PS]
  646. for i in range(XT_SOL_AR_NUM):
  647. regs[REG_AR_START_IDX + i] = stack[XT_SOL_AR_START + i]
  648. # nxt = stack[XT_SOL_NEXT]
  649. return regs,extra_regs
  650. def tcb_is_sane(self, tcb_addr, tcb_size):
  651. """Check tcb address if it is correct
  652. """
  653. return not (tcb_addr < 0x3ffae000 or (tcb_addr + tcb_size) > 0x40000000)
  654. def stack_is_sane(self, sp):
  655. """Check stack address if it is correct
  656. """
  657. return not(sp < 0x3ffae010 or sp > 0x3fffffff)
  658. def addr_is_fake(self, addr):
  659. """Check if address is in fake area
  660. """
  661. return ((addr < 0x3f3fffff and addr >= 0x20000000) or addr >= 0x80000000)
  662. def remove_tmp_file(self, fname):
  663. """Silently removes temporary file
  664. """
  665. try:
  666. os.remove(fname)
  667. except OSError as e:
  668. if e.errno != errno.ENOENT:
  669. logging.warning("Failed to remove temp file '%s' (%d)!" % (fname, e.errno))
  670. def cleanup(self):
  671. """Cleans up loader resources
  672. """
  673. if self.fcore:
  674. self.fcore.close()
  675. if self.fcore_name:
  676. self.remove_tmp_file(self.fcore_name)
  677. def _extract_elf_corefile(self, core_fname=None, off=0, exe_name=None):
  678. """ Reads the ELF formatted core dump image and parse it
  679. """
  680. core_off = off
  681. self.set_version(self.hdr['ver'])
  682. if self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_CRC32:
  683. checksum_len = self.ESP_COREDUMP_CRC_SZ
  684. elif self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_SHA256:
  685. checksum_len = self.ESP_COREDUMP_SHA256_SZ
  686. else:
  687. raise ESPCoreDumpLoaderError("Core dump version '%d' is not supported!" % self.dump_ver)
  688. core_elf = ESPCoreDumpElfFile()
  689. data = self.read_data(core_off, self.hdr['tot_len'] - checksum_len - self.ESP_COREDUMP_HDR_SZ)
  690. with open(core_fname, 'w+b') as fce:
  691. try:
  692. fce.write(data)
  693. fce.flush()
  694. fce.seek(0)
  695. core_elf._read_elf_file(fce)
  696. if exe_name:
  697. exe_elf = ESPCoreDumpElfFile(exe_name)
  698. # Read note segments from core file which are belong to tasks (TCB or stack)
  699. for ns in core_elf.aux_segments:
  700. if ns.type != ESPCoreDumpElfFile.PT_NOTE:
  701. continue
  702. note_read = 0
  703. while note_read < len(ns.data):
  704. note = Elf32NoteDesc("", 0, None)
  705. note_read += note.read(ns.data[note_read:])
  706. # Check for version info note
  707. if 'ESP_CORE_DUMP_INFO' == note.name and note.type == self.ESP_CORE_DUMP_INFO_TYPE and exe_name:
  708. app_sha256 = binascii.hexlify(exe_elf.sha256())
  709. n_ver_len = struct.calcsize("<L")
  710. n_sha256_len = self.ESP_COREDUMP_SHA256_SZ * 2 # SHA256 as hex string
  711. n_ver,coredump_sha256 = struct.unpack("<L%ds" % (n_sha256_len), note.desc[:n_ver_len + n_sha256_len])
  712. if coredump_sha256 != app_sha256 or ESPCoreDumpVersion(n_ver).dump_ver != self.dump_ver:
  713. raise ESPCoreDumpError("Invalid application image for coredump: app_SHA256(%s) != coredump_SHA256(%s)." %
  714. (app_sha256, coredump_sha256))
  715. except ESPCoreDumpError as e:
  716. logging.warning("Failed to extract ELF core dump image into file %s. (Reason: %s)" % (core_fname, e))
  717. return core_fname
  718. def _extract_bin_corefile(self, core_fname=None, rom_elf=None, off=0):
  719. """Creates core dump ELF file
  720. """
  721. core_off = off
  722. with open(core_fname, 'w+b') as fce:
  723. tcbsz_aligned = self.hdr['tcbsz']
  724. if tcbsz_aligned % 4:
  725. tcbsz_aligned = 4 * (old_div(tcbsz_aligned,4) + 1)
  726. core_elf = ESPCoreDumpElfFile()
  727. notes = b''
  728. core_dump_info_notes = b''
  729. task_info_notes = b''
  730. task_status = EspCoreDumpTaskStatus()
  731. for i in range(self.hdr['task_num']):
  732. task_status.task_index = i
  733. task_status.task_flags = EspCoreDumpTaskStatus.TASK_STATUS_CORRECT
  734. data = self.read_data(core_off, self.ESP_COREDUMP_TSK_HDR_SZ)
  735. tcb_addr,stack_top,stack_end = struct.unpack_from(self.ESP_COREDUMP_TSK_HDR_FMT, data)
  736. if stack_end > stack_top:
  737. stack_len = stack_end - stack_top
  738. stack_base = stack_top
  739. else:
  740. stack_len = stack_top - stack_end
  741. stack_base = stack_end
  742. stack_len_aligned = stack_len
  743. if stack_len_aligned % 4:
  744. stack_len_aligned = 4 * (old_div(stack_len_aligned,4) + 1)
  745. core_off += self.ESP_COREDUMP_TSK_HDR_SZ
  746. logging.debug("Read TCB %d bytes @ 0x%x" % (tcbsz_aligned, tcb_addr))
  747. data = self.read_data(core_off, tcbsz_aligned)
  748. task_status.task_tcb_addr = tcb_addr
  749. try:
  750. if self.tcb_is_sane(tcb_addr, tcbsz_aligned):
  751. if self.hdr['tcbsz'] != tcbsz_aligned:
  752. core_elf.add_program_segment(tcb_addr, data[:self.hdr['tcbsz'] - tcbsz_aligned],
  753. ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
  754. else:
  755. core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
  756. # task_status.task_name = bytearray("%s\0" % task_name_str, encoding='ascii')
  757. elif tcb_addr and self.addr_is_fake(tcb_addr):
  758. task_status.task_flags |= EspCoreDumpTaskStatus.TASK_STATUS_TCB_CORRUPTED
  759. except ESPCoreDumpError as e:
  760. logging.warning("Skip TCB %d bytes @ 0x%x. (Reason: %s)" % (tcbsz_aligned, tcb_addr, e))
  761. core_off += tcbsz_aligned
  762. logging.debug("Read stack %d bytes @ 0x%x" % (stack_len_aligned, stack_base))
  763. data = self.read_data(core_off, stack_len_aligned)
  764. if stack_len != stack_len_aligned:
  765. data = data[:stack_len - stack_len_aligned]
  766. task_status.task_stack_start = stack_base
  767. task_status.task_stack_len = stack_len_aligned
  768. try:
  769. if self.stack_is_sane(stack_base):
  770. core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
  771. elif stack_base and self.addr_is_fake(stack_base):
  772. task_status.task_flags |= EspCoreDumpTaskStatus.TASK_STATUS_STACK_CORRUPTED
  773. core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
  774. except ESPCoreDumpError as e:
  775. logging.warning("Skip task's (%x) stack %d bytes @ 0x%x. (Reason: %s)" % (tcb_addr, stack_len_aligned, stack_base, e))
  776. core_off += stack_len_aligned
  777. try:
  778. logging.debug("Stack start_end: 0x%x @ 0x%x" % (stack_top, stack_end))
  779. task_regs,extra_regs = self._get_registers_from_stack(data, stack_end > stack_top)
  780. except Exception as e:
  781. logging.error(e)
  782. return None
  783. task_info_notes += Elf32NoteDesc("TASK_INFO", self.ESP_CORE_DUMP_TASK_INFO_TYPE, task_status.dump()).dump()
  784. prstatus = XtensaPrStatus()
  785. prstatus.pr_cursig = 0 # TODO: set sig only for current/failed task
  786. prstatus.pr_pid = tcb_addr
  787. note = Elf32NoteDesc("CORE", 1, prstatus.dump() + struct.pack("<%dL" % len(task_regs), *task_regs)).dump()
  788. notes += note
  789. if ESPCoreDumpElfFile.REG_EXCCAUSE_IDX in extra_regs and len(core_dump_info_notes) == 0:
  790. # actually there will be only one such note - for crashed task
  791. core_dump_info_notes += Elf32NoteDesc("ESP_CORE_DUMP_INFO", self.ESP_CORE_DUMP_INFO_TYPE, struct.pack("<L", self.hdr['ver'])).dump()
  792. exc_regs = []
  793. for reg_id in extra_regs:
  794. exc_regs.extend([reg_id, extra_regs[reg_id]])
  795. core_dump_info_notes += Elf32NoteDesc("EXTRA_INFO", self.ESP_CORE_DUMP_EXTRA_INFO_TYPE,
  796. struct.pack("<%dL" % (1 + len(exc_regs)), tcb_addr, *exc_regs)).dump()
  797. self.set_version(self.hdr['ver'])
  798. if self.dump_ver == self.ESP_COREDUMP_VERSION_BIN_V2:
  799. for i in range(self.hdr['segs_num']):
  800. data = self.read_data(core_off, self.ESP_COREDUMP_MEM_SEG_HDR_SZ)
  801. core_off += self.ESP_COREDUMP_MEM_SEG_HDR_SZ
  802. mem_start,mem_sz = struct.unpack_from(self.ESP_COREDUMP_MEM_SEG_HDR_FMT, data)
  803. logging.debug("Read memory segment %d bytes @ 0x%x" % (mem_sz, mem_start))
  804. data = self.read_data(core_off, mem_sz)
  805. core_elf.add_program_segment(mem_start, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
  806. core_off += mem_sz
  807. # add notes
  808. try:
  809. core_elf.add_aux_segment(notes, ESPCoreDumpElfFile.PT_NOTE, 0)
  810. except ESPCoreDumpError as e:
  811. logging.warning("Skip NOTES segment %d bytes @ 0x%x. (Reason: %s)" % (len(notes), 0, e))
  812. # add core dump info notes
  813. try:
  814. core_elf.add_aux_segment(core_dump_info_notes, ESPCoreDumpElfFile.PT_NOTE, 0)
  815. except ESPCoreDumpError as e:
  816. logging.warning("Skip core dump info NOTES segment %d bytes @ 0x%x. (Reason: %s)" % (len(core_dump_info_notes), 0, e))
  817. try:
  818. core_elf.add_aux_segment(task_info_notes, ESPCoreDumpElfFile.PT_NOTE, 0)
  819. except ESPCoreDumpError as e:
  820. logging.warning("Skip failed tasks info NOTES segment %d bytes @ 0x%x. (Reason: %s)" % (len(task_info_notes), 0, e))
  821. # add ROM text sections
  822. if rom_elf:
  823. for ps in rom_elf.program_segments:
  824. if (ps.flags & ESPCoreDumpSegment.PF_X) == 0:
  825. continue
  826. try:
  827. core_elf.add_program_segment(ps.addr, ps.data, ESPCoreDumpElfFile.PT_LOAD, ps.flags)
  828. except ESPCoreDumpError as e:
  829. logging.warning("Skip ROM segment %d bytes @ 0x%x. (Reason: %s)" % (len(ps.data), ps.addr, e))
  830. # dump core ELF
  831. core_elf.e_type = ESPCoreDumpElfFile.ET_CORE
  832. core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA
  833. core_elf.dump(fce)
  834. return core_fname
  835. def create_corefile(self, core_fname=None, exe_name=None, rom_elf=None, off=0):
  836. """Creates core dump ELF file
  837. """
  838. data = self.read_data(off, self.ESP_COREDUMP_HDR_SZ)
  839. vals = struct.unpack_from(self.ESP_COREDUMP_HDR_FMT, data)
  840. self.hdr = dict(zip(('tot_len', 'ver', 'task_num', 'tcbsz', 'segs_num'), vals))
  841. if not core_fname:
  842. fce = tempfile.NamedTemporaryFile(mode='w+b', delete=False)
  843. core_fname = fce.name
  844. self.set_version(self.hdr['ver'])
  845. if self.chip_ver == ESPCoreDumpVersion.ESP_CORE_DUMP_CHIP_ESP32S2 or self.chip_ver == ESPCoreDumpVersion.ESP_CORE_DUMP_CHIP_ESP32:
  846. if self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_CRC32 or self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_SHA256:
  847. return self._extract_elf_corefile(core_fname, off + self.ESP_COREDUMP_HDR_SZ, exe_name)
  848. elif self.dump_ver == self.ESP_COREDUMP_VERSION_BIN_V2:
  849. return self._extract_bin_corefile(core_fname, rom_elf, off + self.ESP_COREDUMP_HDR_SZ)
  850. elif self.dump_ver == self.ESP_COREDUMP_VERSION_BIN_V1:
  851. return self._extract_bin_corefile(core_fname, rom_elf, off + self.ESP_COREDUMP_BIN_V1_HDR_SZ)
  852. raise ESPCoreDumpLoaderError("Core dump version '0x%x' is not supported!" % (self.dump_ver))
  853. else:
  854. raise ESPCoreDumpLoaderError("Core dump chip '0x%x' is not supported!" % (self.chip_ver))
  855. def read_data(self, off, sz):
  856. """Reads data from raw core dump got from flash or UART
  857. """
  858. self.fcore.seek(off)
  859. data = self.fcore.read(sz)
  860. return data
  861. class ESPCoreDumpFileLoader(ESPCoreDumpLoader):
  862. """Core dump file loader class
  863. """
  864. def __init__(self, path, b64=False):
  865. """Constructor for core dump file loader
  866. """
  867. super(ESPCoreDumpFileLoader, self).__init__()
  868. self.fcore = self._load_coredump(path, b64)
  869. def _load_coredump(self, path, b64):
  870. """Loads core dump from (raw binary or base64-encoded) file
  871. """
  872. logging.debug("Load core dump from '%s'", path)
  873. self.fcore_name = None
  874. if b64:
  875. fhnd,self.fcore_name = tempfile.mkstemp()
  876. fcore = os.fdopen(fhnd, 'wb')
  877. fb64 = open(path, 'rb')
  878. try:
  879. while True:
  880. line = fb64.readline()
  881. if len(line) == 0:
  882. break
  883. data = base64.standard_b64decode(line.rstrip(b'\r\n'))
  884. fcore.write(data)
  885. fcore.close()
  886. fcore = open(self.fcore_name, 'rb')
  887. except Exception as e:
  888. if self.fcore_name:
  889. self.remove_tmp_file(self.fcore_name)
  890. raise e
  891. finally:
  892. fb64.close()
  893. else:
  894. fcore = open(path, 'rb')
  895. return fcore
  896. class ESPCoreDumpFlashLoader(ESPCoreDumpLoader):
  897. """Core dump flash loader class
  898. """
  899. ESP_COREDUMP_FLASH_LEN_FMT = '<L'
  900. ESP_COREDUMP_FLASH_LEN_SZ = struct.calcsize(ESP_COREDUMP_FLASH_LEN_FMT)
  901. ESP_COREDUMP_PART_TABLE_OFF = 0x8000
  902. def __init__(self, off, tool_path=None, chip='esp32', port=None, baud=None):
  903. """Constructor for core dump flash loader
  904. """
  905. super(ESPCoreDumpFlashLoader, self).__init__()
  906. self.port = port
  907. self.baud = baud
  908. self.chip = chip
  909. self.dump_sz = 0
  910. self.fcore = self._load_coredump(off)
  911. def get_tool_path(self, use_esptool=None):
  912. """Get tool path
  913. """
  914. if use_esptool:
  915. tool_path = os.path.join(idf_path, 'components', 'esptool_py', 'esptool') + os.path.sep
  916. else:
  917. tool_path = os.path.join(idf_path, 'components', 'partition_table') + os.path.sep
  918. return tool_path
  919. def get_core_dump_partition_info(self, part_off=None, tool_path=None):
  920. """Get core dump partition info using parttool
  921. """
  922. logging.info("Retrieving core dump partition offset and size...")
  923. if not tool_path:
  924. tool_path = self.get_tool_path(use_esptool=False)
  925. if not part_off:
  926. part_off = self.ESP_COREDUMP_PART_TABLE_OFF
  927. size = None
  928. offset = None
  929. try:
  930. tool_args = [sys.executable, tool_path + 'parttool.py', "-q", "--partition-table-offset", str(part_off)]
  931. if self.port:
  932. tool_args.extend(['--port', self.port])
  933. invoke_args = tool_args + ["get_partition_info", "--partition-type", "data", "--partition-subtype", "coredump", "--info", "offset", "size"]
  934. (offset_str, size_str) = subprocess.check_output(invoke_args).strip().split(b" ")
  935. size = int(size_str, 16)
  936. offset = int(offset_str, 16)
  937. logging.info("Core dump partition offset=%d, size=%d", offset, size)
  938. except subprocess.CalledProcessError as e:
  939. logging.error("parttool get partition info failed with err %d" % e.returncode)
  940. logging.debug("Command ran: '%s'" % e.cmd)
  941. logging.debug("Command out:")
  942. logging.debug(e.output)
  943. logging.error("Check if the coredump partition exists in partition table.")
  944. raise e
  945. return (offset, size)
  946. def invoke_parttool(self, tool_path=None):
  947. """Loads core dump from flash using parttool
  948. """
  949. part_tool_args = [sys.executable, tool_path + 'parttool.py']
  950. if self.port:
  951. part_tool_args.extend(['--port', self.port])
  952. part_tool_args.extend(['read_partition', '--partition-type', 'data', '--partition-subtype', 'coredump', '--output'])
  953. self.fcore_name = None
  954. f = tempfile.NamedTemporaryFile(mode='w+b', delete=False)
  955. try:
  956. part_tool_args.append(f.name)
  957. self.fcore_name = f.name
  958. # read core dump partition
  959. et_out = subprocess.check_output(part_tool_args)
  960. if len(et_out):
  961. logging.info(et_out.decode('utf-8'))
  962. self.dump_sz = self._read_core_dump_length(f)
  963. f.seek(self.dump_sz)
  964. # cut free space of the partition
  965. f.truncate()
  966. f.seek(0)
  967. except subprocess.CalledProcessError as e:
  968. logging.error("parttool script execution failed with err %d" % e.returncode)
  969. logging.debug("Command ran: '%s'" % e.cmd)
  970. logging.debug("Command out:")
  971. logging.debug(e.output)
  972. if self.fcore_name:
  973. f.close()
  974. self.remove_tmp_file(self.fcore_name)
  975. raise e
  976. return f
  977. def invoke_esptool(self, tool_path=None, off=None):
  978. """Loads core dump from flash using elftool
  979. """
  980. tool_args = [sys.executable, tool_path + 'esptool.py', '-c', self.chip]
  981. if self.port:
  982. tool_args.extend(['-p', self.port])
  983. if self.baud:
  984. tool_args.extend(['-b', str(self.baud)])
  985. f = tempfile.NamedTemporaryFile(mode='w+b', delete=False)
  986. self.fcore_name = None
  987. try:
  988. (part_offset, part_size) = self.get_core_dump_partition_info(tool_path='')
  989. if not off:
  990. off = part_offset # set default offset if not specified
  991. logging.warning("The core dump image offset is not specified. Use partition offset: %d.", part_offset)
  992. if part_offset != off:
  993. logging.warning("Predefined image offset: %d does not match core dump partition offset: %d", off, part_offset)
  994. tool_args.extend(['read_flash', str(off), str(self.ESP_COREDUMP_FLASH_LEN_SZ)])
  995. tool_args.append(f.name)
  996. self.fcore_name = f.name
  997. # read core dump length
  998. et_out = subprocess.check_output(tool_args)
  999. if len(et_out):
  1000. logging.info(et_out.decode('utf-8'))
  1001. self.dump_sz = self._read_core_dump_length(f)
  1002. if self.dump_sz == 0 or self.dump_sz > part_size:
  1003. logging.error("Incorrect size of core dump image: %d, use partition size instead: %d", self.dump_sz, part_size)
  1004. self.dump_sz = part_size
  1005. # set actual size of core dump image and read it from flash
  1006. tool_args[-2] = str(self.dump_sz)
  1007. et_out = subprocess.check_output(tool_args)
  1008. if len(et_out):
  1009. logging.info(et_out.decode('utf-8'))
  1010. except subprocess.CalledProcessError as e:
  1011. logging.error("esptool script execution failed with err %d" % e.returncode)
  1012. logging.debug("Command ran: '%s'" % e.cmd)
  1013. logging.debug("Command out:")
  1014. logging.debug(e.output)
  1015. if self.fcore_name:
  1016. f.close()
  1017. self.remove_tmp_file(self.fcore_name)
  1018. raise e
  1019. return f
  1020. def _load_coredump(self, off=None):
  1021. """Loads core dump from flash using parttool or elftool (if offset is set)
  1022. """
  1023. tool_path = None
  1024. try:
  1025. if off:
  1026. tool_path = ''
  1027. logging.info("Invoke esptool to read image.")
  1028. f = self.invoke_esptool(tool_path=tool_path, off=off)
  1029. else:
  1030. tool_path = ''
  1031. logging.info("Invoke parttool to read image.")
  1032. f = self.invoke_parttool(tool_path=tool_path)
  1033. except subprocess.CalledProcessError as e:
  1034. if len(e.output):
  1035. logging.info(e.output)
  1036. logging.warning("System path is not set. Try to use predefined path.")
  1037. if off:
  1038. tool_path = self.get_tool_path(use_esptool=True)
  1039. f = self.invoke_esptool(tool_path=tool_path, off=off)
  1040. else:
  1041. tool_path = self.get_tool_path(use_esptool=False)
  1042. f = self.invoke_parttool(tool_path=tool_path)
  1043. return f
  1044. def _read_core_dump_length(self, f):
  1045. """Reads core dump length
  1046. """
  1047. data = f.read(self.ESP_COREDUMP_FLASH_LEN_SZ)
  1048. tot_len, = struct.unpack_from(self.ESP_COREDUMP_FLASH_LEN_FMT, data)
  1049. return tot_len
  1050. def create_corefile(self, core_fname=None, exe_name=None, rom_elf=None):
  1051. """Checks flash coredump data integrity and creates ELF file
  1052. """
  1053. data = self.read_data(0, self.ESP_COREDUMP_HDR_SZ)
  1054. self.checksum_len = 0
  1055. _,coredump_ver_data,_,_,_ = struct.unpack_from(self.ESP_COREDUMP_HDR_FMT, data)
  1056. self.set_version(coredump_ver_data)
  1057. if self.chip_ver != ESPCoreDumpVersion.ESP_CORE_DUMP_CHIP_ESP32S2 and self.chip_ver != ESPCoreDumpVersion.ESP_CORE_DUMP_CHIP_ESP32:
  1058. raise ESPCoreDumpLoaderError("Invalid core dump chip version: '%s', should be <= '0x%x'" % (self.chip_ver, self.ESP_CORE_DUMP_CHIP_ESP32S2))
  1059. if self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_CRC32 or self.dump_ver == self.ESP_COREDUMP_VERSION_BIN_V1 \
  1060. or self.dump_ver == self.ESP_COREDUMP_VERSION_BIN_V2:
  1061. logging.debug("Dump size = %d, crc off = 0x%x", self.dump_sz, self.dump_sz - self.ESP_COREDUMP_CRC_SZ)
  1062. data = self.read_data(self.dump_sz - self.ESP_COREDUMP_CRC_SZ, self.ESP_COREDUMP_CRC_SZ)
  1063. dump_crc, = struct.unpack_from(self.ESP_COREDUMP_CRC_FMT, data)
  1064. data = self.read_data(0, self.dump_sz - self.ESP_COREDUMP_CRC_SZ)
  1065. data_crc = binascii.crc32(data) & 0xffffffff
  1066. if dump_crc != data_crc:
  1067. raise ESPCoreDumpLoaderError("Invalid core dump CRC %x, should be %x" % (data_crc, dump_crc))
  1068. elif self.dump_ver == self.ESP_COREDUMP_VERSION_ELF_SHA256:
  1069. dump_sha256 = self.read_data(self.dump_sz - self.ESP_COREDUMP_SHA256_SZ, self.ESP_COREDUMP_SHA256_SZ)
  1070. data = self.read_data(0, self.dump_sz - self.ESP_COREDUMP_SHA256_SZ)
  1071. data_sha256 = sha256(data)
  1072. data_sha256_str = data_sha256.hexdigest()
  1073. dump_sha256_str = binascii.hexlify(dump_sha256).decode('ascii')
  1074. if dump_sha256_str != data_sha256_str:
  1075. raise ESPCoreDumpLoaderError("Invalid core dump SHA256 '%s', should be '%s'" % (dump_sha256_str, data_sha256_str))
  1076. return super(ESPCoreDumpFlashLoader, self).create_corefile(core_fname, exe_name)
  1077. class GDBMIOutRecordHandler(object):
  1078. """GDB/MI output record handler base class
  1079. """
  1080. TAG = ''
  1081. def __init__(self, f, verbose=False):
  1082. """Base constructor for GDB/MI output record handler
  1083. """
  1084. self.verbose = verbose
  1085. def execute(self, ln):
  1086. """Base method to execute GDB/MI output record handler function
  1087. """
  1088. if self.verbose:
  1089. logging.debug("%s.execute: [[%s]]" % (self.__class__.__name__, ln))
  1090. class GDBMIOutStreamHandler(GDBMIOutRecordHandler):
  1091. """GDB/MI output stream handler class
  1092. """
  1093. def __init__(self, f, verbose=False):
  1094. """Constructor for GDB/MI output stream handler
  1095. """
  1096. super(GDBMIOutStreamHandler, self).__init__(None, verbose)
  1097. self.func = f
  1098. def execute(self, ln):
  1099. """Executes GDB/MI output stream handler function
  1100. """
  1101. GDBMIOutRecordHandler.execute(self, ln)
  1102. if self.func:
  1103. # remove TAG / quotes and replace c-string \n with actual NL
  1104. self.func(ln[1:].strip('"').replace('\\n', '\n').replace('\\t', '\t'))
  1105. class GDBMIResultHandler(GDBMIOutRecordHandler):
  1106. """GDB/MI result handler class
  1107. """
  1108. TAG = '^'
  1109. RC_DONE = 'done'
  1110. RC_RUNNING = 'running'
  1111. RC_CONNECTED = 'connected'
  1112. RC_ERROR = 'error'
  1113. RC_EXIT = 'exit'
  1114. def __init__(self, verbose=False):
  1115. """Constructor for GDB/MI result handler
  1116. """
  1117. super(GDBMIResultHandler, self).__init__(None, verbose)
  1118. self.result_class = ''
  1119. self.result_str = ''
  1120. def _parse_rc(self, ln, rc):
  1121. """Parses result code
  1122. """
  1123. rc_str = "{0}{1}".format(self.TAG, rc)
  1124. if not ln.startswith(rc_str):
  1125. return False
  1126. self.result_class = rc
  1127. if len(ln) > len(rc_str):
  1128. self.result_str = ln[len(rc_str):]
  1129. if self.result_str.startswith(','):
  1130. self.result_str = self.result_str[1:]
  1131. else:
  1132. logging.error("Invalid result format: '%s'" % ln)
  1133. else:
  1134. self.result_str = ''
  1135. return True
  1136. def execute(self, ln):
  1137. """Executes GDB/MI result handler function
  1138. """
  1139. GDBMIOutRecordHandler.execute(self, ln)
  1140. if self._parse_rc(ln, self.RC_DONE):
  1141. return
  1142. if self._parse_rc(ln, self.RC_RUNNING):
  1143. return
  1144. if self._parse_rc(ln, self.RC_CONNECTED):
  1145. return
  1146. if self._parse_rc(ln, self.RC_ERROR):
  1147. return
  1148. if self._parse_rc(ln, self.RC_EXIT):
  1149. return
  1150. logging.error("Unknown GDB/MI result: '%s'" % ln)
  1151. class GDBMIThreadListIdsHandler(GDBMIResultHandler):
  1152. """GDB/MI thread-list-ids handler class
  1153. """
  1154. def __init__(self, verbose=False):
  1155. """Constructor for GDB/MI result handler
  1156. """
  1157. super(GDBMIThreadListIdsHandler, self).__init__(verbose)
  1158. self.threads = []
  1159. self.current_thread = ''
  1160. def execute(self, ln):
  1161. """Executes GDB/MI thread-list-ids handler function
  1162. """
  1163. GDBMIResultHandler.execute(self, ln)
  1164. if self.result_class != self.RC_DONE:
  1165. return
  1166. # simple parsing method
  1167. result = re.search(r'thread-ids\s*=\s*\{([^\{\}]*)\}', self.result_str)
  1168. if result:
  1169. for tid in re.finditer(r'thread-id="(\d+)"', result.group(1)):
  1170. self.threads.append(tid.group(1))
  1171. result = re.search(r'current-thread-id="(\d+)"', self.result_str)
  1172. if result:
  1173. self.current_thread = result.group(1)
  1174. class GDBMIThreadSelectHandler(GDBMIResultHandler):
  1175. """GDB/MI thread-select handler class
  1176. """
  1177. def execute(self, ln):
  1178. """Executes GDB/MI thread-select handler function
  1179. """
  1180. GDBMIResultHandler.execute(self, ln)
  1181. if self.result_class != self.RC_DONE:
  1182. return
  1183. class GDBMIThreadInfoHandler(GDBMIResultHandler):
  1184. """GDB/MI thread-info handler class
  1185. """
  1186. def __init__(self, verbose=False):
  1187. """Constructor for GDB/MI result handler
  1188. """
  1189. super(GDBMIThreadInfoHandler, self).__init__(verbose)
  1190. self.current = False
  1191. self.id = ''
  1192. self.target_id = ''
  1193. self.details = ''
  1194. self.name = ''
  1195. self.frame = ''
  1196. self.state = ''
  1197. self.core = ''
  1198. def execute(self, ln):
  1199. """Executes GDB/MI thread-info handler function
  1200. """
  1201. GDBMIResultHandler.execute(self, ln)
  1202. if self.result_class != self.RC_DONE:
  1203. return
  1204. # simple parsing method
  1205. result = re.search(r'id="(\d+)"', self.result_str)
  1206. if result:
  1207. self.id = result.group(1)
  1208. result = re.search(r'current="\*"', self.result_str)
  1209. if result:
  1210. self.current = True
  1211. result = re.search(r'target-id="([^"]+)"', self.result_str)
  1212. if result:
  1213. self.target_id = result.group(1)
  1214. class GDBMIDataEvalHandler(GDBMIResultHandler):
  1215. """GDB/MI data-evaluate-expression handler class
  1216. """
  1217. def __init__(self, verbose=False):
  1218. """Constructor for GDB/MI result handler
  1219. """
  1220. super(GDBMIDataEvalHandler, self).__init__(verbose)
  1221. self.value = ''
  1222. def execute(self, ln):
  1223. """Executes GDB/MI data-evaluate-expression handler function
  1224. """
  1225. GDBMIResultHandler.execute(self, ln)
  1226. if self.result_class != self.RC_DONE:
  1227. return
  1228. # simple parsing method
  1229. if self.verbose:
  1230. logging.debug("GDBMIDataEvalHandler: result '%s'", self.result_str)
  1231. pos = 0
  1232. r = re.compile(r'([a-zA-Z_]+)=(.+)\,')
  1233. while True:
  1234. m = r.search(self.result_str, pos=pos)
  1235. if not m:
  1236. break
  1237. if m.group(1) == 'value':
  1238. if self.verbose:
  1239. logging.debug("GDBMIDataEvalHandler: found value = '%s'", m.group(2))
  1240. self.value = self.result.group(1)
  1241. return
  1242. pos = m.end(2) + 1
  1243. res_str = self.result_str[pos:]
  1244. res_str = res_str.replace(r'\"', '\'')
  1245. m = re.search(r'value="([^"]+)"', res_str)
  1246. if m:
  1247. if self.verbose:
  1248. logging.debug("GDBMIDataEvalHandler: found value = '%s'", m.group(1))
  1249. self.value = m.group(1)
  1250. class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler):
  1251. """GDB/MI console stream handler class
  1252. """
  1253. TAG = '~'
  1254. def load_aux_elf(elf_path):
  1255. """ Loads auxilary ELF file and composes GDB command to read its symbols
  1256. """
  1257. elf = None
  1258. sym_cmd = ''
  1259. if os.path.exists(elf_path):
  1260. elf = ESPCoreDumpElfFile(elf_path)
  1261. for s in elf.sections:
  1262. if s.name == '.text':
  1263. sym_cmd = 'add-symbol-file %s 0x%x' % (elf_path, s.addr)
  1264. return (elf, sym_cmd)
  1265. def dbg_corefile(args):
  1266. """ Command to load core dump from file or flash and run GDB debug session with it
  1267. """
  1268. global CLOSE_FDS
  1269. loader = None
  1270. rom_elf,rom_sym_cmd = load_aux_elf(args.rom_elf)
  1271. if not args.core:
  1272. loader = ESPCoreDumpFlashLoader(args.off, port=args.port, baud=args.baud)
  1273. core_fname = loader.create_corefile(args.save_core, exe_name=args.prog, rom_elf=rom_elf)
  1274. if not core_fname:
  1275. logging.error("Failed to create corefile!")
  1276. loader.cleanup()
  1277. return
  1278. else:
  1279. core_fname = args.core
  1280. if args.core_format and args.core_format != 'elf':
  1281. loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64')
  1282. core_fname = loader.create_corefile(args.save_core, exe_name=args.prog, rom_elf=rom_elf)
  1283. if not core_fname:
  1284. logging.error("Failed to create corefile!")
  1285. loader.cleanup()
  1286. return
  1287. p = subprocess.Popen(bufsize=0,
  1288. args=[args.gdb,
  1289. '--nw', # ignore .gdbinit
  1290. '--core=%s' % core_fname, # core file,
  1291. '-ex', rom_sym_cmd,
  1292. args.prog
  1293. ],
  1294. stdin=None, stdout=None, stderr=None,
  1295. close_fds=CLOSE_FDS
  1296. )
  1297. p.wait()
  1298. if loader:
  1299. if not args.core and not args.save_core:
  1300. loader.remove_tmp_file(core_fname)
  1301. loader.cleanup()
  1302. print('Done!')
  1303. def info_corefile(args):
  1304. """ Command to load core dump from file or flash and print it's data in user friendly form
  1305. """
  1306. global CLOSE_FDS
  1307. def gdbmi_console_stream_handler(ln):
  1308. sys.stdout.write(ln)
  1309. sys.stdout.flush()
  1310. def gdbmi_read2prompt(f, out_handlers=None):
  1311. while True:
  1312. ln = f.readline().decode('utf-8').rstrip(' \r\n')
  1313. if ln == '(gdb)':
  1314. break
  1315. elif len(ln) == 0:
  1316. break
  1317. elif out_handlers:
  1318. for h in out_handlers:
  1319. if ln.startswith(out_handlers[h].TAG):
  1320. out_handlers[h].execute(ln)
  1321. break
  1322. def gdbmi_start(handlers, gdb_cmds):
  1323. gdb_args = [args.gdb,
  1324. '--quiet', # inhibit dumping info at start-up
  1325. '--nx', # inhibit window interface
  1326. '--nw', # ignore .gdbinit
  1327. '--interpreter=mi2', # use GDB/MI v2
  1328. '--core=%s' % core_fname] # core file
  1329. for c in gdb_cmds:
  1330. gdb_args += ['-ex', c]
  1331. gdb_args.append(args.prog)
  1332. p = subprocess.Popen(bufsize=0,
  1333. args=gdb_args,
  1334. stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
  1335. close_fds=CLOSE_FDS)
  1336. gdbmi_read2prompt(p.stdout, handlers)
  1337. return p
  1338. def gdbmi_cmd_exec(p, handlers, gdbmi_cmd):
  1339. for t in handlers:
  1340. handlers[t].result_class = None
  1341. p.stdin.write(bytearray("%s\n" % gdbmi_cmd, encoding='utf-8'))
  1342. gdbmi_read2prompt(p.stdout, handlers)
  1343. if not handlers[GDBMIResultHandler.TAG].result_class or handlers[GDBMIResultHandler.TAG].result_class == GDBMIResultHandler.RC_EXIT:
  1344. logging.error("GDB exited (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str))
  1345. p.wait()
  1346. logging.error("Problem occured! GDB exited, restart it.")
  1347. p = gdbmi_start(handlers, [])
  1348. elif handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE:
  1349. logging.error("GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str))
  1350. return p
  1351. def gdbmi_getinfo(p, handlers, gdb_cmd):
  1352. return gdbmi_cmd_exec(p, handlers, "-interpreter-exec console \"%s\"" % gdb_cmd)
  1353. def gdbmi_get_thread_ids(p):
  1354. handlers = {}
  1355. result = GDBMIThreadListIdsHandler(verbose=False)
  1356. handlers[GDBMIResultHandler.TAG] = result
  1357. handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False)
  1358. p = gdbmi_cmd_exec(p, handlers, "-thread-list-ids")
  1359. return p,result.threads,result.current_thread
  1360. def gdbmi_switch_thread(p, thr_id):
  1361. handlers = {}
  1362. result = GDBMIThreadSelectHandler(verbose=False)
  1363. handlers[GDBMIResultHandler.TAG] = result
  1364. handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False)
  1365. return gdbmi_cmd_exec(p, handlers, "-thread-select %s" % thr_id)
  1366. def gdbmi_get_thread_info(p, thr_id):
  1367. handlers = {}
  1368. result = GDBMIThreadInfoHandler(verbose=False)
  1369. handlers[GDBMIResultHandler.TAG] = result
  1370. handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False)
  1371. if thr_id:
  1372. cmd = "-thread-info %s" % thr_id
  1373. else:
  1374. cmd = "-thread-info"
  1375. p = gdbmi_cmd_exec(p, handlers, cmd)
  1376. return p,result
  1377. def gdbmi_data_evaluate_expression(p, expr):
  1378. handlers = {}
  1379. result = GDBMIDataEvalHandler(verbose=False)
  1380. handlers[GDBMIResultHandler.TAG] = result
  1381. handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False)
  1382. p = gdbmi_cmd_exec(p, handlers, "-data-evaluate-expression \"%s\"" % expr)
  1383. return p,result
  1384. def gdbmi_freertos_get_task_name(p, tcb_addr):
  1385. p,res = gdbmi_data_evaluate_expression(p, "(char*)((TCB_t *)0x%x)->pcTaskName" % tcb_addr)
  1386. result = re.match("0x[a-fA-F0-9]+[^']*'([^']*)'", res.value)
  1387. if result:
  1388. return p,result.group(1)
  1389. return p,''
  1390. def gdb2freertos_thread_id(gdb_thread_id):
  1391. return int(gdb_thread_id.replace("process ", ""), 0)
  1392. loader = None
  1393. rom_elf,rom_sym_cmd = load_aux_elf(args.rom_elf)
  1394. if not args.core:
  1395. loader = ESPCoreDumpFlashLoader(args.off, port=args.port, baud=args.baud)
  1396. core_fname = loader.create_corefile(args.save_core, exe_name=args.prog, rom_elf=rom_elf)
  1397. if not core_fname:
  1398. logging.error("Failed to create corefile!")
  1399. loader.cleanup()
  1400. return
  1401. else:
  1402. core_fname = args.core
  1403. if args.core_format and args.core_format != 'elf':
  1404. loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64')
  1405. core_fname = loader.create_corefile(args.save_core, exe_name=args.prog, rom_elf=rom_elf)
  1406. if not core_fname:
  1407. logging.error("Failed to create corefile!")
  1408. loader.cleanup()
  1409. return
  1410. exe_elf = ESPCoreDumpElfFile(args.prog)
  1411. core_elf = ESPCoreDumpElfFile(core_fname)
  1412. merged_segs = []
  1413. core_segs = core_elf.program_segments
  1414. for s in exe_elf.sections:
  1415. merged = False
  1416. for ps in core_segs:
  1417. if ps.addr <= s.addr and ps.addr + len(ps.data) >= s.addr:
  1418. # sec: |XXXXXXXXXX|
  1419. # seg: |...XXX.............|
  1420. seg_addr = ps.addr
  1421. if ps.addr + len(ps.data) <= s.addr + len(s.data):
  1422. # sec: |XXXXXXXXXX|
  1423. # seg: |XXXXXXXXXXX...|
  1424. # merged: |XXXXXXXXXXXXXX|
  1425. seg_len = len(s.data) + (s.addr - ps.addr)
  1426. else:
  1427. # sec: |XXXXXXXXXX|
  1428. # seg: |XXXXXXXXXXXXXXXXX|
  1429. # merged: |XXXXXXXXXXXXXXXXX|
  1430. seg_len = len(ps.data)
  1431. merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True))
  1432. core_segs.remove(ps)
  1433. merged = True
  1434. elif ps.addr >= s.addr and ps.addr <= s.addr + len(s.data):
  1435. # sec: |XXXXXXXXXX|
  1436. # seg: |...XXX.............|
  1437. seg_addr = s.addr
  1438. if (ps.addr + len(ps.data)) >= (s.addr + len(s.data)):
  1439. # sec: |XXXXXXXXXX|
  1440. # seg: |..XXXXXXXXXXX|
  1441. # merged: |XXXXXXXXXXXXX|
  1442. seg_len = len(s.data) + (ps.addr + len(ps.data)) - (s.addr + len(s.data))
  1443. else:
  1444. # sec: |XXXXXXXXXX|
  1445. # seg: |XXXXXX|
  1446. # merged: |XXXXXXXXXX|
  1447. seg_len = len(s.data)
  1448. merged_segs.append((s.name, seg_addr, seg_len, s.attr_str(), True))
  1449. core_segs.remove(ps)
  1450. merged = True
  1451. if not merged:
  1452. merged_segs.append((s.name, s.addr, len(s.data), s.attr_str(), False))
  1453. handlers = {}
  1454. handlers[GDBMIResultHandler.TAG] = GDBMIResultHandler(verbose=False)
  1455. handlers[GDBMIStreamConsoleHandler.TAG] = GDBMIStreamConsoleHandler(None, verbose=False)
  1456. p = gdbmi_start(handlers, [rom_sym_cmd])
  1457. extra_note = None
  1458. task_info = []
  1459. for seg in core_elf.aux_segments:
  1460. if seg.type != ESPCoreDumpElfFile.PT_NOTE:
  1461. continue
  1462. note_read = 0
  1463. while note_read < len(seg.data):
  1464. note = Elf32NoteDesc("", 0, None)
  1465. note_read += note.read(seg.data[note_read:])
  1466. if note.type == ESPCoreDumpLoader.ESP_CORE_DUMP_EXTRA_INFO_TYPE and 'EXTRA_INFO' in note.name:
  1467. extra_note = note
  1468. if note.type == ESPCoreDumpLoader.ESP_CORE_DUMP_TASK_INFO_TYPE and 'TASK_INFO' in note.name:
  1469. task_info_struct = EspCoreDumpTaskStatus(buf=note.desc)
  1470. task_info.append(task_info_struct)
  1471. print("===============================================================")
  1472. print("==================== ESP32 CORE DUMP START ====================")
  1473. handlers[GDBMIResultHandler.TAG].result_class = None
  1474. handlers[GDBMIStreamConsoleHandler.TAG].func = gdbmi_console_stream_handler
  1475. if extra_note:
  1476. extra_info = struct.unpack("<%dL" % (len(extra_note.desc) / struct.calcsize("<L")), extra_note.desc)
  1477. if extra_info[0] == ESPCoreDumpLoader.ESP_COREDUMP_CURR_TASK_MARKER:
  1478. print("\nCrashed task has been skipped.")
  1479. else:
  1480. p,task_name = gdbmi_freertos_get_task_name(p, extra_info[0])
  1481. print("\nCrashed task handle: 0x%x, name: '%s', GDB name: 'process %d'" % (extra_info[0], task_name, extra_info[0]))
  1482. print("\n================== CURRENT THREAD REGISTERS ===================")
  1483. if extra_note:
  1484. exccause = extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EXCCAUSE_IDX + 1]
  1485. exccause_str = xtensa_exception_cause_dict.get(exccause)
  1486. if not exccause_str:
  1487. exccause_str = ("Invalid EXCCAUSE code", "Invalid EXCAUSE description or not found.")
  1488. print("exccause 0x%x (%s)" % (exccause, exccause_str[0]))
  1489. print("excvaddr 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EXCVADDR_IDX + 1])
  1490. print("epc1 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPC1_IDX + 1])
  1491. print("epc2 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPC2_IDX + 1])
  1492. print("epc3 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPC3_IDX + 1])
  1493. print("epc4 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPC4_IDX + 1])
  1494. print("epc5 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPC5_IDX + 1])
  1495. print("epc6 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPC6_IDX + 1])
  1496. print("epc7 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPC7_IDX + 1])
  1497. print("eps2 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPS2_IDX + 1])
  1498. print("eps3 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPS3_IDX + 1])
  1499. print("eps4 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPS4_IDX + 1])
  1500. print("eps5 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPS5_IDX + 1])
  1501. print("eps6 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPS6_IDX + 1])
  1502. print("eps7 0x%x" % extra_info[1 + 2 * ESPCoreDumpElfFile.REG_EPS7_IDX + 1])
  1503. else:
  1504. print("Exception registers have not been found!")
  1505. p = gdbmi_getinfo(p, handlers, "info registers")
  1506. print("\n==================== CURRENT THREAD STACK =====================")
  1507. p = gdbmi_getinfo(p, handlers, "bt")
  1508. if task_info and task_info[0].task_flags != EspCoreDumpTaskStatus.TASK_STATUS_CORRECT:
  1509. print("The current crashed task is corrupted.")
  1510. print("Task #%d info: flags, tcb, stack (%x, %x, %x)." % (task_info[0].task_index,
  1511. task_info[0].task_flags,
  1512. task_info[0].task_tcb_addr,
  1513. task_info[0].task_stack_start))
  1514. print("\n======================== THREADS INFO =========================")
  1515. p = gdbmi_getinfo(p, handlers, "info threads")
  1516. # THREADS STACKS
  1517. p,threads,cur_thread = gdbmi_get_thread_ids(p)
  1518. print()
  1519. for thr_id in threads:
  1520. task_index = int(thr_id) - 1
  1521. p = gdbmi_switch_thread(p, thr_id)
  1522. p,thr_info_res = gdbmi_get_thread_info(p, thr_id)
  1523. if not thr_info_res.target_id:
  1524. print("WARNING: Unable to switch to thread %s\n" % thr_id)
  1525. continue
  1526. tcb_addr = gdb2freertos_thread_id(thr_info_res.target_id)
  1527. p,task_name = gdbmi_freertos_get_task_name(p, tcb_addr)
  1528. print("==================== THREAD %s (TCB: 0x%x, name: '%s') =====================" % (thr_id, tcb_addr, task_name))
  1529. p = gdbmi_getinfo(p, handlers, "bt")
  1530. if task_info and task_info[task_index].task_flags != EspCoreDumpTaskStatus.TASK_STATUS_CORRECT:
  1531. print("The task '%s' is corrupted." % thr_id)
  1532. print("Task #%d info: flags, tcb, stack (%x, %x, %x)." % (task_info[task_index].task_index,
  1533. task_info[task_index].task_flags,
  1534. task_info[task_index].task_tcb_addr,
  1535. task_info[task_index].task_stack_start))
  1536. print()
  1537. print("\n======================= ALL MEMORY REGIONS ========================")
  1538. print("Name Address Size Attrs")
  1539. for ms in merged_segs:
  1540. print("%s 0x%x 0x%x %s" % (ms[0], ms[1], ms[2], ms[3]))
  1541. for cs in core_segs:
  1542. # core dump exec segments are from ROM, other are belong to tasks (TCB or stack)
  1543. if cs.flags & ESPCoreDumpSegment.PF_X:
  1544. seg_name = 'rom.text'
  1545. else:
  1546. seg_name = 'tasks.data'
  1547. print(".coredump.%s 0x%x 0x%x %s" % (seg_name, cs.addr, len(cs.data), cs.attr_str()))
  1548. if args.print_mem:
  1549. print("\n====================== CORE DUMP MEMORY CONTENTS ========================")
  1550. for cs in core_elf.program_segments:
  1551. # core dump exec segments are from ROM, other are belong to tasks (TCB or stack)
  1552. if cs.flags & ESPCoreDumpSegment.PF_X:
  1553. seg_name = 'rom.text'
  1554. else:
  1555. seg_name = 'tasks.data'
  1556. print(".coredump.%s 0x%x 0x%x %s" % (seg_name, cs.addr, len(cs.data), cs.attr_str()))
  1557. p = gdbmi_getinfo(p, handlers, "x/%dx 0x%x" % (old_div(len(cs.data),4), cs.addr))
  1558. print("\n===================== ESP32 CORE DUMP END =====================")
  1559. print("===============================================================")
  1560. p.stdin.write(b'q\n')
  1561. p.wait()
  1562. p.stdin.close()
  1563. p.stdout.close()
  1564. if loader:
  1565. if not args.core and not args.save_core:
  1566. loader.remove_tmp_file(core_fname)
  1567. loader.cleanup()
  1568. print('Done!')
  1569. def main():
  1570. parser = argparse.ArgumentParser(description='espcoredump.py v%s - ESP32 Core Dump Utility' % __version__, prog='espcoredump')
  1571. parser.add_argument('--chip', '-c',
  1572. help='Target chip type',
  1573. choices=['auto', 'esp32'],
  1574. default=os.environ.get('ESPTOOL_CHIP', 'auto'))
  1575. parser.add_argument(
  1576. '--port', '-p',
  1577. help='Serial port device',
  1578. default=os.environ.get('ESPTOOL_PORT', esptool.ESPLoader.DEFAULT_PORT))
  1579. parser.add_argument(
  1580. '--baud', '-b',
  1581. help='Serial port baud rate used when flashing/reading',
  1582. type=int,
  1583. default=os.environ.get('ESPTOOL_BAUD', esptool.ESPLoader.ESP_ROM_BAUD))
  1584. subparsers = parser.add_subparsers(
  1585. dest='operation',
  1586. help='Run coredumper {command} -h for additional help')
  1587. parser_debug_coredump = subparsers.add_parser(
  1588. 'dbg_corefile',
  1589. help='Starts GDB debugging session with specified corefile')
  1590. parser_debug_coredump.add_argument('--debug', '-d', help='Log level (0..3)', type=int, default=3)
  1591. parser_debug_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb')
  1592. parser_debug_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str)
  1593. parser_debug_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), '
  1594. 'raw (raw) or base64-encoded (b64) binary',
  1595. choices=['b64', 'elf', 'raw'], type=str, default='elf')
  1596. parser_debug_coredump.add_argument('--off', '-o', help='Ofsset of coredump partition in flash '
  1597. '(type "make partition_table" to see).', type=int, default=None)
  1598. parser_debug_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. '
  1599. 'Ignored with "-c"', type=str)
  1600. parser_debug_coredump.add_argument('--rom-elf', '-r', help='Path to ROM ELF file.', type=str, default='esp32_rom.elf')
  1601. parser_debug_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str)
  1602. parser_info_coredump = subparsers.add_parser(
  1603. 'info_corefile',
  1604. help='Print core dump info from file')
  1605. parser_info_coredump.add_argument('--debug', '-d', help='Log level (0..3)', type=int, default=3)
  1606. parser_info_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb')
  1607. parser_info_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str)
  1608. parser_info_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), '
  1609. 'raw (raw) or base64-encoded (b64) binary',
  1610. choices=['b64', 'elf', 'raw'], type=str, default='elf')
  1611. parser_info_coredump.add_argument('--off', '-o', help='Offset of coredump partition in flash (type '
  1612. '"make partition_table" to see).', type=int, default=None)
  1613. parser_info_coredump.add_argument('--save-core', '-s', help='Save core to file. Othwerwise temporary core file will be deleted. '
  1614. 'Does not work with "-c"', type=str)
  1615. parser_info_coredump.add_argument('--rom-elf', '-r', help='Path to ROM ELF file.', type=str, default='esp32_rom.elf')
  1616. parser_info_coredump.add_argument('--print-mem', '-m', help='Print memory dump', action='store_true')
  1617. parser_info_coredump.add_argument('prog', help='Path to program\'s ELF binary', type=str)
  1618. # internal sanity check - every operation matches a module function of the same name
  1619. for operation in subparsers.choices:
  1620. assert operation in globals(), "%s should be a module function" % operation
  1621. args = parser.parse_args()
  1622. log_level = logging.CRITICAL
  1623. if args.debug == 0:
  1624. log_level = logging.CRITICAL
  1625. elif args.debug == 1:
  1626. log_level = logging.ERROR
  1627. elif args.debug == 2:
  1628. log_level = logging.WARNING
  1629. elif args.debug == 3:
  1630. log_level = logging.INFO
  1631. else:
  1632. log_level = logging.DEBUG
  1633. logging.basicConfig(format='%(levelname)s: %(message)s', level=log_level)
  1634. print('espcoredump.py v%s' % __version__)
  1635. operation_func = globals()[args.operation]
  1636. operation_func(args)
  1637. if __name__ == '__main__':
  1638. try:
  1639. main()
  1640. except ESPCoreDumpError as e:
  1641. print('\nA fatal error occurred: %s' % e)
  1642. sys.exit(2)