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