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