# elfloader.c -rw-r--r-- 5.7 KiB View raw
                                                                                
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#include <string.h>
#include <elf.h>
#include <elfloader.h>

#include <stdio.h>
#include <vga_term.h>

// Checks whether the memory at the start of the pointer given
// is the ELF magic number.
int checkMagic(const void *e) {
	const char *magic = ELFMAG;
	int r = memcmp(e, (const void *) magic, 4);
	//if(r == 0) putStr("r is true");
	//else putStr("r is false");
	return r;
}

/*
void loadSectionHeader(const Elf64_Shdr* s) {
   putStr("Loading section header...");
}
*/

// Loads the program from a given ELF header structure.
// beginning = beginning of exe.
// All entries in the program table specifying where things
// are happen relative to that.
void loadProgramHeader(const Elf64_Phdr *p, const uint8_t *const beginning) {
	switch(p->p_type) {
	// Keep on trucking
	case PT_LOAD:
		//putStr("Actual load section");
		break;

	// Things we don't care about
	case PT_NULL:
		//putStr("Null section, skipping");
		return;
	case PT_NOTE:
		//putStr("Note section, skipping");
		return;
	case PT_PHDR:
		//putStr("Program header, skipping");
		return;

	// Cases not handled.
	case PT_DYNAMIC:
		//putStr("Dynamic section, skipping");
		return;
	case PT_INTERP:
		//putStr("Interp section, skipping");
		return;
	case PT_SHLIB:
		//putStr("Shlib section, skipping");
		return;
	default:
		//putStr("Something else, skipping");
		return;
	}
	// XXX: Alignment
	const uint8_t *loadFrom = beginning + p->p_offset;
	const uint8_t *loadTo = (uint8_t *) p->p_vaddr;
	// p_align does not mean what you think it does.
	//const char* loadTo = (char*)((uintptr_t)(p->p_vaddr) & ~(uintptr_t)(align-1)) ;
	const size_t length = p->p_filesz;
	const size_t memLength = p->p_memsz;
	const uint8_t *bssStart = loadTo + length;
	const size_t bssLength = memLength - length;

	kprintf("Loading 0x%x bytes from 0x%x to 0x%x", length, loadFrom, loadTo);


	// We don't need p_memsz right now because there's no paging;
	// all we have to do is leave a gap, which will happen anyway
	// if the ELF file is not bogus.

	memmove((void *) loadTo, (void *) loadFrom, length);
	memset((void *) bssStart, 0, bssLength);
}


// Loads an ELF64 file to the appropriate location it requests
// Returns 0 if the load was successful, nonzero otherwise.
// Takes a pointer to the very beginning of the ELF data.
// XXX: TODO: Make this return some reasonable error values.
// Not executable file, wrong machine type, malformed ELF file (such as
// exe file with no program header or entry point), wrong version,
int loadELF64(const void *from, kernel_entry_point_t *entry) {
	if(checkMagic(from)) {
		return -1;
	}

	// Lots of the things measure as byte offsets from the beginning
	// of the file, so it's handy to have a reference to that.
	const Elf64_Ehdr *header = (Elf64_Ehdr *) from;
	const uint8_t *const headerAddress = (uint8_t *) from;

	if(header->e_type != ET_EXEC) {
		return -1;
	}
	if(header->e_machine != EM_X86_64) {
		return -1;
	}

	//const void* const entry = (void*) header->e_entry;
	Elf64_Phdr *pheader;
	// e_phoff == 0 indicates no program header; ie, not an executable file.
	if(header->e_phoff == 0) {
		return -1;
	}
	pheader = (Elf64_Phdr *)(headerAddress + header->e_phoff);
// Sections are for linking only!  I don't have to worry about them!
	/*
	   const Elf64_Shdr* sheader;
	   // Again, 0 indicates no section headers.
	   if(header->e_shoff == 0) { return -1; }
	   sheader = (Elf64_Shdr*) (headerAddress + header->e_shoff);
	*/
	// I guess these aren't strictly necessary, but it makes it clearer
	// to me.
	uint16_t phEntrySize = header->e_phentsize;
	uint16_t phEntryCount = header->e_phnum;

	/*
	   int shEntrySize = header->e_shentsize;
	   int shEntryCount = header->e_shnum;

	   char display[80];
	   snprintf(display, 80, "Number of sections and program headers: %d, %d",
		    shEntryCount, phEntryCount);
	   putStr(display);


	   for(i = 0; i < shEntryCount; i++) {
	      const char* const current = ((char*) sheader) + (i*shEntrySize);
	      loadSectionHeader((Elf64_Shdr*) current);
	   }
	*/
	for(int i = 0; i < phEntryCount; i++) {
		const char *const current = ((char *) pheader) + (i * phEntrySize);
		loadProgramHeader((Elf64_Phdr *) current, headerAddress);
	}
	*entry = (kernel_entry_point_t) header->e_entry;

	return 0;
}

int64_t elf64End(void *from) {
	if(checkMagic(from)) {
		return -1;
	}

	// Lots of the things measure as byte offsets from the beginning
	// of the file, so it's handy to have a reference to that.
	const Elf64_Ehdr *header = (Elf64_Ehdr *) from;
	const uint8_t *const headerAddress = (uint8_t *) from;

	if(header->e_type != ET_EXEC) {
		return -1;
	}
	if(header->e_machine != EM_X86_64) {
		return -1;
	}

	Elf64_Phdr *pheader;
	// e_phoff == 0 indicates no program header; ie, not an executable file.
	if(header->e_phoff == 0) {
		return -1;
	}
	pheader = (Elf64_Phdr *)(headerAddress + header->e_phoff);

	// I guess these aren't strictly necessary, but it makes it clearer
	// to me.
	uint32_t phEntrySize = header->e_phentsize;
	uint32_t phEntryCount = header->e_phnum;

	int64_t finalEndAddress = 0;
//   char buf[80];
	for(int i = 0; i < phEntryCount; i++) {
		const char *const current = ((char *) pheader) + (i * phEntrySize);
		Elf64_Phdr *currentHeader = (Elf64_Phdr *) current;
		if(currentHeader->p_type != PT_LOAD) {
			continue;
		}
		/*
		      snprintf(buf, 80, "Entry %d at 0x%X: finalEnd 0x%X, vaddr 0x%X, size 0x%X",
			       i, (int) currentHeader, (int) finalEndAddress, (int) currentHeader->p_vaddr,
			       (int) currentHeader->p_memsz);
		      putStr(buf);
		*/
		int64_t currentBase = currentHeader->p_vaddr;
		int64_t currentEnd = currentBase + currentHeader->p_memsz;
		if(finalEndAddress < currentEnd) {
			finalEndAddress = currentEnd;
		}

	}
//      snprintf(buf, 80, "Final end is now 0x%X", (int) finalEndAddress);
//      putStr(buf);

	return finalEndAddress;
}