espcoredump.py 78 KB

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