0001
0002 #include <string.h>
0003 #include "debug.h"
0004
0005 #include "demangle-rust.h"
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045 static const char *hash_prefix = "::h";
0046 static const size_t hash_prefix_len = 3;
0047 static const size_t hash_len = 16;
0048
0049 static bool is_prefixed_hash(const char *start);
0050 static bool looks_like_rust(const char *sym, size_t len);
0051 static bool unescape(const char **in, char **out, const char *seq, char value);
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076 bool
0077 rust_is_mangled(const char *sym)
0078 {
0079 size_t len, len_without_hash;
0080
0081 if (!sym)
0082 return false;
0083
0084 len = strlen(sym);
0085 if (len <= hash_prefix_len + hash_len)
0086
0087 return false;
0088
0089 len_without_hash = len - (hash_prefix_len + hash_len);
0090 if (!is_prefixed_hash(sym + len_without_hash))
0091 return false;
0092
0093 return looks_like_rust(sym, len_without_hash);
0094 }
0095
0096
0097
0098
0099
0100 static bool is_prefixed_hash(const char *str)
0101 {
0102 const char *end;
0103 bool seen[16];
0104 size_t i;
0105 int count;
0106
0107 if (strncmp(str, hash_prefix, hash_prefix_len))
0108 return false;
0109 str += hash_prefix_len;
0110
0111 memset(seen, false, sizeof(seen));
0112 for (end = str + hash_len; str < end; str++)
0113 if (*str >= '0' && *str <= '9')
0114 seen[*str - '0'] = true;
0115 else if (*str >= 'a' && *str <= 'f')
0116 seen[*str - 'a' + 10] = true;
0117 else
0118 return false;
0119
0120
0121 count = 0;
0122 for (i = 0; i < 16; i++)
0123 if (seen[i])
0124 count++;
0125
0126 return count >= 5 && count <= 15;
0127 }
0128
0129 static bool looks_like_rust(const char *str, size_t len)
0130 {
0131 const char *end = str + len;
0132
0133 while (str < end)
0134 switch (*str) {
0135 case '$':
0136 if (!strncmp(str, "$C$", 3))
0137 str += 3;
0138 else if (!strncmp(str, "$SP$", 4)
0139 || !strncmp(str, "$BP$", 4)
0140 || !strncmp(str, "$RF$", 4)
0141 || !strncmp(str, "$LT$", 4)
0142 || !strncmp(str, "$GT$", 4)
0143 || !strncmp(str, "$LP$", 4)
0144 || !strncmp(str, "$RP$", 4))
0145 str += 4;
0146 else if (!strncmp(str, "$u20$", 5)
0147 || !strncmp(str, "$u27$", 5)
0148 || !strncmp(str, "$u5b$", 5)
0149 || !strncmp(str, "$u5d$", 5)
0150 || !strncmp(str, "$u7e$", 5))
0151 str += 5;
0152 else
0153 return false;
0154 break;
0155 case '.':
0156
0157 if (!strncmp(str, "...", 3))
0158 return false;
0159
0160 case 'a' ... 'z':
0161 case 'A' ... 'Z':
0162 case '0' ... '9':
0163 case '_':
0164 case ':':
0165 str++;
0166 break;
0167 default:
0168 return false;
0169 }
0170
0171 return true;
0172 }
0173
0174
0175
0176
0177
0178
0179
0180
0181 void
0182 rust_demangle_sym(char *sym)
0183 {
0184 const char *in;
0185 char *out;
0186 const char *end;
0187
0188 if (!sym)
0189 return;
0190
0191 in = sym;
0192 out = sym;
0193 end = sym + strlen(sym) - (hash_prefix_len + hash_len);
0194
0195 while (in < end)
0196 switch (*in) {
0197 case '$':
0198 if (!(unescape(&in, &out, "$C$", ',')
0199 || unescape(&in, &out, "$SP$", '@')
0200 || unescape(&in, &out, "$BP$", '*')
0201 || unescape(&in, &out, "$RF$", '&')
0202 || unescape(&in, &out, "$LT$", '<')
0203 || unescape(&in, &out, "$GT$", '>')
0204 || unescape(&in, &out, "$LP$", '(')
0205 || unescape(&in, &out, "$RP$", ')')
0206 || unescape(&in, &out, "$u20$", ' ')
0207 || unescape(&in, &out, "$u27$", '\'')
0208 || unescape(&in, &out, "$u5b$", '[')
0209 || unescape(&in, &out, "$u5d$", ']')
0210 || unescape(&in, &out, "$u7e$", '~'))) {
0211 pr_err("demangle-rust: unexpected escape sequence");
0212 goto done;
0213 }
0214 break;
0215 case '_':
0216
0217
0218
0219
0220
0221
0222
0223 if ((in == sym || in[-1] == ':') && in[1] == '$')
0224 in++;
0225 else
0226 *out++ = *in++;
0227 break;
0228 case '.':
0229 if (in[1] == '.') {
0230
0231 *out++ = ':';
0232 *out++ = ':';
0233 in += 2;
0234 } else {
0235
0236 *out++ = '-';
0237 in++;
0238 }
0239 break;
0240 case 'a' ... 'z':
0241 case 'A' ... 'Z':
0242 case '0' ... '9':
0243 case ':':
0244 *out++ = *in++;
0245 break;
0246 default:
0247 pr_err("demangle-rust: unexpected character '%c' in symbol\n",
0248 *in);
0249 goto done;
0250 }
0251
0252 done:
0253 *out = '\0';
0254 }
0255
0256 static bool unescape(const char **in, char **out, const char *seq, char value)
0257 {
0258 size_t len = strlen(seq);
0259
0260 if (strncmp(*in, seq, len))
0261 return false;
0262
0263 **out = value;
0264
0265 *in += len;
0266 *out += 1;
0267
0268 return true;
0269 }