1 // Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include <string.h>
15 #include "esp_attr.h"
16 #include "esp_partition.h"
17 #include "esp_ota_ops.h"
18 #include "esp_spi_flash.h"
19 #include "esp_flash_encrypt.h"
20 #include "sdkconfig.h"
21 #include "core_dump_checksum.h"
22 #include "core_dump_elf.h"
23 #include "esp_core_dump_port.h"
24 #include "esp_core_dump_port_impl.h"
25 #include "esp_core_dump_common.h"
26
27 #define ELF_CLASS ELFCLASS32
28
29 #include "elf.h" // for ELF file types
30
31 #define ELF_SEG_HEADERS_COUNT(_self_) ((_self_)->segs_count)
32
33 #define ELF_HLEN 52
34 #define ELF_CORE_SEC_TYPE 1
35 #define ELF_PR_STATUS_SEG_NUM 0
36 #define ELF_ESP_CORE_DUMP_INFO_TYPE 8266
37 #define ELF_ESP_CORE_DUMP_EXTRA_INFO_TYPE 677
38 #define ELF_NOTE_NAME_MAX_SIZE 32
39 #define ELF_APP_SHA256_SIZE 66
40
41 #define ELF_CHECK_ERR(a, ret_val, str, ...) \
42 if (!(a)) { \
43 ESP_COREDUMP_LOGE("%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
44 return (ret_val); \
45 }
46
47 typedef enum
48 {
49 ELF_STAGE_CALC_SPACE = 0,
50 ELF_STAGE_PLACE_HEADERS = 1,
51 ELF_STAGE_PLACE_DATA = 2
52 } core_dump_elf_stages_t;
53
54 typedef enum _elf_err_t
55 {
56 ELF_PROC_ERR_SKIP_HEADER = 0,
57 ELF_PROC_ERR_STACK_CORRUPTED = -1,
58 ELF_PROC_ERR_WRITE_FAIL = -2,
59 ELF_PROC_ERR_OTHER = -3
60 } core_dump_elf_proc_err_t;
61
62 typedef struct _core_dump_task_info_t
63 {
64 elf_phdr* phdr;
65 void* frame;
66 core_dump_task_header_t* task_hdr;
67 uint32_t task_id;
68 size_t tcb_sz;
69 int* size_ptr;
70 } core_dump_task_data_t;
71
72 typedef struct
73 {
74 uint32_t version; // coredump version
75 uint8_t app_elf_sha256[ELF_APP_SHA256_SIZE]; // sha256 of elf file
76 } core_dump_elf_version_info_t;
77
78 const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_elf";
79
80 // Main ELF handle type
81 typedef struct _core_dump_elf_t
82 {
83 core_dump_elf_version_info_t elf_version_info;
84 uint16_t elf_stage;
85 uint32_t elf_next_data_offset;
86 uint16_t segs_count;
87 core_dump_write_config_t * write_cfg;
88 } core_dump_elf_t;
89
90 // Represents lightweight implementation to save core dump data into ELF formatted binary
91
92 #define ALIGN(b, var) var = align(b, var)
93
94 #if CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF
95
align(uint32_t width,uint32_t in)96 static inline uint32_t align(uint32_t width, uint32_t in)
97 {
98 return (in + (width - 1)) & -width;
99 }
100
101 // Builds elf header and check all data offsets
elf_write_file_header(core_dump_elf_t * self,uint32_t seg_count)102 static int elf_write_file_header(core_dump_elf_t *self, uint32_t seg_count)
103 {
104 elfhdr elf_hdr; // declare as static to save stack space
105
106 if (self->elf_stage == ELF_STAGE_PLACE_HEADERS) {
107 ESP_COREDUMP_LOG_PROCESS("Segment count %u", seg_count);
108 memset(&elf_hdr, 0, sizeof(elfhdr));
109 elf_hdr.e_ident[0] = ELFMAG0;
110 elf_hdr.e_ident[1] = ELFMAG1;
111 elf_hdr.e_ident[2] = ELFMAG2;
112 elf_hdr.e_ident[3] = ELFMAG3;
113 elf_hdr.e_ident[4] = ELFCLASS32;
114 elf_hdr.e_ident[5] = ELFDATA2LSB;
115 elf_hdr.e_ident[6] = EV_CURRENT;
116 elf_hdr.e_ident[7] = ELFOSABI_NONE;
117 elf_hdr.e_ident[8] = 0;
118 elf_hdr.e_type = ET_CORE;
119 elf_hdr.e_machine = esp_core_dump_get_arch_id();
120 elf_hdr.e_flags = 0;
121 elf_hdr.e_version = EV_CURRENT;
122 elf_hdr.e_entry = 0;
123 _Static_assert(sizeof(elfhdr) == ELF_HLEN, "Invalid ELF header struct length!");
124 elf_hdr.e_phoff = sizeof(elfhdr); // program header table's file offset in bytes.
125 elf_hdr.e_phentsize = sizeof(elf_phdr); // size in bytes of one entry in the file program header table
126 elf_hdr.e_phnum = seg_count; // number of program segments
127 elf_hdr.e_shoff = 0; // section header table's file offset in bytes.
128 elf_hdr.e_ehsize = sizeof(elfhdr); // elf header size
129 elf_hdr.e_shentsize = sizeof(elf_shdr); // section header's size in bytes.
130 elf_hdr.e_shnum = 0; // initial section counter is 0
131 elf_hdr.e_shstrndx = SHN_UNDEF; // do not use string table
132 // write built elf header into elf image
133 esp_err_t err = self->write_cfg->write(self->write_cfg->priv, (void*)&elf_hdr, sizeof(elf_hdr));
134 ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
135 "Write ELF header failure (%d)", err);
136 ESP_COREDUMP_LOG_PROCESS("Add file header %u bytes", sizeof(elf_hdr));
137 }
138
139 return self->elf_stage == ELF_STAGE_PLACE_DATA ? 0 : sizeof(elf_hdr);
140 }
141
elf_write_segment_header(core_dump_elf_t * self,elf_phdr * phdr)142 static int elf_write_segment_header(core_dump_elf_t *self, elf_phdr* phdr)
143 {
144 ELF_CHECK_ERR(phdr, ELF_PROC_ERR_SKIP_HEADER,
145 "Header is skipped, stage=(%d).", self->elf_stage);
146
147 phdr->p_offset = self->elf_next_data_offset;
148 // set segment data information and write it into image
149 esp_err_t err = self->write_cfg->write(self->write_cfg->priv, (void*)phdr, sizeof(elf_phdr));
150 ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
151 "Write ELF segment header failure (%d)", err);
152 ESP_COREDUMP_LOG_PROCESS("Add segment header %u bytes: type %d, sz %u, off = 0x%x",
153 sizeof(elf_phdr), phdr->p_type, phdr->p_filesz, phdr->p_offset);
154
155 return sizeof(elf_phdr);
156 }
157
elf_add_segment(core_dump_elf_t * self,uint32_t type,uint32_t vaddr,void * data,uint32_t data_sz)158 static int elf_add_segment(core_dump_elf_t *self,
159 uint32_t type, uint32_t vaddr,
160 void* data, uint32_t data_sz)
161 {
162 esp_err_t err = ESP_FAIL;
163 elf_phdr seg_hdr = { 0 };
164 int data_len = data_sz;
165
166 ELF_CHECK_ERR((data != NULL), ELF_PROC_ERR_OTHER,
167 "Invalid data for segment.");
168
169 ALIGN(4, data_len);
170
171 if (self->elf_stage == ELF_STAGE_CALC_SPACE) {
172 self->segs_count++;
173 return data_len + sizeof(elf_phdr);
174 }
175 if (self->elf_stage == ELF_STAGE_PLACE_HEADERS) {
176 seg_hdr.p_type = type;
177 seg_hdr.p_vaddr = vaddr;
178 seg_hdr.p_paddr = vaddr;
179 seg_hdr.p_filesz = data_len;
180 seg_hdr.p_memsz = data_len;
181 seg_hdr.p_flags = (PF_R | PF_W);
182 int ret = elf_write_segment_header(self, &seg_hdr);
183 ELF_CHECK_ERR((ret > 0), ret,
184 "Write ELF segment data failure (%d)", ret);
185 self->elf_next_data_offset += data_len;
186 return ret;
187 }
188 ESP_COREDUMP_LOG_PROCESS("Add segment size=%u, start_off=0x%x",
189 (uint32_t)data_len, self->elf_next_data_offset);
190 // write segment data only when write function is set and phdr = NULL
191 // write data into segment
192 err = self->write_cfg->write(self->write_cfg->priv, data, (uint32_t)data_len);
193 ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
194 "Write ELF segment data failure (%d)", err);
195 self->elf_next_data_offset += data_len;
196 return data_len;
197 }
198
elf_write_note(core_dump_elf_t * self,const char * name,uint32_t type,void * data,uint32_t data_sz)199 static int elf_write_note(core_dump_elf_t *self,
200 const char* name,
201 uint32_t type,
202 void* data,
203 uint32_t data_sz)
204 {
205 esp_err_t err = ESP_FAIL;
206 // temporary buffer for note name
207 static char name_buffer[ELF_NOTE_NAME_MAX_SIZE] = { 0 };
208 elf_note note_hdr = { 0 };
209 uint32_t name_len = strlen(name) + 1; // get name length including terminator
210 uint32_t data_len = data_sz;
211
212 ELF_CHECK_ERR(data, ELF_PROC_ERR_OTHER,
213 "Invalid data pointer %x.", (uint32_t)data);
214 ELF_CHECK_ERR((name_len <= ELF_NOTE_NAME_MAX_SIZE), 0,
215 "Segment note name is too long %d.", name_len);
216
217 ALIGN(4, data_len);
218 ALIGN(4, name_len);
219 uint32_t note_size = name_len + data_len + sizeof(elf_note);
220 ALIGN(4, note_size);
221
222 // write segment data during second pass
223 if (self->elf_stage == ELF_STAGE_PLACE_DATA) {
224 memcpy((void*)name_buffer, (void*)name, name_len);
225 note_hdr.n_namesz = name_len;
226 note_hdr.n_descsz = data_sz;
227 note_hdr.n_type = type;
228 // write note header
229 err = self->write_cfg->write(self->write_cfg->priv, (void*)¬e_hdr, sizeof(note_hdr));
230 ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
231 "Write ELF note header failure (%d)", err);
232 // write note name
233 err = self->write_cfg->write(self->write_cfg->priv, (void*)name_buffer, name_len);
234 ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
235 "Write ELF note name failure (%d)", err);
236 // write note data
237 err = self->write_cfg->write(self->write_cfg->priv, (void*)data, data_len);
238 ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
239 "Write ELF note data failure (%d)", err);
240 ESP_COREDUMP_LOG_PROCESS("Add note size=%d, start_off=0x%x",
241 note_size, self->elf_next_data_offset);
242 }
243 return note_size; // return actual note size
244 }
245
elf_add_note(core_dump_elf_t * self,const char * name,uint32_t type,void * data,uint32_t data_sz)246 static int elf_add_note(core_dump_elf_t *self,
247 const char* name,
248 uint32_t type,
249 void* data,
250 uint32_t data_sz)
251 {
252 ELF_CHECK_ERR((data != NULL), ELF_PROC_ERR_OTHER,
253 "Invalid data pointer for segment");
254
255 int note_size = elf_write_note(self, name, type, data, data_sz);
256 ELF_CHECK_ERR((note_size > 0), note_size,
257 "Write ELF note data failure, returned (%d)", note_size);
258 return note_size; // return actual note segment size
259 }
260
261 // Append note with registers dump to segment note
elf_add_regs(core_dump_elf_t * self,core_dump_task_header_t * task)262 static int elf_add_regs(core_dump_elf_t *self, core_dump_task_header_t *task)
263 {
264 void *reg_dump;
265
266 uint32_t len = esp_core_dump_get_task_regs_dump(task, ®_dump);
267 if (len == 0) {
268 ESP_COREDUMP_LOGE("Zero size register dump for task 0x%x!", task->tcb_addr);
269 return ELF_PROC_ERR_OTHER;
270 }
271
272 // append note data with dump to existing note
273 return elf_add_note(self,
274 "CORE", // note name
275 ELF_CORE_SEC_TYPE, // note type for reg dump
276 reg_dump, // register dump with pr_status
277 len);
278 }
279
elf_add_stack(core_dump_elf_t * self,core_dump_task_header_t * task)280 static int elf_add_stack(core_dump_elf_t *self, core_dump_task_header_t *task)
281 {
282 uint32_t stack_vaddr, stack_len = 0, stack_paddr = 0;
283
284 ELF_CHECK_ERR((task), ELF_PROC_ERR_OTHER, "Invalid task pointer.");
285
286 stack_len = esp_core_dump_get_stack(task, &stack_vaddr, &stack_paddr);
287 ESP_COREDUMP_LOG_PROCESS("Add stack for task 0x%x: addr 0x%x, sz %u",
288 task->tcb_addr, stack_vaddr, stack_len);
289 int ret = elf_add_segment(self, PT_LOAD,
290 (uint32_t)stack_vaddr,
291 (void*)stack_paddr,
292 (uint32_t) stack_len);
293 return ret;
294 }
295
elf_add_tcb(core_dump_elf_t * self,core_dump_task_header_t * task)296 static int elf_add_tcb(core_dump_elf_t *self, core_dump_task_header_t *task)
297 {
298 ELF_CHECK_ERR((task), ELF_PROC_ERR_OTHER, "Invalid task pointer.");
299 // add task tcb data into program segment of ELF
300 ESP_COREDUMP_LOG_PROCESS("Add TCB for task 0x%x: addr 0x%x, sz %u",
301 task->tcb_addr, task->tcb_addr,
302 esp_core_dump_get_tcb_len());
303 int ret = elf_add_segment(self, PT_LOAD,
304 (uint32_t)task->tcb_addr,
305 task->tcb_addr,
306 esp_core_dump_get_tcb_len());
307 return ret;
308 }
309
elf_process_task_tcb(core_dump_elf_t * self,core_dump_task_header_t * task)310 static int elf_process_task_tcb(core_dump_elf_t *self, core_dump_task_header_t *task)
311 {
312 int ret = ELF_PROC_ERR_OTHER;
313
314 ELF_CHECK_ERR((task), ELF_PROC_ERR_OTHER, "Invalid input data.");
315
316 // save tcb of the task as is and apply segment size
317 ret = elf_add_tcb(self, task);
318 if (ret <= 0) {
319 ESP_COREDUMP_LOGE("Task (TCB:%x) processing failure = %d",
320 task->tcb_addr,
321 ret);
322 }
323 return ret;
324 }
325
elf_process_task_stack(core_dump_elf_t * self,core_dump_task_header_t * task)326 static int elf_process_task_stack(core_dump_elf_t *self, core_dump_task_header_t *task)
327 {
328 int ret = ELF_PROC_ERR_OTHER;
329
330 ELF_CHECK_ERR((task), ELF_PROC_ERR_OTHER, "Invalid input data.");
331
332 ret = elf_add_stack(self, task);
333 if (ret <= 0) {
334 ESP_COREDUMP_LOGE("Task (TCB:%x), (Stack:%x), stack processing failure = %d.",
335 task->tcb_addr,
336 task->stack_start,
337 ret);
338 }
339 return ret;
340 }
341
elf_process_note_segment(core_dump_elf_t * self,int notes_size)342 static int elf_process_note_segment(core_dump_elf_t *self, int notes_size)
343 {
344 int ret;
345 elf_phdr seg_hdr = { 0 };
346
347 if (self->elf_stage == ELF_STAGE_PLACE_HEADERS) {
348 // segment header for PR_STATUS notes
349 seg_hdr.p_type = PT_NOTE;
350 seg_hdr.p_vaddr = 0;
351 seg_hdr.p_paddr = 0;
352 seg_hdr.p_filesz = notes_size;
353 seg_hdr.p_memsz = notes_size;
354 seg_hdr.p_flags = (PF_R | PF_W);
355 ret = elf_write_segment_header(self, &seg_hdr);
356 ELF_CHECK_ERR((ret > 0), ret, "NOTE segment header write failure, returned (%d).", ret);
357 self->elf_next_data_offset += notes_size;
358 return sizeof(seg_hdr);
359 } else if (self->elf_stage == ELF_STAGE_CALC_SPACE) {
360 self->segs_count++;
361 notes_size += sizeof(seg_hdr);
362 } else {
363 // in "Place Data" phase segment body is been already filled by other functions
364 ESP_COREDUMP_LOG_PROCESS("Add NOTE segment, size=%d, start_off=0x%x",
365 notes_size, self->elf_next_data_offset);
366 self->elf_next_data_offset += notes_size;
367 }
368 return (int)notes_size;
369 }
370
elf_process_tasks_regs(core_dump_elf_t * self)371 static int elf_process_tasks_regs(core_dump_elf_t *self)
372 {
373 core_dump_task_header_t task_hdr = { 0 };
374 void *task = NULL;
375 int len = 0;
376 int ret = 0;
377
378 esp_core_dump_reset_tasks_snapshots_iter();
379 task = esp_core_dump_get_current_task_handle();
380 if (esp_core_dump_get_task_snapshot(task, &task_hdr, NULL)) {
381 // place current task dump first
382 ret = elf_add_regs(self, &task_hdr);
383 if (self->elf_stage == ELF_STAGE_PLACE_HEADERS) {
384 // when writing segments headers this function writes nothing
385 ELF_CHECK_ERR((ret >= 0), ret, "Task %x, PR_STATUS write failed, return (%d).", task, ret);
386 } else {
387 ELF_CHECK_ERR((ret > 0), ret, "Task %x, PR_STATUS write failed, return (%d).", task, ret);
388 }
389 len += ret;
390 }
391 // processes PR_STATUS and register dump for each task
392 // each call to the processing function appends PR_STATUS note into note segment
393 // and writes data or updates the segment note header accordingly (if phdr is set)
394 task = NULL;
395 while ((task = esp_core_dump_get_next_task(task))) {
396 if (task == esp_core_dump_get_current_task_handle()) {
397 continue; // skip current task (already processed)
398 }
399 if (esp_core_dump_get_task_snapshot(task, &task_hdr, NULL)) {
400 ret = elf_add_regs(self, &task_hdr);
401 if (self->elf_stage == ELF_STAGE_PLACE_HEADERS) {
402 // when writing segments headers this function writes nothing
403 ELF_CHECK_ERR((ret >= 0), ret, "Task %x, PR_STATUS write failed, return (%d).", task, ret);
404 } else {
405 ELF_CHECK_ERR((ret > 0), ret, "Task %x, PR_STATUS write failed, return (%d).", task, ret);
406 }
407 len += ret;
408 }
409 }
410 ret = elf_process_note_segment(self, len); // tasks regs note
411 ELF_CHECK_ERR((ret > 0), ret,
412 "PR_STATUS note segment processing failure, returned(%d).", ret);
413 return ret;
414 }
415
elf_save_task(core_dump_elf_t * self,core_dump_task_header_t * task)416 static int elf_save_task(core_dump_elf_t *self, core_dump_task_header_t *task)
417 {
418 int elf_len = 0;
419
420 int ret = elf_process_task_tcb(self, task);
421 ELF_CHECK_ERR((ret > 0), ret,
422 "Task %x, TCB write failed, return (%d).", task->tcb_addr, ret);
423 elf_len += ret;
424 ret = elf_process_task_stack(self, task);
425 ELF_CHECK_ERR((ret != ELF_PROC_ERR_WRITE_FAIL), ELF_PROC_ERR_WRITE_FAIL,
426 "Task %x, stack write failed, return (%d).", task->tcb_addr, ret);
427 elf_len += ret;
428 return elf_len;
429 }
430
elf_write_tasks_data(core_dump_elf_t * self)431 static int elf_write_tasks_data(core_dump_elf_t *self)
432 {
433 int elf_len = 0;
434 void *task = NULL;
435 core_dump_task_header_t task_hdr = { 0 };
436 core_dump_mem_seg_header_t interrupted_stack = { 0 };
437 int ret = ELF_PROC_ERR_OTHER;
438 uint16_t tasks_num = 0;
439 uint16_t bad_tasks_num = 0;
440
441 ESP_COREDUMP_LOG_PROCESS("================ Processing task registers ================");
442 ret = elf_process_tasks_regs(self);
443 ELF_CHECK_ERR((ret > 0), ret, "Tasks regs addition failed, return (%d).", ret);
444 elf_len += ret;
445
446 ESP_COREDUMP_LOG_PROCESS("================ Processing task data ================");
447 // processes all task's stack data and writes segment data into partition
448 // if flash configuration is set
449 task = NULL;
450 esp_core_dump_reset_tasks_snapshots_iter();
451 while ((task = esp_core_dump_get_next_task(task))) {
452 tasks_num++;
453 if (!esp_core_dump_get_task_snapshot(task, &task_hdr, &interrupted_stack)) {
454 bad_tasks_num++;
455 continue;
456 }
457 ret = elf_save_task(self, &task_hdr);
458 ELF_CHECK_ERR((ret > 0), ret,
459 "Task %x, TCB write failed, return (%d).", task, ret);
460 elf_len += ret;
461 if (interrupted_stack.size > 0) {
462 ESP_COREDUMP_LOG_PROCESS("Add interrupted task stack %lu bytes @ %x",
463 interrupted_stack.size, interrupted_stack.start);
464 ret = elf_add_segment(self, PT_LOAD,
465 (uint32_t)interrupted_stack.start,
466 (void*)interrupted_stack.start,
467 (uint32_t)interrupted_stack.size);
468 ELF_CHECK_ERR((ret > 0), ret, "Interrupted task stack write failed, return (%d).", ret);
469 elf_len += ret;
470 }
471 }
472 ESP_COREDUMP_LOG_PROCESS("Found %d bad task out of %d", bad_tasks_num, tasks_num);
473 return elf_len;
474 }
475
elf_write_core_dump_user_data(core_dump_elf_t * self)476 static int elf_write_core_dump_user_data(core_dump_elf_t *self)
477 {
478 int data_len = 0;
479 int total_sz = 0;
480 uint32_t start = 0;
481
482 for (coredump_region_t i = COREDUMP_MEMORY_START; i < COREDUMP_MEMORY_MAX; i++) {
483 data_len = esp_core_dump_get_user_ram_info(i, &start);
484
485 ELF_CHECK_ERR((data_len >= 0), ELF_PROC_ERR_OTHER, "invalid memory region");
486
487 if (data_len > 0) {
488 int ret = elf_add_segment(self, PT_LOAD,
489 (uint32_t)start,
490 (void*)start,
491 (uint32_t) data_len);
492
493 ELF_CHECK_ERR((ret > 0), ret, "memory region write failed. Returned (%d).", ret);
494 total_sz += ret;
495 }
496 }
497
498 return total_sz;
499 }
500
elf_write_core_dump_info(core_dump_elf_t * self)501 static int elf_write_core_dump_info(core_dump_elf_t *self)
502 {
503 void *extra_info = NULL;
504
505 ESP_COREDUMP_LOG_PROCESS("================ Processing coredump info ================");
506 int data_len = (int)sizeof(self->elf_version_info.app_elf_sha256);
507 data_len = esp_ota_get_app_elf_sha256((char*)self->elf_version_info.app_elf_sha256, (size_t)data_len);
508 ESP_COREDUMP_LOG_PROCESS("Application SHA256='%s', length=%d.",
509 self->elf_version_info.app_elf_sha256, data_len);
510 self->elf_version_info.version = esp_core_dump_elf_version();
511 int ret = elf_add_note(self,
512 "ESP_CORE_DUMP_INFO",
513 ELF_ESP_CORE_DUMP_INFO_TYPE,
514 &self->elf_version_info,
515 sizeof(self->elf_version_info));
516 ELF_CHECK_ERR((ret > 0), ret, "Version info note write failed. Returned (%d).", ret);
517 data_len = ret;
518
519 uint32_t extra_info_len = esp_core_dump_get_extra_info(&extra_info);
520 if (extra_info_len == 0) {
521 ESP_COREDUMP_LOGE("Zero size extra info!");
522 return ELF_PROC_ERR_OTHER;
523 }
524
525 ret = elf_add_note(self,
526 "EXTRA_INFO",
527 ELF_ESP_CORE_DUMP_EXTRA_INFO_TYPE,
528 extra_info,
529 extra_info_len);
530 ELF_CHECK_ERR((ret > 0), ret, "Extra info note write failed. Returned (%d).", ret);
531 data_len += ret;
532
533 ret = elf_process_note_segment(self, data_len);
534 ELF_CHECK_ERR((ret > 0), ret,
535 "EXTRA_INFO note segment processing failure, returned(%d).", ret);
536 return ret;
537 }
538
esp_core_dump_do_write_elf_pass(core_dump_elf_t * self)539 static int esp_core_dump_do_write_elf_pass(core_dump_elf_t *self)
540 {
541 int tot_len = 0;
542
543 int data_sz = elf_write_file_header(self, ELF_SEG_HEADERS_COUNT(self));
544 if (self->elf_stage == ELF_STAGE_PLACE_DATA) {
545 ELF_CHECK_ERR((data_sz >= 0), data_sz, "ELF header writing error, returned (%d).", data_sz);
546 } else {
547 ELF_CHECK_ERR((data_sz > 0), data_sz, "ELF header writing error, returned (%d).", data_sz);
548 }
549 tot_len += data_sz;
550 // Calculate whole size include headers for all tasks and main elf header
551 data_sz = elf_write_tasks_data(self);
552 ELF_CHECK_ERR((data_sz > 0), data_sz, "ELF Size writing error, returned (%d).", data_sz);
553 tot_len += data_sz;
554
555 // write core dump memory regions defined by user
556 data_sz = elf_write_core_dump_user_data(self);
557 ELF_CHECK_ERR((data_sz >= 0), data_sz, "memory regions writing error, returned (%d).", data_sz);
558 tot_len += data_sz;
559
560 // write data with version control information and some extra info
561 // this should go after tasks processing
562 data_sz = elf_write_core_dump_info(self);
563 ELF_CHECK_ERR((data_sz > 0), data_sz, "Version info writing failed. Returned (%d).", data_sz);
564 tot_len += data_sz;
565
566 return tot_len;
567 }
568
esp_core_dump_write_elf(core_dump_write_config_t * write_cfg)569 esp_err_t esp_core_dump_write_elf(core_dump_write_config_t *write_cfg)
570 {
571 static core_dump_elf_t self = { 0 };
572 static core_dump_header_t dump_hdr = { 0 };
573 esp_err_t err = ESP_OK;
574 int tot_len = sizeof(dump_hdr);
575 int write_len = sizeof(dump_hdr);
576
577 ELF_CHECK_ERR((write_cfg), ESP_ERR_INVALID_ARG, "Invalid input data.");
578
579 self.write_cfg = write_cfg;
580
581 // On first pass (do not write actual data), but calculate data length needed to allocate memory
582 self.elf_stage = ELF_STAGE_CALC_SPACE;
583 ESP_COREDUMP_LOG_PROCESS("================= Calc data size ===============");
584 int ret = esp_core_dump_do_write_elf_pass(&self);
585 if (ret < 0) return ret;
586 tot_len += ret;
587 ESP_COREDUMP_LOG_PROCESS("Core dump tot_len=%lu", tot_len);
588 ESP_COREDUMP_LOG_PROCESS("============== Data size = %d bytes ============", tot_len);
589
590 // Prepare write elf
591 if (write_cfg->prepare) {
592 err = write_cfg->prepare(write_cfg->priv, (uint32_t*)&tot_len);
593 if (err != ESP_OK) {
594 ESP_COREDUMP_LOGE("Failed to prepare core dump storage (%d)!", err);
595 return err;
596 }
597 }
598
599 // Write start
600 if (write_cfg->start) {
601 err = write_cfg->start(write_cfg->priv);
602 if (err != ESP_OK) {
603 ESP_COREDUMP_LOGE("Failed to start core dump (%d)!", err);
604 return err;
605 }
606 }
607
608 // Write core dump header
609 dump_hdr.data_len = tot_len;
610 dump_hdr.version = esp_core_dump_elf_version();
611 dump_hdr.tasks_num = 0; // unused in ELF format
612 dump_hdr.tcb_sz = 0; // unused in ELF format
613 dump_hdr.mem_segs_num = 0; // unused in ELF format
614 err = write_cfg->write(write_cfg->priv,
615 (void*)&dump_hdr,
616 sizeof(core_dump_header_t));
617 if (err != ESP_OK) {
618 ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err);
619 return err;
620 }
621
622 self.elf_stage = ELF_STAGE_PLACE_HEADERS;
623 // set initial offset to elf segments data area
624 self.elf_next_data_offset = sizeof(elfhdr) + ELF_SEG_HEADERS_COUNT(&self) * sizeof(elf_phdr);
625 ret = esp_core_dump_do_write_elf_pass(&self);
626 if (ret < 0) return ret;
627 write_len += ret;
628 ESP_COREDUMP_LOG_PROCESS("============== Headers size = %d bytes ============", write_len);
629
630 self.elf_stage = ELF_STAGE_PLACE_DATA;
631 // set initial offset to elf segments data area, this is not necessary in this stage, just for pretty debug output
632 self.elf_next_data_offset = sizeof(elfhdr) + ELF_SEG_HEADERS_COUNT(&self) * sizeof(elf_phdr);
633 ret = esp_core_dump_do_write_elf_pass(&self);
634 if (ret < 0) return ret;
635 write_len += ret;
636 ESP_COREDUMP_LOG_PROCESS("=========== Data written size = %d bytes ==========", write_len);
637
638 // Write end, update checksum
639 if (write_cfg->end) {
640 err = write_cfg->end(write_cfg->priv);
641 if (err != ESP_OK) {
642 ESP_COREDUMP_LOGE("Failed to end core dump (%d)!", err);
643 return err;
644 }
645 }
646 return err;
647 }
648
649 #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
650
651 /* Below are the helper function to parse the core dump ELF stored in flash */
652
elf_core_dump_image_mmap(spi_flash_mmap_handle_t * core_data_handle,const void ** map_addr)653 static esp_err_t elf_core_dump_image_mmap(spi_flash_mmap_handle_t* core_data_handle, const void **map_addr)
654 {
655 size_t out_size;
656 assert (core_data_handle);
657 assert(map_addr);
658
659 /* Find the partition that could potentially contain a (previous) core dump. */
660 const esp_partition_t *core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
661 ESP_PARTITION_SUBTYPE_DATA_COREDUMP,
662 NULL);
663 if (!core_part) {
664 ESP_COREDUMP_LOGE("Core dump partition not found!");
665 return ESP_ERR_NOT_FOUND;
666 }
667 if (core_part->size < sizeof(uint32_t)) {
668 ESP_COREDUMP_LOGE("Core dump partition too small!");
669 return ESP_ERR_INVALID_SIZE;
670 }
671 /* Data read from the mmapped core dump partition will be garbage if flash
672 * encryption is enabled in hardware and core dump partition is not encrypted
673 */
674 if (esp_flash_encryption_enabled() && !core_part->encrypted) {
675 ESP_COREDUMP_LOGE("Flash encryption enabled in hardware and core dump partition is not encrypted!");
676 return ESP_ERR_NOT_SUPPORTED;
677 }
678 /* Read the size of the core dump file from the partition */
679 esp_err_t ret = esp_partition_read(core_part, 0, &out_size, sizeof(uint32_t));
680 if (ret != ESP_OK) {
681 ESP_COREDUMP_LOGE("Failed to read core dump data size");
682 return ret;
683 }
684 /* map the full core dump parition, including the checksum. */
685 return esp_partition_mmap(core_part, 0, out_size, SPI_FLASH_MMAP_DATA,
686 map_addr, core_data_handle);
687 }
688
elf_parse_version_info(esp_core_dump_summary_t * summary,void * data)689 static void elf_parse_version_info(esp_core_dump_summary_t *summary, void *data)
690 {
691 core_dump_elf_version_info_t *version = (core_dump_elf_version_info_t *)data;
692 summary->core_dump_version = version->version;
693 memcpy(summary->app_elf_sha256, version->app_elf_sha256, ELF_APP_SHA256_SIZE);
694 ESP_COREDUMP_LOGD("Core dump version 0x%x", summary->core_dump_version);
695 ESP_COREDUMP_LOGD("App ELF SHA2 %s", (char *)summary->app_elf_sha256);
696 }
697
elf_parse_exc_task_name(esp_core_dump_summary_t * summary,void * tcb_data)698 static void elf_parse_exc_task_name(esp_core_dump_summary_t *summary, void *tcb_data)
699 {
700 StaticTask_t *tcb = (StaticTask_t *) tcb_data;
701 /* An ugly way to get the task name. We could possibly use pcTaskGetTaskName here.
702 * But that has assumption that TCB pointer can be used as TaskHandle. So let's
703 * keep it this way. */
704 memset(summary->exc_task, 0, sizeof(summary->exc_task));
705 strlcpy(summary->exc_task, (char *)tcb->ucDummy7, sizeof(summary->exc_task));
706 ESP_COREDUMP_LOGD("Crashing task %s", summary->exc_task);
707 }
708
esp_core_dump_get_summary(esp_core_dump_summary_t * summary)709 esp_err_t esp_core_dump_get_summary(esp_core_dump_summary_t *summary)
710 {
711 int i;
712 elf_phdr *ph;
713 elf_note *note;
714 const void *map_addr;
715 size_t consumed_note_sz;
716 spi_flash_mmap_handle_t core_data_handle;
717
718 if (!summary) {
719 return ESP_ERR_INVALID_ARG;
720 }
721 esp_err_t err = elf_core_dump_image_mmap(&core_data_handle, &map_addr);
722 if (err != ESP_OK) {
723 return err;
724 }
725 uint8_t *ptr = (uint8_t *) map_addr + sizeof(core_dump_header_t);
726 elfhdr *eh = (elfhdr *)ptr;
727
728 ESP_COREDUMP_LOGD("ELF ident %02x %c %c %c", eh->e_ident[0], eh->e_ident[1], eh->e_ident[2], eh->e_ident[3]);
729 ESP_COREDUMP_LOGD("Ph_num %d offset %x", eh->e_phnum, eh->e_phoff);
730
731 for (i = 0; i < eh->e_phnum; i++) {
732 ph = (elf_phdr *)((ptr + i * sizeof(*ph)) + eh->e_phoff);
733 ESP_COREDUMP_LOGD("PHDR type %d off %x vaddr %x paddr %x filesz %x memsz %x flags %x align %x",
734 ph->p_type, ph->p_offset, ph->p_vaddr, ph->p_paddr, ph->p_filesz, ph->p_memsz,
735 ph->p_flags, ph->p_align);
736 if (ph->p_type == PT_NOTE) {
737 consumed_note_sz = 0;
738 while(consumed_note_sz < ph->p_memsz) {
739 note = (elf_note *)(ptr + ph->p_offset + consumed_note_sz);
740 char *nm = (char *)(ptr + ph->p_offset + consumed_note_sz + sizeof(elf_note));
741 ESP_COREDUMP_LOGD("Note NameSZ %x DescSZ %x Type %x name %s", note->n_namesz,
742 note->n_descsz, note->n_type, nm);
743 if (strncmp(nm, "EXTRA_INFO", note->n_namesz) == 0 ) {
744 esp_core_dump_summary_parse_extra_info(summary, (void *)(nm + note->n_namesz));
745 }
746 if (strncmp(nm, "ESP_CORE_DUMP_INFO", note->n_namesz) == 0 ) {
747 elf_parse_version_info(summary, (void *)(nm + note->n_namesz));
748 }
749 consumed_note_sz += note->n_namesz + note->n_descsz + sizeof(elf_note);
750 ALIGN(4, consumed_note_sz);
751 }
752 }
753 }
754 /* Following code assumes that task stack segment follows the TCB segment for the respective task.
755 * In general ELF does not impose any restrictions on segments' order so this can be changed without impacting core dump version.
756 * More universal and flexible way would be to retrieve stack start address from crashed task TCB segment and then look for the stack segment with that address.
757 */
758 int flag = 0;
759 for (i = 0; i < eh->e_phnum; i++) {
760 ph = (elf_phdr *)((ptr + i * sizeof(*ph)) + eh->e_phoff);
761 if (ph->p_type == PT_LOAD) {
762 if (flag) {
763 esp_core_dump_summary_parse_exc_regs(summary, (void *)(ptr + ph->p_offset));
764 esp_core_dump_summary_parse_backtrace_info(&summary->exc_bt_info, (void *) ph->p_vaddr,
765 (void *)(ptr + ph->p_offset), ph->p_memsz);
766 break;
767 }
768 if (ph->p_vaddr == summary->exc_tcb) {
769 elf_parse_exc_task_name(summary, (void *)(ptr + ph->p_offset));
770 flag = 1;
771 }
772 }
773 }
774 spi_flash_munmap(core_data_handle);
775 return ESP_OK;
776 }
777
778 #endif // CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
779
780 #endif //CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF
781