summaryrefslogtreecommitdiff
path: root/elf.c
blob: 439433b5ae5039bcbfb17491208222b2d950e556 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
 * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
 *
 * This file is part of nios2sim-ng.
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License. See the file "COPYING" in the main directory of this archive
 * for more details.
 */

#include <stdio.h>
#include <stdint.h>
#include <libelf.h>
#include <gelf.h>

#include "nios2sim-ng.h"
#include "image.h"

/* not defined with libelf */
#define EM_ALTERA_NIOS2		113

/*
 * http://elftoolchain.sourceforge.net/for-review/libelf-by-example-20100112.pdf
 */

static int elf_read_sections(Elf *e, struct memory *mem)
{
	Elf_Scn *scn = NULL;
	size_t shstrndx;
	char *name;

	if (elf_getshdrstrndx(e, &shstrndx) != 0) {
		err("elf_getshdrstrndx() failed: %s\n", elf_errmsg(-1));
		return -1;
	}

	while ((scn = elf_nextscn(e, scn)) != NULL) {
		GElf_Shdr shdr;
		GElf_Phdr phdr;

		if (gelf_getshdr(scn, &shdr) == NULL) {
			err("Couldn't get section header: %s\n", elf_errmsg(-1));
			return -1;
		}

		/* Is it a .text section? */
		if (shdr.sh_type == SHT_PROGBITS
		    && (shdr.sh_flags & ~SHF_WRITE) == (SHF_ALLOC | SHF_EXECINSTR)) {
			Elf_Data *data;
			size_t n;

			if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL) {
				err("Couldn't get section name: %s\n", elf_errmsg(-1));
				return -1;
			}

			dbg("Section %zu: %s (size=%zu, flags=%08lx, addr=%08lx, off=%08lx)\n", elf_ndxscn(scn), name, shdr.sh_size, shdr.sh_flags, shdr.sh_addr, shdr.sh_offset);

			data = NULL;
			n = 0;
			while (n < shdr.sh_size &&
					(data = elf_getdata(scn, data)) != NULL) {
				memcpy(&mem->base[(shdr.sh_addr + n) / 4], data->d_buf, data->d_size);

				n += data->d_size;
			}
		}
	}

	return 0;
}

int elf_load(FILE *fp, const char *name, struct memory *mem)
{
	int fd = fileno(fp);
	int ret;
	Elf *e;
	GElf_Ehdr ehdr;
	size_t n;

	if (unlikely(fd < 0)) {
		err("Couldn't get file descriptor for '%s'\n", name);
		return -1;
	}

	if (elf_version(EV_CURRENT) == EV_NONE) {
		err("ELF library initialization failed\n");
		return -1;
	}

	ret = -1;
	e = elf_begin(fd, ELF_C_READ, NULL);
	if (unlikely(e == NULL)) {
		err("Couldn't get ELF file handle for '%s': %s\n", name, elf_errmsg(-1));
		goto out;
	}

	if (elf_kind(e) != ELF_K_ELF) {
		err("'%s' is not an ELF object\n", name);
		goto out;
	}

	if (gelf_getclass(e) != ELFCLASS32) {
		err("'%s' has invalid ELF class\n", name);
		goto out;
	}

	if (gelf_getehdr(e, &ehdr) == NULL) {
		err("gelf_getehdr() failed: %s\n", elf_errmsg(-1));
		goto out;
	}

	if (ehdr.e_type != ET_EXEC || ehdr.e_machine != EM_ALTERA_NIOS2) {
		err("'%s' is not a Nios2 ELF executable (%x, %x)\n", name, ehdr.e_type, ehdr.e_machine);
		goto out;
	}

	ret = elf_read_sections(e, mem);
out:
	elf_end(e);
	return ret;
}