From 9e72ada13145edbe257e949ffa06028f8802b8c0 Mon Sep 17 00:00:00 2001 From: Alastair Robertson Date: Wed, 25 Jan 2023 06:37:16 -0800 Subject: [PATCH] Support dynamic polymorphic inheritance i.e. classes which contain virtual functions and use a virtual pointer --- src/Common.h | 2 + src/OICodeGen.cpp | 269 +++++++++++++++++- src/OICodeGen.h | 14 +- src/OID.cpp | 7 + src/OIDebugger.cpp | 2 +- src/OIGenerator.cpp | 10 +- src/OIGenerator.h | 4 +- src/OILibraryImpl.cpp | 2 +- src/Serialize.cpp | 1 + src/Serialize.h | 2 +- src/SymbolService.cpp | 46 +-- src/SymbolService.h | 3 +- src/TreeBuilder.cpp | 20 +- test/integration/inheritance_polymorphic.toml | 38 ++- .../inheritance_polymorphic_diamond.toml | 88 ++++-- ...eritance_polymorphic_non_dynamic_base.toml | 19 +- tools/OILGen.cpp | 4 +- 17 files changed, 451 insertions(+), 80 deletions(-) diff --git a/src/Common.h b/src/Common.h index 7d6d471..e16392f 100644 --- a/src/Common.h +++ b/src/Common.h @@ -56,4 +56,6 @@ struct TypeHierarchy { std::set knownDummyTypeList; std::map pointerToTypeMap; std::set thriftIssetStructTypes; + std::map> + descendantClasses; }; diff --git a/src/OICodeGen.cpp b/src/OICodeGen.cpp index 4ae2d2f..acd61b0 100644 --- a/src/OICodeGen.cpp +++ b/src/OICodeGen.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -43,8 +44,9 @@ static size_t g_level = 0; #define VLOG(verboselevel) \ LOG_IF(INFO, VLOG_IS_ON(verboselevel)) << std::string(2 * g_level, ' ') -std::unique_ptr OICodeGen::buildFromConfig(const Config &c) { - auto cg = std::unique_ptr(new OICodeGen(c)); +std::unique_ptr OICodeGen::buildFromConfig(const Config &c, + SymbolService &s) { + auto cg = std::unique_ptr(new OICodeGen(c, s)); for (const auto &path : c.containerConfigPaths) { if (!cg->registerContainer(path)) { @@ -56,7 +58,8 @@ std::unique_ptr OICodeGen::buildFromConfig(const Config &c) { return cg; } -OICodeGen::OICodeGen(const Config &c) : config{c} { +OICodeGen::OICodeGen(const Config &c, SymbolService &s) + : config{c}, symbols{s} { // TODO: Should folly::Range just be added as a container? auto typesToStub = std::array{ "SharedMutex", @@ -986,6 +989,106 @@ std::string OICodeGen::typeToName(drgn_type *type) { return typeName; } +bool OICodeGen::recordChildren(drgn_type *type) { + drgn_type_template_parameter *parents = drgn_type_parents(type); + + for (size_t i = 0; i < drgn_type_num_parents(type); i++) { + drgn_qualified_type t{}; + + if (auto *err = drgn_template_parameter_type(&parents[i], &t); + err != nullptr) { + LOG(ERROR) << "Error when looking up parent class for type " << type + << " err " << err->code << " " << err->message; + drgn_error_destroy(err); + continue; + } + + drgn_type *parent = drgnUnderlyingType(t.type); + if (!isDrgnSizeComplete(parent)) { + VLOG(1) << "Incomplete size for parent class (" << drgn_type_tag(parent) + << ") of " << drgn_type_tag(type); + continue; + } + + const char *parentName = drgn_type_tag(parent); + if (parentName == nullptr) { + VLOG(1) << "No name for parent class (" << parent << ") of " + << drgn_type_tag(type); + continue; + } + + /* + * drgn pointers are not stable, so use string representation for reverse + * mapping for now. We need to find a better way of creating this + * childClasses map - ideally drgn would do this for us. + */ + childClasses[parentName].push_back(type); + VLOG(1) << drgn_type_tag(type) << "(" << type << ") is a child of " + << drgn_type_tag(parent) << "(" << parent << ")"; + } + + return true; +} + +/* + * Build a mapping of Class -> Children + * + * drgn only gives us the mapping Class -> Parents, so we must iterate over all + * types in the program to build the reverse mapping. + */ +bool OICodeGen::enumerateChildClasses() { + if (!config.polymorphicInheritance) { + return true; + } + + if ((setenv("DRGN_ENABLE_TYPE_ITERATOR", "1", 1)) < 0) { + LOG(ERROR) + << "Could not set DRGN_ENABLE_TYPE_ITERATOR environment variable"; + return false; + } + + drgn_type_iterator *typesIterator; + auto *prog = symbols.getDrgnProgram(); + drgn_error *err = drgn_type_iterator_create(prog, &typesIterator); + if (err) { + LOG(ERROR) << "Error initialising drgn_type_iterator: " << err->code << ", " + << err->message; + drgn_error_destroy(err); + return false; + } + + int i = 0; + int j = 0; + while (true) { + i++; + drgn_qualified_type *t; + err = drgn_type_iterator_next(typesIterator, &t); + if (err) { + LOG(ERROR) << "Error from drgn_type_iterator_next: " << err->code << ", " + << err->message; + drgn_error_destroy(err); + continue; + } + + if (!t) { + break; + } + j++; + + auto kind = drgn_type_kind(t->type); + if (kind != DRGN_TYPE_CLASS && kind != DRGN_TYPE_STRUCT) { + continue; + } + + if (!recordChildren(t->type)) { + return false; + } + } + + drgn_type_iterator_destroy(typesIterator); + return true; +} + // The top level function which enumerates the rootType object. This function // fills out : - // 1. struct/class definitions @@ -994,6 +1097,10 @@ std::string OICodeGen::typeToName(drgn_type *type) { // // They are appended to the jit code in that order (1), (2), (3) bool OICodeGen::populateDefsAndDecls() { + if (!enumerateChildClasses()) { + return false; + } + if (drgn_type_has_type(rootType.type) && drgn_type_kind(rootType.type) == DRGN_TYPE_FUNCTION) { rootType = drgn_type_type(rootType.type); @@ -1085,6 +1192,12 @@ drgn_type *OICodeGen::drgnUnderlyingType(drgn_type *type) { bool OICodeGen::enumerateClassParents(drgn_type *type, const std::string &typeName) { + if (drgn_type_num_parents(type) == 0) { + // Be careful to early exit here to avoid accidentally initialising + // parentClasses when there are no parents. + return true; + } + drgn_type_template_parameter *parents = drgn_type_parents(type); for (size_t i = 0; i < drgn_type_num_parents(type); ++i) { @@ -1247,6 +1360,41 @@ bool OICodeGen::isEmptyClassOrFunctionType(drgn_type *type, drgn_type_num_members(type) == 0); } +/* + * Returns true if the provided type represents a dynamic class. + * + * From the Itanium C++ ABI, a dynamic class is defined as: + * A class requiring a virtual table pointer (because it or its bases have + * one or more virtual member functions or virtual base classes). + */ +bool OICodeGen::isDynamic(drgn_type *type) const { + if (!config.polymorphicInheritance || !drgn_type_has_virtuality(type)) { + return false; + } + + if (drgn_type_virtuality(type) != 0 /*DW_VIRTUALITY_none*/) { + // Virtual class - not fully supported by OI yet + return true; + } + + drgn_type_member_function *functions = drgn_type_functions(type); + for (size_t i = 0; i < drgn_type_num_functions(type); i++) { + drgn_qualified_type t{}; + if (auto *err = drgn_member_function_type(&functions[i], &t)) { + LOG(ERROR) << "Error when looking up member function for type " << type + << " err " << err->code << " " << err->message; + drgn_error_destroy(err); + continue; + } + if (drgn_type_virtuality(t.type) != 0 /*DW_VIRTUALITY_none*/) { + // Virtual function + return true; + } + } + + return false; +} + bool OICodeGen::enumerateClassType(drgn_type *type) { std::string typeName = getStructName(type); VLOG(2) << "Transformed typename: " << typeName << " " << type; @@ -1289,6 +1437,12 @@ bool OICodeGen::enumerateClassType(drgn_type *type) { return true; } } else if (ifGenerateMemberDefinition(typeName)) { + if (isDynamic(type)) { + const auto &children = childClasses[drgn_type_tag(type)]; + for (const auto &child : children) { + enumerateTypesRecurse(child); + } + } if (!generateMemberDefinition(type, typeName)) { return false; } @@ -1645,19 +1799,115 @@ void OICodeGen::getFuncDefClassMembers( } } +void OICodeGen::enumerateDescendants(drgn_type *type, drgn_type *baseType) { + auto it = childClasses.find(drgn_type_tag(type)); + if (it == childClasses.end()) { + return; + } + + // TODO this list may end up containing duplicates + const auto &children = it->second; + descendantClasses[baseType].insert(descendantClasses[baseType].end(), + children.begin(), children.end()); + + for (const auto &child : children) { + enumerateDescendants(child, baseType); + } +} + void OICodeGen::getFuncDefinitionStr(std::string &code, drgn_type *type, const std::string &typeName) { if (classMembersMap.find(type) == classMembersMap.end()) { return; } - code += std::string("void getSizeType(const ") + typeName + - std::string("& t, size_t& returnArg) {\n"); + std::string funcName = "getSizeType"; + if (isDynamic(type)) { + funcName = "getSizeTypeConcrete"; + } + + code += + "void " + funcName + "(const " + typeName + "& t, size_t& returnArg) {\n"; std::unordered_map memberNames; getFuncDefClassMembers(code, type, memberNames); code += "}\n"; + + if (isDynamic(type)) { + enumerateDescendants(type, type); + auto it = descendantClasses.find(type); + if (it == descendantClasses.end()) { + return; + } + const auto &descendants = it->second; + + std::vector> concreteClasses; + concreteClasses.reserve(descendants.size()); + + for (const auto &child : descendants) { + auto fqChildName = *fullyQualifiedName(child); + + // We must split this assignment and append because the C++ standard lacks + // an operator for concatenating std::string and std::string_view... + std::string childVtableName = "vtable for "; + childVtableName += fqChildName; + + auto optVtableSym = symbols.locateSymbol(childVtableName, true); + if (!optVtableSym) { + LOG(ERROR) << "Failed to find vtable address for '" << childVtableName; + LOG(ERROR) << "Falling back to non dynamic mode"; + concreteClasses.clear(); + break; + } + auto vtableSym = *optVtableSym; + + auto oiChildName = *getNameForType(child); + concreteClasses.push_back({oiChildName, vtableSym}); + } + + for (const auto &child : concreteClasses) { + const auto &className = child.first; + code += "void getSizeTypeConcrete(const " + className + + "& t, size_t& returnArg);\n"; + } + + code += std::string("void getSizeType(const ") + typeName + + std::string("& t, size_t& returnArg) {\n"); + // The Itanium C++ ABI defines that the vptr must appear at the zero-offset + // position in any class in which they are present. + code += " auto *vptr = *reinterpret_cast(&t);\n"; + code += " uintptr_t topOffset = *(vptr - 2);\n"; + code += " uintptr_t vptrVal = reinterpret_cast(vptr);\n"; + + for (size_t i = 0; i < concreteClasses.size(); i++) { + // The vptr will point to *somewhere* in the vtable of this object's + // concrete class. The exact offset into the vtable can vary based on a + // number of factors, so we compare the vptr against the vtable range for + // each possible class to determine the concrete type. + // + // This works for C++ compilers which follow the GNU v3 ABI, i.e. GCC and + // Clang. Other compilers may differ. + const auto &[className, vtableSym] = concreteClasses[i]; + uintptr_t vtableMinAddr = vtableSym.addr; + uintptr_t vtableMaxAddr = vtableSym.addr + vtableSym.size; + code += " if (vptrVal >= 0x" + + (boost::format("%x") % vtableMinAddr).str() + " && vptrVal < 0x" + + (boost::format("%x") % vtableMaxAddr).str() + ") {\n"; + code += " SAVE_DATA(" + std::to_string(i) + ");\n"; + code += + " uintptr_t baseAddress = reinterpret_cast(&t) + " + "topOffset;\n"; + code += " getSizeTypeConcrete(*reinterpret_cast(baseAddress), returnArg);\n"; + code += " return;\n"; + code += " }\n"; + } + + code += " SAVE_DATA(-1);\n"; + code += " getSizeTypeConcrete(t, returnArg);\n"; + code += "}\n"; + } } std::string OICodeGen::templateTransformType(const std::string &typeName) { @@ -3666,6 +3916,7 @@ TypeHierarchy OICodeGen::getTypeHierarchy() { .knownDummyTypeList = knownDummyTypeList, .pointerToTypeMap = pointerToTypeMap, .thriftIssetStructTypes = thriftIssetStructTypes, + .descendantClasses = descendantClasses, }; } @@ -3686,13 +3937,17 @@ std::string OICodeGen::Config::toString() const { (packStructs ? "" : "Dont") + "PackStructs,"s + (genPaddingStats ? "" : "Dont") + "GenPaddingStats,"s + (captureThriftIsset ? "" : "Dont") + "CaptureThriftIsset,"s + + (polymorphicInheritance ? "" : "Dont") + "PolymorphicInheritance,"s + ignoreMembers; } std::vector OICodeGen::Config::toOptions() const { return {"", // useDataSegment is always true? - (chaseRawPointers ? "-n" : ""), (packStructs ? "" : "-z"), - (genPaddingStats ? "" : "-w"), (captureThriftIsset ? "-T" : "")}; + (chaseRawPointers ? "-n" : ""), + (packStructs ? "" : "-z"), + (genPaddingStats ? "" : "-w"), + (captureThriftIsset ? "-T" : ""), + (polymorphicInheritance ? "-P" : "")}; } void OICodeGen::initializeCodeGen() { diff --git a/src/OICodeGen.h b/src/OICodeGen.h index ede3517..690624e 100644 --- a/src/OICodeGen.h +++ b/src/OICodeGen.h @@ -58,6 +58,7 @@ class OICodeGen { bool packStructs; bool genPaddingStats; bool captureThriftIsset; + bool polymorphicInheritance; std::set containerConfigPaths{}; std::set defaultHeaders{}; @@ -71,10 +72,11 @@ class OICodeGen { private: // Private constructor. Please use the fallible `OICodeGen::buildFromConfig` // for the expected behaviour. - OICodeGen(const Config &); + OICodeGen(const Config &, SymbolService &); public: - static std::unique_ptr buildFromConfig(const Config &); + static std::unique_ptr buildFromConfig(const Config &, + SymbolService &); bool generate(std::string &code); [[deprecated("Use generate(std::string&) instead.")]] bool @@ -112,6 +114,7 @@ class OICodeGen { bool enumerateTypesRecurse(drgn_type *type); static std::string_view drgnKindStr(drgn_type *type); std::set processedTypes; + bool isDynamic(drgn_type *type) const; private: Config config{}; @@ -139,6 +142,10 @@ class OICodeGen { std::map typedefMap; std::map> parentClasses; + std::map> childClasses; + std::map> descendantClasses; + + SymbolService &symbols; size_t pad_index = 0; std::unordered_map> paddingIndexMap; @@ -206,6 +213,8 @@ class OICodeGen { bool getDrgnTypeName(drgn_type *type, std::string &outName); bool getDrgnTypeNameInt(drgn_type *type, std::string &outName); + bool recordChildren(drgn_type *type); + bool enumerateChildClasses(); bool populateDefsAndDecls(); static void memberTransformName( std::map &templateTransformMap, @@ -224,6 +233,7 @@ class OICodeGen { const std::vector ¶mIdxs, bool &ifStub); bool getContainerTemplateParams(drgn_type *type, bool &ifStub); + void enumerateDescendants(drgn_type *type, drgn_type *baseType); void getFuncDefinitionStr(std::string &code, drgn_type *type, const std::string &typeName); std::optional getDrgnTypeSize(drgn_type *type); diff --git a/src/OID.cpp b/src/OID.cpp index 7da8d41..7b119ed 100644 --- a/src/OID.cpp +++ b/src/OID.cpp @@ -152,6 +152,8 @@ constexpr static OIOpts opts{ "Padded structs will be written to file called PADDING"}, OIOpt{'T', "capture-thrift-isset", no_argument, nullptr, "Capture the isset value for Thrift fields"}, + OIOpt{'P', "polymorphic-inheritance", no_argument, nullptr, + "Follow runtime polymorphic inheritance hierarchies"}, OIOpt{'m', "mode", required_argument, "[prod]", "Allows to specify a mode of operation/group of settings"}, }; @@ -459,6 +461,7 @@ int main(int argc, char *argv[]) { bool packStructs = true; bool dumpDataSegment = false; bool captureThriftIsset = false; + bool polymorphicInheritance = false; Metrics::Tracing _("main"); #ifndef OSS_ENABLE @@ -635,6 +638,9 @@ int main(int argc, char *argv[]) { case 'T': captureThriftIsset = true; break; + case 'P': + polymorphicInheritance = true; + break; case 'h': default: usage(); @@ -679,6 +685,7 @@ int main(int argc, char *argv[]) { .packStructs = packStructs, .genPaddingStats = oidConfig.genPaddingStats, .captureThriftIsset = captureThriftIsset, + .polymorphicInheritance = polymorphicInheritance, }; TreeBuilder::Config tbConfig{ diff --git a/src/OIDebugger.cpp b/src/OIDebugger.cpp index 839133c..e734446 100644 --- a/src/OIDebugger.cpp +++ b/src/OIDebugger.cpp @@ -2942,7 +2942,7 @@ std::optional OIDebugger::generateCode(const irequest &req) { #include "OITraceCode.cpp" ; - auto codegen = OICodeGen::buildFromConfig(generatorConfig); + auto codegen = OICodeGen::buildFromConfig(generatorConfig, *symbols); if (!codegen) { return nullopt; } diff --git a/src/OIGenerator.cpp b/src/OIGenerator.cpp index 9b180b6..3f98d28 100644 --- a/src/OIGenerator.cpp +++ b/src/OIGenerator.cpp @@ -86,8 +86,9 @@ OIGenerator::findOilTypesAndNames(drgnplusplus::program& prog) { bool OIGenerator::generateForType(const OICodeGen::Config& generatorConfig, const OICompiler::Config& compilerConfig, const drgn_qualified_type& type, - const std::string& linkageName) { - auto codegen = OICodeGen::buildFromConfig(generatorConfig); + const std::string& linkageName, + SymbolService& symbols) { + auto codegen = OICodeGen::buildFromConfig(generatorConfig, symbols); if (!codegen) { LOG(ERROR) << "failed to initialise codegen"; return false; @@ -119,7 +120,7 @@ bool OIGenerator::generateForType(const OICodeGen::Config& generatorConfig, return compiler.compile(code, sourcePath, outputPath); } -int OIGenerator::generate(fs::path& primaryObject) { +int OIGenerator::generate(fs::path& primaryObject, SymbolService& symbols) { drgnplusplus::program prog; { @@ -152,7 +153,8 @@ int OIGenerator::generate(fs::path& primaryObject) { size_t failures = 0; for (const auto& [type, linkageName] : oilTypes) { - if (!generateForType(generatorConfig, compilerConfig, type, linkageName)) { + if (!generateForType(generatorConfig, compilerConfig, type, linkageName, + symbols)) { LOG(WARNING) << "failed to generate for symbol `" << linkageName << "`. this is non-fatal but the call will not work."; failures++; diff --git a/src/OIGenerator.h b/src/OIGenerator.h index 13cc322..ea437a2 100644 --- a/src/OIGenerator.h +++ b/src/OIGenerator.h @@ -28,7 +28,7 @@ namespace ObjectIntrospection { class OIGenerator { public: - int generate(fs::path& primaryObject); + int generate(fs::path& primaryObject, SymbolService& symbols); void setOutputPath(fs::path _outputPath) { outputPath = std::move(_outputPath); @@ -50,7 +50,7 @@ class OIGenerator { bool generateForType(const OICodeGen::Config& generatorConfig, const OICompiler::Config& compilerConfig, const drgn_qualified_type& type, - const std::string& linkageName); + const std::string& linkageName, SymbolService& symbols); }; } // namespace ObjectIntrospection diff --git a/src/OILibraryImpl.cpp b/src/OILibraryImpl.cpp index 836f804..48ea7f0 100644 --- a/src/OILibraryImpl.cpp +++ b/src/OILibraryImpl.cpp @@ -185,7 +185,7 @@ int OILibraryImpl::compileCode() { #include "OITraceCode.cpp" ; - auto codegen = OICodeGen::buildFromConfig(generatorConfig); + auto codegen = OICodeGen::buildFromConfig(generatorConfig, *symbols); if (!codegen) { return OIL_COMPILATION_FAILURE; } diff --git a/src/Serialize.cpp b/src/Serialize.cpp index 6402fc8..6678408 100644 --- a/src/Serialize.cpp +++ b/src/Serialize.cpp @@ -374,6 +374,7 @@ void serialize(Archive &ar, struct TypeHierarchy &th, ar &th.knownDummyTypeList; ar &th.pointerToTypeMap; ar &th.thriftIssetStructTypes; + ar &th.descendantClasses; } INSTANCIATE_SERIALIZE(struct TypeHierarchy) diff --git a/src/Serialize.h b/src/Serialize.h index ddef335..d6d8ee6 100644 --- a/src/Serialize.h +++ b/src/Serialize.h @@ -51,7 +51,7 @@ DEFINE_TYPE_VERSION(struct drgn_type, 152, 4) DEFINE_TYPE_VERSION(DrgnClassMemberInfo, 64, 3) DEFINE_TYPE_VERSION(struct drgn_qualified_type, 16, 2) DEFINE_TYPE_VERSION(RootInfo, 48, 2) -DEFINE_TYPE_VERSION(TypeHierarchy, 336, 5) +DEFINE_TYPE_VERSION(TypeHierarchy, 384, 6) #undef DEFINE_TYPE_VERSION diff --git a/src/SymbolService.cpp b/src/SymbolService.cpp index d1b75b1..de967c9 100644 --- a/src/SymbolService.cpp +++ b/src/SymbolService.cpp @@ -101,10 +101,11 @@ SymbolService::~SymbolService() { } struct ModParams { - const char *st; - GElf_Sym s; + std::string_view symName; + GElf_Sym sym; GElf_Addr value; std::vector> &exeAddrs; + bool demangle; }; /** @@ -146,15 +147,20 @@ static int moduleCallback(Dwfl_Module *mod, void ** /* userData */, Elf *elf = nullptr; GElf_Word shndxp = 0; - const char *sname = dwfl_module_getsym_info(mod, i, &m->s, &m->value, - &shndxp, &elf, nullptr); - - if (sname == nullptr || sname[0] == '\0') { + const char *lookupResult = dwfl_module_getsym_info( + mod, i, &m->sym, &m->value, &shndxp, &elf, nullptr); + if (lookupResult == nullptr || lookupResult[0] == '\0') { continue; } + std::string symName = lookupResult; + + if (m->demangle) { + symName = boost::core::demangle(symName.c_str()); + } + switch - GELF_ST_TYPE(m->s.st_info) { + GELF_ST_TYPE(m->sym.st_info) { case STT_SECTION: case STT_FILE: case STT_TLS: @@ -162,10 +168,9 @@ static int moduleCallback(Dwfl_Module *mod, void ** /* userData */, break; case STT_OBJECT: - if (shndxp != SHN_UNDEF && m->st && !strcmp(sname, m->st)) { - VLOG(1) << "Symbol lookup successful for " << sname << " in module " - << name; - m->st = nullptr; + if (shndxp != SHN_UNDEF && symName == m->symName) { + VLOG(1) << "Symbol lookup successful for " << symName + << " in module " << name; return DWARF_CB_ABORT; } break; @@ -176,12 +181,10 @@ static int moduleCallback(Dwfl_Module *mod, void ** /* userData */, * to us here has NOTYPE yet readelf shows me it is defined * as an STT_FUNC. Confused... */ - if (shndxp != SHN_UNDEF && m->st && !strcmp(sname, m->st) && + if (shndxp != SHN_UNDEF && symName == m->symName && isExecutableAddr(m->value, m->exeAddrs)) { - m->st = nullptr; - - VLOG(1) << "Symbol lookup successful for " << sname << " in module " - << name; + VLOG(1) << "Symbol lookup successful for " << symName + << " in module " << name; return DWARF_CB_ABORT; } @@ -201,7 +204,7 @@ static int moduleCallback(Dwfl_Module *mod, void ** /* userData */, * @return - A std::optional with the symbol's information */ std::optional SymbolService::locateSymbol( - const std::string &symName) { + const std::string &symName, bool demangle) { static char *debuginfo_path; static const Dwfl_Callbacks proc_callbacks{ .find_elf = dwfl_linux_proc_find_elf, @@ -259,15 +262,18 @@ std::optional SymbolService::locateSymbol( LOG(ERROR) << "dwfl_report_end: " << dwfl_errmsg(-1); } - ModParams m = { - .st = symName.c_str(), .s = {}, .value = 0, .exeAddrs = executableAddrs}; + ModParams m = {.symName = symName, + .sym = {}, + .value = 0, + .exeAddrs = executableAddrs, + .demangle = demangle}; dwfl_getmodules(dwfl, moduleCallback, (void *)&m, 0); if (m.value == 0) { return std::nullopt; } - return SymbolInfo{m.value, m.s.st_size}; + return SymbolInfo{m.value, m.sym.st_size}; } static std::string bytesToHexString(const unsigned char *bytes, int nbbytes) { diff --git a/src/SymbolService.h b/src/SymbolService.h index 5b82732..af2ff12 100644 --- a/src/SymbolService.h +++ b/src/SymbolService.h @@ -43,7 +43,8 @@ class SymbolService { struct drgn_program *getDrgnProgram(); std::optional locateBuildID(); - std::optional locateSymbol(const std::string &); + std::optional locateSymbol(const std::string &, + bool demangle = false); std::shared_ptr findFuncDesc(const irequest &); std::shared_ptr findGlobalDesc(const std::string &); diff --git a/src/TreeBuilder.cpp b/src/TreeBuilder.cpp index 26de16b..12fdcfe 100644 --- a/src/TreeBuilder.cpp +++ b/src/TreeBuilder.cpp @@ -444,7 +444,23 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) { } else if (isContainer(variable)) { processContainer(variable, node); } else { - auto entry = th->classMembersMap.find(variable.type); + drgn_type *objectType = variable.type; + if (auto it = th->descendantClasses.find(objectType); + it != th->descendantClasses.end()) { + // The first item of data in dynamic classes identifies which + // concrete type we should process it as, represented as an index + // into the vector of child classes, or -1 to processes this type + // as itself. + const auto &descendants = it->second; + auto val = next(); + if (val != (uint64_t)-1) { + objectType = descendants[val]; + node.typeName = drgnTypeToName(objectType); + node.staticSize = getDrgnTypeSize(objectType); + } + } + + auto entry = th->classMembersMap.find(objectType); if (entry == th->classMembersMap.end() || entry->second.empty()) { break; } @@ -455,7 +471,7 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) { auto childID = node.children->first; bool captureThriftIsset = - th->thriftIssetStructTypes.contains(variable.type); + th->thriftIssetStructTypes.contains(objectType); for (std::size_t i = 0; i < members.size(); i++) { std::optional isset; diff --git a/test/integration/inheritance_polymorphic.toml b/test/integration/inheritance_polymorphic.toml index 21665aa..61838ed 100644 --- a/test/integration/inheritance_polymorphic.toml +++ b/test/integration/inheritance_polymorphic.toml @@ -20,6 +20,8 @@ definitions = ''' ''' [cases] [cases.a_as_a] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const A&"] arg_types = ["A"] setup = "return {};" @@ -33,6 +35,8 @@ definitions = ''' ]}]''' [cases.b_as_a] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const A&"] arg_types = ["B"] setup = ''' @@ -41,14 +45,17 @@ definitions = ''' return b; ''' expect_json = '''[{ - "typeName":"A", - "staticSize":16, - "dynamicSize":0, + "typeName":"B", + "staticSize":40, + "dynamicSize":12, "members":[ {"staticSize":8, "dynamicSize":0}, - {"name":"int_a", "staticSize":4, "dynamicSize":0} + {"name":"int_a", "staticSize":4, "dynamicSize":0}, + {"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4} ]}]''' [cases.b_as_b] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const B&"] arg_types = ["B"] setup = ''' @@ -67,6 +74,8 @@ definitions = ''' ]}]''' [cases.c_as_a] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const A&"] arg_types = ["C"] setup = ''' @@ -75,14 +84,18 @@ definitions = ''' return c; ''' expect_json = '''[{ - "typeName":"A", - "staticSize":16, - "dynamicSize":0, + "typeName":"C", + "staticSize":48, + "dynamicSize":12, "members":[ {"staticSize":8, "dynamicSize":0}, - {"name":"int_a", "staticSize":4, "dynamicSize":0} + {"name":"int_a", "staticSize":4, "dynamicSize":0}, + {"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}, + {"name":"int_c", "staticSize":4, "dynamicSize":0} ]}]''' [cases.c_as_b] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const B&"] arg_types = ["C"] setup = ''' @@ -91,15 +104,18 @@ definitions = ''' return c; ''' expect_json = '''[{ - "typeName":"B", - "staticSize":40, + "typeName":"C", + "staticSize":48, "dynamicSize":12, "members":[ {"staticSize":8, "dynamicSize":0}, {"name":"int_a", "staticSize":4, "dynamicSize":0}, - {"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4} + {"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}, + {"name":"int_c", "staticSize":4, "dynamicSize":0} ]}]''' [cases.c_as_c] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const C&"] arg_types = ["C"] setup = ''' diff --git a/test/integration/inheritance_polymorphic_diamond.toml b/test/integration/inheritance_polymorphic_diamond.toml index 66de16a..59c8a7d 100644 --- a/test/integration/inheritance_polymorphic_diamond.toml +++ b/test/integration/inheritance_polymorphic_diamond.toml @@ -26,6 +26,8 @@ definitions = ''' ''' [cases] [cases.root_as_root] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const Root&"] arg_types = ["Root"] setup = "return {};" @@ -39,6 +41,8 @@ definitions = ''' ]}]''' [cases.middle1_as_root] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const Root&"] arg_types = ["Middle1"] setup = ''' @@ -47,14 +51,17 @@ definitions = ''' return m; ''' expect_json = '''[{ - "typeName":"Root", - "staticSize":16, - "dynamicSize":0, + "typeName":"Middle1", + "staticSize":40, + "dynamicSize":12, "members":[ {"staticSize":8, "dynamicSize":0}, - {"name":"int_root", "staticSize":4, "dynamicSize":0} + {"name":"int_root", "staticSize":4, "dynamicSize":0}, + {"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4} ]}]''' [cases.middle1_as_middle1] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const Middle1&"] arg_types = ["Middle1"] setup = ''' @@ -73,6 +80,8 @@ definitions = ''' ]}]''' [cases.middle2_as_root] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const Root&"] arg_types = ["Middle2"] setup = ''' @@ -81,14 +90,17 @@ definitions = ''' return m; ''' expect_json = '''[{ - "typeName":"Root", - "staticSize":16, - "dynamicSize":0, + "typeName":"Middle2", + "staticSize":40, + "dynamicSize":8, "members":[ {"staticSize":8, "dynamicSize":0}, - {"name":"int_root", "staticSize":4, "dynamicSize":0} + {"name":"int_root", "staticSize":4, "dynamicSize":0}, + {"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4} ]}]''' [cases.middle2_as_middle2] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const Middle2&"] arg_types = ["Middle2"] setup = ''' @@ -107,6 +119,8 @@ definitions = ''' ]}]''' [cases.child_as_middle1_root] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] # We need to explicitly cast from Child to Middle1 before going to root to # resolve the diamond problem param_types = ["const Root&"] @@ -118,14 +132,21 @@ definitions = ''' return static_cast(*c); ''' expect_json = '''[{ - "typeName":"Root", - "staticSize":16, - "dynamicSize":0, + "typeName":"Child", + "staticSize":88, + "dynamicSize":20, "members":[ {"staticSize":8, "dynamicSize":0}, - {"name":"int_root", "staticSize":4, "dynamicSize":0} + {"name":"int_root", "staticSize":4, "dynamicSize":0}, + {"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}, + {"staticSize":8, "dynamicSize":0}, + {"name":"int_root", "staticSize":4, "dynamicSize":0}, + {"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4}, + {"name":"int_child", "staticSize":4, "dynamicSize":0} ]}]''' [cases.child_as_middle2_root] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] # We need to explicitly cast from Child to Middle2 before going to root to # resolve the diamond problem param_types = ["const Root&"] @@ -137,14 +158,21 @@ definitions = ''' return static_cast(*c); ''' expect_json = '''[{ - "typeName":"Root", - "staticSize":16, - "dynamicSize":0, + "typeName":"Child", + "staticSize":88, + "dynamicSize":20, "members":[ {"staticSize":8, "dynamicSize":0}, - {"name":"int_root", "staticSize":4, "dynamicSize":0} + {"name":"int_root", "staticSize":4, "dynamicSize":0}, + {"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}, + {"staticSize":8, "dynamicSize":0}, + {"name":"int_root", "staticSize":4, "dynamicSize":0}, + {"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4}, + {"name":"int_child", "staticSize":4, "dynamicSize":0} ]}]''' [cases.child_as_middle1] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const Middle1&"] arg_types = ["Child"] setup = ''' @@ -154,15 +182,21 @@ definitions = ''' return c; ''' expect_json = '''[{ - "typeName":"Middle1", - "staticSize":40, - "dynamicSize":12, + "typeName":"Child", + "staticSize":88, + "dynamicSize":20, "members":[ {"staticSize":8, "dynamicSize":0}, {"name":"int_root", "staticSize":4, "dynamicSize":0}, - {"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4} + {"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}, + {"staticSize":8, "dynamicSize":0}, + {"name":"int_root", "staticSize":4, "dynamicSize":0}, + {"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4}, + {"name":"int_child", "staticSize":4, "dynamicSize":0} ]}]''' [cases.child_as_middle2] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const Middle2&"] arg_types = ["Child"] setup = ''' @@ -172,15 +206,21 @@ definitions = ''' return c; ''' expect_json = '''[{ - "typeName":"Middle2", - "staticSize":40, - "dynamicSize":8, + "typeName":"Child", + "staticSize":88, + "dynamicSize":20, "members":[ {"staticSize":8, "dynamicSize":0}, {"name":"int_root", "staticSize":4, "dynamicSize":0}, - {"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4} + {"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}, + {"staticSize":8, "dynamicSize":0}, + {"name":"int_root", "staticSize":4, "dynamicSize":0}, + {"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4}, + {"name":"int_child", "staticSize":4, "dynamicSize":0} ]}]''' [cases.child_as_child] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const Child&"] arg_types = ["Child"] setup = ''' diff --git a/test/integration/inheritance_polymorphic_non_dynamic_base.toml b/test/integration/inheritance_polymorphic_non_dynamic_base.toml index 8fefcb1..2bb214f 100644 --- a/test/integration/inheritance_polymorphic_non_dynamic_base.toml +++ b/test/integration/inheritance_polymorphic_non_dynamic_base.toml @@ -19,6 +19,8 @@ definitions = ''' ''' [cases] [cases.a_as_a] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const A&"] arg_types = ["A"] setup = "return {};" @@ -31,6 +33,8 @@ definitions = ''' ]}]''' [cases.b_as_a] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const A&"] arg_types = ["B"] setup = ''' @@ -46,6 +50,8 @@ definitions = ''' {"name":"int_a", "staticSize":4, "dynamicSize":0} ]}]''' [cases.b_as_b] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const B&"] arg_types = ["B"] setup = ''' @@ -64,6 +70,8 @@ definitions = ''' ]}]''' [cases.c_as_a] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const A&"] arg_types = ["C"] setup = ''' @@ -79,6 +87,8 @@ definitions = ''' {"name":"int_a", "staticSize":4, "dynamicSize":0} ]}]''' [cases.c_as_b] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const B&"] arg_types = ["C"] setup = ''' @@ -87,15 +97,18 @@ definitions = ''' return c; ''' expect_json = '''[{ - "typeName":"B", - "staticSize":40, + "typeName":"C", + "staticSize":48, "dynamicSize":12, "members":[ {"name":"int_a", "staticSize":4, "dynamicSize":0}, {"staticSize":8, "dynamicSize":0}, - {"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4} + {"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}, + {"name":"int_c", "staticSize":4, "dynamicSize":0} ]}]''' [cases.c_as_c] + oil_skip = "Polymorphic inheritance disabled in OIL" + cli_options = ["--polymorphic-inheritance"] param_types = ["const C&"] arg_types = ["C"] setup = ''' diff --git a/tools/OILGen.cpp b/tools/OILGen.cpp index 00e1b4b..5ebaa21 100644 --- a/tools/OILGen.cpp +++ b/tools/OILGen.cpp @@ -90,5 +90,7 @@ int main(int argc, char* argv[]) { oigen.setConfigFilePath(std::move(configFilePath)); oigen.setSourceFileDumpPath(sourceFileDumpPath); - return oigen.generate(primaryObject); + SymbolService symbols(primaryObject); + + return oigen.generate(primaryObject, symbols); }