/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of libminidump.
Copyright 2012 Lennart Poettering
libminidump is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
libminidump is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with libminidump; If not, see
.
***/
#include
#include
#include
#include
#include
#include
#include
#include "coredump-util.h"
int coredump_read_header(int fd, ElfW(Ehdr) *header) {
ssize_t l;
assert(fd >= 0);
assert(header);
l = pread(fd, header, sizeof(*header), 0);
if (l < 0)
return -errno;
if (l != sizeof(*header))
return -EIO;
if (memcmp(header->e_ident, ELFMAG, SELFMAG) != 0)
return -EBADMSG;
if (header->e_type != ET_CORE)
return -EBADMSG;
if (header->e_ehsize != sizeof(ElfW(Ehdr)))
return -EBADMSG;
if (header->e_phentsize != sizeof(ElfW(Phdr)))
return -EBADMSG;
#if __WORDSIZE == 32
if (header->e_ident[EI_CLASS] != ELFCLASS32)
return -EBADMSG;
#elif __WORDSIZE == 64
if (header->e_ident[EI_CLASS] != ELFCLASS64)
return -EBADMSG;
#else
#error "Unknown word size."
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
if (header->e_ident[EI_DATA] != ELFDATA2LSB)
return -EBADMSG;
#elif __BYTE_ORDER == __BIG_ENDIAN
if (header->e_ident[EI_DATA] != ELFDATA2MSB)
return -EBADMSG;
#else
#error "Unknown endianess."
#endif
#if defined(__i386)
if (header->e_machine != EM_386)
return -EBADMSG;
#elif defined(__x86_64)
if (header->e_machine != EM_X86_64)
return -EBADMSG;
#else
#error "Unknown machine."
#endif
return 0;
}
int coredump_read_segment_header(int fd, const ElfW(Ehdr) *header, unsigned long i, ElfW(Phdr) *segment) {
ssize_t l;
assert(fd >= 0);
assert(header);
assert(segment);
if (header->e_phoff == 0)
return -E2BIG;
if (i >= header->e_phnum)
return -E2BIG;
l = pread(fd, segment, sizeof(*segment),
header->e_phoff + i * header->e_phentsize);
if (l < 0)
return -errno;
if (l != sizeof(*segment))
return -EIO;
return 0;
}
int coredump_find_note_segment(int fd, const ElfW(Ehdr) *header, off_t *offset, off_t *length) {
unsigned long i;
int r;
assert(fd >= 0);
assert(header);
assert(offset);
assert(length);
for (i = 0; i < header->e_phnum; i++) {
ElfW(Phdr) segment;
r = coredump_read_segment_header(fd, header, i, &segment);
if (r < 0)
return r;
if (segment.p_type == PT_NOTE) {
*offset = segment.p_offset;
*length = segment.p_filesz;
return 1;
}
}
return 0;
}
int coredump_read_memory(int fd, const ElfW(Ehdr) *header, unsigned long source, void *destination, size_t length) {
unsigned long i;
int r;
assert(fd >= 0);
assert(header);
assert(destination);
assert(length > 0);
for (i = 0; i < header->e_phnum; i++) {
ElfW(Phdr) segment;
ssize_t l;
r = coredump_read_segment_header(fd, header, i, &segment);
if (r < 0)
return r;
if (segment.p_type != PT_LOAD)
continue;
if (source >= segment.p_vaddr + segment.p_filesz)
continue;
if (source + length < segment.p_vaddr)
continue;
/* We assume that what we are looking for lies
* entirely within one segment. */
if (source < segment.p_vaddr ||
source + length > segment.p_vaddr + segment.p_filesz)
return -EIO;
l = pread(fd,
destination, length,
segment.p_offset + (source - segment.p_vaddr));
if (l < 0)
return -errno;
if ((size_t) l != length)
return -EIO;
return 1;
}
return 0;
}
int coredump_next_note(int fd, off_t *offset, off_t *length, ElfW(Nhdr) *n, off_t *name, off_t *descriptor) {
ssize_t l;
off_t j;
assert(fd >= 0);
assert(offset);
assert(length);
assert(n);
assert(name);
assert(descriptor);
l = pread(fd, n, sizeof(*n), *offset);
if (l < 0)
return -errno;
if (l != sizeof(*n))
return -EIO;
j = sizeof(*n) +
roundup(n->n_namesz, sizeof(int)) +
roundup(n->n_descsz, sizeof(int));
if (j > *length)
return -EIO;
*name = *offset + sizeof(*n);
*descriptor = *offset + sizeof(*n) + roundup(n->n_namesz, sizeof(int));
*offset += j;
*length -= j;
return 0;
}