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