mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Add guarded storage registers support for the ELF input and the ELF output format. See `man 2 s390_guarded_storage` for details about guarded storage. Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com> Reviewed-by: Steffen Eiden <seiden@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
358 lines
9.6 KiB
C
358 lines
9.6 KiB
C
/*
|
|
* Copyright IBM Corp. 2001, 2018, 2021
|
|
*
|
|
* 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 <string.h>
|
|
|
|
#include "lib/util_libc.h"
|
|
#include "lib/util_log.h"
|
|
|
|
#include "df_elf.h"
|
|
|
|
void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum)
|
|
{
|
|
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
|
|
ehdr->e_ident[EI_CLASS] = ELFCLASS64;
|
|
ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
|
|
ehdr->e_ident[EI_VERSION] = ELF_VERSION_1;
|
|
ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
|
|
ehdr->e_ident[EI_ABIVERSION] = 0;
|
|
memset(ehdr->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
|
|
ehdr->e_type = ET_CORE;
|
|
ehdr->e_machine = EM_S390;
|
|
ehdr->e_version = ELF_VERSION_1;
|
|
ehdr->e_entry = 0;
|
|
ehdr->e_phoff = sizeof(Elf64_Ehdr);
|
|
ehdr->e_shoff = 0;
|
|
ehdr->e_flags = 0;
|
|
ehdr->e_ehsize = sizeof(Elf64_Ehdr);
|
|
ehdr->e_phentsize = sizeof(Elf64_Phdr);
|
|
ehdr->e_phnum = phnum;
|
|
ehdr->e_shentsize = 0;
|
|
ehdr->e_shnum = 0;
|
|
ehdr->e_shstrndx = 0;
|
|
return ehdr + 1;
|
|
}
|
|
|
|
bool ehdr_is_elf_object(const Elf64_Ehdr *ehdr)
|
|
{
|
|
return (ehdr->e_version == ELF_VERSION_1) && (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0);
|
|
}
|
|
|
|
bool ehdr_is_vmcore(const Elf64_Ehdr *ehdr)
|
|
{
|
|
return ehdr->e_type == ET_CORE;
|
|
}
|
|
|
|
bool ehdr_is_s390x(const Elf64_Ehdr *ehdr)
|
|
{
|
|
return ehdr->e_machine == EM_S390 && ehdr->e_ident[EI_DATA] == ELFDATA2MSB &&
|
|
ehdr->e_ident[EI_CLASS] == ELFCLASS64;
|
|
}
|
|
|
|
int ehdr_check_s390x(const Elf64_Ehdr *ehdr)
|
|
{
|
|
if (!ehdr_is_elf_object(ehdr) || !ehdr_is_vmcore(ehdr))
|
|
return -ENODEV;
|
|
if (!ehdr_is_s390x(ehdr))
|
|
ERR_EXIT("Only s390x (64 bit) core dump files are supported");
|
|
return 0;
|
|
}
|
|
|
|
Elf64_Ehdr *read_elf_hdr(const struct zg_fh *fh)
|
|
{
|
|
Elf64_Ehdr *ehdr;
|
|
const size_t ehdr_size = sizeof(*ehdr);
|
|
|
|
if (zg_size(fh) < ehdr_size)
|
|
return NULL;
|
|
|
|
ehdr = util_malloc(ehdr_size);
|
|
zg_read(fh, ehdr, ehdr_size, ZG_CHECK);
|
|
return ehdr;
|
|
}
|
|
|
|
Elf64_Phdr *read_elf_phdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, unsigned int *phdr_count)
|
|
{
|
|
const Elf64_Half phnum = ehdr->e_phnum;
|
|
size_t phdrs_size;
|
|
Elf64_Phdr *phdrs;
|
|
|
|
if (!phnum) {
|
|
*phdr_count = phnum;
|
|
return NULL;
|
|
}
|
|
|
|
if (ehdr->e_phoff > OFF_T_MAX)
|
|
ERR_EXIT("Unsupported offset");
|
|
|
|
/* Cannot wraparound since `Elf64_Half`` is `uint16_t` */
|
|
phdrs_size = sizeof(*phdrs) * phnum;
|
|
phdrs = util_malloc(phdrs_size);
|
|
zg_seek(fh, (off_t)ehdr->e_phoff, ZG_CHECK);
|
|
zg_read(fh, phdrs, phdrs_size, ZG_CHECK);
|
|
*phdr_count = phnum;
|
|
return phdrs;
|
|
}
|
|
|
|
Elf64_Shdr *read_elf_shdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, unsigned int *shdr_count)
|
|
{
|
|
const Elf64_Half shnum = ehdr->e_shnum;
|
|
size_t shdrs_size;
|
|
Elf64_Shdr *shdrs;
|
|
|
|
if (!shnum) {
|
|
*shdr_count = shnum;
|
|
return NULL;
|
|
}
|
|
|
|
if (ehdr->e_shoff > OFF_T_MAX)
|
|
ERR_EXIT("Unsupported offset");
|
|
|
|
/* Cannot wraparound since `Elf64_Half`` is `uint16_t` */
|
|
shdrs_size = sizeof(*shdrs) * shnum;
|
|
shdrs = util_malloc(shdrs_size);
|
|
zg_seek(fh, (off_t)ehdr->e_shoff, ZG_CHECK);
|
|
zg_read(fh, shdrs, shdrs_size, ZG_CHECK);
|
|
*shdr_count = shnum;
|
|
return shdrs;
|
|
}
|
|
|
|
unsigned char *read_elf_section_data(const struct zg_fh *fh, const Elf64_Shdr *shdr, size_t *size)
|
|
{
|
|
const size_t sh_size = shdr->sh_size;
|
|
unsigned char *ret;
|
|
|
|
if (!sh_size) {
|
|
*size = sh_size;
|
|
return NULL;
|
|
}
|
|
if (shdr->sh_offset > OFF_T_MAX)
|
|
ERR_EXIT("Unsupported offset");
|
|
|
|
ret = util_malloc(sh_size);
|
|
zg_seek(fh, (off_t)shdr->sh_offset, ZG_CHECK);
|
|
zg_read(fh, ret, sh_size, ZG_CHECK);
|
|
*size = sh_size;
|
|
return ret;
|
|
}
|
|
|
|
char *read_elf_shstrtab(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, const Elf64_Shdr *shdrs,
|
|
const unsigned int shnum, size_t *shstrtab_size)
|
|
{
|
|
const size_t shstrndx = ehdr->e_shstrndx;
|
|
Elf64_Xword tmp_shstrtab_size;
|
|
Elf64_Off shstrndx_off;
|
|
char *shstrtab;
|
|
|
|
/* file has no section name string table */
|
|
if (shstrndx == SHN_UNDEF) {
|
|
*shstrtab_size = 0;
|
|
return NULL;
|
|
}
|
|
|
|
/* Invalid ELF file */
|
|
if (shstrndx >= shnum) {
|
|
util_log_print(UTIL_LOG_ERROR, "Invalid ELF file: invalid string table index\n");
|
|
*shstrtab_size = 0;
|
|
return NULL;
|
|
}
|
|
|
|
shstrndx_off = shdrs[shstrndx].sh_offset;
|
|
if (shstrndx_off > OFF_T_MAX)
|
|
ERR_EXIT("Unsupported offset");
|
|
|
|
tmp_shstrtab_size = shdrs[shstrndx].sh_size;
|
|
shstrtab = util_malloc(tmp_shstrtab_size);
|
|
zg_seek(fh, (off_t)shstrndx_off, ZG_CHECK);
|
|
zg_read(fh, shstrtab, tmp_shstrtab_size, ZG_CHECK);
|
|
*shstrtab_size = tmp_shstrtab_size;
|
|
return shstrtab;
|
|
}
|
|
|
|
const Elf64_Shdr *find_elf_shdr_by_name(const Elf64_Shdr *shdrs, const unsigned int shnum,
|
|
const char *const shstrtab, const size_t shstrtab_size,
|
|
const char *name)
|
|
{
|
|
const Elf64_Shdr *shdr = shdrs;
|
|
unsigned int idx;
|
|
|
|
for (idx = 0; idx < shnum; idx++, shdr++) {
|
|
/* Invalid ELF file */
|
|
if (shdr->sh_name >= shstrtab_size) {
|
|
util_log_print(UTIL_LOG_ERROR,
|
|
"Invalid ELF file: section name is too large\n");
|
|
return NULL;
|
|
}
|
|
if (strcmp(name, &shstrtab[shdr->sh_name]) == 0)
|
|
return shdr;
|
|
}
|
|
/* Not found */
|
|
return NULL;
|
|
}
|
|
|
|
int nt_read(const struct zg_fh *fh, const Elf64_Nhdr *note, void *buf, size_t buf_len)
|
|
{
|
|
ssize_t nread;
|
|
|
|
/* We cannot read more than the current note provides */
|
|
if (note->n_descsz < buf_len)
|
|
return -EINVAL;
|
|
/* Skip note's name and position file at note's descriptor */
|
|
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_namesz), ZG_CHECK);
|
|
/* Read note's descriptor */
|
|
nread = zg_read(fh, buf, buf_len, ZG_CHECK_ERR);
|
|
if (nread < 0 || (size_t)nread != buf_len)
|
|
return -EINVAL;
|
|
/* Skip the rest of note's descriptor until the next note */
|
|
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_descsz) - buf_len, ZG_CHECK);
|
|
return 0;
|
|
}
|
|
|
|
void nt_skip(const struct zg_fh *fh, const Elf64_Nhdr *note)
|
|
{
|
|
/* Skip note's name + descriptor and position file at the next note */
|
|
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_namesz) + ELF_NOTE_ROUNDUP(note->n_descsz),
|
|
ZG_CHECK);
|
|
}
|
|
|
|
void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
|
|
const char *name)
|
|
{
|
|
Elf64_Nhdr *note;
|
|
u64 len;
|
|
|
|
note = (Elf64_Nhdr *)buf;
|
|
note->n_namesz = strlen(name) + 1;
|
|
note->n_descsz = d_len;
|
|
note->n_type = type;
|
|
len = sizeof(Elf64_Nhdr);
|
|
|
|
memcpy(buf + len, name, note->n_namesz);
|
|
len = ELF_NOTE_ROUNDUP(len + note->n_namesz);
|
|
|
|
memcpy(buf + len, desc, note->n_descsz);
|
|
len = ELF_NOTE_ROUNDUP(len + note->n_descsz);
|
|
|
|
return PTR_ADD(buf, len);
|
|
}
|
|
|
|
void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu)
|
|
{
|
|
struct nt_prstatus_64 nt_prstatus;
|
|
static int cpu_nr = 1;
|
|
|
|
memset(&nt_prstatus, 0, sizeof(nt_prstatus));
|
|
memcpy(&nt_prstatus.gprs, cpu->gprs, sizeof(cpu->gprs));
|
|
memcpy(&nt_prstatus.psw, cpu->psw, sizeof(cpu->psw));
|
|
memcpy(&nt_prstatus.acrs, cpu->acrs, sizeof(cpu->acrs));
|
|
nt_prstatus.pr_pid = cpu_nr;
|
|
cpu_nr++;
|
|
|
|
return nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus),
|
|
NOTE_NAME_CORE);
|
|
}
|
|
|
|
void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu)
|
|
{
|
|
struct nt_fpregset_64 nt_fpregset;
|
|
|
|
memset(&nt_fpregset, 0, sizeof(nt_fpregset));
|
|
memcpy(&nt_fpregset.fpc, &cpu->fpc, sizeof(cpu->fpc));
|
|
memcpy(&nt_fpregset.fprs, &cpu->fprs, sizeof(cpu->fprs));
|
|
|
|
return nt_init(ptr, NT_FPREGSET, &nt_fpregset, sizeof(nt_fpregset),
|
|
NOTE_NAME_CORE);
|
|
}
|
|
|
|
void *nt_s390_timer(void *ptr, const struct dfi_cpu *cpu)
|
|
{
|
|
return nt_init(ptr, NT_S390_TIMER, &cpu->timer, sizeof(cpu->timer),
|
|
NOTE_NAME_LINUX);
|
|
}
|
|
|
|
void *nt_s390_tod_cmp(void *ptr, const struct dfi_cpu *cpu)
|
|
{
|
|
return nt_init(ptr, NT_S390_TODCMP, &cpu->todcmp,
|
|
sizeof(cpu->todcmp), NOTE_NAME_LINUX);
|
|
}
|
|
|
|
void *nt_s390_tod_preg(void *ptr, const struct dfi_cpu *cpu)
|
|
{
|
|
return nt_init(ptr, NT_S390_TODPREG, &cpu->todpreg,
|
|
sizeof(cpu->todpreg), NOTE_NAME_LINUX);
|
|
}
|
|
|
|
void *nt_s390_ctrs(void *ptr, const struct dfi_cpu *cpu)
|
|
{
|
|
return nt_init(ptr, NT_S390_CTRS, &cpu->ctrs, sizeof(cpu->ctrs),
|
|
NOTE_NAME_LINUX);
|
|
}
|
|
|
|
void *nt_s390_prefix(void *ptr, const struct dfi_cpu *cpu)
|
|
{
|
|
return nt_init(ptr, NT_S390_PREFIX, &cpu->prefix,
|
|
sizeof(cpu->prefix), NOTE_NAME_LINUX);
|
|
}
|
|
|
|
void *nt_s390_vxrs_low(void *ptr, const struct dfi_cpu *cpu)
|
|
{
|
|
return nt_init(ptr, NT_S390_VXRS_LOW, &cpu->vxrs_low,
|
|
sizeof(cpu->vxrs_low), NOTE_NAME_LINUX);
|
|
}
|
|
|
|
void *nt_s390_vxrs_high(void *ptr, const struct dfi_cpu *cpu)
|
|
{
|
|
return nt_init(ptr, NT_S390_VXRS_HIGH, &cpu->vxrs_high,
|
|
sizeof(cpu->vxrs_high), NOTE_NAME_LINUX);
|
|
}
|
|
|
|
void *nt_s390_gs_cb(void *ptr, const struct dfi_cpu *cpu)
|
|
{
|
|
return nt_init(ptr, NT_S390_GS_CB, &cpu->gscb, sizeof(cpu->gscb), NOTE_NAME_LINUX);
|
|
}
|
|
|
|
void *nt_prpsinfo(void *ptr)
|
|
{
|
|
struct nt_prpsinfo_64 prpsinfo;
|
|
|
|
memset(&prpsinfo, 0, sizeof(prpsinfo));
|
|
prpsinfo.pr_state = 0;
|
|
prpsinfo.pr_sname = 'R';
|
|
prpsinfo.pr_zomb = 0;
|
|
strcpy(prpsinfo.pr_fname, "vmlinux");
|
|
|
|
return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo),
|
|
NOTE_NAME_CORE);
|
|
}
|
|
|
|
void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo)
|
|
{
|
|
if (!vmcoreinfo)
|
|
return ptr;
|
|
return nt_init(ptr, 0, vmcoreinfo, strlen(vmcoreinfo),
|
|
NOTE_NAME_VMCOREINFO);
|
|
}
|
|
|
|
/* Keep in sync with `struct dfi_cpu` */
|
|
size_t get_max_note_size_per_cpu(void)
|
|
{
|
|
size_t size = 0;
|
|
|
|
size += ELF64_NOTE_SIZE(NOTE_NAME_CORE, sizeof(struct nt_prstatus_64));
|
|
size += ELF64_NOTE_SIZE(NOTE_NAME_CORE, sizeof(struct nt_fpregset_64));
|
|
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, timer));
|
|
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, todcmp));
|
|
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, todpreg));
|
|
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, ctrs));
|
|
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, prefix));
|
|
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, vxrs_low));
|
|
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, vxrs_high));
|
|
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, gscb));
|
|
|
|
return size;
|
|
}
|