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