xref: /haiku/src/kits/debug/SymbolLookup.cpp (revision 776c58b2b56d8bcf33638a2ecb6c697f95a1cbf3)
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: %" B_PRId32 "\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 %" B_PRId32
44 				": %s\n", 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 	ssize_t cookie = 0;
90 	status_t error;
91 	while ((error = get_next_area_info(fTeam, &cookie, &areaInfo)) == B_OK) {
92 		TRACE(("area %" B_PRId32 ": address: %p, size: %ld, name: %s\n",
93 			areaInfo.area, 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, %" B_PRId32 ")\n",
115 		remoteAddress, 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, %" B_PRId32 ")\n", address,
163 		size));
164 
165 	for (AreaList::ConstIterator it = fAreas.GetIterator(); it.HasNext();) {
166 		Area *area = it.Next();
167 		if (area->ContainsAddress(address, size))
168 			return *area;
169 	}
170 
171 	TRACE(("RemoteMemoryAccessor::_FindArea(): No area found for address %p\n",
172 		address));
173 	throw Exception(B_ENTRY_NOT_FOUND);
174 }
175 
176 
177 // _FindAreaNoThrow
178 Area*
179 RemoteMemoryAccessor::_FindAreaNoThrow(const void *address, int32 size) const
180 {
181 	for (AreaList::ConstIterator it = fAreas.GetIterator(); it.HasNext();) {
182 		Area *area = it.Next();
183 		if (area->ContainsAddress(address, size))
184 			return area;
185 	}
186 
187 	return NULL;
188 }
189 
190 
191 // #pragma mark -
192 
193 
194 class SymbolLookup::LoadedImage : public Image {
195 public:
196 								LoadedImage(SymbolLookup* symbolLookup,
197 									const image_t* image, int32 symbolCount);
198 	virtual						~LoadedImage();
199 
200 	virtual	const elf_sym*		LookupSymbol(addr_t address,
201 									addr_t* _baseAddress,
202 									const char** _symbolName,
203 									size_t *_symbolNameLen,
204 									bool *_exactMatch) const;
205 	virtual	status_t			NextSymbol(int32& iterator,
206 									const char** _symbolName,
207 									size_t* _symbolNameLen,
208 									addr_t* _symbolAddress, size_t* _symbolSize,
209 									int32* _symbolType) const;
210 
211 private:
212 			SymbolLookup*			fSymbolLookup;
213 			const image_t*			fImage;
214 			int32					fSymbolCount;
215 			size_t					fTextDelta;
216 };
217 
218 
219 // #pragma mark -
220 
221 
222 // constructor
223 SymbolLookup::SymbolLookup(team_id team)
224 	:
225 	RemoteMemoryAccessor(team),
226 	fDebugArea(NULL),
227 	fImages()
228 {
229 }
230 
231 
232 // destructor
233 SymbolLookup::~SymbolLookup()
234 {
235 	while (Image* image = fImages.RemoveHead())
236 		delete image;
237 }
238 
239 
240 // Init
241 status_t
242 SymbolLookup::Init()
243 {
244 	TRACE(("SymbolLookup::Init()\n"));
245 
246 	status_t error = RemoteMemoryAccessor::Init();
247 	if (error != B_OK)
248 		return error;
249 
250 	if (fTeam != B_SYSTEM_TEAM) {
251 		TRACE(("SymbolLookup::Init(): searching debug area...\n"));
252 
253 		// find the runtime loader debug area
254 		runtime_loader_debug_area *remoteDebugArea = NULL;
255 		ssize_t cookie = 0;
256 		area_info areaInfo;
257 		while (get_next_area_info(fTeam, &cookie, &areaInfo) == B_OK) {
258 			if (strcmp(areaInfo.name, RUNTIME_LOADER_DEBUG_AREA_NAME) == 0) {
259 				remoteDebugArea = (runtime_loader_debug_area*)areaInfo.address;
260 				break;
261 			}
262 		}
263 
264 		if (remoteDebugArea) {
265 			TRACE(("SymbolLookup::Init(): found debug area, translating "
266 				"address...\n"));
267 		} else {
268 			TRACE(("SymbolLookup::Init(): Couldn't find debug area!\n"));
269 		}
270 
271 		// translate the address
272 		try {
273 			if (remoteDebugArea != NULL) {
274 				fDebugArea = &Read(*remoteDebugArea);
275 
276 				TRACE(("SymbolLookup::Init(): translated debug area is at: %p, "
277 					"loaded_images: %p\n", fDebugArea, fDebugArea->loaded_images));
278 			}
279 		} catch (Exception exception) {
280 			// we can live without the debug area
281 		}
282 	}
283 
284 	// create a list of the team's images
285 	image_info imageInfo;
286 	int32 cookie = 0;
287 	while (get_next_image_info(fTeam, &cookie, &imageInfo) == B_OK) {
288 		Image* image;
289 
290 		if (fTeam == B_SYSTEM_TEAM) {
291 			// kernel image
292 			KernelImage* kernelImage = new(std::nothrow) KernelImage;
293 			if (kernelImage == NULL)
294 				return B_NO_MEMORY;
295 
296 			error = kernelImage->Init(imageInfo);
297 			image = kernelImage;
298 		} else {
299 			// userland image -- try to load an image file
300 			ImageFile* imageFile = new(std::nothrow) ImageFile;
301 			if (imageFile == NULL)
302 				return B_NO_MEMORY;
303 
304 			error = imageFile->Init(imageInfo);
305 			image = imageFile;
306 		}
307 
308 		if (error != B_OK) {
309 			// initialization error -- fall back to the loaded image
310 			delete image;
311 
312 			const image_t* loadedImage = _FindLoadedImageByID(imageInfo.id);
313 			if (loadedImage == NULL)
314 				continue;
315 
316 			image = new(std::nothrow) LoadedImage(this, loadedImage,
317 				Read(loadedImage->symhash[1]));
318 			if (image == NULL)
319 				return B_NO_MEMORY;
320 		}
321 
322 		fImages.Add(image);
323 	}
324 
325 	return B_OK;
326 }
327 
328 
329 // LookupSymbolAddress
330 status_t
331 SymbolLookup::LookupSymbolAddress(addr_t address, addr_t *_baseAddress,
332 	const char **_symbolName, size_t *_symbolNameLen, const char **_imageName,
333 	bool *_exactMatch) const
334 {
335 	TRACE(("SymbolLookup::LookupSymbolAddress(%p)\n", (void*)address));
336 
337 	Image* image = _FindImageAtAddress(address);
338 	if (!image)
339 		return B_ENTRY_NOT_FOUND;
340 
341 	if (_imageName != NULL)
342 		*_imageName = image->Name();
343 
344 	const elf_sym* symbolFound = image->LookupSymbol(address, _baseAddress,
345 		_symbolName, _symbolNameLen, _exactMatch);
346 
347 	TRACE(("SymbolLookup::LookupSymbolAddress(): done: symbol: %p, image name: "
348 		"%s, exact match: %d\n", symbolFound, image->name, exactMatch));
349 
350 	if (symbolFound != NULL)
351 		return B_OK;
352 
353 	// symbol not found -- return the image itself
354 
355 	if (_baseAddress)
356 		*_baseAddress = image->TextAddress();
357 
358 	if (_imageName)
359 		*_imageName = image->Name();
360 
361 	if (_symbolName)
362 		*_symbolName = NULL;
363 
364 	if (_exactMatch)
365 		*_exactMatch = false;
366 
367 	if (_symbolNameLen != NULL)
368 		*_symbolNameLen = 0;
369 
370 	return B_OK;
371 }
372 
373 
374 // InitSymbolIterator
375 status_t
376 SymbolLookup::InitSymbolIterator(image_id imageID,
377 	SymbolIterator& iterator) const
378 {
379 	TRACE(("SymbolLookup::InitSymbolIterator(): image ID: %" B_PRId32 "\n",
380 		imageID));
381 
382 	// find the image
383 	iterator.image = _FindImageByID(imageID);
384 
385 	// If that didn't work, find the loaded image.
386 	if (iterator.image == NULL) {
387 		TRACE(("SymbolLookup::InitSymbolIterator() done: image not "
388 			"found\n"));
389 		return B_ENTRY_NOT_FOUND;
390 	}
391 
392 	iterator.currentIndex = -1;
393 
394 	return B_OK;
395 }
396 
397 
398 // InitSymbolIterator
399 status_t
400 SymbolLookup::InitSymbolIteratorByAddress(addr_t address,
401 	SymbolIterator& iterator) const
402 {
403 	TRACE(("SymbolLookup::InitSymbolIteratorByAddress(): base address: %#lx\n",
404 		address));
405 
406 	// find the image
407 	iterator.image = _FindImageAtAddress(address);
408 	if (iterator.image == NULL) {
409 		TRACE(("SymbolLookup::InitSymbolIteratorByAddress() done: image "
410 			"not found\n"));
411 		return B_ENTRY_NOT_FOUND;
412 	}
413 
414 	iterator.currentIndex = -1;
415 
416 	return B_OK;
417 }
418 
419 
420 // NextSymbol
421 status_t
422 SymbolLookup::NextSymbol(SymbolIterator& iterator, const char** _symbolName,
423 	size_t* _symbolNameLen, addr_t* _symbolAddress, size_t* _symbolSize,
424 	int32* _symbolType) const
425 {
426 	return iterator.image->NextSymbol(iterator.currentIndex, _symbolName,
427 		_symbolNameLen, _symbolAddress, _symbolSize, _symbolType);
428 }
429 
430 
431 // GetSymbol
432 status_t
433 SymbolLookup::GetSymbol(image_id imageID, const char* name, int32 symbolType,
434 	void** _symbolLocation, size_t* _symbolSize, int32* _symbolType) const
435 {
436 	Image* image = _FindImageByID(imageID);
437 	if (image == NULL)
438 		return B_ENTRY_NOT_FOUND;
439 
440 	return image->GetSymbol(name, symbolType, _symbolLocation, _symbolSize,
441 		_symbolType);
442 }
443 
444 
445 // _FindLoadedImageAtAddress
446 const image_t *
447 SymbolLookup::_FindLoadedImageAtAddress(addr_t address) const
448 {
449 	TRACE(("SymbolLookup::_FindLoadedImageAtAddress(%p)\n", (void*)address));
450 
451 	if (fDebugArea == NULL)
452 		return NULL;
453 
454 	// iterate through the loaded images
455 	for (const image_t *image = &Read(*Read(fDebugArea->loaded_images->head));
456 		 image;
457 		 image = &Read(*image->next)) {
458 		if (image->regions[0].vmstart <= address
459 			&& address < image->regions[0].vmstart + image->regions[0].size) {
460 			return image;
461 		}
462 	}
463 
464 	return NULL;
465 }
466 
467 
468 // _FindLoadedImageByID
469 const image_t*
470 SymbolLookup::_FindLoadedImageByID(image_id id) const
471 {
472 	if (fDebugArea == NULL)
473 		return NULL;
474 
475 	// iterate through the images
476 	for (const image_t *image = &Read(*Read(fDebugArea->loaded_images->head));
477 		 image;
478 		 image = &Read(*image->next)) {
479 		if (image->id == id)
480 			return image;
481 	}
482 
483 	return NULL;
484 }
485 
486 
487 // _FindImageAtAddress
488 Image*
489 SymbolLookup::_FindImageAtAddress(addr_t address) const
490 {
491 	DoublyLinkedList<Image>::ConstIterator it = fImages.GetIterator();
492 	while (Image* image = it.Next()) {
493 		addr_t textAddress = image->TextAddress();
494 		if (address >= textAddress && address < textAddress + image->TextSize())
495 			return image;
496 	}
497 
498 	return NULL;
499 }
500 
501 
502 // _FindImageByID
503 Image*
504 SymbolLookup::_FindImageByID(image_id id) const
505 {
506 	DoublyLinkedList<Image>::ConstIterator it = fImages.GetIterator();
507 	while (Image* image = it.Next()) {
508 		if (image->ID() == id)
509 			return image;
510 	}
511 
512 	return NULL;
513 }
514 
515 
516 // _SymbolNameLen
517 size_t
518 SymbolLookup::_SymbolNameLen(const char* address) const
519 {
520 	Area* area = AreaForLocalAddress(address);
521 	if (area == NULL)
522 		return 0;
523 
524 	return strnlen(address, (addr_t)area->LocalAddress() + area->Size()
525 		- (addr_t)address);
526 }
527 
528 
529 // #pragma mark - LoadedImage
530 
531 
532 SymbolLookup::LoadedImage::LoadedImage(SymbolLookup* symbolLookup,
533 	const image_t* image, int32 symbolCount)
534 	:
535 	fSymbolLookup(symbolLookup),
536 	fImage(image),
537 	fSymbolCount(symbolCount),
538 	fTextDelta(image->regions[0].delta)
539 {
540 	// init info
541 	fInfo.id = fImage->id;
542 	fInfo.type = fImage->type;
543 	fInfo.sequence = 0;
544 	fInfo.init_order = 0;
545 	fInfo.init_routine = (void (*)())fImage->init_routine;
546 	fInfo.term_routine = (void (*)())fImage->term_routine;
547 	fInfo.device = -1;
548 	fInfo.node = -1;
549 	strlcpy(fInfo.name, fImage->path, sizeof(fInfo.name));
550 	fInfo.text = (void*)fImage->regions[0].vmstart;
551 	fInfo.data = (void*)fImage->regions[1].vmstart;
552 	fInfo.text_size = fImage->regions[0].vmsize;
553 	fInfo.data_size = fImage->regions[1].vmsize;
554 }
555 
556 
557 SymbolLookup::LoadedImage::~LoadedImage()
558 {
559 }
560 
561 
562 const elf_sym*
563 SymbolLookup::LoadedImage::LookupSymbol(addr_t address, addr_t* _baseAddress,
564 	const char** _symbolName, size_t *_symbolNameLen, bool *_exactMatch) const
565 {
566 	TRACE(("LoadedImage::LookupSymbol(): found image: ID: %" B_PRId32 ", text: "
567 		"address: %p, size: %ld\n", fImage->id,
568 		(void*)fImage->regions[0].vmstart, fImage->regions[0].size));
569 
570 	// search the image for the symbol
571 	const elf_sym *symbolFound = NULL;
572 	addr_t deltaFound = INT_MAX;
573 	bool exactMatch = false;
574 	const char *symbolName = NULL;
575 
576 	int32 symbolCount = fSymbolLookup->Read(fImage->symhash[1]);
577 	const elf_region_t *textRegion = fImage->regions;				// local
578 
579 	for (int32 i = 0; i < symbolCount; i++) {
580 		const elf_sym *symbol = &fSymbolLookup->Read(fImage->syms[i]);
581 
582 		// The symbol table contains not only symbols referring to functions
583 		// and data symbols within the shared object, but also referenced
584 		// symbols of other shared objects, as well as section and file
585 		// references. We ignore everything but function and data symbols
586 		// that have an st_value != 0 (0 seems to be an indication for a
587 		// symbol defined elsewhere -- couldn't verify that in the specs
588 		// though).
589 		if ((symbol->Type() != STT_FUNC && symbol->Type() != STT_OBJECT)
590 			|| symbol->st_value == 0
591 			|| symbol->st_value + symbol->st_size + textRegion->delta
592 				> textRegion->vmstart + textRegion->size) {
593 			continue;
594 		}
595 
596 		// skip symbols starting after the given address
597 		addr_t symbolAddress = symbol->st_value + textRegion->delta;
598 
599 		if (symbolAddress > address)
600 			continue;
601 		addr_t symbolDelta = address - symbolAddress;
602 
603 		if (!symbolFound || symbolDelta < deltaFound) {
604 			symbolName = (const char*)fSymbolLookup->PrepareAddressNoThrow(
605 				SYMNAME(fImage, symbol), 1);
606 			if (symbolName == NULL)
607 				continue;
608 
609 			deltaFound = symbolDelta;
610 			symbolFound = symbol;
611 
612 			if (symbolDelta >= 0 && symbolDelta < symbol->st_size) {
613 				// exact match
614 				exactMatch = true;
615 				break;
616 			}
617 		}
618 	}
619 
620 	TRACE(("LoadedImage::LookupSymbol(): done: symbol: %p, image name: "
621 		"%s, exact match: %d\n", symbolFound, fImage->name, exactMatch));
622 
623 	if (symbolFound != NULL) {
624 		if (_baseAddress)
625 			*_baseAddress = symbolFound->st_value + textRegion->delta;
626 		if (_symbolName)
627 			*_symbolName = symbolName;
628 		if (_exactMatch)
629 			*_exactMatch = exactMatch;
630 		if (_symbolNameLen != NULL)
631 			*_symbolNameLen = fSymbolLookup->_SymbolNameLen(symbolName);
632 	}
633 
634 	return symbolFound;
635 }
636 
637 
638 status_t
639 SymbolLookup::LoadedImage::NextSymbol(int32& iterator, const char** _symbolName,
640 	size_t* _symbolNameLen, addr_t* _symbolAddress, size_t* _symbolSize,
641 	int32* _symbolType) const
642 {
643 	while (true) {
644 		if (++iterator >= fSymbolCount)
645 			return B_ENTRY_NOT_FOUND;
646 
647 		const elf_sym* symbol
648 			= &fSymbolLookup->Read(fImage->syms[iterator]);
649 		if ((symbol->Type() != STT_FUNC && symbol->Type() != STT_OBJECT)
650 			|| symbol->st_value == 0) {
651 			continue;
652 		}
653 
654 		*_symbolName = (const char*)fSymbolLookup->PrepareAddressNoThrow(
655 			SYMNAME(fImage, symbol), 1);
656 		*_symbolNameLen = fSymbolLookup->_SymbolNameLen(*_symbolName);
657 		*_symbolAddress = symbol->st_value + fTextDelta;
658 		*_symbolSize = symbol->st_size;
659 		*_symbolType = symbol->Type() == STT_FUNC ? B_SYMBOL_TYPE_TEXT
660 			: B_SYMBOL_TYPE_DATA;
661 
662 		return B_OK;
663 	}
664 }
665