From 7cbb8da692f48c459a5b881438c0bad704713a36 Mon Sep 17 00:00:00 2001 From: "chao.an" Date: Wed, 17 Nov 2021 23:53:28 +0800 Subject: [PATCH] binfmt/elf: add bare metal coredump support Signed-off-by: chao.an --- arch/arm/include/elf.h | 9 + binfmt/Makefile | 1 + binfmt/binfmt_coredump.c | 69 ++++++ binfmt/elf.c | 31 +++ binfmt/libelf/Kconfig | 11 + binfmt/libelf/Make.defs | 6 + binfmt/libelf/libelf_coredump.c | 370 ++++++++++++++++++++++++++++++++ binfmt/nxflat.c | 1 + include/nuttx/binfmt/binfmt.h | 31 +++ include/nuttx/binfmt/elf.h | 30 +++ include/nuttx/elf.h | 64 ++++++ 11 files changed, 623 insertions(+) create mode 100644 binfmt/binfmt_coredump.c create mode 100644 binfmt/libelf/libelf_coredump.c diff --git a/arch/arm/include/elf.h b/arch/arm/include/elf.h index e74fbf45920..f23bc873da2 100644 --- a/arch/arm/include/elf.h +++ b/arch/arm/include/elf.h @@ -248,4 +248,13 @@ #define DT_ARM_PREEMPTMAP 0x70000002 #define DT_ARM_RESERVED2 0x70000003 +/* ELF register definitions */ + +/* Holds the general purpose registers $a1 * through to $pc + * at indices 0 to 15. At index 16 the program status register. + * Index 17 should be set to zero. + */ + +typedef unsigned long elf_gregset_t[18]; + #endif /* __ARCH_ARM_INCLUDE_ELF_H */ diff --git a/binfmt/Makefile b/binfmt/Makefile index ad37a6b702a..2050090dfbd 100644 --- a/binfmt/Makefile +++ b/binfmt/Makefile @@ -25,6 +25,7 @@ include $(TOPDIR)/Make.defs CSRCS = binfmt_globals.c binfmt_initialize.c binfmt_register.c binfmt_unregister.c CSRCS += binfmt_loadmodule.c binfmt_unloadmodule.c binfmt_execmodule.c CSRCS += binfmt_exec.c binfmt_copyargv.c binfmt_dumpmodule.c +CSRCS += binfmt_coredump.c ifeq ($(CONFIG_BINFMT_LOADABLE),y) CSRCS += binfmt_exit.c diff --git a/binfmt/binfmt_coredump.c b/binfmt/binfmt_coredump.c new file mode 100644 index 00000000000..3ac198a6fd8 --- /dev/null +++ b/binfmt/binfmt_coredump.c @@ -0,0 +1,69 @@ +/**************************************************************************** + * binfmt/binfmt_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 "binfmt.h" + +#ifndef CONFIG_BINFMT_DISABLE + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: core_dump + * + * Description: + * This function for generating core dump stream. + * + ****************************************************************************/ + +int core_dump(FAR struct memory_region_s *regions, + FAR struct lib_outstream_s *stream) +{ + FAR struct binfmt_s *binfmt; + int ret = -ENOENT; + + for (binfmt = g_binfmts; binfmt; binfmt = binfmt->next) + { + /* Use this handler to try to load the format */ + + if (binfmt->coredump) + { + ret = binfmt->coredump(regions, stream); + if (ret == OK) + { + break; + } + } + } + + return ret; +} + +#endif /* CONFIG_BINFMT_DISABLE */ diff --git a/binfmt/elf.c b/binfmt/elf.c index fd99c3d58ae..14963613152 100644 --- a/binfmt/elf.c +++ b/binfmt/elf.c @@ -72,6 +72,10 @@ static int elf_loadbinary(FAR struct binary_s *binp, FAR const char *filename, FAR const struct symtab_s *exports, int nexports); +#ifdef CONFIG_ELF_COREDUMP +static int elf_dumpbinary(FAR struct memory_region_s *regions, + FAR struct lib_outstream_s *stream); +#endif #if defined(CONFIG_DEBUG_FEATURES) && defined(CONFIG_DEBUG_BINFMT) static void elf_dumploadinfo(FAR struct elf_loadinfo_s *loadinfo); #endif @@ -85,6 +89,9 @@ static struct binfmt_s g_elfbinfmt = NULL, /* next */ elf_loadbinary, /* load */ NULL, /* unload */ +#ifdef CONFIG_ELF_COREDUMP + elf_dumpbinary, /* coredump */ +#endif }; /**************************************************************************** @@ -295,6 +302,30 @@ errout_with_init: return ret; } +/**************************************************************************** + * Name: elf_dumpbinary + * + * Description: + * Generat the core dump stream as ELF structure. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ELF_COREDUMP +static int elf_dumpbinary(FAR struct memory_region_s *regions, + FAR struct lib_outstream_s *stream) +{ + struct elf_dumpinfo_s dumpinfo; + + dumpinfo.regions = regions; + dumpinfo.stream = stream; + + return elf_coredump(&dumpinfo); +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/binfmt/libelf/Kconfig b/binfmt/libelf/Kconfig index 4cbe8c0ac62..8ec94504b83 100644 --- a/binfmt/libelf/Kconfig +++ b/binfmt/libelf/Kconfig @@ -62,3 +62,14 @@ config ELF_SYMBOL_CACHECOUNT ---help--- This is a cache that is used to store elf symbol table to reduce access fs. Default: 256 + +config ELF_COREDUMP + bool "ELF Coredump" + select DEBUG_TCBINFO + default n + ---help--- + Generate ELF core dump to provide information about the CPU state and the + memory state of program. + The memory state embeds a snapshot of all segments mapped in the + memory space of the program. The CPU state contains register values + when the core dump has been generated. diff --git a/binfmt/libelf/Make.defs b/binfmt/libelf/Make.defs index 3646fd3f68e..1f9cb16c699 100644 --- a/binfmt/libelf/Make.defs +++ b/binfmt/libelf/Make.defs @@ -30,6 +30,12 @@ CSRCS += libelf_bind.c libelf_init.c libelf_addrenv.c libelf_iobuffer.c CSRCS += libelf_load.c libelf_read.c libelf_sections.c libelf_symbols.c CSRCS += libelf_uninit.c libelf_unload.c libelf_verify.c +ifeq ($(CONFIG_ELF_COREDUMP),y) +CSRCS += libelf_coredump.c + +CFLAGS += ${shell $(INCDIR) "$(CC)" $(TOPDIR)$(DELIM)sched} +endif + ifeq ($(CONFIG_BINFMT_CONSTRUCTORS),y) CSRCS += libelf_ctors.c libelf_dtors.c endif diff --git a/binfmt/libelf/libelf_coredump.c b/binfmt/libelf/libelf_coredump.c new file mode 100644 index 00000000000..e8eb84bf95d --- /dev/null +++ b/binfmt/libelf/libelf_coredump.c @@ -0,0 +1,370 @@ +/**************************************************************************** + * 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 ELF_BLOCKSIZE 1024 + +#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 > ELF_BLOCKSIZE ? + ELF_BLOCKSIZE : 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)); + + strncpy(name, tcb->name, sizeof(name)); + elf_emit(cinfo, name, sizeof(name)); + + info.pr_pid = tcb->pid; + strncpy(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_offs[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); +} diff --git a/binfmt/nxflat.c b/binfmt/nxflat.c index 476a04a1797..9eac32ea631 100644 --- a/binfmt/nxflat.c +++ b/binfmt/nxflat.c @@ -84,6 +84,7 @@ static struct binfmt_s g_nxflatbinfmt = NULL, /* next */ nxflat_loadbinary, /* load */ nxflat_unloadbinary, /* unload */ + NULL, /* coredump */ }; /**************************************************************************** diff --git a/include/nuttx/binfmt/binfmt.h b/include/nuttx/binfmt/binfmt.h index e49a80f4eaa..416cc6c4a42 100644 --- a/include/nuttx/binfmt/binfmt.h +++ b/include/nuttx/binfmt/binfmt.h @@ -33,6 +33,7 @@ #include #include +#include /**************************************************************************** * Pre-processor Definitions @@ -102,6 +103,15 @@ struct binary_s CODE int (*unload)(FAR struct binary_s *bin); }; +/* This describes binfmt coredump filed */ + +struct memory_region_s +{ + uintptr_t start; /* Start address of this region */ + uintptr_t end; /* End address of this region */ + uint32_t flags; /* Figure 5-3: Segment Flag Bits: PF_[X|W|R] */ +}; + /* This describes one binary format handler */ struct binfmt_s @@ -120,6 +130,11 @@ struct binfmt_s /* Unload module callback */ CODE int (*unload)(FAR struct binary_s *bin); + + /* Unload module callback */ + + CODE int (*coredump)(FAR struct memory_region_s *regions, + FAR struct lib_outstream_s *stream); }; /**************************************************************************** @@ -175,6 +190,22 @@ int register_binfmt(FAR struct binfmt_s *binfmt); int unregister_binfmt(FAR struct binfmt_s *binfmt); +/**************************************************************************** + * Name: core_dump + * + * Description: + * This function for generating core dump stream. + * + * Returned Value: + * This is a NuttX internal function so it follows the convention that + * 0 (OK) is returned on success and a negated errno is returned on + * failure. + * + ****************************************************************************/ + +int core_dump(FAR struct memory_region_s *regions, + FAR struct lib_outstream_s *stream); + /**************************************************************************** * Name: load_module * diff --git a/include/nuttx/binfmt/elf.h b/include/nuttx/binfmt/elf.h index 7d59f285903..46e74bb5362 100644 --- a/include/nuttx/binfmt/elf.h +++ b/include/nuttx/binfmt/elf.h @@ -135,6 +135,18 @@ struct elf_loadinfo_s struct file file; /* Descriptor for the file being loaded */ }; +/* This struct provides a description of the dump information of + * memory regions. + */ + +#ifdef CONFIG_ELF_COREDUMP +struct elf_dumpinfo_s +{ + FAR struct memory_region_s *regions; + FAR struct lib_outstream_s *stream; +}; +#endif + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -255,6 +267,24 @@ int elf_bind(FAR struct elf_loadinfo_s *loadinfo, int elf_unload(struct elf_loadinfo_s *loadinfo); +/**************************************************************************** + * 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. + * + ****************************************************************************/ + +#ifdef CONFIG_ELF_COREDUMP +int elf_coredump(FAR struct elf_dumpinfo_s *dumpinfo); +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/include/nuttx/elf.h b/include/nuttx/elf.h index 19713d006f6..b9c2588f0d7 100644 --- a/include/nuttx/elf.h +++ b/include/nuttx/elf.h @@ -26,6 +26,70 @@ ****************************************************************************/ #include +#ifdef CONFIG_ELF_COREDUMP +#include +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ELF_PRARGSZ (80) /* Number of chars for args */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifdef CONFIG_ELF_COREDUMP +typedef struct elf_prpsinfo_s +{ + char pr_state; /* Numeric process state */ + char pr_sname; /* Char for pr_state */ + char pr_zomb; /* Zombie */ + char pr_nice; /* Nice val */ + unsigned long pr_flag; /* Flags */ + unsigned short pr_uid; + unsigned short pr_gid; + int pr_pid; + int pr_ppid; + int pr_pgrp; + int pr_sid; + char pr_fname[16]; /* Filename of executable */ + char pr_psargs[ELF_PRARGSZ]; /* Initial part of arg list */ +} elf_prpsinfo_t; + +typedef struct elf_siginfo_s +{ + int si_signo; /* Signal number */ + int si_code; /* Extra code */ + int si_errno; /* Errno */ +} elf_siginfo_t; + +typedef struct elf_timeval_s +{ + long tv_sec; /* Seconds */ + long tv_usec; /* Microseconds */ +} elf_timeval_t; + +typedef struct elf_prstatus_s +{ + elf_siginfo_t pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + short pr_padding; /* Padding align */ + unsigned long pr_sigpend; /* Set of pending signals */ + unsigned long pr_sighold; /* Set of held signals */ + int pr_pid; + int pr_ppid; + int pr_pgrp; + int pr_sid; + elf_timeval_t pr_utime; /* User time */ + elf_timeval_t pr_stime; /* System time */ + elf_timeval_t pr_cutime; /* Cumulative user time */ + elf_timeval_t pr_cstime; /* Cumulative system time */ + elf_gregset_t pr_regs; + int pr_fpvalid; /* True if math co-processor being used */ +} elf_prstatus_t; +#endif /**************************************************************************** * Public Function Prototypes