binfmt/elf: add bare metal coredump support

Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
chao.an 2021-11-17 23:53:28 +08:00 committed by Xiang Xiao
parent 36a6f4cd09
commit 7cbb8da692
11 changed files with 623 additions and 0 deletions

View file

@ -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 */

View file

@ -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

69
binfmt/binfmt_coredump.c Normal file
View file

@ -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 <nuttx/config.h>
#include <nuttx/binfmt/binfmt.h>
#include <errno.h>
#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 */

View file

@ -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
****************************************************************************/

View file

@ -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.

View file

@ -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

View file

@ -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 <nuttx/config.h>
#include <sys/stat.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
****************************************************************************/
#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);
}

View file

@ -84,6 +84,7 @@ static struct binfmt_s g_nxflatbinfmt =
NULL, /* next */
nxflat_loadbinary, /* load */
nxflat_unloadbinary, /* unload */
NULL, /* coredump */
};
/****************************************************************************

View file

@ -33,6 +33,7 @@
#include <nuttx/arch.h>
#include <nuttx/sched.h>
#include <nuttx/streams.h>
/****************************************************************************
* 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
*

View file

@ -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)
}

View file

@ -26,6 +26,70 @@
****************************************************************************/
#include <elf.h>
#ifdef CONFIG_ELF_COREDUMP
#include <arch/elf.h>
#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