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: 31 Input(const char* string, size_t length) 32 : 33 fString(string), 34 fLength(length) 35 { 36 } 37 38 const char* String() const 39 { 40 return fString; 41 } 42 43 int CharsRemaining() const 44 { 45 return fLength; 46 } 47 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 61 Input& operator++() 62 { 63 Skip(1); 64 return *this; 65 } 66 Input operator++(int) 67 { 68 Input result(*this); 69 ++(*this); 70 return result; 71 } 72 73 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 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 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 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 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 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 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 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 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 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 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 340 first_argument(Input mangled) 341 { 342 skip_namespaces(mangled); 343 344 return mangled; 345 } 346 347 348 static bool 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 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* 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 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