xref: /haiku/src/kits/debug/Image.cpp (revision 8efb6db7b9a20a97998f1c6fdf412f783aee7bc4)
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 
21 using namespace BPrivate::Debug;
22 
23 
24 // #pragma mark - Image
25 
26 
Image()27 Image::Image()
28 {
29 }
30 
31 
~Image()32 Image::~Image()
33 {
34 }
35 
36 
37 status_t
GetSymbol(const char * name,int32 symbolType,void ** _symbolLocation,size_t * _symbolSize,int32 * _symbolType) const38 Image::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 
SymbolTableBasedImage()69 SymbolTableBasedImage::SymbolTableBasedImage()
70 	:
71 	fLoadDelta(0),
72 	fSymbolTable(NULL),
73 	fStringTable(NULL),
74 	fSymbolCount(0),
75 	fStringTableSize(0)
76 {
77 }
78 
79 
~SymbolTableBasedImage()80 SymbolTableBasedImage::~SymbolTableBasedImage()
81 {
82 }
83 
84 
85 const elf_sym*
LookupSymbol(addr_t address,addr_t * _baseAddress,const char ** _symbolName,size_t * _symbolNameLen,bool * _exactMatch) const86 SymbolTableBasedImage::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 
135 status_t
NextSymbol(int32 & iterator,const char ** _symbolName,size_t * _symbolNameLen,addr_t * _symbolAddress,size_t * _symbolSize,int32 * _symbolType) const136 SymbolTableBasedImage::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 
163 size_t
_SymbolNameLen(const char * symbolName) const164 SymbolTableBasedImage::_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 
ImageFile()179 ImageFile::ImageFile()
180 	:
181 	fFD(-1),
182 	fFileSize(0),
183 	fMappedFile((uint8*)MAP_FAILED)
184 {
185 }
186 
187 
~ImageFile()188 ImageFile::~ImageFile()
189 {
190 	if (fMappedFile != MAP_FAILED)
191 		munmap(fMappedFile, fFileSize);
192 
193 	if (fFD >= 0)
194 		close(fFD);
195 }
196 
197 
198 status_t
Init(const image_info & info)199 ImageFile::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 
221 status_t
Init(const char * path)222 ImageFile::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 
256 status_t
_LoadFile(const char * path,addr_t * _textAddress,size_t * _textSize,addr_t * _dataAddress,size_t * _dataSize)257 ImageFile::_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 
336 status_t
_FindTableInSection(elf_ehdr * elfHeader,uint16 sectionType)337 ImageFile::_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 
KernelImage()378 KernelImage::KernelImage()
379 {
380 }
381 
382 
~KernelImage()383 KernelImage::~KernelImage()
384 {
385 	delete[] fSymbolTable;
386 	delete[] fStringTable;
387 }
388 
389 
390 status_t
Init(const image_info & info)391 KernelImage::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 
CommPageImage()416 CommPageImage::CommPageImage()
417 {
418 }
419 
420 
~CommPageImage()421 CommPageImage::~CommPageImage()
422 {
423 	delete[] fSymbolTable;
424 	delete[] fStringTable;
425 }
426 
427 
428 status_t
Init(const image_info & info)429 CommPageImage::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