mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
735d8f5493
Before attempting to read ELF program headers, the file must be positioned at the offset of the first program header as specified in the ELF header. Furthermore, read all ELF program headers with one call to zg_read(). This has the advantage of not having to restore the file offset every time after it is changed in pt_notes_add() and other functions which need to read the same file as well. Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
336 lines
7.0 KiB
C
336 lines
7.0 KiB
C
/*
|
|
* zgetdump - Tool for copying and converting System z dumps
|
|
*
|
|
* ELF core dump input format
|
|
*
|
|
* Copyright IBM Corp. 2001, 2018
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <elf.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "lib/util_libc.h"
|
|
#include "zgetdump.h"
|
|
|
|
/*
|
|
* Read memory for given memory chunk
|
|
*/
|
|
static void dfi_elf_mem_chunk_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off,
|
|
void *buf, u64 cnt)
|
|
{
|
|
u64 elf_load_off = *((u64 *) mem_chunk->data);
|
|
|
|
zg_seek(g.fh, elf_load_off + off, ZG_CHECK);
|
|
zg_read(g.fh, buf, cnt, ZG_CHECK);
|
|
}
|
|
|
|
/*
|
|
* Add load (memory chunk) to DFI dump
|
|
*/
|
|
static int pt_load_add(Elf64_Phdr *phdr)
|
|
{
|
|
u64 *off_ptr;
|
|
|
|
if (phdr->p_paddr != phdr->p_vaddr) {
|
|
phdr->p_paddr = phdr->p_vaddr;
|
|
STDERR("Dump file \"%s\" is a user space core dump\n",
|
|
g.opts.device);
|
|
}
|
|
if (phdr->p_memsz == 0)
|
|
return -EINVAL;
|
|
if (phdr->p_offset + phdr->p_filesz > zg_size(g.fh))
|
|
return -EINVAL;
|
|
if (phdr->p_filesz == 0) {
|
|
/* Add zero memory chunk */
|
|
dfi_mem_chunk_add(phdr->p_paddr, phdr->p_memsz, NULL,
|
|
dfi_mem_chunk_read_zero, NULL);
|
|
} else {
|
|
off_ptr = zg_alloc(sizeof(*off_ptr));
|
|
*off_ptr = phdr->p_offset;
|
|
dfi_mem_chunk_add(phdr->p_paddr, phdr->p_memsz, off_ptr,
|
|
dfi_elf_mem_chunk_read_fn, zg_free);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Skip name of note
|
|
*/
|
|
static void nt_name_skip(Elf64_Nhdr *note)
|
|
{
|
|
zg_seek_cur(g.fh, ROUNDUP(note->n_namesz, 4), ZG_CHECK);
|
|
}
|
|
|
|
/*
|
|
* Read note
|
|
*/
|
|
static int nt_read(Elf64_Nhdr *note, void *buf)
|
|
{
|
|
off_t buf_len = ROUNDUP(note->n_descsz, 4);
|
|
char tmp_buf[buf_len];
|
|
|
|
nt_name_skip(note);
|
|
if (zg_read(g.fh, tmp_buf, buf_len, ZG_CHECK_ERR) != buf_len)
|
|
return -EINVAL;
|
|
if (buf)
|
|
memcpy(buf, tmp_buf, note->n_descsz);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Skip note
|
|
*/
|
|
static int nt_skip(Elf64_Nhdr *note)
|
|
{
|
|
return nt_read(note, NULL);
|
|
}
|
|
|
|
/*
|
|
* Ensure that CPU is already defined by prstatus note
|
|
*/
|
|
static void check_cpu(struct dfi_cpu *cpu, const char *note_str)
|
|
{
|
|
if (cpu)
|
|
return;
|
|
ERR_EXIT("Invalid ELF dump (%s before prstatus found)", note_str);
|
|
}
|
|
|
|
/*
|
|
* Read prstatus note and return new DFI CPU
|
|
*/
|
|
static struct dfi_cpu *nt_prstatus_read(Elf64_Nhdr *note)
|
|
{
|
|
struct dfi_cpu *cpu = dfi_cpu_alloc();
|
|
struct nt_prstatus_64 nt_prstatus;
|
|
|
|
if (nt_read(note, &nt_prstatus))
|
|
return NULL;
|
|
|
|
memcpy(cpu->gprs, &nt_prstatus.gprs, sizeof(cpu->gprs));
|
|
memcpy(cpu->psw, &nt_prstatus.psw, sizeof(cpu->psw));
|
|
memcpy(cpu->acrs, &nt_prstatus.acrs, sizeof(cpu->acrs));
|
|
|
|
dfi_cpu_add(cpu);
|
|
return cpu;
|
|
}
|
|
|
|
/*
|
|
* Read fpregset note
|
|
*/
|
|
static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
{
|
|
struct nt_fpregset_64 nt_fpregset;
|
|
|
|
check_cpu(cpu, "FPREGSET");
|
|
if (nt_read(note, &nt_fpregset))
|
|
return -EINVAL;
|
|
|
|
memcpy(&cpu->fpc, &nt_fpregset.fpc, sizeof(cpu->fpc));
|
|
memcpy(cpu->fprs, &nt_fpregset.fprs, sizeof(cpu->fprs));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read s390 timer note
|
|
*/
|
|
static int nt_s390_timer_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
{
|
|
check_cpu(cpu, "S390_TIMER");
|
|
return nt_read(note, &cpu->timer);
|
|
}
|
|
|
|
/*
|
|
* Read s390 todcmp note
|
|
*/
|
|
static int nt_s390_todcmp_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
{
|
|
check_cpu(cpu, "S390_TODCMP");
|
|
return nt_read(note, &cpu->todcmp);
|
|
}
|
|
|
|
/*
|
|
* Read s390 todpreg note
|
|
*/
|
|
static int nt_s390_todpreg_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
{
|
|
check_cpu(cpu, "S390_TODPREG");
|
|
return nt_read(note, &cpu->todpreg);
|
|
}
|
|
|
|
/*
|
|
* Read s390 ctrs note
|
|
*/
|
|
static int nt_s390_ctrs_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
{
|
|
check_cpu(cpu, "S390_CTRS");
|
|
return nt_read(note, &cpu->ctrs);
|
|
}
|
|
|
|
/*
|
|
* Read s390 prefix note
|
|
*/
|
|
static int nt_s390_prefix_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
{
|
|
check_cpu(cpu, "S390_PREFIX");
|
|
return nt_read(note, &cpu->prefix);
|
|
}
|
|
|
|
/*
|
|
* Read s390 vxrs_low note
|
|
*/
|
|
static int nt_s390_vxrs_low_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
{
|
|
check_cpu(cpu, "S390_VXRS_LOW");
|
|
return nt_read(note, &cpu->vxrs_low);
|
|
}
|
|
|
|
/*
|
|
* Read s390 vxrs_high note
|
|
*/
|
|
static int nt_s390_vxrs_high_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
{
|
|
check_cpu(cpu, "S390_VXRS_HIGH");
|
|
return nt_read(note, &cpu->vxrs_high);
|
|
}
|
|
|
|
/*
|
|
* Add all notes for notes phdr
|
|
*/
|
|
static int pt_notes_add(Elf64_Phdr *phdr)
|
|
{
|
|
struct dfi_cpu *cpu_current = NULL;
|
|
u64 notes_start_off;
|
|
Elf64_Nhdr note;
|
|
int rc;
|
|
|
|
zg_seek(g.fh, phdr->p_offset, ZG_CHECK);
|
|
notes_start_off = zg_tell(g.fh, ZG_CHECK);
|
|
while (zg_tell(g.fh, ZG_CHECK) - notes_start_off < phdr->p_filesz) {
|
|
rc = zg_read(g.fh, ¬e, sizeof(note), ZG_CHECK_ERR);
|
|
if (rc != sizeof(note))
|
|
return -EINVAL;
|
|
switch (note.n_type) {
|
|
case NT_PRSTATUS:
|
|
cpu_current = nt_prstatus_read(¬e);
|
|
if (!cpu_current)
|
|
return -EINVAL;
|
|
break;
|
|
case NT_FPREGSET:
|
|
if (nt_fpregset_read(cpu_current, ¬e))
|
|
return -EINVAL;
|
|
break;
|
|
case NT_S390_TIMER:
|
|
if (nt_s390_timer_read(cpu_current, ¬e))
|
|
return -EINVAL;
|
|
break;
|
|
case NT_S390_TODCMP:
|
|
if (nt_s390_todcmp_read(cpu_current, ¬e))
|
|
return -EINVAL;
|
|
break;
|
|
case NT_S390_TODPREG:
|
|
if (nt_s390_todpreg_read(cpu_current, ¬e))
|
|
return -EINVAL;
|
|
break;
|
|
case NT_S390_CTRS:
|
|
if (nt_s390_ctrs_read(cpu_current, ¬e))
|
|
return -EINVAL;
|
|
break;
|
|
case NT_S390_PREFIX:
|
|
if (nt_s390_prefix_read(cpu_current, ¬e))
|
|
return -EINVAL;
|
|
break;
|
|
case NT_S390_VXRS_LOW:
|
|
if (nt_s390_vxrs_low_read(cpu_current, ¬e))
|
|
return -EINVAL;
|
|
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
|
|
break;
|
|
case NT_S390_VXRS_HIGH:
|
|
if (nt_s390_vxrs_high_read(cpu_current, ¬e))
|
|
return -EINVAL;
|
|
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
|
|
break;
|
|
default:
|
|
if (nt_skip(¬e))
|
|
return -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read ELF header
|
|
*/
|
|
static int read_elf_hdr(Elf64_Ehdr *ehdr)
|
|
{
|
|
if (zg_size(g.fh) < sizeof(*ehdr))
|
|
return -ENODEV;
|
|
zg_read(g.fh, ehdr, sizeof(*ehdr), ZG_CHECK);
|
|
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
|
|
return -ENODEV;
|
|
if (ehdr->e_type != ET_CORE)
|
|
return -ENODEV;
|
|
if (ehdr->e_machine != EM_S390 || ehdr->e_ident[EI_CLASS] != ELFCLASS64)
|
|
ERR_EXIT("Only s390x (64 bit) core dump files are supported");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize ELF input dump format
|
|
*/
|
|
static int dfi_elf_init(void)
|
|
{
|
|
Elf64_Ehdr ehdr;
|
|
Elf64_Phdr *phdr;
|
|
int i;
|
|
|
|
if (read_elf_hdr(&ehdr) != 0)
|
|
return -ENODEV;
|
|
|
|
df_elf_ensure_s390x();
|
|
dfi_arch_set(DFI_ARCH_64);
|
|
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
|
|
|
phdr = util_malloc(sizeof(*phdr) * ehdr.e_phnum);
|
|
zg_seek(g.fh, ehdr.e_phoff, ZG_CHECK);
|
|
zg_read(g.fh, phdr, sizeof(*phdr) * ehdr.e_phnum, ZG_CHECK);
|
|
for (i = 0; i < ehdr.e_phnum; i++) {
|
|
switch (phdr[i].p_type) {
|
|
case PT_LOAD:
|
|
if (pt_load_add(&phdr[i])) {
|
|
free(phdr);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case PT_NOTE:
|
|
if (pt_notes_add(&phdr[i])) {
|
|
free(phdr);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
free(phdr);
|
|
|
|
dfi_attr_version_set(ehdr.e_ident[EI_VERSION]);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ELF DFI operations
|
|
*/
|
|
struct dfi dfi_elf = {
|
|
.name = "elf",
|
|
.init = dfi_elf_init,
|
|
.feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
|
|
};
|