xref: /haiku/src/add-ons/kernel/debugger/demangle/gcc2.cpp (revision 9642f7705b27e5c270c15fa526d14e1848c2c27d)
1 /*
2  * Copyright 2008-2009, Axel Dörfler, axeld@pinc-software.de.
3  * This file may be used under the terms of the MIT License.
4  */
5 
6 
7 #include <ctype.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include <TypeConstants.h>
12 
13 #ifdef _KERNEL_MODE
14 #	include <debug.h>
15 #endif
16 
17 #include "demangle.h"
18 
19 
20 //#define TRACE_GCC2_DEMANGLER
21 #ifdef TRACE_GCC2_DEMANGLER
22 #	define TRACE(x...) kprintf(x)
23 #else
24 #	define TRACE(x...) ;
25 #endif
26 
27 
28 enum {
29 	TYPE_FUNCTION,
30 	TYPE_METHOD,
31 };
32 
33 
34 static void
35 ignore_qualifiers(const char** _arg)
36 {
37 	while (isupper(**_arg)) {
38 		if (**_arg == 'Q') {
39 			// argument has namespaces
40 			break;
41 		}
42 		if (**_arg == 'F') {
43 			// skip function declaration
44 			while (**_arg && **_arg != '_')
45 				(*_arg)++;
46 
47 			if (**_arg == 0)
48 				break;
49 		}
50 
51 		(*_arg)++;
52 	}
53 }
54 
55 
56 static uint32
57 argument_type(const char* arg, size_t& length)
58 {
59 	length = sizeof(int);
60 
61 	switch (char type = arg[0]) {
62 		case 'P':	// pointer
63 		case 'R':	// reference
64 			length = sizeof(void*);
65 			ignore_qualifiers(&arg);
66 			if (arg[0] == 'c')
67 				return B_STRING_TYPE;
68 			if (arg[0] == 't') {
69 				// TODO: templates are not yet supported
70 				return 0;
71 			}
72 
73 			return type == 'P' ? B_POINTER_TYPE : B_REF_TYPE;
74 		case 'x':
75 			length = sizeof(long long);
76 			return B_INT64_TYPE;
77 		case 'l':
78 			if (sizeof(long) == 4)
79 				return B_INT32_TYPE;
80 			return B_INT64_TYPE;
81 		case 'i':
82 			return B_INT32_TYPE;
83 		case 's':
84 			return B_INT16_TYPE;
85 		case 'c':
86 			return B_INT8_TYPE;
87 		case 'b':
88 			return B_BOOL_TYPE;
89 		case 'U':
90 			switch (arg[1]) {
91 				case 'x':
92 					length = sizeof(long long);
93 					return B_UINT64_TYPE;
94 				case 'l':
95 					if (sizeof(long) == 4)
96 						return B_UINT32_TYPE;
97 					return B_UINT64_TYPE;
98 				case 'i':
99 					return B_UINT32_TYPE;
100 				case 's':
101 					return B_UINT16_TYPE;
102 				case 'c':
103 					return B_UINT8_TYPE;
104 				default:
105 					return B_UINT32_TYPE;
106 			}
107 			break;
108 
109 		case 'f':
110 			return B_FLOAT_TYPE;
111 		case 'd':
112 			length = sizeof(double);
113 			return B_DOUBLE_TYPE;
114 		case 'r':
115 			// TODO: is "long double" supported under Haiku at all?
116 			return 0;
117 
118 		case 't':
119 			// TODO: templates are not yet supported
120 			return 0;
121 
122 		default:
123 			return B_ANY_TYPE;
124 	}
125 }
126 
127 
128 static uint32
129 parse_number(const char** _arg, bool numberLeft)
130 {
131 	const char* arg = *_arg;
132 
133 	while (isdigit(arg[0]))
134 		arg++;
135 
136 	uint32 value;
137 
138 	if (arg[0] == '_' && (!numberLeft || isdigit(arg[1]))) {
139 		// long value
140 		value = strtoul(*_arg, (char**)_arg, 10);
141 		if (**_arg == '_')
142 			(*_arg)++;
143 	} else {
144 		value = **_arg - '0';
145 		(*_arg)++;
146 	}
147 
148 	return value;
149 }
150 
151 
152 static uint32
153 parse_repeats(const char** _arg, uint32& index)
154 {
155 	if (**_arg != 'N')
156 		return 0;
157 
158 	(*_arg)++;
159 
160 	uint32 count = parse_number(_arg, true);
161 	index = parse_number(_arg, false);
162 
163 	return count;
164 }
165 
166 
167 static void
168 skip_numbers(const char** _arg, int32 count)
169 {
170 	// skip leading character
171 	(*_arg)++;
172 
173 	while (count-- > 0) {
174 		parse_number(_arg, count != 0);
175 	}
176 }
177 
178 
179 static uint32
180 count_namespaces(const char** _mangled)
181 {
182 	const char* mangled = *_mangled;
183 
184 	int32 namespaces = 0;
185 	if (mangled[0] == 'Q') {
186 		// more than one namespace
187 		if (mangled[1] == '_') {
188 			// more than 9 namespaces
189 			namespaces = strtoul(mangled + 2, (char**)&mangled, 10);
190 			if (mangled[0] != '_')
191 				namespaces = 0;
192 
193 			mangled++;
194 		} else {
195 			namespaces = mangled[1] - '0';
196 			mangled += 2;
197 		}
198 	} else if (isdigit(mangled[0]))
199 		namespaces = 1;
200 
201 	*_mangled = mangled;
202 	return namespaces;
203 }
204 
205 
206 static void
207 skip_namespaces(const char** _mangled)
208 {
209 	const char* mangled = *_mangled;
210 	int32 namespaces = count_namespaces(&mangled);
211 
212 	while (namespaces-- > 0) {
213 		if (!isdigit(mangled[0]))
214 			break;
215 
216 		mangled += strtoul(mangled, (char**)&mangled, 10);
217 	}
218 
219 	*_mangled = mangled;
220 }
221 
222 
223 static bool
224 has_named_argument(const char** _arg)
225 {
226 	ignore_qualifiers(_arg);
227 
228 	// See if it's a built-in type
229 	return **_arg == 'Q' || isdigit(**_arg);
230 }
231 
232 
233 static uint32
234 argument_length(const char** _arg)
235 {
236 	if (**_arg == 'N') {
237 		// skip repeats
238 		skip_numbers(_arg, 2);
239 		return 0;
240 	} else if (**_arg == 'T') {
241 		// skip reference
242 		skip_numbers(_arg, 1);
243 		return 0;
244 	}
245 
246 	ignore_qualifiers(_arg);
247 
248 	if (!**_arg)
249 		return 0;
250 
251 	// See if it's a built-in type
252 	if (**_arg != 'Q' && !isdigit(**_arg))
253 		return 1;
254 
255 	const char* mangled = *_arg;
256 	skip_namespaces(&mangled);
257 
258 	return mangled - *_arg;
259 }
260 
261 
262 static const char*
263 next_argument(const char* arg)
264 {
265 	if (arg == NULL || !arg[0])
266 		return NULL;
267 
268 	uint32 length = argument_length(&arg);
269 	arg += length;
270 
271 	if (!arg[0])
272 		return NULL;
273 
274 	return arg;
275 }
276 
277 
278 static const char*
279 first_argument(const char* mangled)
280 {
281 	skip_namespaces(&mangled);
282 
283 	return mangled;
284 }
285 
286 
287 static const char*
288 mangled_start(const char* name, size_t* _symbolLength, int32* _symbolType)
289 {
290 	if (name == NULL)
291 		return NULL;
292 
293 	// search '__' starting from the end, don't accept them at the start
294 	size_t pos = strlen(name) - 1;
295 	const char* mangled = NULL;
296 
297 	while (pos > 1) {
298 		if (name[pos] == '_') {
299 			if (name[pos - 1] == '_') {
300 				mangled = name + pos + 1;
301 				break;
302 			} else
303 				pos--;
304 		}
305 		pos--;
306 	}
307 
308 	if (mangled == NULL)
309 		return NULL;
310 
311 	if (mangled[0] == 'H') {
312 		// TODO: we don't support templates yet
313 		return NULL;
314 	}
315 
316 	if (_symbolLength != NULL)
317 		*_symbolLength = pos - 1;
318 
319 	if (mangled[0] == 'F') {
320 		if (_symbolType != NULL)
321 			*_symbolType = TYPE_FUNCTION;
322 		return mangled + 1;
323 	}
324 
325 	if (_symbolType != NULL)
326 		*_symbolType = TYPE_METHOD;
327 	return mangled;
328 }
329 
330 
331 static status_t
332 get_next_argument_internal(uint32* _cookie, const char* symbol, char* name,
333 	size_t nameSize, int32* _type, size_t* _argumentLength, bool repeating)
334 {
335 	const char* mangled = mangled_start(symbol, NULL, NULL);
336 	if (mangled == NULL)
337 		return B_BAD_VALUE;
338 
339 	const char* arg = first_argument(mangled);
340 
341 	// (void) is not an argument
342 	if (arg[0] == 'v')
343 		return B_ENTRY_NOT_FOUND;
344 
345 	uint32 current = *_cookie;
346 	if (current > 32)
347 		return B_TOO_MANY_ARGS;
348 
349 	for (uint32 i = 0; i < current; i++) {
350 		arg = next_argument(arg);
351 		if (arg != NULL && arg[0] == 'N') {
352 			// repeat argument 'count' times
353 			uint32 index;
354 			uint32 count = parse_repeats(&arg, index);
355 			if (current <= i + count) {
356 				if (repeating)
357 					return B_LINK_LIMIT;
358 
359 				// it's a repeat case
360 				status_t status = get_next_argument_internal(&index, symbol,
361 					name, nameSize, _type, _argumentLength, true);
362 				if (status == B_OK)
363 					(*_cookie)++;
364 				return status;
365 			}
366 
367 			i += count - 1;
368 		}
369 	}
370 
371 	if (arg == NULL)
372 		return B_ENTRY_NOT_FOUND;
373 
374 	TRACE("\n\targ %ld: %s\n", current, arg);
375 
376 	if (arg[0] == 'T') {
377 		// duplicate argument
378 		if (repeating)
379 			return B_LINK_LIMIT;
380 
381 		arg++;
382 		uint32 index = parse_number(&arg, false);
383 		status_t status = get_next_argument_internal(&index, symbol, name,
384 			nameSize, _type, _argumentLength, true);
385 		if (status == B_OK)
386 			(*_cookie)++;
387 		return status;
388 	}
389 
390 	(*_cookie)++;
391 
392 	size_t argumentLength;
393 	int32 type = argument_type(arg, argumentLength);
394 	if (type == 0)
395 		return B_NOT_SUPPORTED;
396 
397 	if (_type != NULL)
398 		*_type = type;
399 	if (_argumentLength != NULL)
400 		*_argumentLength = argumentLength;
401 
402 	name[0] = '\0';
403 	if (!has_named_argument(&arg))
404 		return B_OK;
405 
406 	const char* namespaceStart = arg;
407 	int32 namespaces = count_namespaces(&namespaceStart);
408 
409 	while (namespaces-- > 0) {
410 		if (namespaceStart[0] == 't') {
411 			// It's a template class after all
412 			return B_BAD_TYPE;
413 		}
414 		if (!isdigit(namespaceStart[0]))
415 			break;
416 
417 		uint32 length = strtoul(namespaceStart, (char**)&namespaceStart, 10);
418 		uint32 max = strlen(name) + length + 1;
419 		strlcat(name, namespaceStart, min_c(max, nameSize));
420 		if (namespaces > 0)
421 			strlcat(name, "::", nameSize);
422 		namespaceStart += length;
423 	}
424 
425 	return B_OK;
426 }
427 
428 
429 //	#pragma mark -
430 
431 
432 const char*
433 demangle_symbol_gcc2(const char* name, char* buffer, size_t bufferSize,
434 	bool* _isObjectMethod)
435 {
436 	size_t nameLength;
437 	int32 type;
438 	const char* mangled = mangled_start(name, &nameLength, &type);
439 	if (mangled == NULL)
440 		return NULL;
441 
442 	if (mangled[0] == 'C') {
443 		// ignore const method
444 		type = TYPE_METHOD;
445 		mangled++;
446 	}
447 
448 	if (_isObjectMethod != NULL) {
449 		// we can only guess with GCC2 mangling
450 		*_isObjectMethod = type == TYPE_METHOD;
451 	}
452 
453 	const char* namespaceStart = mangled;
454 	int32 namespaces = count_namespaces(&namespaceStart);
455 
456 	buffer[0] = '\0';
457 
458 	while (namespaces-- > 0) {
459 		if (namespaceStart[0] == 't') {
460 			// It's a template class after all
461 			return NULL;
462 		}
463 		if (!isdigit(namespaceStart[0]))
464 			break;
465 
466 		uint32 length = strtoul(namespaceStart, (char**)&namespaceStart, 10);
467 		uint32 max = strlen(buffer) + length + 1;
468 		strlcat(buffer, namespaceStart, min_c(max, bufferSize));
469 		strlcat(buffer, "::", bufferSize);
470 		namespaceStart += length;
471 	}
472 
473 	size_t max = strlen(buffer) + nameLength + 1;
474 	strlcat(buffer, name, min_c(max, bufferSize));
475 	return buffer;
476 }
477 
478 
479 status_t
480 get_next_argument_gcc2(uint32* _cookie, const char* symbol, char* name,
481 	size_t nameSize, int32* _type, size_t* _argumentLength)
482 {
483 	status_t error = get_next_argument_internal(_cookie, symbol, name, nameSize,
484 		_type, _argumentLength, false);
485 	if (error != B_OK)
486 		return error;
487 
488 	// append the missing '*'/'&' for pointer/ref types
489 	if (name[0] != '\0' && (*_type == B_POINTER_TYPE || *_type == B_REF_TYPE))
490 		strlcat(name, *_type == B_POINTER_TYPE ? "*" : "&", nameSize);
491 
492 	return B_OK;
493 }
494