From 4c858e7be483b892807ebdb096b0ff4c2fd5f08e Mon Sep 17 00:00:00 2001 From: Giuliano Belinassi Date: Thu, 22 Aug 2024 18:03:51 -0300 Subject: [PATCH] Add option to pass two debuginfos SUSE packages contain the stripped .so file with .dynsym and the debuginfo (.debug) provided in -debuginfo packages. We need both of them in Userspace Livepatching since 15.5 for proper analysis. This commit patches the ElfCache and InlineAnalysis so it can use both of them. For kernel we expect it to only use one ELF file for now (the first one). Signed-off-by: Giuliano Belinassi --- libcextract/ArgvParser.cpp | 9 +++++++-- libcextract/ArgvParser.hh | 7 ++++--- libcextract/ElfCXX.cpp | 15 +++++++++++++-- libcextract/ElfCXX.hh | 29 +++++++++++++++++++++++++++-- libcextract/InlineAnalysis.cpp | 23 ++++++++++++++--------- libcextract/InlineAnalysis.hh | 30 ++++++++++++++++++++++++++---- libcextract/Passes.hh | 6 +++--- 7 files changed, 94 insertions(+), 25 deletions(-) diff --git a/libcextract/ArgvParser.cpp b/libcextract/ArgvParser.cpp index c428aca..1686a6b 100644 --- a/libcextract/ArgvParser.cpp +++ b/libcextract/ArgvParser.cpp @@ -74,7 +74,7 @@ ArgvParser::ArgvParser(int argc, char **argv) Ibt(false), AllowLateExternalization(false), PatchObject(""), - DebuginfoPath(nullptr), + Debuginfos(), IpaclonesPath(nullptr), SymversPath(nullptr), DescOutputPath(nullptr), @@ -89,6 +89,11 @@ ArgvParser::ArgvParser(int argc, char **argv) Insert_Required_Parameters(); + const char *DebuginfoPath = nullptr; + if (Debuginfos.size() > 0) { + DebuginfoPath = Debuginfos[0].c_str(); + } + /* For kernel, check if the object patch is not the same as DebugInfo. If they * are not the same, it means that the module from PatchObject is builtin, so * assign vmlinux to PatchObject. */ @@ -252,7 +257,7 @@ bool ArgvParser::Handle_Clang_Extract_Arg(const char *str) return true; } if (prefix("-DCE_DEBUGINFO_PATH=", str)) { - DebuginfoPath = Extract_Single_Arg_C(str); + Debuginfos = Extract_Args(str); return true; } diff --git a/libcextract/ArgvParser.hh b/libcextract/ArgvParser.hh index 77546b5..c0a85eb 100644 --- a/libcextract/ArgvParser.hh +++ b/libcextract/ArgvParser.hh @@ -94,9 +94,9 @@ class ArgvParser return PatchObject; } - inline const char *Get_Debuginfo_Path(void) + inline std::vector &Get_Debuginfo_Path(void) { - return DebuginfoPath; + return Debuginfos; } inline const char *Get_Ipaclones_Path(void) @@ -163,7 +163,8 @@ class ArgvParser bool AllowLateExternalization; std::string PatchObject; - const char *DebuginfoPath; + std::vector Debuginfos; + const char *IpaclonesPath; const char *SymversPath; diff --git a/libcextract/ElfCXX.cpp b/libcextract/ElfCXX.cpp index a5a1223..d3c7f4c 100644 --- a/libcextract/ElfCXX.cpp +++ b/libcextract/ElfCXX.cpp @@ -335,19 +335,30 @@ void ElfSymbolCache::Insert_Symbols_Into_Hash(SymbolTableHash &map, ElfSection & } ElfSymbolCache::ElfSymbolCache(ElfObject &eo) - : EO(eo) + : ElfSymbolCache() +{ + Analyze_ELF(eo); +} + +void ElfSymbolCache::Analyze_ELF(ElfObject &eo) { /* Look for dynsym and symtab sections. */ - for (auto it = EO.section_begin(); it != EO.section_end(); ++it) + for (auto it = eo.section_begin(); it != eo.section_end(); ++it) { ElfSection §ion = *it; switch (section.Get_Section_Type()) { case SHT_DYNSYM: Insert_Symbols_Into_Hash(DynsymMap, section); + + assert(ObjectPath == "" && "Multiple libraries passed as debuginfo?"); + ObjectPath = eo.Get_Path(); break; case SHT_SYMTAB: Insert_Symbols_Into_Hash(SymtabMap, section); + + /* We can also have symtab in the .so library. */ + DebuginfoPath = eo.Get_Path(); break; case SHT_PROGBITS: diff --git a/libcextract/ElfCXX.hh b/libcextract/ElfCXX.hh index 35dbbdc..422a503 100644 --- a/libcextract/ElfCXX.hh +++ b/libcextract/ElfCXX.hh @@ -346,6 +346,18 @@ class ElfSymbolCache /** Build cache from ElfObject). */ ElfSymbolCache(ElfObject &eo); + /** Build empty cache to be filled later. */ + ElfSymbolCache(void) + : DynsymMap(), + SymtabMap(), + Mod(""), + DebuginfoPath(""), + ObjectPath("") + {}; + + /** Analyze ELF object. */ + void Analyze_ELF(ElfObject &eo); + /* Get symbol if available in the Dynsym table. Or 0 if not available. */ inline unsigned char Get_Symbol_Info_Dynsym(const std::string &sym) { @@ -377,6 +389,16 @@ class ElfSymbolCache std::vector Get_All_Symbols(void); + inline const std::string &Get_Debuginfo_Path(void) const + { + return DebuginfoPath; + } + + inline const std::string &Get_Object_Path(void) const + { + return ObjectPath; + } + /** Dump for debugging reasons. */ void Dump_Cache(void); @@ -394,6 +416,9 @@ class ElfSymbolCache /** Kernel module name, if .modinfo section is present. */ std::string Mod; - /** Reference to the ElfObject used to build this object. */ - ElfObject &EO; + /** Path to the debuginfo object (contains .symtab section). */ + std::string DebuginfoPath; + + /** Path to the .so file object (contains .dynsym section). */ + std::string ObjectPath; }; diff --git a/libcextract/InlineAnalysis.cpp b/libcextract/InlineAnalysis.cpp index 06a2c7f..6067734 100644 --- a/libcextract/InlineAnalysis.cpp +++ b/libcextract/InlineAnalysis.cpp @@ -22,10 +22,10 @@ #include #include -InlineAnalysis::InlineAnalysis(const char *elf_path, const char *ipaclones_path, - const char *symvers_path, bool is_kernel) - : ElfObj(nullptr), - ElfCache(nullptr), +InlineAnalysis::InlineAnalysis(const std::vector &elfs_path, + const char *ipaclones_path, + const char *symvers_path, bool is_kernel) + : ElfCache(nullptr), Ipa(nullptr), Symv(nullptr), Kernel(is_kernel) @@ -33,9 +33,16 @@ InlineAnalysis::InlineAnalysis(const char *elf_path, const char *ipaclones_path, try { /* Debuginfo information is not needed for inline analysis. But is desired for better precision. That is why whe declare those objects dynamically. */ - if (elf_path) { - ElfObj = new ElfObject(elf_path); - ElfCache = new ElfSymbolCache(*ElfObj); + if (elfs_path.size() > 0) { + /* Only create an ElfSymbolCache if we received elfs from command line. */ + ElfCache = new ElfSymbolCache(); + } + + /* Initialize the SymbolCache. */ + for (auto it = elfs_path.begin(); it != elfs_path.end(); ++it) { + const std::string &path = *it; + ElfObject elf(path); + ElfCache->Analyze_ELF(elf); } if (ipaclones_path) { @@ -62,8 +69,6 @@ InlineAnalysis::~InlineAnalysis(void) { if (ElfCache) delete ElfCache; - if (ElfObj) - delete ElfObj; if (Ipa) delete Ipa; if (Symv) diff --git a/libcextract/InlineAnalysis.hh b/libcextract/InlineAnalysis.hh index c3aeb71..2076040 100644 --- a/libcextract/InlineAnalysis.hh +++ b/libcextract/InlineAnalysis.hh @@ -48,7 +48,15 @@ class InlineAnalysis available, and ipaclone_path can be a directory full of many ipa-clones generated through LTO or not. Symvers can be NULL is we are creating a userspace livepatch */ - InlineAnalysis(const char *elf_path, const char *ipaclone_path, const char *symvers_path, bool is_kernel); + InlineAnalysis(const std::vector &elfs_path, + const char *ipaclone_path, const char *symvers_path, + bool is_kernel); + + InlineAnalysis(const char *elf_path, const char *ipaclone_path, + const char *symvers_path, bool is_kernel) + : InlineAnalysis(std::vector({elf_path}), + ipaclone_path, symvers_path, is_kernel) + {} ~InlineAnalysis(void); @@ -93,12 +101,27 @@ class InlineAnalysis /** True if this class was built with debuginfo enabled. */ inline bool Have_Debuginfo(void) { - return ElfObj && ElfCache; + return ElfCache; } inline const std::string &Get_Debuginfo_Path(void) { - return ElfObj->Get_Path(); + if (ElfCache) { + return ElfCache->Get_Debuginfo_Path(); + } + + static const std::string empty; + return empty; + } + + inline const std::string &Get_Object_Path(void) + { + if (ElfCache) { + return ElfCache->Get_Object_Path(); + } + + static const std::string empty; + return empty; } /** Check if we have Ipa-clones information. */ @@ -143,7 +166,6 @@ class InlineAnalysis /** Put color information in the graphviz .DOT file. */ void Print_Node_Colors(const std::set &set, FILE *fp); - ElfObject *ElfObj; ElfSymbolCache *ElfCache; IpaClones *Ipa; Symvers *Symv; diff --git a/libcextract/Passes.hh b/libcextract/Passes.hh index 4f2a295..0e52fbc 100644 --- a/libcextract/Passes.hh +++ b/libcextract/Passes.hh @@ -58,7 +58,7 @@ class PassManager { PatchObject(args.Get_PatchObject()), HeadersToExpand(args.Get_Headers_To_Expand()), ClangArgs(args.Get_Args_To_Clang()), - DebuginfoPath(args.Get_Debuginfo_Path()), + Debuginfos(args.Get_Debuginfo_Path()), IpaclonesPath(args.Get_Ipaclones_Path()), SymversPath(args.Get_Symvers_Path()), DscOutputPath(args.Get_Dsc_Output_Path()), @@ -67,7 +67,7 @@ class PassManager { args.Get_Include_Expansion_Policy(), Kernel)), NamesLog(), PassNum(0), - IA(DebuginfoPath, IpaclonesPath, SymversPath, args.Is_Kernel()) + IA(Debuginfos, IpaclonesPath, SymversPath, args.Is_Kernel()) { } @@ -121,7 +121,7 @@ class PassManager { std::vector &ClangArgs; /* Path to Debuginfo, if exists. */ - const char *DebuginfoPath; + std::vector &Debuginfos; /* Path to Ipaclones, if exists. */ const char *IpaclonesPath;