Files
s390-tools/zdump/dfi_elf.c
Michael Holzheu b627b8d8e1 Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version.

Changes on top of s390-tools-1.39.0:

 - Add MIT license to all source files
 - Add LICENSE file
 - Transform REAMDE to README.md (markdown)
 - Add AUTHORS.md file
 - Add CONTRIBUTING.md file
 - Move changelog from README to CHANGELOG.md file

Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
2017-08-21 10:55:40 +02:00

323 lines
6.7 KiB
C

/*
* zgetdump - Tool for copying and converting System z dumps
*
* ELF core dump input format
*
* Copyright IBM Corp. 2001, 2017
*
* 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 "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_filesz == 0) /* Skip null pt loads */
return 0;
off_ptr = zg_alloc(sizeof(*off_ptr));
*off_ptr = phdr->p_offset;
dfi_mem_chunk_add(phdr->p_paddr, phdr->p_filesz, off_ptr,
dfi_elf_mem_chunk_read_fn, zg_free);
if (phdr->p_offset + phdr->p_filesz > zg_size(g.fh))
return -EINVAL;
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)
{
u64 start_off = zg_tell(g.fh, ZG_CHECK);
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, &note, sizeof(note), ZG_CHECK_ERR);
if (rc != sizeof(note))
return -EINVAL;
switch (note.n_type) {
case NT_PRSTATUS:
cpu_current = nt_prstatus_read(&note);
if (!cpu_current)
return -EINVAL;
break;
case NT_FPREGSET:
if (nt_fpregset_read(cpu_current, &note))
return -EINVAL;
break;
case NT_S390_TIMER:
if (nt_s390_timer_read(cpu_current, &note))
return -EINVAL;
break;
case NT_S390_TODCMP:
if (nt_s390_todcmp_read(cpu_current, &note))
return -EINVAL;
break;
case NT_S390_TODPREG:
if (nt_s390_todpreg_read(cpu_current, &note))
return -EINVAL;
break;
case NT_S390_CTRS:
if (nt_s390_ctrs_read(cpu_current, &note))
return -EINVAL;
break;
case NT_S390_PREFIX:
if (nt_s390_prefix_read(cpu_current, &note))
return -EINVAL;
break;
case NT_S390_VXRS_LOW:
if (nt_s390_vxrs_low_read(cpu_current, &note))
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, &note))
return -EINVAL;
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
break;
default:
if (nt_skip(&note))
return -EINVAL;
break;
}
}
zg_seek(g.fh, start_off, ZG_CHECK);
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);
for (i = 0; i < ehdr.e_phnum; i++) {
zg_read(g.fh, &phdr, sizeof(phdr), ZG_CHECK);
switch (phdr.p_type) {
case PT_LOAD:
if (pt_load_add(&phdr))
return -EINVAL;
break;
case PT_NOTE:
if (pt_notes_add(&phdr))
return -EINVAL;
break;
default:
break;
}
}
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,
};