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