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