| // Copyright (C) 2020-2025 Free Software Foundation, Inc. |
| |
| // This file is part of GCC. |
| |
| // GCC is free software; you can redistribute it and/or modify it under |
| // the terms of the GNU General Public License as published by the Free |
| // Software Foundation; either version 3, or (at your option) any later |
| // version. |
| |
| // GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| // WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| // for more details. |
| |
| // You should have received a copy of the GNU General Public License |
| // along with GCC; see the file COPYING3. If not see |
| // <http://www.gnu.org/licenses/>. |
| |
| #include "rust-mangle.h" |
| #include "fnv-hash.h" |
| #include "rust-unicode.h" |
| #include "rust-diagnostics.h" |
| #include "rust-system.h" |
| |
| namespace Rust { |
| namespace Compile { |
| |
| const std::string kLegacySymbolPrefix = "_ZN"; |
| static const std::string kLegacySymbolDelim = "E"; |
| static const std::string kLegacyGenericDelim = "$C$"; |
| static const std::string kLegacySubstBegin = "$LT$"; |
| static const std::string kLegacySubstEnd = "$GT$"; |
| static const std::string kLegacySpace = "$u20$"; |
| static const std::string kLegacyRef = "$RF$"; |
| static const std::string kLegacyPtr = "$BP$"; |
| static const std::string kLegacyLeftSqParen = "$u5b$"; // [ |
| static const std::string kLegacyRightSqParen = "$u5d$"; // ] |
| static const std::string kLegacyLeftBrace = "$u7b$"; // { |
| static const std::string kLegacyRightBrace = "$u7d$"; // } |
| static const std::string kQualPathBegin = "_" + kLegacySubstBegin; |
| static const std::string kLegacyComma = "$C$"; |
| |
| static std::string |
| legacy_mangle_name (const std::string &name) |
| { |
| // example |
| // <&T as core::fmt::Debug>::fmt: |
| // _ZN42_$LT$$RF$T$u20$as$u20$core..fmt..Debug$GT$3fmt17h6dac924c0051eef7E |
| // replace all white space with $ and & with RF |
| // |
| // <example::Bar as example::A>::fooA: |
| // _ZN43_$LT$example..Bar$u20$as$u20$example..A$GT$4fooA17hfc615fa76c7db7a0E: |
| // |
| // core::ptr::const_ptr::<impl *const T>::cast: |
| // _ZN4core3ptr9const_ptr33_$LT$impl$u20$$BP$const$u20$T$GT$4cast17hb79f4617226f1d55E: |
| // |
| // core::ptr::const_ptr::<impl *const [T]>::as_ptr: |
| // _ZN4core3ptr9const_ptr43_$LT$impl$u20$$BP$const$u20$$u5b$T$u5d$$GT$6as_ptr17he16e0dcd9473b04fE: |
| // |
| // example::Foo<T>::new: |
| // _ZN7example12Foo$LT$T$GT$3new17h9a2aacb7fd783515E: |
| // |
| // <example::Identity as example::FnLike<&T,&T>>::call |
| // _ZN74_$LT$example..Identity$u20$as$u20$example..FnLike$LT$$RF$T$C$$RF$T$GT$$GT$4call17ha9ee58935895acb3E |
| |
| tl::optional<Utf8String> utf8_name = Utf8String::make_utf8_string (name); |
| rust_assert (utf8_name.has_value ()); |
| std::vector<Codepoint> chars = utf8_name.value ().get_chars (); |
| std::string buffer; |
| for (size_t i = 0; i < chars.size (); i++) |
| { |
| std::string m; |
| Codepoint c = chars.at (i); |
| |
| if (c == ' ') |
| m = kLegacySpace; |
| else if (c == '&') |
| m = kLegacyRef; |
| else if (i == 0 && c == '<') |
| m = kQualPathBegin; |
| else if (c == '<') |
| m = kLegacySubstBegin; |
| else if (c == '>') |
| m = kLegacySubstEnd; |
| else if (c == '*') |
| m = kLegacyPtr; |
| else if (c == '[') |
| m = kLegacyLeftSqParen; |
| else if (c == ']') |
| m = kLegacyRightSqParen; |
| else if (c == '{') |
| m = kLegacyLeftBrace; |
| else if (c == '}') |
| m = kLegacyRightBrace; |
| else if (c == ',') |
| m = kLegacyComma; |
| else if (c == ':') |
| { |
| rust_assert (i + 1 < chars.size ()); |
| rust_assert (chars.at (i + 1) == ':'); |
| i++; |
| m = ".."; |
| } |
| else if (c.is_ascii ()) |
| // ASCII |
| m.push_back (c.value); |
| else |
| { |
| // Non-ASCII |
| std::stringstream escaped; |
| escaped << std::hex << "$u" << c.value << "$"; |
| m += escaped.str (); |
| } |
| buffer += m; |
| } |
| |
| return std::to_string (buffer.size ()) + buffer; |
| } |
| |
| static std::string |
| legacy_mangle_canonical_path (const Resolver::CanonicalPath &path) |
| { |
| std::string buffer; |
| for (size_t i = 0; i < path.size (); i++) |
| { |
| auto &seg = path.get_seg_at (i); |
| buffer += legacy_mangle_name (seg.second); |
| } |
| return buffer; |
| } |
| |
| // rustc uses a sip128 hash for legacy mangling, but an fnv 128 was quicker to |
| // implement for now |
| static std::string |
| legacy_hash (const std::string &fingerprint) |
| { |
| Hash::FNV128 hasher; |
| hasher.write ((const unsigned char *) fingerprint.c_str (), |
| fingerprint.size ()); |
| |
| uint64_t hi, lo; |
| hasher.sum (&hi, &lo); |
| |
| char hex[16 + 1]; |
| memset (hex, 0, sizeof hex); |
| snprintf (hex, sizeof hex, "%08" PRIx64 "%08" PRIx64, lo, hi); |
| |
| return "h" + std::string (hex, sizeof (hex) - 1); |
| } |
| |
| std::string |
| legacy_mangle_item (const TyTy::BaseType *ty, |
| const Resolver::CanonicalPath &path) |
| { |
| const std::string hash = legacy_hash (ty->mangle_string ()); |
| const std::string hash_sig = legacy_mangle_name (hash); |
| |
| return kLegacySymbolPrefix + legacy_mangle_canonical_path (path) + hash_sig |
| + kLegacySymbolDelim; |
| } |
| |
| } // namespace Compile |
| } // namespace Rust |