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