/// \file NameBuilder.cpp // // This file is distributed under the MIT License. See LICENSE.md for details. // #include "revng/Model/Binary.h" const model::NamingConfiguration &model::NameBuilder::configuration() const { return Binary.Configuration().Naming(); } static llvm::Error makeDuplicateSymbolError(const std::string &Name, const std::string &FirstPath, const std::string &SecondPath) { std::string Result; Result += "Duplicate global symbol \""; Result += Name; Result += "\":\n\n"; Result += " " + FirstPath + "\n"; Result += " " + SecondPath + "\n"; return revng::createError(std::move(Result)); } template static std::string key(const T &Object) { return getNameFromYAMLScalar(KeyedObjectTraits::key(Object)); } static std::string path(const model::Function &F) { return "/Functions/" + key(F); } static std::string path(const model::DynamicFunction &F) { return "/ImportedDynamicFunctions/" + key(F); } static std::string path(const model::TypeDefinition &T) { return "/TypeDefinitions/" + key(T); } static std::string path(const model::EnumDefinition &D, const model::EnumEntry &Entry) { return path(static_cast(D)) + "/EnumDefinition/Entries/" + key(Entry); } static std::string path(const model::Segment &Segment) { return "/Segments/" + key(Segment); } llvm::Error model::NameBuilder::isNameForbidden(llvm::StringRef Name) { // Do these strictly for now, then soften them if needed. if (Name.starts_with(configuration().structPaddingPrefix())) return revng::createError("it is reserved for struct padding"); if (Name.starts_with(configuration().artificialReturnValuePrefix())) return revng::createError("it is reserved for artificial return value " "structs"); if (Name.starts_with(configuration().artificialArrayWrapperPrefix())) return revng::createError("it is reserved for artificial array wrappers"); // TODO: more of these? return llvm::Error::success(); } llvm::Error model::NameBuilder::populateGlobalNamespace() { revng_assert(not GlobalNamespace.has_value()); GlobalNamespace = GlobalNamespaceMap(); auto RegisterGlobalSymbol = [this](std::string Name, std::string Path) -> llvm::Error { if (not Name.empty()) { if (llvm::Error Error = isNameForbidden(Name)) { struct ExtractMessage { std::string &Out; llvm::Error operator()(const llvm::StringError &Error) { Out = Error.getMessage(); return llvm::Error::success(); } }; auto CatchAll = [](const llvm::ErrorInfoBase &) -> llvm::Error { revng_abort("Unsupported error type."); }; std::string Out; llvm::handleAllErrors(std::move(Error), ExtractMessage{ Out }, CatchAll); revng_assert(not Out.empty()); return revng::createError("The name \"" + Name + "\" of \"" + Path + "\" is not allowed: " + std::move(Out) + ".\n"); } auto [Iterator, Success] = GlobalNamespace->try_emplace(Name, Path); if (not Success) return makeDuplicateSymbolError(Name, Iterator->second, Path); } return llvm::Error::success(); }; // Namespacing rules: // // 1. each struct/union induces a namespace for its field names; // 2. each prototype induces a namespace for its arguments (and local // variables, but those are not part of the model yet); // 3. the global namespace includes segment names, function names, dynamic // function names, type names, entries of `enum`s and helper names; // // Verify needs to verify that each namespace has no internal clashes. // Also, the global namespace clashes with everything. // // TODO: find a way to add all the helper names to the Result. for (const Function &F : Binary.Functions()) if (auto Error = RegisterGlobalSymbol(F.CustomName().str().str(), path(F))) return Error; for (const DynamicFunction &DF : Binary.ImportedDynamicFunctions()) if (auto Error = RegisterGlobalSymbol(DF.CustomName().str().str(), path(DF))) return Error; for (const model::UpcastableTypeDefinition &D : Binary.TypeDefinitions()) { if (auto Error = RegisterGlobalSymbol(D->CustomName().str().str(), path(*D))) return Error; if (auto *Enum = llvm::dyn_cast(D.get())) for (auto &Entry : Enum->Entries()) if (auto Error = RegisterGlobalSymbol(Entry.CustomName().str().str(), path(*Enum, Entry))) return Error; } for (const Segment &S : Binary.Segments()) if (auto Error = RegisterGlobalSymbol(S.CustomName().str().str(), path(S))) return Error; return llvm::Error::success(); } static std::string toIdentifier(const MetaAddress &Address) { return model::Identifier::sanitize(Address.toString()).str().str(); } model::Identifier model::NameBuilder::name(const model::Segment &Segment) { if (not Segment.CustomName().empty()) { revng_assert(globalNamespace().contains(Segment.CustomName()), "Cache is outdated."); return Segment.CustomName(); } else { auto Result = std::string(configuration().unnamedSegmentPrefix()) + toIdentifier(Segment.StartAddress()) + "_" + std::to_string(Segment.VirtualSize()); while (globalNamespace().contains(Result)) Result += configuration().collisionResolutionSuffix(); return Identifier(Result); } } model::Identifier model::NameBuilder::name(const model::Function &Function) { if (not Function.CustomName().empty()) { revng_assert(globalNamespace().contains(Function.CustomName()), "Cache is outdated."); return Function.CustomName(); } else { auto Result = std::string(configuration().unnamedFunctionPrefix()) + toIdentifier(Function.Entry()); while (globalNamespace().contains(Result)) Result += configuration().collisionResolutionSuffix(); return Identifier(Result); } } model::Identifier model::NameBuilder::name(const model::DynamicFunction &Function) { if (not Function.CustomName().empty()) { revng_assert(globalNamespace().contains(Function.CustomName()), "Cache is outdated."); return Function.CustomName(); } else { auto Result = std::string(configuration().unnamedDynamicFunctionPrefix()) + Function.OriginalName(); while (globalNamespace().contains(Result)) Result += configuration().collisionResolutionSuffix(); return Identifier(Result); } } model::Identifier model::NameBuilder::name(const model::TypeDefinition &Definition) { if (not Definition.CustomName().empty()) { revng_assert(globalNamespace().contains(Definition.CustomName()), "Cache is outdated."); return Definition.CustomName(); } else { auto K = model::TypeDefinitionKind::automaticNamePrefix(Definition.Kind()); auto Result = std::string(configuration().unnamedTypeDefinitionPrefix()) + K.str() + std::to_string(Definition.ID()); while (globalNamespace().contains(Result)) Result += configuration().collisionResolutionSuffix(); return Identifier(Result); } } model::Identifier model::NameBuilder::name(const model::EnumDefinition &Definition, const model::EnumEntry &Entry) { revng_assert(Definition.Entries().count(Entry.Value()) != 0); model::Identifier Result; // Decide on a name if (Entry.CustomName().empty()) { llvm::StringRef Prefix{ configuration().unnamedEnumEntryPrefix() }; (Prefix + name(Definition) + llvm::Twine(Entry.Value())).toVector(Result); } else { Result = Entry.CustomName(); revng_assert(globalNamespace().contains(Result), "Cache is outdated."); return Result; } // Ensure it doesn't collide with anything // TODO: factor these out? auto NameCollides = [this, &Entry, &Definition](const auto &Name) { for (const auto &AnotherEntry : Definition.Entries()) if (Entry.key() != AnotherEntry.key()) if (not AnotherEntry.CustomName().empty()) if (AnotherEntry.CustomName() == Name) return true; return globalNamespace().contains(Name); }; while (NameCollides(Result)) Result += configuration().collisionResolutionSuffix(); return Result; } model::Identifier model::NameBuilder::name(const model::StructDefinition &Definition, const model::StructField &Field) { model::Identifier Result; // Decide on a name if (Field.CustomName().empty()) { llvm::StringRef Prefix{ configuration().unnamedStructFieldPrefix() }; (Prefix + llvm::Twine(Field.Offset())).toVector(Result); } else { Result = Field.CustomName(); } // Ensure it doesn't collide with anything // TODO: factor these out? auto NameCollides = [this, &Field, &Definition](const auto &Name) { for (const auto &AnotherField : Definition.Fields()) if (Field.key() != AnotherField.key()) if (not AnotherField.CustomName().empty()) if (AnotherField.CustomName() == Name) return true; return globalNamespace().contains(Name); }; while (NameCollides(Result)) Result += configuration().collisionResolutionSuffix(); return Result; } model::Identifier model::NameBuilder::name(const model::UnionDefinition &Definition, const model::UnionField &Field) { model::Identifier Result; // Decide on a name if (Field.CustomName().empty()) { llvm::StringRef Prefix{ configuration().unnamedUnionFieldPrefix() }; (Prefix + llvm::Twine(Field.Index())).toVector(Result); } else { Result = Field.CustomName(); } // Ensure it doesn't collide with anything // TODO: factor these out? auto NameCollides = [this, &Field, &Definition](const auto &Name) { for (const auto &AnotherField : Definition.Fields()) if (Field.key() != AnotherField.key()) if (not AnotherField.CustomName().empty()) if (AnotherField.CustomName() == Name) return true; return globalNamespace().contains(Name); }; while (NameCollides(Result)) Result += configuration().collisionResolutionSuffix(); return Result; } using CFT = model::CABIFunctionDefinition; model::Identifier model::NameBuilder::argumentName(const CFT &Function, const model::Argument &Argument) { model::Identifier Result; // Decide on a name if (Argument.CustomName().empty()) { llvm::StringRef Prefix{ configuration().unnamedFunctionArgumentPrefix() }; (Prefix + llvm::Twine(Argument.Index())).toVector(Result); } else { Result = Argument.CustomName(); } // Ensure it doesn't collide with anything // TODO: factor these out? auto NameCollides = [this, &Argument, &Function](const auto &Name) { for (const auto &AnotherArgument : Function.Arguments()) if (Argument.key() != AnotherArgument.key()) if (not AnotherArgument.CustomName().empty()) if (AnotherArgument.CustomName() == Name) return true; return globalNamespace().contains(Name); }; while (NameCollides(Result)) Result += configuration().collisionResolutionSuffix(); return Result; } using NTR = model::NamedTypedRegister; using RFT = model::RawFunctionDefinition; model::Identifier model::NameBuilder::argumentName(const RFT &Function, const NTR &Argument) { model::Identifier Result; // Decide on a name if (Argument.CustomName().empty()) { llvm::StringRef Prefix = configuration().unnamedFunctionRegisterPrefix(); (Prefix + getRegisterName(Argument.Location())).toVector(Result); } else { Result = Argument.CustomName(); } // Ensure it doesn't collide with anything // TODO: factor these out? auto NameCollides = [this, &Argument, &Function](const auto &Name) { for (const auto &AnotherArgument : Function.Arguments()) if (Argument.key() != AnotherArgument.key()) if (not AnotherArgument.CustomName().empty()) if (AnotherArgument.CustomName() == Name) return true; return globalNamespace().contains(Name); }; while (NameCollides(Result)) Result += configuration().collisionResolutionSuffix(); return Result; } using NTR = model::NamedTypedRegister; using RFT = model::RawFunctionDefinition; model::Identifier model::NameBuilder::returnValueName(const RFT &Function, const NTR &ReturnValue) { model::Identifier Result; // Decide on a name if (ReturnValue.CustomName().empty()) { llvm::StringRef Prefix = configuration().unnamedFunctionRegisterPrefix(); (Prefix + getRegisterName(ReturnValue.Location())).toVector(Result); } else { Result = ReturnValue.CustomName(); } // Ensure it doesn't collide with anything // TODO: factor these out? auto NameCollides = [this, &ReturnValue, &Function](const auto &Name) { for (const auto &AnotherReturnValue : Function.ReturnValues()) if (ReturnValue.key() != AnotherReturnValue.key()) if (not AnotherReturnValue.CustomName().empty()) if (AnotherReturnValue.CustomName() == Name) return true; return globalNamespace().contains(Name); }; while (NameCollides(Result)) Result += configuration().collisionResolutionSuffix(); return Result; } using T = model::Type; RecursiveCoroutine model::NameBuilder::artificialArrayWrapperNameImpl(const T &Type) { if (auto *Array = llvm::dyn_cast(&Type)) { std::string Result = "array_" + std::to_string(Array->ElementCount()) + "_of_"; Result += rc_recur artificialArrayWrapperNameImpl(*Array->ElementType()); rc_return Result; } else if (auto *D = llvm::dyn_cast(&Type)) { std::string Result = (D->IsConst() ? "const_" : ""); rc_return std::move(Result += this->name(D->unwrap()).str().str()); } else if (auto *Pointer = llvm::dyn_cast(&Type)) { std::string Result = (D->IsConst() ? "const_ptr_to_" : "ptr_to_"); Result += rc_recur artificialArrayWrapperNameImpl(*Pointer->PointeeType()); rc_return std::move(Result); } else if (auto *Primitive = llvm::dyn_cast(&Type)) { std::string Result = (D->IsConst() ? "const_" : ""); rc_return std::move(Result += Primitive->getCName()); } else { revng_abort("Unsupported model::Type."); } } model::Identifier model::NameBuilder::artificialArrayWrapperFieldName() { std::string Result{ configuration().artificialArrayWrapperFieldName() }; while (globalNamespace().contains(Result)) Result += configuration().collisionResolutionSuffix(); return Identifier(Result); }