1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/scummsys.h"
24
25 #if defined(DYNAMIC_MODULES) && defined(USE_ELF_LOADER)
26
27 #include "backends/plugins/elf/elf-loader.h"
28 #include "backends/plugins/elf/memory-manager.h"
29
30 #include "common/debug.h"
31 #include "common/file.h"
32 #include "common/fs.h"
33 #include "common/ptr.h"
34
35 #include <malloc.h> // for memalign()
36
DLObject()37 DLObject::DLObject() :
38 _file(0),
39 _segment(0),
40 _symtab(0),
41 _strtab(0),
42 _segmentSize(0),
43 _segmentOffset(0),
44 _segmentVMA(0),
45 _symbol_cnt(0),
46 _symtab_sect(-1),
47 _dtors_start(0),
48 _dtors_end(0) {
49 }
50
~DLObject()51 DLObject::~DLObject() {
52 discardSymtab();
53 discardSegment();
54 }
55
56 // Expel the symbol table from memory
discardSymtab()57 void DLObject::discardSymtab() {
58 free(_symtab);
59 _symtab = 0;
60
61 free(_strtab);
62 _strtab = 0;
63
64 _symbol_cnt = 0;
65 }
66
discardSegment()67 void DLObject::discardSegment() {
68 if (_segment) {
69 // Restore default protection before returning memory
70 protectMemory(_segment, _segmentSize, PF_R | PF_W);
71 deallocateMemory(_segment, _segmentSize);
72 }
73
74 _segment = 0;
75 _segmentSize = 0;
76 _segmentOffset = 0;
77 _segmentVMA = 0;
78 }
79
80 // Unload all objects from memory
unload()81 void DLObject::unload() {
82 discardSymtab();
83 discardSegment();
84 }
85
readElfHeader(Elf32_Ehdr * ehdr)86 bool DLObject::readElfHeader(Elf32_Ehdr *ehdr) {
87 assert(_file);
88
89 // Start reading the elf header. Check for errors and magic
90 if (_file->read(ehdr, sizeof(*ehdr)) != sizeof(*ehdr) ||
91 memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
92 warning("elfloader: No ELF file.");
93 return false;
94 }
95
96 if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) {
97 warning("elfloader: Wrong ELF file class.");
98 return false;
99 }
100
101 if (ehdr->e_ident[EI_DATA] !=
102 #ifdef SCUMM_BIG_ENDIAN
103 ELFDATA2MSB
104 #else
105 ELFDATA2LSB
106 #endif
107 ) {
108 warning("elfloader: Wrong ELF file endianess.");
109 return false;
110 }
111
112 if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
113 warning("elfloader: Wrong ELF file version.");
114 return false;
115 }
116
117 if (ehdr->e_type != ET_EXEC) {
118 warning("elfloader: No executable ELF file.");
119 return false;
120 }
121
122 if (ehdr->e_machine !=
123 #ifdef ARM_TARGET
124 EM_ARM
125 #endif
126 #ifdef MIPS_TARGET
127 EM_MIPS
128 #endif
129 #ifdef PPC_TARGET
130 EM_PPC
131 #endif
132 ) {
133 warning("elfloader: Wrong ELF file architecture.");
134 return false;
135 }
136
137 if (ehdr->e_phentsize < sizeof(Elf32_Phdr) || // Check for size of program header
138 ehdr->e_shentsize != sizeof(Elf32_Shdr)) { // Check for size of section header
139 warning("elfloader: Invalid ELF structure sizes.");
140 return false;
141 }
142
143 debug(2, "elfloader: phoff = %d, phentsz = %d, phnum = %d",
144 ehdr->e_phoff, ehdr->e_phentsize, ehdr->e_phnum);
145
146 return true;
147 }
148
readProgramHeaders(Elf32_Ehdr * ehdr,Elf32_Phdr * phdr,Elf32_Half num)149 bool DLObject::readProgramHeaders(Elf32_Ehdr *ehdr, Elf32_Phdr *phdr, Elf32_Half num) {
150 assert(_file);
151
152 // Read program header
153 if (!_file->seek(ehdr->e_phoff + sizeof(*phdr) * num, SEEK_SET) ||
154 _file->read(phdr, sizeof(*phdr)) != sizeof(*phdr)) {
155 warning("elfloader: Program header load failed.");
156 return false;
157 }
158
159 // Check program header values
160 if (phdr->p_type != PT_LOAD || phdr->p_filesz > phdr->p_memsz) {
161 warning("elfloader: Invalid program header.");
162 return false;
163 }
164
165 debug(2, "elfloader: offs = %x, filesz = %x, memsz = %x, align = %x",
166 phdr->p_offset, phdr->p_filesz, phdr->p_memsz, phdr->p_align);
167
168 return true;
169 }
170
loadSegment(Elf32_Phdr * phdr)171 bool DLObject::loadSegment(Elf32_Phdr *phdr) {
172 _segment = (byte *)allocateMemory(phdr->p_align, phdr->p_memsz);
173
174 if (!_segment) {
175 warning("elfloader: Out of memory.");
176 return false;
177 }
178
179 debug(2, "elfloader: Allocated segment @ %p", _segment);
180
181 // Get offset to load segment into
182 _segmentSize = phdr->p_memsz;
183 _segmentVMA = phdr->p_vaddr;
184
185 // Set .bss segment to 0 if necessary
186 if (phdr->p_memsz > phdr->p_filesz) {
187 debug(2, "elfloader: Setting %p to %p to 0 for bss",
188 _segment + phdr->p_filesz, _segment + phdr->p_memsz);
189 memset(_segment + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
190 }
191
192 debug(2, "elfloader: Reading the segment into memory");
193
194 // Read the segment into memory
195 if (!_file->seek(phdr->p_offset, SEEK_SET) ||
196 _file->read(_segment, phdr->p_filesz) != phdr->p_filesz) {
197 warning("elfloader: Segment load failed.");
198 return false;
199 }
200
201 debug(2, "elfloader: Segment has been read into memory");
202
203 return true;
204 }
205
loadSectionHeaders(Elf32_Ehdr * ehdr)206 Elf32_Shdr * DLObject::loadSectionHeaders(Elf32_Ehdr *ehdr) {
207 assert(_file);
208
209 Elf32_Shdr *shdr = 0;
210
211 // Allocate memory for section headers
212 if (!(shdr = (Elf32_Shdr *)malloc(ehdr->e_shnum * sizeof(*shdr)))) {
213 warning("elfloader: Out of memory.");
214 return 0;
215 }
216
217 // Read from file into section headers
218 if (!_file->seek(ehdr->e_shoff, SEEK_SET) ||
219 _file->read(shdr, ehdr->e_shnum * sizeof(*shdr)) !=
220 ehdr->e_shnum * sizeof(*shdr)) {
221 warning("elfloader: Section headers load failed.");
222 free(shdr);
223 return 0;
224 }
225
226 return shdr;
227 }
228
findSymbolTableSection(Elf32_Ehdr * ehdr,Elf32_Shdr * shdr)229 int DLObject::findSymbolTableSection(Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) {
230 int SymbolTableSection = -1;
231
232 // Loop over sections, looking for symbol table linked to a string table
233 for (uint32 i = 0; i < ehdr->e_shnum; i++) {
234 if (shdr[i].sh_type == SHT_SYMTAB &&
235 shdr[i].sh_entsize == sizeof(Elf32_Sym) &&
236 shdr[i].sh_link < ehdr->e_shnum &&
237 shdr[shdr[i].sh_link].sh_type == SHT_STRTAB) {
238 SymbolTableSection = i;
239 break;
240 }
241 }
242
243 return SymbolTableSection;
244 }
245
loadSymbolTable(Elf32_Ehdr * ehdr,Elf32_Shdr * shdr)246 int DLObject::loadSymbolTable(Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) {
247 assert(_file);
248
249 _symtab_sect = findSymbolTableSection(ehdr, shdr);
250
251 // Check for no symbol table
252 if (_symtab_sect < 0) {
253 warning("elfloader: No symbol table.");
254 return -1;
255 }
256
257 debug(2, "elfloader: Symbol section at section %d, size %x",
258 _symtab_sect, shdr[_symtab_sect].sh_size);
259
260 // Allocate memory for symbol table
261 if (!(_symtab = (Elf32_Sym *)malloc(shdr[_symtab_sect].sh_size))) {
262 warning("elfloader: Out of memory.");
263 return -1;
264 }
265
266 // Read symbol table into memory
267 if (!_file->seek(shdr[_symtab_sect].sh_offset, SEEK_SET) ||
268 _file->read(_symtab, shdr[_symtab_sect].sh_size) !=
269 shdr[_symtab_sect].sh_size) {
270 warning("elfloader: Symbol table load failed.");
271 free(_symtab);
272 _symtab = 0;
273 return -1;
274 }
275
276 // Set number of symbols
277 _symbol_cnt = shdr[_symtab_sect].sh_size / sizeof(Elf32_Sym);
278 debug(2, "elfloader: Loaded %d symbols.", _symbol_cnt);
279
280 return _symtab_sect;
281 }
282
loadStringTable(Elf32_Shdr * shdr)283 bool DLObject::loadStringTable(Elf32_Shdr *shdr) {
284 assert(_file);
285
286 uint32 string_sect = shdr[_symtab_sect].sh_link;
287
288 // Allocate memory for string table
289 if (!(_strtab = (char *)malloc(shdr[string_sect].sh_size))) {
290 warning("elfloader: Out of memory.");
291 return false;
292 }
293
294 // Read string table into memory
295 if (!_file->seek(shdr[string_sect].sh_offset, SEEK_SET) ||
296 _file->read(_strtab, shdr[string_sect].sh_size) !=
297 shdr[string_sect].sh_size) {
298 warning("elfloader: Symbol table strings load failed.");
299 free(_strtab);
300 _strtab = 0;
301 return false;
302 }
303
304 return true;
305 }
306
relocateSymbols(ptrdiff_t offset)307 void DLObject::relocateSymbols(ptrdiff_t offset) {
308 // Loop over symbols, add relocation offset
309 Elf32_Sym *s = _symtab;
310
311 for (uint32 c = _symbol_cnt; c--; s++) {
312 // Make sure we don't relocate special valued symbols
313 if (s->st_shndx < SHN_LOPROC) {
314 s->st_value += offset;
315
316 if (s->st_value < Elf32_Addr(_segment) ||
317 s->st_value > Elf32_Addr(_segment) + _segmentSize)
318 warning("elfloader: Symbol out of bounds! st_value = %x", s->st_value);
319 }
320 }
321 }
322
323 // Track the size of the plugin through memory manager without loading
324 // the plugin into memory.
325 //
trackSize(const char * path)326 void DLObject::trackSize(const char *path) {
327
328 _file = Common::FSNode(path).createReadStream();
329
330 if (!_file) {
331 warning("elfloader: File %s not found.", path);
332 return;
333 }
334
335 Elf32_Ehdr ehdr;
336 Elf32_Phdr phdr;
337
338 if (!readElfHeader(&ehdr)) {
339 delete _file;
340 _file = 0;
341 return;
342 }
343
344 ELFMemMan.trackPlugin(true); // begin tracking the plugin size
345
346 // Load the segments
347 for (uint32 i = 0; i < ehdr.e_phnum; i++) {
348 debug(2, "elfloader: Loading segment %d", i);
349
350 if (!readProgramHeaders(&ehdr, &phdr, i)) {
351 delete _file;
352 _file = 0;
353 return;
354 }
355
356 if (phdr.p_flags & PF_X) { // check for executable, allocated segment
357 ELFMemMan.trackAlloc(phdr.p_align, phdr.p_memsz);
358 }
359 }
360
361 ELFMemMan.trackPlugin(false); // we're done tracking the plugin size
362
363 delete _file;
364 _file = 0;
365 // No need to track the symbol table sizes -- they get discarded
366 }
367
load()368 bool DLObject::load() {
369 Elf32_Ehdr ehdr;
370 Elf32_Phdr phdr;
371
372 if (readElfHeader(&ehdr) == false)
373 return false;
374
375 //Load the segments
376 for (uint32 i = 0; i < ehdr.e_phnum; i++) {
377 debug(2, "elfloader: Loading segment %d", i);
378
379 if (readProgramHeaders(&ehdr, &phdr, i) == false)
380 return false;
381
382 if (!loadSegment(&phdr))
383 return false;
384 }
385
386 Elf32_Shdr *shdr = loadSectionHeaders(&ehdr);
387 if (!shdr)
388 return false;
389
390 _symtab_sect = loadSymbolTable(&ehdr, shdr);
391 if (_symtab_sect < 0) {
392 free(shdr);
393 return false;
394 }
395
396 if (!loadStringTable(shdr)) {
397 free(shdr);
398 return false;
399 }
400
401 // Offset by our segment allocated address
402 // must use _segmentVMA here for multiple segments (MIPS)
403 _segmentOffset = ptrdiff_t(_segment) - _segmentVMA;
404 relocateSymbols(_segmentOffset);
405
406 if (!relocateRels(&ehdr, shdr)) {
407 free(shdr);
408 return false;
409 }
410
411 protectMemory(_segment, _segmentSize, phdr.p_flags);
412
413 return true;
414 }
415
open(const char * path)416 bool DLObject::open(const char *path) {
417 void *ctors_start, *ctors_end;
418
419 debug(2, "elfloader: open(\"%s\")", path);
420
421 _file = Common::FSNode(path).createReadStream();
422
423 if (!_file) {
424 warning("elfloader: File %s not found.", path);
425 return false;
426 }
427
428 debug(2, "elfloader: %s found!", path);
429
430 /*Try to load and relocate*/
431 if (!load()) {
432 unload();
433 return false;
434 }
435
436 debug(2, "elfloader: Loaded!");
437
438 delete _file;
439 _file = 0;
440
441 flushDataCache(_segment, _segmentSize);
442
443 ctors_start = symbol("___plugin_ctors");
444 ctors_end = symbol("___plugin_ctors_end");
445 _dtors_start = symbol("___plugin_dtors");
446 _dtors_end = symbol("___plugin_dtors_end");
447
448 if (!ctors_start || !ctors_end || !_dtors_start || !_dtors_end) {
449 warning("elfloader: Missing ctors/dtors.");
450 _dtors_start = _dtors_end = 0;
451 unload();
452 return false;
453 }
454
455 debug(2, "elfloader: Calling constructors.");
456 for (void (**f)(void) = (void (**)(void))ctors_start; f != ctors_end; f++)
457 (**f)();
458
459 debug(2, "elfloader: %s opened ok.", path);
460
461 return true;
462 }
463
close()464 bool DLObject::close() {
465 if (_dtors_start && _dtors_end)
466 for (void (**f)(void) = (void (**)(void))_dtors_start; f != _dtors_end; f++)
467 (**f)();
468
469 _dtors_start = _dtors_end = 0;
470 unload();
471 return true;
472 }
473
symbol(const char * name)474 void *DLObject::symbol(const char *name) {
475 debug(2, "elfloader: Symbol(\"%s\")", name);
476
477 if (!_symtab || !_strtab || _symbol_cnt < 1) {
478 warning("elfloader: No symbol table loaded.");
479 return 0;
480 }
481
482 Elf32_Sym *s = _symtab;
483
484 for (uint32 c = _symbol_cnt; c--; s++)
485 // We can only import symbols that are global or weak in the plugin
486 if ((SYM_BIND(s->st_info) == STB_GLOBAL ||
487 SYM_BIND(s->st_info) == STB_WEAK) &&
488 !strcmp(name, _strtab + s->st_name)) {
489 // We found the symbol
490 debug(2, "elfloader: => 0x%08x", s->st_value);
491 return (void *)s->st_value;
492 }
493
494 // We didn't find the symbol
495 warning("elfloader: Symbol \"%s\" not found.", name);
496 return 0;
497 }
498
allocateMemory(uint32 align,uint32 size)499 void *DLObject::allocateMemory(uint32 align, uint32 size) {
500 return ELFMemMan.pluginAllocate(align, size);
501 }
502
deallocateMemory(void * ptr,uint32 size)503 void DLObject::deallocateMemory(void *ptr, uint32 size) {
504 ELFMemMan.pluginDeallocate(ptr);
505 }
506
507 #endif /* defined(DYNAMIC_MODULES) && defined(USE_ELF_LOADER) */
508