1/*
2 * Copyright 2005-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "Image.h"
7
8#include <errno.h>
9#include <fcntl.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/mman.h>
13#include <unistd.h>
14
15#include <new>
16
17#include <runtime_loader.h>
18#include <syscalls.h>
19
20
21using namespace BPrivate::Debug;
22
23
24// #pragma mark - Image
25
26
27Image::Image()
28{
29}
30
31
32Image::~Image()
33{
34}
35
36
37status_t
38Image::GetSymbol(const char* name, int32 symbolType, void** _symbolLocation,
39	size_t* _symbolSize, int32* _symbolType) const
40{
41	// TODO: At least for ImageFile we could do hash lookups!
42	int32 iterator = 0;
43	const char* foundName;
44	size_t foundNameLen;
45	addr_t foundAddress;
46	size_t foundSize;
47	int32 foundType;
48	while (NextSymbol(iterator, &foundName, &foundNameLen, &foundAddress,
49			&foundSize, &foundType) == B_OK) {
50		if ((symbolType == B_SYMBOL_TYPE_ANY || symbolType == foundType)
51			&& strcmp(name, foundName) == 0) {
52			if (_symbolLocation != NULL)
53				*_symbolLocation = (void*)foundAddress;
54			if (_symbolSize != NULL)
55				*_symbolSize = foundSize;
56			if (_symbolType != NULL)
57				*_symbolType = foundType;
58			return B_OK;
59		}
60	}
61
62	return B_ENTRY_NOT_FOUND;
63}
64
65
66// #pragma mark - SymbolTableBasedImage
67
68
69SymbolTableBasedImage::SymbolTableBasedImage()
70	:
71	fLoadDelta(0),
72	fSymbolTable(NULL),
73	fStringTable(NULL),
74	fSymbolCount(0),
75	fStringTableSize(0)
76{
77}
78
79
80SymbolTableBasedImage::~SymbolTableBasedImage()
81{
82}
83
84
85const elf_sym*
86SymbolTableBasedImage::LookupSymbol(addr_t address, addr_t* _baseAddress,
87	const char** _symbolName, size_t *_symbolNameLen, bool *_exactMatch) const
88{
89	const elf_sym* symbolFound = NULL;
90	const char* symbolName = NULL;
91	bool exactMatch = false;
92	addr_t deltaFound = ~(addr_t)0;
93
94	for (int32 i = 0; i < fSymbolCount; i++) {
95		const elf_sym* symbol = &fSymbolTable[i];
96
97		if (symbol->st_value == 0
98			|| symbol->st_size >= (size_t)fInfo.text_size + fInfo.data_size) {
99			continue;
100		}
101
102		addr_t symbolAddress = symbol->st_value + fLoadDelta;
103		if (symbolAddress > address)
104			continue;
105
106		addr_t symbolDelta = address - symbolAddress;
107		if (symbolDelta >= 0 && symbolDelta < symbol->st_size)
108			exactMatch = true;
109
110		if (exactMatch || symbolDelta < deltaFound) {
111			deltaFound = symbolDelta;
112			symbolFound = symbol;
113			symbolName = fStringTable + symbol->st_name;
114
115			if (exactMatch)
116				break;
117		}
118	}
119
120	if (symbolFound != NULL) {
121		if (_baseAddress != NULL)
122			*_baseAddress = symbolFound->st_value + fLoadDelta;
123		if (_symbolName != NULL)
124			*_symbolName = symbolName;
125		if (_exactMatch != NULL)
126			*_exactMatch = exactMatch;
127		if (_symbolNameLen != NULL)
128			*_symbolNameLen = _SymbolNameLen(symbolName);
129	}
130
131	return symbolFound;
132}
133
134
135status_t
136SymbolTableBasedImage::NextSymbol(int32& iterator, const char** _symbolName,
137	size_t* _symbolNameLen, addr_t* _symbolAddress, size_t* _symbolSize,
138	int32* _symbolType) const
139{
140	while (true) {
141		if (++iterator >= fSymbolCount)
142			return B_ENTRY_NOT_FOUND;
143
144		const elf_sym* symbol = &fSymbolTable[iterator];
145
146		if ((symbol->Type() != STT_FUNC && symbol->Type() != STT_OBJECT)
147			|| symbol->st_value == 0) {
148			continue;
149		}
150
151		*_symbolName = fStringTable + symbol->st_name;
152		*_symbolNameLen = _SymbolNameLen(*_symbolName);
153		*_symbolAddress = symbol->st_value + fLoadDelta;
154		*_symbolSize = symbol->st_size;
155		*_symbolType = symbol->Type() == STT_FUNC ? B_SYMBOL_TYPE_TEXT
156			: B_SYMBOL_TYPE_DATA;
157
158		return B_OK;
159	}
160}
161
162
163size_t
164SymbolTableBasedImage::_SymbolNameLen(const char* symbolName) const
165{
166	if (symbolName == NULL || (addr_t)symbolName < (addr_t)fStringTable
167		|| (addr_t)symbolName >= (addr_t)fStringTable + fStringTableSize) {
168		return 0;
169	}
170
171	return strnlen(symbolName,
172		(addr_t)fStringTable + fStringTableSize - (addr_t)symbolName);
173}
174
175
176// #pragma mark - ImageFile
177
178
179ImageFile::ImageFile()
180	:
181	fFD(-1),
182	fFileSize(0),
183	fMappedFile((uint8*)MAP_FAILED)
184{
185}
186
187
188ImageFile::~ImageFile()
189{
190	if (fMappedFile != MAP_FAILED)
191		munmap(fMappedFile, fFileSize);
192
193	if (fFD >= 0)
194		close(fFD);
195}
196
197
198status_t
199ImageFile::Init(const image_info& info)
200{
201	// just copy the image info
202	fInfo = info;
203
204	// load the file
205	addr_t textAddress;
206	size_t textSize;
207	addr_t dataAddress;
208	size_t dataSize;
209	status_t error = _LoadFile(info.name, &textAddress, &textSize, &dataAddress,
210		&dataSize);
211	if (error != B_OK)
212		return error;
213
214	// compute the load delta
215	fLoadDelta = (addr_t)fInfo.text - textAddress;
216
217	return B_OK;
218}
219
220
221status_t
222ImageFile::Init(const char* path)
223{
224	// load the file
225	addr_t textAddress;
226	size_t textSize;
227	addr_t dataAddress;
228	size_t dataSize;
229	status_t error = _LoadFile(path, &textAddress, &textSize, &dataAddress,
230		&dataSize);
231	if (error != B_OK)
232		return error;
233
234	// init the image info
235	fInfo.id = -1;
236	fInfo.type = B_LIBRARY_IMAGE;
237	fInfo.sequence = 0;
238	fInfo.init_order = 0;
239	fInfo.init_routine = 0;
240	fInfo.term_routine = 0;
241	fInfo.device = -1;
242	fInfo.node = -1;
243	strlcpy(fInfo.name, path, sizeof(fInfo.name));
244	fInfo.text = (void*)textAddress;
245	fInfo.data = (void*)dataAddress;
246	fInfo.text_size = textSize;
247	fInfo.data_size = dataSize;
248
249	// the image isn't loaded, so no delta
250	fLoadDelta = 0;
251
252	return B_OK;
253}
254
255
256status_t
257ImageFile::_LoadFile(const char* path, addr_t* _textAddress, size_t* _textSize,
258	addr_t* _dataAddress, size_t* _dataSize)
259{
260	// open and stat() the file
261	fFD = open(path, O_RDONLY);
262	if (fFD < 0)
263		return errno;
264
265	struct stat st;
266	if (fstat(fFD, &st) < 0)
267		return errno;
268
269	fFileSize = st.st_size;
270	if (fFileSize < (off_t)sizeof(elf_ehdr))
271		return B_NOT_AN_EXECUTABLE;
272
273	// map it
274	fMappedFile = (uint8*)mmap(NULL, fFileSize, PROT_READ, MAP_PRIVATE, fFD, 0);
275	if (fMappedFile == MAP_FAILED)
276		return errno;
277
278	// examine the elf header
279	elf_ehdr* elfHeader = (elf_ehdr*)fMappedFile;
280	if (memcmp(elfHeader->e_ident, ELFMAG, 4) != 0)
281		return B_NOT_AN_EXECUTABLE;
282
283	if (elfHeader->e_ident[4] != ELF_CLASS)
284		return B_NOT_AN_EXECUTABLE;
285
286	// verify the location of the program headers
287	int32 programHeaderCount = elfHeader->e_phnum;
288	if (elfHeader->e_phoff < sizeof(elf_ehdr)
289		|| elfHeader->e_phentsize < sizeof(elf_phdr)
290		|| (off_t)(elfHeader->e_phoff + programHeaderCount
291				* elfHeader->e_phentsize)
292			> fFileSize) {
293		return B_NOT_AN_EXECUTABLE;
294	}
295
296	elf_phdr* programHeaders
297		= (elf_phdr*)(fMappedFile + elfHeader->e_phoff);
298
299	// verify the location of the section headers
300	int32 sectionCount = elfHeader->e_shnum;
301	if (elfHeader->e_shoff < sizeof(elf_ehdr)
302		|| elfHeader->e_shentsize < sizeof(elf_shdr)
303		|| (off_t)(elfHeader->e_shoff + sectionCount * elfHeader->e_shentsize)
304			> fFileSize) {
305		return B_NOT_AN_EXECUTABLE;
306	}
307
308	// find the text and data segment -- we need load address and size
309	*_textAddress = 0;
310	*_textSize = 0;
311	*_dataAddress = 0;
312	*_dataSize = 0;
313	for (int32 i = 0; i < programHeaderCount; i++) {
314		elf_phdr* header = (elf_phdr*)
315			((uint8*)programHeaders + i * elfHeader->e_phentsize);
316		if (header->p_type == PT_LOAD) {
317			if ((header->p_flags & PF_WRITE) == 0) {
318				*_textAddress = header->p_vaddr;
319				*_textSize = header->p_memsz;
320			} else {
321				*_dataAddress = header->p_vaddr;
322				*_dataSize = header->p_memsz;
323				break;
324			}
325		}
326	}
327
328	status_t error = _FindTableInSection(elfHeader, SHT_SYMTAB);
329	if (error != B_OK)
330		error = _FindTableInSection(elfHeader, SHT_DYNSYM);
331
332	return error;
333}
334
335
336status_t
337ImageFile::_FindTableInSection(elf_ehdr* elfHeader, uint16 sectionType)
338{
339	elf_shdr* sectionHeaders
340		= (elf_shdr*)(fMappedFile + elfHeader->e_shoff);
341
342	// find the symbol table
343	for (int32 i = 0; i < elfHeader->e_shnum; i++) {
344		elf_shdr* sectionHeader = (elf_shdr*)
345			((uint8*)sectionHeaders + i * elfHeader->e_shentsize);
346
347		if (sectionHeader->sh_type == sectionType) {
348			elf_shdr& stringHeader = *(elf_shdr*)
349				((uint8*)sectionHeaders
350					+ sectionHeader->sh_link * elfHeader->e_shentsize);
351
352			if (stringHeader.sh_type != SHT_STRTAB)
353				return B_BAD_DATA;
354
355			if ((off_t)(sectionHeader->sh_offset + sectionHeader->sh_size)
356					> fFileSize
357				|| (off_t)(stringHeader.sh_offset + stringHeader.sh_size)
358					> fFileSize) {
359				return B_BAD_DATA;
360			}
361
362			fSymbolTable = (elf_sym*)(fMappedFile + sectionHeader->sh_offset);
363			fStringTable = (char*)(fMappedFile + stringHeader.sh_offset);
364			fSymbolCount = sectionHeader->sh_size / sizeof(elf_sym);
365			fStringTableSize = stringHeader.sh_size;
366
367			return B_OK;
368		}
369	}
370
371	return B_BAD_DATA;
372}
373
374
375// #pragma mark - KernelImage
376
377
378KernelImage::KernelImage()
379{
380}
381
382
383KernelImage::~KernelImage()
384{
385	delete[] fSymbolTable;
386	delete[] fStringTable;
387}
388
389
390status_t
391KernelImage::Init(const image_info& info)
392{
393	fInfo = info;
394
395	// get the table sizes
396	fSymbolCount = 0;
397	fStringTableSize = 0;
398	status_t error = _kern_read_kernel_image_symbols(fInfo.id,
399		NULL, &fSymbolCount, NULL, &fStringTableSize, NULL);
400	if (error != B_OK)
401		return error;
402
403	// allocate the tables
404	fSymbolTable = new(std::nothrow) elf_sym[fSymbolCount];
405	fStringTable = new(std::nothrow) char[fStringTableSize];
406	if (fSymbolTable == NULL || fStringTable == NULL)
407		return B_NO_MEMORY;
408
409	// get the info
410	return _kern_read_kernel_image_symbols(fInfo.id,
411		fSymbolTable, &fSymbolCount, fStringTable, &fStringTableSize,
412		&fLoadDelta);
413}
414
415
416CommPageImage::CommPageImage()
417{
418}
419
420
421CommPageImage::~CommPageImage()
422{
423	delete[] fSymbolTable;
424	delete[] fStringTable;
425}
426
427
428status_t
429CommPageImage::Init(const image_info& info)
430{
431	// find kernel image for commpage
432	image_id commPageID = -1;
433	image_info commPageInfo;
434
435	int32 cookie = 0;
436	while (_kern_get_next_image_info(B_SYSTEM_TEAM, &cookie, &commPageInfo,
437			sizeof(image_info)) == B_OK) {
438		if (!strcmp("commpage", commPageInfo.name)) {
439			commPageID = commPageInfo.id;
440			break;
441		}
442	}
443	if (commPageID < 0)
444		return B_ENTRY_NOT_FOUND;
445
446	fInfo = commPageInfo;
447	fInfo.text = info.text;
448
449	// get the table sizes
450	fSymbolCount = 0;
451	fStringTableSize = 0;
452	status_t error = _kern_read_kernel_image_symbols(commPageID, NULL,
453		&fSymbolCount, NULL, &fStringTableSize, NULL);
454	if (error != B_OK)
455		return error;
456
457	// allocate the tables
458	fSymbolTable = new(std::nothrow) elf_sym[fSymbolCount];
459	fStringTable = new(std::nothrow) char[fStringTableSize];
460	if (fSymbolTable == NULL || fStringTable == NULL)
461		return B_NO_MEMORY;
462
463	// get the info
464	error = _kern_read_kernel_image_symbols(commPageID,
465		fSymbolTable, &fSymbolCount, fStringTable, &fStringTableSize, NULL);
466	if (error != B_OK) {
467		delete[] fSymbolTable;
468		delete[] fStringTable;
469		return error;
470	}
471
472	fLoadDelta = (addr_t)info.text;
473
474	return B_OK;
475}
476