1 /* 2 * Copyright 2018, Haiku, Inc. All rights reserved. 3 * Based on Demumble; Copyright 2016-2018, Nico Weber. 4 * https://github.com/nico/demumble/ 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 #include <stddef.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <algorithm> 24 25 #include "Demangler.h" 26 27 28 static void print_help(FILE* out) 29 { 30 fprintf(out, 31 "usage: haikuc++filt [options] [symbols...]\n" 32 "\n" 33 "if symbols are unspecified, reads from stdin.\n" 34 "\n" 35 "options:\n" 36 " -m only print mangled names that were demangled," 37 "omit other output\n" 38 " -u use unbuffered output\n" 39 " --no-gcc2 ignore GCC 2-style symbols\n"); 40 } 41 42 43 static bool starts_with(const char* s, const char* prefix) 44 { 45 return strncmp(s, prefix, strlen(prefix)) == 0; 46 } 47 48 49 static void print_demangled(const char* s) 50 { 51 const char* cxa_in = s; 52 if (starts_with(s, "__Z") || starts_with(s, "____Z")) 53 cxa_in += 1; 54 printf("%s", Demangler::Demangle(cxa_in).String()); 55 } 56 57 58 static bool is_mangle_char_posix(char c) 59 { 60 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || 61 (c >= '0' && c <= '9') || c == '_'; 62 } 63 64 65 static bool look_for_itanium_prefix(char** str, char* end) 66 { 67 char* s = *str; 68 s += strcspn(s, "_?"); 69 if (s == end) 70 return false; 71 72 // Itanium symbols start with 1-4 underscores followed by Z. 73 // strnstr() is BSD, so use a small local buffer and strstr(). 74 const int N = 5; // == strlen("____Z") 75 char prefix[N + 1]; 76 strncpy(prefix, s, N); 77 prefix[N] = '\0'; 78 if (strstr(prefix, "_Z")) { 79 *str = s; 80 return true; 81 } 82 return false; 83 } 84 85 86 static bool look_for_gcc2_symbol(char** str, char* end) 87 { 88 // search '__' starting from the end, don't accept them at the start 89 char* s = *str; 90 size_t pos = (end - s) - 1; 91 char* mangled = NULL; 92 93 while (pos > 1) { 94 if (s[pos] == '_') { 95 if (s[pos - 1] == '_') { 96 mangled = s + pos + 1; 97 break; 98 } else 99 pos--; 100 } 101 pos--; 102 } 103 104 // if we've found a symbol, go backwards to its beginning 105 while (mangled != NULL && mangled > (s + 1) 106 && is_mangle_char_posix(mangled[-1])) { 107 mangled--; 108 } 109 110 if (mangled != NULL) 111 *str = mangled; 112 113 return mangled != NULL; 114 } 115 116 117 static char buf[8192]; 118 int main(int argc, char* argv[]) 119 { 120 enum { kPrintAll, kPrintMatching } print_mode = kPrintAll; 121 bool noGCC2 = false; 122 123 while (argc > 1 && argv[1][0] == '-') { 124 if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { 125 print_help(stdout); 126 return 0; 127 } else if (strcmp(argv[1], "-m") == 0) { 128 print_mode = kPrintMatching; 129 } else if (strcmp(argv[1], "--no-gcc2") == 0) { 130 noGCC2 = true; 131 } else if (strcmp(argv[1], "-u") == 0) { 132 setbuf(stdout, NULL); 133 } else if (strcmp(argv[1], "--") == 0) { 134 --argc; 135 ++argv; 136 break; 137 } else { 138 fprintf(stderr, "c++filt: unrecognized option `%s'\n", argv[1]); 139 print_help(stderr); 140 return 1; 141 } 142 --argc; 143 ++argv; 144 } 145 for (int i = 1; i < argc; ++i) { 146 print_demangled(argv[i]); 147 printf("\n"); 148 } 149 if (argc != 1) 150 return 0; 151 // Read stdin instead. 152 // By default, don't demangle types. Mangled function names are unlikely 153 // to appear in text for since they start with _Z (or ___Z) or ?? / ?$ / ?@. 154 // But type manglings can be regular words ("Pi" is "int*"). 155 // (For command-line args, do try to demangle types though.) 156 while (fgets(buf, sizeof(buf), stdin)) { 157 bool need_separator = false; 158 char* cur = buf; 159 char* end = cur + strlen(cur); 160 161 while (cur != end) { 162 if (print_mode == kPrintMatching && need_separator) 163 printf("\n"); 164 need_separator = false; 165 166 // Check if we have a symbol, and then for how long it is. 167 size_t n_sym = 0; 168 char* real_cur = cur; 169 if (look_for_itanium_prefix(&real_cur, end) || 170 (!noGCC2 && look_for_gcc2_symbol(&real_cur, end))) { 171 // Print all the stuff before the symbol. 172 if (print_mode == kPrintAll) 173 printf("%.*s", static_cast<int>(real_cur - cur), cur); 174 cur = real_cur; 175 while (cur + n_sym != end && is_mangle_char_posix(cur[n_sym])) 176 ++n_sym; 177 } else { 178 // No symbols found in this block; skip it. 179 printf("%s", cur); 180 cur = end; 181 continue; 182 } 183 if (n_sym == 0) { 184 ++cur; 185 continue; 186 } 187 188 char tmp = cur[n_sym]; 189 cur[n_sym] = '\0'; 190 print_demangled(cur); 191 need_separator = true; 192 cur[n_sym] = tmp; 193 194 cur += n_sym; 195 } 196 } 197 } 198