908df725ad
reason: 1 On different architectures, we can utilize more optimized strategies to implement up_current_regs/up_set_current_regs. eg. use interrupt registersor percpu registers. code size before text data bss dec hex filename 262848 49985 63893 376726 5bf96 nuttx after text data bss dec hex filename 262844 49985 63893 376722 5bf92 nuttx size change -4 Configuring NuttX and compile: $ ./tools/configure.sh -l qemu-armv8a:nsh_smp $ make Running with qemu $ qemu-system-aarch64 -cpu cortex-a53 -smp 4 -nographic \ -machine virt,virtualization=on,gic-version=3 \ -net none -chardev stdio,id=con,mux=on -serial chardev:con \ -mon chardev=con,mode=readline -kernel ./nuttx Signed-off-by: hujun5 <hujun5@xiaomi.com>
667 lines
17 KiB
C
667 lines
17 KiB
C
/****************************************************************************
|
|
* binfmt/libelf/libelf_coredump.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* 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 <nuttx/config.h>
|
|
#include <nuttx/arch.h>
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/param.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <debug.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/elf.h>
|
|
#include <nuttx/binfmt/elf.h>
|
|
#include <nuttx/binfmt/binfmt.h>
|
|
#include <nuttx/sched.h>
|
|
#include <sched/sched.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#ifdef PAGESIZE
|
|
# define ELF_PAGESIZE PAGESIZE
|
|
#else
|
|
# define ELF_PAGESIZE 1024
|
|
#endif
|
|
|
|
#define PROGRAM_ALIGNMENT 64
|
|
|
|
#define ROUNDUP(x, y) ((x + (y - 1)) / (y)) * (y)
|
|
#define ROUNDDOWN(x ,y) (((x) / (y)) * (y))
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static uint8_t g_running_regs[XCPTCONTEXT_SIZE] aligned_data(16);
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: elf_flush
|
|
*
|
|
* Description:
|
|
* Flush the out stream
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int elf_flush(FAR struct elf_dumpinfo_s *cinfo)
|
|
{
|
|
return lib_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 = 0;
|
|
|
|
while (total > 0)
|
|
{
|
|
ret = lib_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 = 0;
|
|
|
|
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_hdr
|
|
*
|
|
* Description:
|
|
* Fill the elf header
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int elf_emit_hdr(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_ntcb
|
|
*
|
|
* Description:
|
|
* Calculate the note segment size
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int elf_get_ntcb(void)
|
|
{
|
|
int count = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < g_npidhash; i++)
|
|
{
|
|
if (g_pidhash[i] != NULL)
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: elf_get_note_size
|
|
*
|
|
* Description:
|
|
* Calculate the note segment size
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int elf_get_note_size(int stksegs)
|
|
{
|
|
int total;
|
|
|
|
total = stksegs * (sizeof(Elf_Nhdr) + ROUNDUP(CONFIG_TASK_NAME_SIZE, 8) +
|
|
sizeof(elf_prstatus_t));
|
|
total += stksegs * (sizeof(Elf_Nhdr) + ROUNDUP(CONFIG_TASK_NAME_SIZE, 8) +
|
|
sizeof(elf_prpsinfo_t));
|
|
return total;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: elf_emit_tcb_note
|
|
*
|
|
* Description:
|
|
* Fill the note segment information from tcb
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void elf_emit_tcb_note(FAR struct elf_dumpinfo_s *cinfo,
|
|
FAR struct tcb_s *tcb)
|
|
{
|
|
char name[ROUNDUP(CONFIG_TASK_NAME_SIZE, 8)];
|
|
elf_prstatus_t status;
|
|
elf_prpsinfo_t info;
|
|
FAR uintptr_t *regs;
|
|
Elf_Nhdr nhdr;
|
|
int i;
|
|
|
|
memset(&info, 0x0, sizeof(info));
|
|
memset(&status, 0x0, sizeof(status));
|
|
|
|
/* 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;
|
|
|
|
if (running_task() == tcb)
|
|
{
|
|
if (up_interrupt_context())
|
|
{
|
|
regs = (FAR uintptr_t *)up_current_regs();
|
|
}
|
|
else
|
|
{
|
|
up_saveusercontext(g_running_regs);
|
|
regs = (FAR uintptr_t *)g_running_regs;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
regs = (FAR uintptr_t *)tcb->xcp.regs;
|
|
}
|
|
|
|
if (regs != NULL)
|
|
{
|
|
for (i = 0; i < nitems(status.pr_regs); i++)
|
|
{
|
|
if (g_tcbinfo.reg_off.p[i] == UINT16_MAX)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
status.pr_regs[i] = *(FAR uintptr_t *)
|
|
((FAR uint8_t *)regs + g_tcbinfo.reg_off.p[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
elf_emit(cinfo, &status, sizeof(status));
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: elf_emit_note
|
|
*
|
|
* Description:
|
|
* Fill the note segment information
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void elf_emit_note(FAR struct elf_dumpinfo_s *cinfo)
|
|
{
|
|
int i;
|
|
|
|
if (cinfo->pid == INVALID_PROCESS_ID)
|
|
{
|
|
for (i = 0; i < g_npidhash; i++)
|
|
{
|
|
if (g_pidhash[i] != NULL)
|
|
{
|
|
elf_emit_tcb_note(cinfo, g_pidhash[i]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
elf_emit_tcb_note(cinfo, nxsched_get_tcb(cinfo->pid));
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: elf_emit_tcb_stack
|
|
*
|
|
* Description:
|
|
* Fill the task stack information from tcb
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void elf_emit_tcb_stack(FAR struct elf_dumpinfo_s *cinfo,
|
|
FAR struct tcb_s *tcb)
|
|
{
|
|
uintptr_t buf = 0;
|
|
uintptr_t sp;
|
|
size_t len;
|
|
|
|
if (running_task() != tcb)
|
|
{
|
|
sp = up_getusrsp(tcb->xcp.regs);
|
|
|
|
if (sp > (uintptr_t)tcb->stack_base_ptr &&
|
|
sp < (uintptr_t)tcb->stack_base_ptr + tcb->adj_stack_size)
|
|
{
|
|
len = ((uintptr_t)tcb->stack_base_ptr +
|
|
tcb->adj_stack_size) - sp;
|
|
buf = sp;
|
|
}
|
|
#ifdef CONFIG_STACK_COLORATION
|
|
else
|
|
{
|
|
len = up_check_tcbstack(tcb);
|
|
buf = (uintptr_t)tcb->stack_base_ptr +
|
|
(tcb->adj_stack_size - len);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (buf == 0)
|
|
{
|
|
buf = (uintptr_t)tcb->stack_alloc_ptr;
|
|
len = tcb->adj_stack_size +
|
|
(tcb->stack_base_ptr - tcb->stack_alloc_ptr);
|
|
}
|
|
|
|
sp = ROUNDDOWN(buf, PROGRAM_ALIGNMENT);
|
|
len = ROUNDUP(len + (buf - sp), PROGRAM_ALIGNMENT);
|
|
buf = sp;
|
|
|
|
elf_emit(cinfo, (FAR void *)buf, len);
|
|
|
|
/* Align to page */
|
|
|
|
elf_emit_align(cinfo);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: elf_emit_stack
|
|
*
|
|
* Description:
|
|
* Fill the task stack information
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void elf_emit_stack(FAR struct elf_dumpinfo_s *cinfo)
|
|
{
|
|
int i;
|
|
|
|
if (cinfo->pid == INVALID_PROCESS_ID)
|
|
{
|
|
for (i = 0; i < g_npidhash; i++)
|
|
{
|
|
if (g_pidhash[i] != NULL)
|
|
{
|
|
elf_emit_tcb_stack(cinfo, g_pidhash[i]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
elf_emit_tcb_stack(cinfo, nxsched_get_tcb(cinfo->pid));
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: elf_emit_memory
|
|
*
|
|
* Description:
|
|
* Fill the note segment information
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void elf_emit_memory(FAR struct elf_dumpinfo_s *cinfo, int memsegs)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < memsegs; i++)
|
|
{
|
|
if (cinfo->regions[i].flags & PF_REGISTER)
|
|
{
|
|
FAR uintptr_t *start = (FAR uintptr_t *)cinfo->regions[i].start;
|
|
FAR uintptr_t *end = (FAR uintptr_t *)cinfo->regions[i].end;
|
|
uintptr_t buf[64];
|
|
size_t offset = 0;
|
|
|
|
while (start < end)
|
|
{
|
|
buf[offset++] = *start++;
|
|
|
|
if (offset % (sizeof(buf) / sizeof(uintptr_t)) == 0)
|
|
{
|
|
elf_emit(cinfo, buf, sizeof(buf));
|
|
offset = 0;
|
|
}
|
|
}
|
|
|
|
if (offset != 0)
|
|
{
|
|
elf_emit(cinfo, buf, offset * sizeof(uintptr_t));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
elf_emit(cinfo, (FAR void *)cinfo->regions[i].start,
|
|
cinfo->regions[i].end - cinfo->regions[i].start);
|
|
}
|
|
|
|
/* Align to page */
|
|
|
|
elf_emit_align(cinfo);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: elf_emit_tcb_phdr
|
|
*
|
|
* Description:
|
|
* Fill the program segment header from tcb
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void elf_emit_tcb_phdr(FAR struct elf_dumpinfo_s *cinfo,
|
|
FAR struct tcb_s *tcb,
|
|
FAR Elf_Phdr *phdr, FAR off_t *offset)
|
|
{
|
|
uintptr_t sp;
|
|
|
|
phdr->p_vaddr = 0;
|
|
|
|
if (running_task() != tcb)
|
|
{
|
|
sp = up_getusrsp(tcb->xcp.regs);
|
|
|
|
if (sp > (uintptr_t)tcb->stack_base_ptr &&
|
|
sp < (uintptr_t)tcb->stack_base_ptr + tcb->adj_stack_size)
|
|
{
|
|
phdr->p_filesz = ((uintptr_t)tcb->stack_base_ptr +
|
|
tcb->adj_stack_size) - sp;
|
|
phdr->p_vaddr = sp;
|
|
}
|
|
#ifdef CONFIG_STACK_COLORATION
|
|
else
|
|
{
|
|
phdr->p_filesz = up_check_tcbstack(tcb);
|
|
phdr->p_vaddr = (uintptr_t)tcb->stack_base_ptr +
|
|
(tcb->adj_stack_size - phdr->p_filesz);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (phdr->p_vaddr == 0)
|
|
{
|
|
phdr->p_vaddr = (uintptr_t)tcb->stack_alloc_ptr;
|
|
phdr->p_filesz = tcb->adj_stack_size +
|
|
(tcb->stack_base_ptr - tcb->stack_alloc_ptr);
|
|
}
|
|
|
|
sp = ROUNDDOWN(phdr->p_vaddr, PROGRAM_ALIGNMENT);
|
|
phdr->p_filesz = ROUNDUP(phdr->p_filesz +
|
|
(phdr->p_vaddr - sp), PROGRAM_ALIGNMENT);
|
|
phdr->p_vaddr = sp;
|
|
|
|
phdr->p_type = PT_LOAD;
|
|
phdr->p_offset = ROUNDUP(*offset, ELF_PAGESIZE);
|
|
phdr->p_paddr = phdr->p_vaddr;
|
|
phdr->p_memsz = phdr->p_filesz;
|
|
phdr->p_flags = PF_X | PF_W | PF_R;
|
|
phdr->p_align = ELF_PAGESIZE;
|
|
*offset += ROUNDUP(phdr->p_memsz, ELF_PAGESIZE);
|
|
|
|
elf_emit(cinfo, phdr, sizeof(*phdr));
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: elf_emit_phdr
|
|
*
|
|
* Description:
|
|
* Fill the program segment header
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void elf_emit_phdr(FAR struct elf_dumpinfo_s *cinfo,
|
|
int stksegs, int memsegs)
|
|
{
|
|
off_t offset = cinfo->stream->nput +
|
|
(stksegs + memsegs + 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(stksegs);
|
|
offset += phdr.p_filesz;
|
|
|
|
elf_emit(cinfo, &phdr, sizeof(phdr));
|
|
|
|
if (cinfo->pid == INVALID_PROCESS_ID)
|
|
{
|
|
for (i = 0; i < g_npidhash; i++)
|
|
{
|
|
if (g_pidhash[i] != NULL)
|
|
{
|
|
elf_emit_tcb_phdr(cinfo, g_pidhash[i], &phdr, &offset);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
elf_emit_tcb_phdr(cinfo, nxsched_get_tcb(cinfo->pid),
|
|
&phdr, &offset);
|
|
}
|
|
|
|
/* Write program headers for segments dump */
|
|
|
|
for (i = 0; i < memsegs; i++)
|
|
{
|
|
phdr.p_type = PT_LOAD;
|
|
phdr.p_offset = ROUNDUP(offset, ELF_PAGESIZE);
|
|
phdr.p_vaddr = cinfo->regions[i].start;
|
|
phdr.p_paddr = phdr.p_vaddr;
|
|
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)
|
|
{
|
|
irqstate_t flags;
|
|
int memsegs = 0;
|
|
int stksegs;
|
|
|
|
flags = enter_critical_section();
|
|
|
|
if (cinfo->pid != INVALID_PROCESS_ID)
|
|
{
|
|
if (nxsched_get_tcb(cinfo->pid) == NULL)
|
|
{
|
|
leave_critical_section(flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
stksegs = 1;
|
|
}
|
|
else
|
|
{
|
|
stksegs = elf_get_ntcb();
|
|
}
|
|
|
|
/* Check the memory region */
|
|
|
|
if (cinfo->regions != NULL)
|
|
{
|
|
for (; cinfo->regions[memsegs].start <
|
|
cinfo->regions[memsegs].end; memsegs++);
|
|
}
|
|
|
|
/* Fill notes section */
|
|
|
|
elf_emit_hdr(cinfo, stksegs + memsegs + 1);
|
|
|
|
/* Fill all the program information about the process for the
|
|
* notes. This also sets up the file header.
|
|
*/
|
|
|
|
elf_emit_phdr(cinfo, stksegs, memsegs);
|
|
|
|
/* Fill note information */
|
|
|
|
elf_emit_note(cinfo);
|
|
|
|
/* Align to page */
|
|
|
|
elf_emit_align(cinfo);
|
|
|
|
/* Dump stack */
|
|
|
|
elf_emit_stack(cinfo);
|
|
|
|
/* Dump memory segments */
|
|
|
|
if (memsegs > 0)
|
|
{
|
|
elf_emit_memory(cinfo, memsegs);
|
|
}
|
|
|
|
/* Flush the dump */
|
|
|
|
elf_flush(cinfo);
|
|
|
|
leave_critical_section(flags);
|
|
|
|
return OK;
|
|
}
|