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