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