xref: /haiku/src/kits/debug/SymbolLookup.cpp (revision 6dcd0ccf238263a3e5eb2e2a44e2ed0da1617a42)
1 /*
2  * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "SymbolLookup.h"
7 
8 #include <new>
9 
10 #include <string.h>
11 #include <runtime_loader.h>
12 
13 
14 using std::nothrow;
15 using namespace BPrivate;
16 
17 // PrepareAddress
18 const void *
19 Area::PrepareAddress(const void *address)
20 {
21 	TRACE(("Area::PrepareAddress(%p): area: %ld\n", address, fRemoteID));
22 
23 	// clone the area, if not done already
24 	if (fLocalID < 0) {
25 		fLocalID = clone_area("cloned area", &fLocalAddress, B_ANY_ADDRESS,
26 			B_READ_AREA, fRemoteID);
27 		if (fLocalID < 0) {
28 			TRACE(("Area::PrepareAddress(): Failed to clone area %ld: %s\n",
29 				fRemoteID, strerror(fLocalID)));
30 			throw Exception(fLocalID);
31 		}
32 	}
33 
34 	// translate the address
35 	const void *result = (const void*)((addr_t)address - (addr_t)fRemoteAddress
36 		+ (addr_t)fLocalAddress);
37 
38 	TRACE(("Area::PrepareAddress(%p) done: %p\n", address, result));
39 
40 	return result;
41 }
42 
43 
44 // #pragma mark -
45 
46 // constructor
47 RemoteMemoryAccessor::RemoteMemoryAccessor(team_id team)
48 	: fTeam(team),
49 	  fAreas()
50 {
51 }
52 
53 // destructor
54 RemoteMemoryAccessor::~RemoteMemoryAccessor()
55 {
56 	// delete the areas
57 	while (Area *area = fAreas.Head()) {
58 		fAreas.Remove(area);
59 		delete area;
60 	}
61 }
62 
63 // Init
64 status_t
65 RemoteMemoryAccessor::Init()
66 {
67 	// get a list of the team's areas
68 	area_info areaInfo;
69 	int32 cookie = 0;
70 	status_t error;
71 	while ((error = get_next_area_info(fTeam, &cookie, &areaInfo)) == B_OK) {
72 
73 		TRACE(("area %ld: address: %p, size: %ld, name: %s\n", areaInfo.area,
74 			areaInfo.address, areaInfo.size, areaInfo.name));
75 
76 		Area *area = new(nothrow) Area(areaInfo.area, areaInfo.address,
77 			areaInfo.size);
78 		if (!area)
79 			return B_NO_MEMORY;
80 
81 		fAreas.Add(area);
82 	}
83 
84 	if (fAreas.IsEmpty())
85 		return error;
86 
87 	return B_OK;
88 }
89 
90 // PrepareAddress
91 const void *
92 RemoteMemoryAccessor::PrepareAddress(const void *remoteAddress, int32 size)
93 {
94 	TRACE(("RemoteMemoryAccessor::PrepareAddress(%p, %ld)\n", remoteAddress,
95 		size));
96 
97 	if (!remoteAddress) {
98 		TRACE(("RemoteMemoryAccessor::PrepareAddress(): Got null address!\n"));
99 		throw Exception(B_BAD_VALUE);
100 	}
101 
102 	return _FindArea(remoteAddress, size).PrepareAddress(remoteAddress);
103 }
104 
105 // _FindArea
106 Area &
107 RemoteMemoryAccessor::_FindArea(const void *address, int32 size)
108 {
109 	TRACE(("RemoteMemoryAccessor::_FindArea(%p, %ld)\n", address, size));
110 
111 	for (AreaList::Iterator it = fAreas.GetIterator(); it.HasNext();) {
112 		Area *area = it.Next();
113 		if (area->ContainsAddress(address, size))
114 			return *area;
115 	}
116 
117 	TRACE(("RemoteMemoryAccessor::_FindArea(): No area found for address %p\n",
118 		address));
119 	throw Exception(B_ENTRY_NOT_FOUND);
120 }
121 
122 
123 // #pragma mark -
124 
125 // constructor
126 SymbolLookup::SymbolLookup(team_id team)
127 	: RemoteMemoryAccessor(team),
128 	  fDebugArea(NULL)
129 {
130 }
131 
132 // destructor
133 SymbolLookup::~SymbolLookup()
134 {
135 }
136 
137 // Init
138 status_t
139 SymbolLookup::Init()
140 {
141 	TRACE(("SymbolLookup::Init()\n"));
142 
143 	status_t error = RemoteMemoryAccessor::Init();
144 	if (error != B_OK)
145 		return error;
146 
147 	TRACE(("SymbolLookup::Init(): searching debug area...\n"));
148 
149 	// find the runtime loader debug area
150 	runtime_loader_debug_area *remoteDebugArea = NULL;
151 	int32 cookie = 0;
152 	area_info areaInfo;
153 	while (get_next_area_info(fTeam, &cookie, &areaInfo) == B_OK) {
154 		if (strcmp(areaInfo.name, RUNTIME_LOADER_DEBUG_AREA_NAME) == 0) {
155 			remoteDebugArea = (runtime_loader_debug_area*)areaInfo.address;
156 			break;
157 		}
158 	}
159 
160 	if (!remoteDebugArea) {
161 		TRACE(("SymbolLookup::Init(): Couldn't find debug area!\n"));
162 		return B_ERROR;
163 	}
164 
165 	TRACE(("SymbolLookup::Init(): found debug area, translating address...\n"));
166 
167 	// translate the address
168 	try {
169 		fDebugArea = &Read(*remoteDebugArea);
170 
171 		TRACE(("SymbolLookup::Init(): translated debug area is at: %p, "
172 			"loaded_images: %p\n", fDebugArea, fDebugArea->loaded_images));
173 
174 	} catch (Exception exception) {
175 		return exception.Error();
176 	}
177 
178 	return B_OK;
179 }
180 
181 // LookupSymbolAddress
182 status_t
183 SymbolLookup::LookupSymbolAddress(addr_t address, addr_t *_baseAddress,
184 	const char **_symbolName, const char **_imageName, bool *_exactMatch)
185 {
186 	// Note, that this function doesn't find all symbols that we would like
187 	// to find. E.g. static functions do not appear in the symbol table
188 	// as function symbols, but as sections without name and size. The .symtab
189 	// section together with the .strtab section, which apparently differ from
190 	// the tables referred to by the .dynamic section, also contain proper names
191 	// and sizes for those symbols. Therefore, to get completely satisfying
192 	// results, we would need to read those tables from the shared object.
193 
194 	TRACE(("SymbolLookup::LookupSymbolAddress(%p)\n", (void*)address));
195 
196 	// get the image for the address
197 	const image_t *image = _FindImageAtAddress(address);
198 	if (!image)
199 		return B_ENTRY_NOT_FOUND;
200 
201 	TRACE(("SymbolLookup::LookupSymbolAddress(): found image: ID: %ld, text: "
202 		"address: %p, size: %ld\n",
203 		image->id, (void*)image->regions[0].vmstart, image->regions[0].size));
204 
205 	// search the image for the symbol
206 	const struct Elf32_Sym *symbolFound = NULL;
207 	addr_t deltaFound = INT_MAX;
208 	bool exactMatch = false;
209 	const char *symbolName = NULL;	// remote
210 
211 	int32 hashTabSize = Read(image->symhash[0]);
212 	const uint32 *hashBuckets = image->symhash + 2;					// remote
213 	const uint32 *hashChains = image->symhash + 2 + hashTabSize;	// remote
214 	const elf_region_t *textRegion = image->regions;				// local
215 
216 	for (int32 i = 0; i < hashTabSize; i++) {
217 		for (int32 j = Read(hashBuckets[i]);
218 			 j != STN_UNDEF;
219 			 j = Read(hashChains[j])) {
220 
221 			const struct Elf32_Sym *symbol = &Read(image->syms[j]);
222 
223 			// The symbol table contains not only symbols referring to functions
224 			// and data symbols within the shared object, but also referenced
225 			// symbols of other shared objects, as well as section and file
226 			// references. We ignore everything but function and data symbols
227 			// that have an st_value != 0 (0 seems to be an indication for a
228 			// symbol defined elsewhere -- couldn't verify that in the specs
229 			// though).
230 			if ((ELF32_ST_TYPE(symbol->st_info) != STT_FUNC
231 					&& ELF32_ST_TYPE(symbol->st_info) != STT_OBJECT)
232 				|| symbol->st_value == 0
233 				|| symbol->st_value + symbol->st_size + textRegion->delta
234 					> textRegion->vmstart + textRegion->size) {
235 				continue;
236 			}
237 
238 			// skip symbols starting after the given address
239 			addr_t symbolAddress = symbol->st_value + textRegion->delta;
240 
241 			if (symbolAddress > address)
242 				continue;
243 			addr_t symbolDelta = address - symbolAddress;
244 
245 			if (!symbolFound || symbolDelta < deltaFound) {
246 				deltaFound = symbolDelta;
247 				symbolFound = symbol;
248 				symbolName = SYMNAME(image, symbol);
249 
250 				if (symbolDelta >= 0 && symbolDelta < symbol->st_size) {
251 					// exact match
252 					exactMatch = true;
253 					break;
254 				}
255 			}
256 		}
257 	}
258 
259 	TRACE(("SymbolLookup::LookupSymbolAddress(): done: symbol: %p, image name: "
260 		"%s, exact match: %d\n", symbolFound, image->name, exactMatch));
261 
262 	if (_baseAddress) {
263 		if (symbolFound)
264 			*_baseAddress = symbolFound->st_value + textRegion->delta;
265 		else
266 			*_baseAddress = textRegion->vmstart;
267 	}
268 
269 	if (_imageName)
270 		*_imageName = image->name;
271 
272 	if (_symbolName)
273 		*_symbolName = symbolName;	// remote address
274 
275 	if (_exactMatch)
276 		*_exactMatch = exactMatch;
277 
278 	return B_OK;
279 }
280 
281 // _FindImageAtAddress
282 const image_t *
283 SymbolLookup::_FindImageAtAddress(addr_t address)
284 {
285 	TRACE(("SymbolLookup::_FindImageAtAddress(%p)\n", (void*)address));
286 
287 	// iterate through the images
288 	for (const image_t *image = &Read(*Read(fDebugArea->loaded_images->head));
289 		 image;
290 		 image = &Read(*image->next)) {
291 		if (image->regions[0].vmstart <= address
292 			&& address < image->regions[0].vmstart + image->regions[0].size) {
293 			return image;
294 		}
295 	}
296 
297 	return NULL;
298 }
299 
300