blob: de46d288dc29cdbb2327533decf107f38706023f [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <endian.h>
#include <elf.h>
#if !defined(__BYTE_ORDER) || !defined(__LITTLE_ENDIAN) || !defined(__BIG_ENDIAN)
#error Endian defines missing
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
# define ELFDATALOCAL ELFDATA2LSB
#elif __BYTE_ORDER == __BIG_ENDIAN
# define ELFDATALOCAL ELFDATA2MSB
#else
# error Unknown byte order
#endif
#define MAP_WINDOW_SIZE (64*1024*1024)
#define DEV_MEM "/dev/mem"
#define ALIGN_MASK(x,y) (((x) + (y)) & ~(y))
#define ALIGN(x,y) ALIGN_MASK(x, (y) - 1)
static void *map_addr(int fd, unsigned long size, off_t offset)
{
unsigned long page_size = getpagesize();
unsigned long map_offset = offset & (page_size - 1);
size_t len = ALIGN(size + map_offset, page_size);
void *result;
result = mmap(0, len, PROT_READ, MAP_SHARED, fd, offset - map_offset);
if (result == MAP_FAILED) {
fprintf(stderr, "Cannot mmap " DEV_MEM " offset: %#llx size: %lu: %s\n",
(unsigned long long)offset, size, strerror(errno));
exit(5);
}
return result + map_offset;
}
static void unmap_addr(void *addr, unsigned long size)
{
unsigned long page_size = getpagesize();
unsigned long map_offset = (uintptr_t)addr & (page_size - 1);
size_t len = ALIGN(size + map_offset, page_size);
int ret;
addr -= map_offset;
ret = munmap(addr, len);
if (ret < 0) {
fprintf(stderr, "munmap failed: %s\n",
strerror(errno));
exit(6);
}
}
static void *xmalloc(size_t size)
{
void *result;
result = malloc(size);
if (result == NULL) {
fprintf(stderr, "malloc of %u bytes failed: %s\n",
(unsigned int)size, strerror(errno));
exit(7);
}
return result;
}
static void *collect_notes(
int fd, Elf64_Ehdr *ehdr, Elf64_Phdr *phdr, size_t *note_bytes)
{
int i;
size_t bytes, result_bytes;
char *notes;
result_bytes = 0;
/* Find the worst case note memory usage */
bytes = 0;
for(i = 0; i < ehdr->e_phnum; i++) {
if (phdr[i].p_type == PT_NOTE) {
bytes += phdr[i].p_filesz;
}
}
/* Allocate the worst case note array */
notes = xmalloc(bytes);
/* Walk through and capture the notes */
for(i = 0; i < ehdr->e_phnum; i++) {
Elf64_Nhdr *hdr, *lhdr, *nhdr;
void *pnotes;
if (phdr[i].p_type != PT_NOTE) {
continue;
}
/* First snapshot the notes */
pnotes = map_addr(fd, phdr[i].p_filesz, phdr[i].p_offset);
memcpy(notes + result_bytes, pnotes, phdr[i].p_filesz);
unmap_addr(pnotes, phdr[i].p_filesz);
/* Walk through the new notes and find the real length */
hdr = (Elf64_Nhdr *)(notes + result_bytes);
lhdr = (Elf64_Nhdr *)(notes + result_bytes + phdr[i].p_filesz);
for(; hdr < lhdr; hdr = nhdr) {
size_t hdr_size;
/* If there is not a name this is a invalid/reserved note
* stop here.
*/
if (hdr->n_namesz == 0) {
break;
}
hdr_size =
sizeof(*hdr) +
((hdr->n_namesz + 3) & ~3) +
((hdr->n_descsz + 3) & ~3);
nhdr = (Elf64_Nhdr *)(((char *)hdr) + hdr_size);
/* if the note does not fit in the segment stop here */
if (nhdr > lhdr) {
break;
}
/* Update result_bytes for after each good header */
result_bytes = ((char *)hdr) - notes;
}
}
*note_bytes = result_bytes;
return notes;
}
static void *generate_new_headers(
Elf64_Ehdr *ehdr, Elf64_Phdr *phdr, size_t note_bytes, size_t *header_bytes)
{
unsigned phnum;
size_t bytes;
char *headers;
Elf64_Ehdr *nehdr;
Elf64_Phdr *nphdr;
unsigned long long offset;
int i;
/* Count the number of program headers.
* When we are done there will be only one note header.
*/
phnum = 1;
for(i = 0; i < ehdr->e_phnum; i++) {
if (phdr[i].p_type == PT_NOTE) {
continue;
}
phnum++;
}
/* Compute how many bytes we will need for headers */
bytes = sizeof(*ehdr) + sizeof(*phdr)*phnum;
/* Allocate memory for the headers */
headers = xmalloc(bytes);
/* Setup pointers to the new headers */
nehdr = (Elf64_Ehdr *)headers;
nphdr = (Elf64_Phdr *)(headers + sizeof(*nehdr));
/* Copy and adjust the Elf header */
memcpy(nehdr, ehdr, sizeof(*nehdr));
nehdr->e_phoff = sizeof(*nehdr);
nehdr->e_phnum = phnum;
nehdr->e_shoff = 0;
nehdr->e_shentsize = 0;
nehdr->e_shnum = 0;
nehdr->e_shstrndx = 0;
/* Write the note program header */
nphdr->p_type = PT_NOTE;
nphdr->p_offset = bytes;
nphdr->p_vaddr = 0;
nphdr->p_paddr = 0;
nphdr->p_filesz = note_bytes;
nphdr->p_memsz = note_bytes;
nphdr->p_flags = 0;
nphdr->p_align = 0;
nphdr++;
/* Write the rest of the program headers */
offset = bytes + note_bytes;
for(i = 0; i < ehdr->e_phnum; i++) {
if (phdr[i].p_type == PT_NOTE) {
continue;
}
memcpy(nphdr, &phdr[i], sizeof(*nphdr));
nphdr->p_offset = offset;
nphdr++;
offset += phdr[i].p_filesz;
}
*header_bytes = bytes;
return headers;
}
static void write_all(int fd, const void *buf, size_t count)
{
ssize_t result;
size_t written = 0;
const char *ptr;
size_t left;
ptr = buf;
left = count;
do {
result = write(fd, ptr, left);
if (result >= 0) {
written += result;
ptr += result;
left -= result;
}
else if ((errno != EAGAIN) && (errno != EINTR)) {
fprintf(stderr, "write failed: %s\n",
strerror(errno));
exit(8);
}
} while(written < count);
}
int main(int argc, char **argv)
{
char *start_addr_str, *end;
unsigned long long start_addr;
Elf64_Ehdr *ehdr;
Elf64_Phdr *phdr;
void *notes, *headers;
size_t note_bytes, header_bytes;
int fd;
int i;
start_addr_str = 0;
if (argc > 2) {
fprintf(stderr, "Invalid argument count\n");
exit(9);
}
if (argc == 2) {
start_addr_str = argv[1];
}
if (!start_addr_str) {
start_addr_str = getenv("elfcorehdr");
}
if (!start_addr_str) {
fprintf(stderr, "Cannot find the start of the core dump\n");
exit(1);
}
start_addr = strtoull(start_addr_str, &end, 0);
if ((start_addr_str == end) || (*end != '\0')) {
fprintf(stderr, "Bad core dump start addres: %s\n",
start_addr_str);
exit(2);
}
fd = open(DEV_MEM, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Cannot open " DEV_MEM ": %s\n",
strerror(errno));
exit(3);
}
/* Get the elf header */
ehdr = map_addr(fd, sizeof(*ehdr), start_addr);
/* Verify the ELF header */
if ( (ehdr->e_ident[EI_MAG0] != ELFMAG0) ||
(ehdr->e_ident[EI_MAG1] != ELFMAG1) ||
(ehdr->e_ident[EI_MAG2] != ELFMAG2) ||
(ehdr->e_ident[EI_MAG3] != ELFMAG3) ||
(ehdr->e_ident[EI_CLASS] != ELFCLASS64) ||
(ehdr->e_ident[EI_DATA] != ELFDATALOCAL) ||
(ehdr->e_ident[EI_VERSION] != EV_CURRENT) ||
(ehdr->e_type != ET_CORE) ||
(ehdr->e_version != EV_CURRENT) ||
(ehdr->e_ehsize != sizeof(Elf64_Ehdr)) ||
(ehdr->e_phentsize != sizeof(Elf64_Phdr)) ||
(ehdr->e_phnum == 0))
{
fprintf(stderr, "Invalid Elf header\n");
exit(4);
}
/* Get the program header */
phdr = map_addr(fd, sizeof(*phdr)*(ehdr->e_phnum),
start_addr + ehdr->e_phoff);
/* Collect up the notes */
note_bytes = 0;
notes = collect_notes(fd, ehdr, phdr, &note_bytes);
/* Generate new headers */
header_bytes = 0;
headers = generate_new_headers(ehdr, phdr, note_bytes, &header_bytes);
/* Write out everything */
write_all(STDOUT_FILENO, headers, header_bytes);
write_all(STDOUT_FILENO, notes, note_bytes);
for(i = 0; i < ehdr->e_phnum; i++) {
unsigned long long offset, size;
size_t wsize;
if (phdr[i].p_type == PT_NOTE) {
continue;
}
offset = phdr[i].p_offset;
size = phdr[i].p_filesz;
wsize = MAP_WINDOW_SIZE;
if (wsize > size) {
wsize = size;
}
for(;size > 0; size -= wsize, offset += wsize) {
void *buf;
wsize = MAP_WINDOW_SIZE;
if (wsize > size) {
wsize = size;
}
buf = map_addr(fd, wsize, offset);
write_all(STDOUT_FILENO, buf, wsize);
unmap_addr(buf, wsize);
}
}
free(notes);
close(fd);
return 0;
}