xref: /haiku/src/kits/debug/SymbolLookup.cpp (revision 1b80286772b529a3d6de3bbeb0720c62e6a32fed)
1 /*
2  * Copyright 2005-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "SymbolLookup.h"
7 
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 
12 #include <new>
13 
14 #include <runtime_loader.h>
15 #include <syscalls.h>
16 
17 #include "Image.h"
18 
19 
20 #undef TRACE
21 //#define TRACE_DEBUG_SYMBOL_LOOKUP
22 #ifdef TRACE_DEBUG_SYMBOL_LOOKUP
23 #	define TRACE(x) printf x
24 #else
25 #	define TRACE(x) ;
26 #endif
27 
28 
29 using namespace BPrivate::Debug;
30 
31 
32 // PrepareAddress
33 const void *
34 Area::PrepareAddress(const void *address)
35 {
36 	TRACE(("Area::PrepareAddress(%p): area: %ld\n", address, fRemoteID));
37 
38 	// clone the area, if not done already
39 	if (fLocalID < 0) {
40 		fLocalID = clone_area("cloned area", &fLocalAddress, B_ANY_ADDRESS,
41 			B_READ_AREA, fRemoteID);
42 		if (fLocalID < 0) {
43 			TRACE(("Area::PrepareAddress(): Failed to clone area %ld: %s\n",
44 				fRemoteID, strerror(fLocalID)));
45 			throw Exception(fLocalID);
46 		}
47 	}
48 
49 	// translate the address
50 	const void *result = (const void*)((addr_t)address - (addr_t)fRemoteAddress
51 		+ (addr_t)fLocalAddress);
52 
53 	TRACE(("Area::PrepareAddress(%p) done: %p\n", address, result));
54 
55 	return result;
56 }
57 
58 
59 // #pragma mark -
60 
61 // constructor
62 RemoteMemoryAccessor::RemoteMemoryAccessor(team_id team)
63 	: fTeam(team),
64 	  fAreas()
65 {
66 }
67 
68 // destructor
69 RemoteMemoryAccessor::~RemoteMemoryAccessor()
70 {
71 	// delete the areas
72 	while (Area *area = fAreas.Head()) {
73 		fAreas.Remove(area);
74 		delete area;
75 	}
76 }
77 
78 // Init
79 status_t
80 RemoteMemoryAccessor::Init()
81 {
82 	// If the team is the kernel team, we don't try to clone the areas. Only
83 	// SymbolLookup's image file functionality will be available.
84 	if (fTeam == B_SYSTEM_TEAM)
85 		return B_OK;
86 
87 	// get a list of the team's areas
88 	area_info areaInfo;
89 	int32 cookie = 0;
90 	status_t error;
91 	while ((error = get_next_area_info(fTeam, &cookie, &areaInfo)) == B_OK) {
92 		TRACE(("area %ld: address: %p, size: %ld, name: %s\n", areaInfo.area,
93 			areaInfo.address, areaInfo.size, areaInfo.name));
94 
95 		Area *area = new(std::nothrow) Area(areaInfo.area, areaInfo.address,
96 			areaInfo.size);
97 		if (!area)
98 			return B_NO_MEMORY;
99 
100 		fAreas.Add(area);
101 	}
102 
103 	if (fAreas.IsEmpty())
104 		return error;
105 
106 	return B_OK;
107 }
108 
109 // PrepareAddress
110 const void *
111 RemoteMemoryAccessor::PrepareAddress(const void *remoteAddress,
112 	int32 size) const
113 {
114 	TRACE(("RemoteMemoryAccessor::PrepareAddress(%p, %ld)\n", remoteAddress,
115 		size));
116 
117 	if (!remoteAddress) {
118 		TRACE(("RemoteMemoryAccessor::PrepareAddress(): Got null address!\n"));
119 		throw Exception(B_BAD_VALUE);
120 	}
121 
122 	return _FindArea(remoteAddress, size).PrepareAddress(remoteAddress);
123 }
124 
125 
126 const void *
127 RemoteMemoryAccessor::PrepareAddressNoThrow(const void *remoteAddress,
128 	int32 size) const
129 {
130 	if (remoteAddress == NULL)
131 		return NULL;
132 
133 	Area* area = _FindAreaNoThrow(remoteAddress, size);
134 	if (area == NULL)
135 		return NULL;
136 
137 	return area->PrepareAddress(remoteAddress);
138 }
139 
140 
141 // AreaForLocalAddress
142 Area*
143 RemoteMemoryAccessor::AreaForLocalAddress(const void* address) const
144 {
145 	if (address == NULL)
146 		return NULL;
147 
148 	for (AreaList::ConstIterator it = fAreas.GetIterator(); it.HasNext();) {
149 		Area* area = it.Next();
150 		if (area->ContainsLocalAddress(address))
151 			return area;
152 	}
153 
154 	return NULL;
155 }
156 
157 
158 // _FindArea
159 Area &
160 RemoteMemoryAccessor::_FindArea(const void *address, int32 size) const
161 {
162 	TRACE(("RemoteMemoryAccessor::_FindArea(%p, %ld)\n", address, size));
163 
164 	for (AreaList::ConstIterator it = fAreas.GetIterator(); it.HasNext();) {
165 		Area *area = it.Next();
166 		if (area->ContainsAddress(address, size))
167 			return *area;
168 	}
169 
170 	TRACE(("RemoteMemoryAccessor::_FindArea(): No area found for address %p\n",
171 		address));
172 	throw Exception(B_ENTRY_NOT_FOUND);
173 }
174 
175 
176 // _FindAreaNoThrow
177 Area*
178 RemoteMemoryAccessor::_FindAreaNoThrow(const void *address, int32 size) const
179 {
180 	for (AreaList::ConstIterator it = fAreas.GetIterator(); it.HasNext();) {
181 		Area *area = it.Next();
182 		if (area->ContainsAddress(address, size))
183 			return area;
184 	}
185 
186 	return NULL;
187 }
188 
189 
190 // #pragma mark -
191 
192 
193 class SymbolLookup::LoadedImage : public Image {
194 public:
195 								LoadedImage(SymbolLookup* symbolLookup,
196 									const image_t* image, int32 symbolCount);
197 	virtual						~LoadedImage();
198 
199 	virtual	const Elf32_Sym*	LookupSymbol(addr_t address,
200 									addr_t* _baseAddress,
201 									const char** _symbolName,
202 									size_t *_symbolNameLen,
203 									bool *_exactMatch) const;
204 	virtual	status_t			NextSymbol(int32& iterator,
205 									const char** _symbolName,
206 									size_t* _symbolNameLen,
207 									addr_t* _symbolAddress, size_t* _symbolSize,
208 									int32* _symbolType) const;
209 
210 private:
211 			SymbolLookup*			fSymbolLookup;
212 			const image_t*			fImage;
213 			int32					fSymbolCount;
214 			size_t					fTextDelta;
215 };
216 
217 
218 // #pragma mark -
219 
220 
221 // constructor
222 SymbolLookup::SymbolLookup(team_id team)
223 	:
224 	RemoteMemoryAccessor(team),
225 	fDebugArea(NULL),
226 	fImages()
227 {
228 }
229 
230 
231 // destructor
232 SymbolLookup::~SymbolLookup()
233 {
234 	while (Image* image = fImages.RemoveHead())
235 		delete image;
236 }
237 
238 
239 // Init
240 status_t
241 SymbolLookup::Init()
242 {
243 	TRACE(("SymbolLookup::Init()\n"));
244 
245 	status_t error = RemoteMemoryAccessor::Init();
246 	if (error != B_OK)
247 		return error;
248 
249 	if (fTeam != B_SYSTEM_TEAM) {
250 		TRACE(("SymbolLookup::Init(): searching debug area...\n"));
251 
252 		// find the runtime loader debug area
253 		runtime_loader_debug_area *remoteDebugArea = NULL;
254 		int32 cookie = 0;
255 		area_info areaInfo;
256 		while (get_next_area_info(fTeam, &cookie, &areaInfo) == B_OK) {
257 			if (strcmp(areaInfo.name, RUNTIME_LOADER_DEBUG_AREA_NAME) == 0) {
258 				remoteDebugArea = (runtime_loader_debug_area*)areaInfo.address;
259 				break;
260 			}
261 		}
262 
263 		if (remoteDebugArea) {
264 			TRACE(("SymbolLookup::Init(): found debug area, translating "
265 				"address...\n"));
266 		} else {
267 			TRACE(("SymbolLookup::Init(): Couldn't find debug area!\n"));
268 		}
269 
270 		// translate the address
271 		try {
272 			if (remoteDebugArea != NULL) {
273 				fDebugArea = &Read(*remoteDebugArea);
274 
275 				TRACE(("SymbolLookup::Init(): translated debug area is at: %p, "
276 					"loaded_images: %p\n", fDebugArea, fDebugArea->loaded_images));
277 			}
278 		} catch (Exception exception) {
279 			// we can live without the debug area
280 		}
281 	}
282 
283 	// create a list of the team's images
284 	image_info imageInfo;
285 	int32 cookie = 0;
286 	while (get_next_image_info(fTeam, &cookie, &imageInfo) == B_OK) {
287 		Image* image;
288 
289 		if (fTeam == B_SYSTEM_TEAM) {
290 			// kernel image
291 			KernelImage* kernelImage = new(std::nothrow) KernelImage;
292 			if (kernelImage == NULL)
293 				return B_NO_MEMORY;
294 
295 			error = kernelImage->Init(imageInfo);
296 			image = kernelImage;
297 		} else {
298 			// userland image -- try to load an image file
299 			ImageFile* imageFile = new(std::nothrow) ImageFile;
300 			if (imageFile == NULL)
301 				return B_NO_MEMORY;
302 
303 			error = imageFile->Init(imageInfo);
304 			image = imageFile;
305 		}
306 
307 		if (error != B_OK) {
308 			// initialization error -- fall back to the loaded image
309 			delete image;
310 
311 			const image_t* loadedImage = _FindLoadedImageByID(imageInfo.id);
312 			if (loadedImage == NULL)
313 				continue;
314 
315 			image = new(std::nothrow) LoadedImage(this, loadedImage,
316 				Read(loadedImage->symhash[1]));
317 			if (image == NULL)
318 				return B_NO_MEMORY;
319 		}
320 
321 		fImages.Add(image);
322 	}
323 
324 	return B_OK;
325 }
326 
327 
328 // LookupSymbolAddress
329 status_t
330 SymbolLookup::LookupSymbolAddress(addr_t address, addr_t *_baseAddress,
331 	const char **_symbolName, size_t *_symbolNameLen, const char **_imageName,
332 	bool *_exactMatch) const
333 {
334 	TRACE(("SymbolLookup::LookupSymbolAddress(%p)\n", (void*)address));
335 
336 	Image* image = _FindImageAtAddress(address);
337 	if (!image)
338 		return B_ENTRY_NOT_FOUND;
339 
340 	if (_imageName != NULL)
341 		*_imageName = image->Name();
342 
343 	const Elf32_Sym* symbolFound = image->LookupSymbol(address, _baseAddress,
344 		_symbolName, _symbolNameLen, _exactMatch);
345 
346 	TRACE(("SymbolLookup::LookupSymbolAddress(): done: symbol: %p, image name: "
347 		"%s, exact match: %d\n", symbolFound, image->name, exactMatch));
348 
349 	if (symbolFound != NULL)
350 		return B_OK;
351 
352 	// symbol not found -- return the image itself
353 
354 	if (_baseAddress)
355 		*_baseAddress = image->TextAddress();
356 
357 	if (_imageName)
358 		*_imageName = image->Name();
359 
360 	if (_symbolName)
361 		*_symbolName = NULL;
362 
363 	if (_exactMatch)
364 		*_exactMatch = false;
365 
366 	if (_symbolNameLen != NULL)
367 		*_symbolNameLen = 0;
368 
369 	return B_OK;
370 }
371 
372 
373 // InitSymbolIterator
374 status_t
375 SymbolLookup::InitSymbolIterator(image_id imageID,
376 	SymbolIterator& iterator) const
377 {
378 	TRACE(("SymbolLookup::InitSymbolIterator(): image ID: %ld\n", imageID));
379 
380 	// find the image
381 	iterator.image = _FindImageByID(imageID);
382 
383 	// If that didn't work, find the loaded image.
384 	if (iterator.image == NULL) {
385 		TRACE(("SymbolLookup::InitSymbolIterator() done: image not "
386 			"found\n"));
387 		return B_ENTRY_NOT_FOUND;
388 	}
389 
390 	iterator.currentIndex = -1;
391 
392 	return B_OK;
393 }
394 
395 
396 // InitSymbolIterator
397 status_t
398 SymbolLookup::InitSymbolIteratorByAddress(addr_t address,
399 	SymbolIterator& iterator) const
400 {
401 	TRACE(("SymbolLookup::InitSymbolIteratorByAddress(): base address: %#lx\n",
402 		address));
403 
404 	// find the image
405 	iterator.image = _FindImageAtAddress(address);
406 	if (iterator.image == NULL) {
407 		TRACE(("SymbolLookup::InitSymbolIteratorByAddress() done: image "
408 			"not found\n"));
409 		return B_ENTRY_NOT_FOUND;
410 	}
411 
412 	iterator.currentIndex = -1;
413 
414 	return B_OK;
415 }
416 
417 
418 // NextSymbol
419 status_t
420 SymbolLookup::NextSymbol(SymbolIterator& iterator, const char** _symbolName,
421 	size_t* _symbolNameLen, addr_t* _symbolAddress, size_t* _symbolSize,
422 	int32* _symbolType) const
423 {
424 	return iterator.image->NextSymbol(iterator.currentIndex, _symbolName,
425 		_symbolNameLen, _symbolAddress, _symbolSize, _symbolType);
426 }
427 
428 
429 // _FindLoadedImageAtAddress
430 const image_t *
431 SymbolLookup::_FindLoadedImageAtAddress(addr_t address) const
432 {
433 	TRACE(("SymbolLookup::_FindLoadedImageAtAddress(%p)\n", (void*)address));
434 
435 	if (fDebugArea == NULL)
436 		return NULL;
437 
438 	// iterate through the loaded images
439 	for (const image_t *image = &Read(*Read(fDebugArea->loaded_images->head));
440 		 image;
441 		 image = &Read(*image->next)) {
442 		if (image->regions[0].vmstart <= address
443 			&& address < image->regions[0].vmstart + image->regions[0].size) {
444 			return image;
445 		}
446 	}
447 
448 	return NULL;
449 }
450 
451 
452 // _FindLoadedImageByID
453 const image_t*
454 SymbolLookup::_FindLoadedImageByID(image_id id) const
455 {
456 	if (fDebugArea == NULL)
457 		return NULL;
458 
459 	// iterate through the images
460 	for (const image_t *image = &Read(*Read(fDebugArea->loaded_images->head));
461 		 image;
462 		 image = &Read(*image->next)) {
463 		if (image->id == id)
464 			return image;
465 	}
466 
467 	return NULL;
468 }
469 
470 
471 // _FindImageAtAddress
472 Image*
473 SymbolLookup::_FindImageAtAddress(addr_t address) const
474 {
475 	DoublyLinkedList<Image>::ConstIterator it = fImages.GetIterator();
476 	while (Image* image = it.Next()) {
477 		addr_t textAddress = image->TextAddress();
478 		if (address >= textAddress && address < textAddress + image->TextSize())
479 			return image;
480 	}
481 
482 	return NULL;
483 }
484 
485 
486 // _FindImageByID
487 Image*
488 SymbolLookup::_FindImageByID(image_id id) const
489 {
490 	DoublyLinkedList<Image>::ConstIterator it = fImages.GetIterator();
491 	while (Image* image = it.Next()) {
492 		if (image->ID() == id)
493 			return image;
494 	}
495 
496 	return NULL;
497 }
498 
499 
500 // _SymbolNameLen
501 size_t
502 SymbolLookup::_SymbolNameLen(const char* address) const
503 {
504 	Area* area = AreaForLocalAddress(address);
505 	if (area == NULL)
506 		return 0;
507 
508 	return strnlen(address, (addr_t)area->LocalAddress() + area->Size()
509 		- (addr_t)address);
510 }
511 
512 
513 // #pragma mark - LoadedImage
514 
515 
516 SymbolLookup::LoadedImage::LoadedImage(SymbolLookup* symbolLookup,
517 	const image_t* image, int32 symbolCount)
518 	:
519 	fSymbolLookup(symbolLookup),
520 	fImage(image),
521 	fSymbolCount(symbolCount),
522 	fTextDelta(image->regions[0].delta)
523 {
524 	// init info
525 	fInfo.id = fImage->id;
526 	fInfo.type = fImage->type;
527 	fInfo.sequence = 0;
528 	fInfo.init_order = 0;
529 	fInfo.init_routine = (void (*)())fImage->init_routine;
530 	fInfo.term_routine = (void (*)())fImage->term_routine;
531 	fInfo.device = -1;
532 	fInfo.node = -1;
533 	strlcpy(fInfo.name, fImage->path, sizeof(fInfo.name));
534 	fInfo.text = (void*)fImage->regions[0].vmstart;
535 	fInfo.data = (void*)fImage->regions[1].vmstart;
536 	fInfo.text_size = fImage->regions[0].vmsize;
537 	fInfo.data_size = fImage->regions[1].vmsize;
538 }
539 
540 
541 SymbolLookup::LoadedImage::~LoadedImage()
542 {
543 }
544 
545 
546 const Elf32_Sym*
547 SymbolLookup::LoadedImage::LookupSymbol(addr_t address, addr_t* _baseAddress,
548 	const char** _symbolName, size_t *_symbolNameLen, bool *_exactMatch) const
549 {
550 	TRACE(("LoadedImage::LookupSymbol(): found image: ID: %ld, text: "
551 		"address: %p, size: %ld\n", fImage->id,
552 		(void*)fImage->regions[0].vmstart, fImage->regions[0].size));
553 
554 	// search the image for the symbol
555 	const struct Elf32_Sym *symbolFound = NULL;
556 	addr_t deltaFound = INT_MAX;
557 	bool exactMatch = false;
558 	const char *symbolName = NULL;
559 
560 	int32 symbolCount = fSymbolLookup->Read(fImage->symhash[1]);
561 	const elf_region_t *textRegion = fImage->regions;				// local
562 
563 	for (int32 i = 0; i < symbolCount; i++) {
564 		const struct Elf32_Sym *symbol = &fSymbolLookup->Read(fImage->syms[i]);
565 
566 		// The symbol table contains not only symbols referring to functions
567 		// and data symbols within the shared object, but also referenced
568 		// symbols of other shared objects, as well as section and file
569 		// references. We ignore everything but function and data symbols
570 		// that have an st_value != 0 (0 seems to be an indication for a
571 		// symbol defined elsewhere -- couldn't verify that in the specs
572 		// though).
573 		if ((ELF32_ST_TYPE(symbol->st_info) != STT_FUNC
574 				&& ELF32_ST_TYPE(symbol->st_info) != STT_OBJECT)
575 			|| symbol->st_value == 0
576 			|| symbol->st_value + symbol->st_size + textRegion->delta
577 				> textRegion->vmstart + textRegion->size) {
578 			continue;
579 		}
580 
581 		// skip symbols starting after the given address
582 		addr_t symbolAddress = symbol->st_value + textRegion->delta;
583 
584 		if (symbolAddress > address)
585 			continue;
586 		addr_t symbolDelta = address - symbolAddress;
587 
588 		if (!symbolFound || symbolDelta < deltaFound) {
589 			symbolName = (const char*)fSymbolLookup->PrepareAddressNoThrow(
590 				SYMNAME(fImage, symbol), 1);
591 			if (symbolName == NULL)
592 				continue;
593 
594 			deltaFound = symbolDelta;
595 			symbolFound = symbol;
596 
597 			if (symbolDelta >= 0 && symbolDelta < symbol->st_size) {
598 				// exact match
599 				exactMatch = true;
600 				break;
601 			}
602 		}
603 	}
604 
605 	TRACE(("LoadedImage::LookupSymbol(): done: symbol: %p, image name: "
606 		"%s, exact match: %d\n", symbolFound, fImage->name, exactMatch));
607 
608 	if (symbolFound != NULL) {
609 		if (_baseAddress)
610 			*_baseAddress = symbolFound->st_value + textRegion->delta;
611 		if (_symbolName)
612 			*_symbolName = symbolName;
613 		if (_exactMatch)
614 			*_exactMatch = exactMatch;
615 		if (_symbolNameLen != NULL)
616 			*_symbolNameLen = fSymbolLookup->_SymbolNameLen(symbolName);
617 	}
618 
619 	return symbolFound;
620 }
621 
622 
623 status_t
624 SymbolLookup::LoadedImage::NextSymbol(int32& iterator, const char** _symbolName,
625 	size_t* _symbolNameLen, addr_t* _symbolAddress, size_t* _symbolSize,
626 	int32* _symbolType) const
627 {
628 	while (true) {
629 		if (++iterator >= fSymbolCount)
630 			return B_ENTRY_NOT_FOUND;
631 
632 		const struct Elf32_Sym* symbol
633 			= &fSymbolLookup->Read(fImage->syms[iterator]);
634 		if ((ELF32_ST_TYPE(symbol->st_info) != STT_FUNC
635 				&& ELF32_ST_TYPE(symbol->st_info) != STT_OBJECT)
636 			|| symbol->st_value == 0) {
637 			continue;
638 		}
639 
640 		*_symbolName = (const char*)fSymbolLookup->PrepareAddressNoThrow(
641 			SYMNAME(fImage, symbol), 1);
642 		*_symbolNameLen = fSymbolLookup->_SymbolNameLen(*_symbolName);
643 		*_symbolAddress = symbol->st_value + fTextDelta;
644 		*_symbolSize = symbol->st_size;
645 		*_symbolType = ELF32_ST_TYPE(symbol->st_info) == STT_FUNC
646 			? B_SYMBOL_TYPE_TEXT : B_SYMBOL_TYPE_DATA;
647 
648 		return B_OK;
649 	}
650 }
651