/**************************************************************************** * binfmt/libelf/libelf_coredump.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define ELF_PAGESIZE 4096 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define ROUNDUP(x, y) ((x + (y - 1)) / (y)) * (y) /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: elf_flush * * Description: * Flush the out stream * ****************************************************************************/ static int elf_flush(FAR struct elf_dumpinfo_s *cinfo) { return cinfo->stream->flush(cinfo->stream); } /**************************************************************************** * Name: elf_emit * * Description: * Send the dump data to binfmt_outstream_s * ****************************************************************************/ static int elf_emit(FAR struct elf_dumpinfo_s *cinfo, FAR const void *buf, size_t len) { FAR const uint8_t *ptr = buf; size_t total = len; int ret; while (total > 0) { ret = cinfo->stream->puts(cinfo->stream, ptr, total); if (ret < 0) { break; } total -= ret; ptr += ret; } return ret < 0 ? ret : len - total; } /**************************************************************************** * Name: elf_emit_align * * Description: * Align the filled data according to the current offset * ****************************************************************************/ static int elf_emit_align(FAR struct elf_dumpinfo_s *cinfo) { off_t align = ROUNDUP(cinfo->stream->nput, ELF_PAGESIZE) - cinfo->stream->nput; unsigned char null[256]; off_t total = align; off_t ret; memset(null, 0, sizeof(null)); while (total > 0) { ret = elf_emit(cinfo, null, total > sizeof(null) ? sizeof(null) : total); if (ret <= 0) { break; } total -= ret; } return ret < 0 ? ret : align; } /**************************************************************************** * Name: elf_emit_header * * Description: * Fill the elf header * ****************************************************************************/ static int elf_emit_header(FAR struct elf_dumpinfo_s *cinfo, int segs) { Elf_Ehdr ehdr; memset(&ehdr, 0, sizeof(ehdr)); memcpy(ehdr.e_ident, ELFMAG, EI_MAGIC_SIZE); ehdr.e_ident[EI_CLASS] = ELF_CLASS; ehdr.e_ident[EI_DATA] = ELF_DATA; ehdr.e_ident[EI_VERSION] = EV_CURRENT; ehdr.e_ident[EI_OSABI] = ELF_OSABI; ehdr.e_type = ET_CORE; ehdr.e_machine = EM_ARCH; ehdr.e_version = EV_CURRENT; ehdr.e_phoff = sizeof(Elf_Ehdr); ehdr.e_flags = EF_FLAG; ehdr.e_ehsize = sizeof(Elf_Ehdr); ehdr.e_phentsize = sizeof(Elf_Phdr); ehdr.e_phnum = segs; return elf_emit(cinfo, &ehdr, sizeof(ehdr)); } /**************************************************************************** * Name: elf_get_note_size * * Description: * Calculate the note segment size * ****************************************************************************/ static int elf_get_note_size(void) { int count = 0; int total; int i; for (i = 0; i < g_npidhash; i++) { if (g_pidhash[i]) { count++; } } total = count * (sizeof(Elf_Nhdr) + ROUNDUP(CONFIG_TASK_NAME_SIZE, 8) + sizeof(elf_prstatus_t)); total += count * (sizeof(Elf_Nhdr) + ROUNDUP(CONFIG_TASK_NAME_SIZE, 8) + sizeof(elf_prpsinfo_t)); return total; } /**************************************************************************** * Name: elf_emit_note_info * * Description: * Fill the note segment information * ****************************************************************************/ static void elf_emit_note_info(FAR struct elf_dumpinfo_s *cinfo) { char name[ROUNDUP(CONFIG_TASK_NAME_SIZE, 8)]; FAR struct tcb_s *tcb; elf_prstatus_t status; elf_prpsinfo_t info; Elf_Nhdr nhdr; int i; int j; memset(&info, 0x0, sizeof(info)); memset(&status, 0x0, sizeof(status)); for (i = 0; i < g_npidhash; i++) { if (g_pidhash[i] == NULL) { continue; } tcb = g_pidhash[i]; /* Fill Process info */ nhdr.n_namesz = sizeof(name); nhdr.n_descsz = sizeof(info); nhdr.n_type = NT_PRPSINFO; elf_emit(cinfo, &nhdr, sizeof(nhdr)); strlcpy(name, tcb->name, sizeof(name)); elf_emit(cinfo, name, sizeof(name)); info.pr_pid = tcb->pid; strlcpy(info.pr_fname, tcb->name, sizeof(info.pr_fname)); elf_emit(cinfo, &info, sizeof(info)); /* Fill Process status */ nhdr.n_descsz = sizeof(status); nhdr.n_type = NT_PRSTATUS; elf_emit(cinfo, &nhdr, sizeof(nhdr)); elf_emit(cinfo, name, sizeof(name)); status.pr_pid = tcb->pid; for (j = 0; j < ARRAY_SIZE(status.pr_regs); j++) { status.pr_regs[j] = *(uintptr_t *)((uint8_t *)tcb + g_tcbinfo.reg_off.p[j]); } elf_emit(cinfo, &status, sizeof(status)); } } /**************************************************************************** * Name: elf_emit_program_header * * Description: * Fill the program segment header * ****************************************************************************/ static void elf_emit_program_header(FAR struct elf_dumpinfo_s *cinfo, int segs) { off_t offset = cinfo->stream->nput + (segs + 1) * sizeof(Elf_Phdr); Elf_Phdr phdr; int i; memset(&phdr, 0, sizeof(Elf_Phdr)); phdr.p_type = PT_NOTE; phdr.p_offset = offset; phdr.p_filesz = elf_get_note_size(); offset += phdr.p_filesz; elf_emit(cinfo, &phdr, sizeof(phdr)); /* Write program headers for segments dump */ for (i = 0; i < segs; i++) { phdr.p_type = PT_LOAD; phdr.p_offset = ROUNDUP(offset, ELF_PAGESIZE); phdr.p_vaddr = cinfo->regions[i].start; phdr.p_paddr = cinfo->regions[i].start; phdr.p_filesz = cinfo->regions[i].end - cinfo->regions[i].start; phdr.p_memsz = phdr.p_filesz; phdr.p_flags = cinfo->regions[i].flags; phdr.p_align = ELF_PAGESIZE; offset += ROUNDUP(phdr.p_memsz, ELF_PAGESIZE); elf_emit(cinfo, &phdr, sizeof(phdr)); } } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: elf_coredump * * Description: * Generat the core dump stream as ELF structure. * * Input Parameters: * dumpinfo - elf coredump informations * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ int elf_coredump(FAR struct elf_dumpinfo_s *cinfo) { int segs = 0; int i; /* Check the memory region */ if (cinfo->regions) { for (; cinfo->regions[segs].start < cinfo->regions[segs].end; segs++); } if (segs == 0) { return -EINVAL; } /* Fill notes section */ elf_emit_header(cinfo, segs + 1); /* Fill all the program information about the process for the * notes. This also sets up the file header. */ elf_emit_program_header(cinfo, segs); /* Fill note information */ elf_emit_note_info(cinfo); /* Align to page */ elf_emit_align(cinfo); /* Start dump the memory */ for (i = 0; i < segs; i++) { elf_emit(cinfo, (FAR void *)cinfo->regions[i].start, cinfo->regions[i].end - cinfo->regions[i].start); /* Align to page */ elf_emit_align(cinfo); } /* Flush the dump */ return elf_flush(cinfo); }