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