diff --git a/libcextract/ArgvParser.cpp b/libcextract/ArgvParser.cpp index 990c75e..617b503 100644 --- a/libcextract/ArgvParser.cpp +++ b/libcextract/ArgvParser.cpp @@ -65,6 +65,7 @@ ArgvParser::ArgvParser(int argc, char **argv) DumpPasses(false), RenameSymbols(false), Kernel(false), + Ibt(false), DebuginfoPath(nullptr), IpaclonesPath(nullptr), SymversPath(nullptr), @@ -166,6 +167,20 @@ bool ArgvParser::Handle_Clang_Extract_Arg(const char *str) return false; } + if (!strcmp("-D__USE_IBT__", str)) { + Ibt = true; + return false; + } + + if (prefix("-DKBUILD_MODNAME=", str)) { + /* Avoid storing double quotes */ + PatchObject = Extract_Single_Arg(str); + PatchObject.erase(std::remove(PatchObject.begin(), PatchObject.end(), '\"' ), + PatchObject.end()); + + return false; + } + if (prefix("-DCE_EXTRACT_FUNCTIONS=", str)) { FunctionsToExtract = Extract_Args(str); diff --git a/libcextract/ArgvParser.hh b/libcextract/ArgvParser.hh index 81b912e..02ddc04 100644 --- a/libcextract/ArgvParser.hh +++ b/libcextract/ArgvParser.hh @@ -84,6 +84,16 @@ class ArgvParser return Kernel; } + inline bool Has_Ibt(void) + { + return Ibt; + } + + inline std::string Get_PatchObject(void) + { + return PatchObject; + } + inline const char *Get_Debuginfo_Path(void) { return DebuginfoPath; @@ -140,6 +150,9 @@ class ArgvParser bool DumpPasses; bool RenameSymbols; bool Kernel; + /* If the file was compiled with IBT support */ + bool Ibt; + std::string PatchObject; const char *DebuginfoPath; const char *IpaclonesPath; diff --git a/libcextract/Passes.cpp b/libcextract/Passes.cpp index ca6ffa9..f1b6c34 100644 --- a/libcextract/Passes.cpp +++ b/libcextract/Passes.cpp @@ -437,7 +437,7 @@ class FunctionExternalizerPass : public Pass virtual bool Run_Pass(PassManager::Context *ctx) { /* Issue externalization. */ - SymbolExternalizer externalizer(ctx->AST.get(), ctx->IA, ctx->DumpPasses); + SymbolExternalizer externalizer(ctx->AST.get(), ctx->IA, ctx->Ibt, ctx->PatchObject, ctx->DumpPasses); externalizer.Externalize_Symbols(ctx->Externalize); if (ctx->RenameSymbols) { /* The FuncExtractNames will be modified, as the function will be diff --git a/libcextract/Passes.hh b/libcextract/Passes.hh index 41a5ccb..f60bf79 100644 --- a/libcextract/Passes.hh +++ b/libcextract/Passes.hh @@ -53,6 +53,8 @@ class PassManager { DumpPasses(args.Should_Dump_Passes()), RenameSymbols(args.Should_Rename_Symbols()), Kernel(args.Is_Kernel()), + Ibt(args.Has_Ibt()), + PatchObject(args.Get_PatchObject()), HeadersToExpand(args.Get_Headers_To_Expand()), ClangArgs(args.Get_Args_To_Clang()), DebuginfoPath(args.Get_Debuginfo_Path()), @@ -102,6 +104,12 @@ class PassManager { /** If the source code comes from Linux Kernel */ bool Kernel; + /** If the code was compiled with IBT support */ + bool Ibt; + + /** Object that will be patched. */ + std::string PatchObject; + /** Which includes we must expand? */ std::vector HeadersToExpand; diff --git a/libcextract/SymbolExternalizer.cpp b/libcextract/SymbolExternalizer.cpp index 80036a9..9f94ebf 100644 --- a/libcextract/SymbolExternalizer.cpp +++ b/libcextract/SymbolExternalizer.cpp @@ -326,6 +326,19 @@ void TextModifications::Commit(void) /* Insert into the list of FileIDs. */ const FileEntry *fentry = SM.getFileEntryForID(begin_id); + + /* There are some cases where the fentry is known to return NULL. Check if + those are the cases we already acknownledged. */ + if (fentry == nullptr) { + PresumedLoc ploc = SM.getPresumedLoc(a.ToChange.getBegin()); + if (ploc.getFilename() == StringRef("")) { + /* Locations comming from the command line can be ignored. */ + continue; + } + + /* Crash with assertion. */ + assert(fentry && "FileEntry is NULL on a non-acknowledged case"); + } /* Insert the FileEntry if we don't have one. */ if (FileEntryMap.find(fentry) == FileEntryMap.end()) { /* Insert it. */ @@ -520,6 +533,11 @@ void SymbolExternalizer::Remove_Text(const SourceRange &range, int prio) Replace_Text(range, "", prio); } +void SymbolExternalizer::Insert_Text(const SourceRange &range, StringRef text, int prio) +{ + Replace_Text(range, text, prio); +} + VarDecl *SymbolExternalizer::Create_Externalized_Var(DeclaratorDecl *decl, const std::string &name) { /* Hack a new Variable Declaration node in which holds the address of our @@ -548,6 +566,10 @@ VarDecl *SymbolExternalizer::Create_Externalized_Var(DeclaratorDecl *decl, const /* Create a type that is a pointer to the externalized object's type. */ QualType pointer_to = astctx.getPointerType(decl->getType()); + /* For IBT, create an extern variable with the same type from the original */ + if (Ibt) + pointer_to = decl->getType(); + /* Get context of decl. */ DeclContext *decl_ctx; @@ -558,13 +580,22 @@ VarDecl *SymbolExternalizer::Create_Externalized_Var(DeclaratorDecl *decl, const decl_ctx = decl->getDeclContext(); } + /* + * For normal externalization, create a static variable. When dealing with IBT + * restrictions, create an extern variable that will be sorted out later by + * the code that is using it, like Linux for example. + */ + StorageClass sc = SC_Static; + if (Ibt) + sc = SC_Extern; + VarDecl *ret = VarDecl::Create(astctx, decl_ctx, decl->getBeginLoc(), decl->getEndLoc(), id, pointer_to, nullptr, - SC_Static + sc ); /* return node. */ @@ -748,7 +779,7 @@ bool SymbolExternalizer::_Externalize_Symbol(const std::string &to_externalize, TypeUpdaterVisitor(*this, new_decl, to_externalize, wrap) .TraverseDecl(decl); - FunctionUpdater(*this, new_decl, to_externalize, wrap) + FunctionUpdater(*this, new_decl, to_externalize, wrap || Ibt) .Update_References_To_Symbol(decl); } @@ -758,9 +789,10 @@ bool SymbolExternalizer::_Externalize_Symbol(const std::string &to_externalize, /* If we found the first instance of the function we want to externalize, then proceed to create and replace the function declaration node with a variable declaration node of proper type. */ - std::string new_name = EXTERNALIZED_PREFIX + decl->getName().str(); + std::string old_name = decl->getName().str(); + std::string new_name = EXTERNALIZED_PREFIX + old_name; new_decl = Create_Externalized_Var(decl, new_name); - Log.push_back({.OldName = decl->getName().str(), + Log.push_back({.OldName = old_name, .NewName = new_name, .Type = ExternalizationType::STRONG}); @@ -768,6 +800,10 @@ bool SymbolExternalizer::_Externalize_Symbol(const std::string &to_externalize, std::string o; llvm::raw_string_ostream outstr(o); new_decl->print(outstr, AST->getLangOpts()); + if (Ibt) { + outstr << " \\\n" << "\tKLP_RELOC_SYMBOL(" << PatchObject << ", " << + IA.Get_Symbol_Module(old_name) << ", " << old_name << ")"; + } outstr << ";\n"; Replace_Text(decl->getSourceRange(), outstr.str(), 1000); @@ -775,8 +811,15 @@ bool SymbolExternalizer::_Externalize_Symbol(const std::string &to_externalize, must_update = true; wrap = false; - /* Update any macros that may reference the symbol. */ std::string replacement = "(*" + new_decl->getName().str() + ")"; + /* + * IBT uses extern variables, so we need to use the same type from the + * private symbol. + */ + if (Ibt) + replacement = new_decl->getName().str(); + + /* Update any macros that may reference the symbol. */ Rewrite_Macros(to_externalize, replacement); /* Slaps the new node into the position of where was the function @@ -892,6 +935,14 @@ void SymbolExternalizer::Externalize_Symbol(const std::string &to_externalize) void SymbolExternalizer::Externalize_Symbols(std::vector const &to_externalize_array) { + if (Ibt) { + SourceManager &sm = AST->getSourceManager(); + FileID fi = sm.getMainFileID(); + SourceLocation sl = sm.getLocForStartOfFile(fi); + SourceRange sr(sl, sl); + Insert_Text(sr, "#include \n", 1000); + } + for (const std::string &to_externalize : to_externalize_array) { Externalize_Symbol(to_externalize); } diff --git a/libcextract/SymbolExternalizer.hh b/libcextract/SymbolExternalizer.hh index 9ba2234..b2e943e 100644 --- a/libcextract/SymbolExternalizer.hh +++ b/libcextract/SymbolExternalizer.hh @@ -182,11 +182,13 @@ class TextModifications class SymbolExternalizer { public: - SymbolExternalizer(ASTUnit *ast, InlineAnalysis &ia, bool dump = false) + SymbolExternalizer(ASTUnit *ast, InlineAnalysis &ia, bool ibt, std::string patch_object, bool dump = false) : AST(ast), MW(ast->getPreprocessor()), TM(ast, dump), - IA(ia) + IA(ia), + Ibt(ibt), + PatchObject(patch_object) { } @@ -269,6 +271,7 @@ class SymbolExternalizer used in case that there are two replacements to the same piece of text. */ void Replace_Text(const SourceRange &range, StringRef new_name, int priority); void Remove_Text(const SourceRange &range, int priority); + void Insert_Text(const SourceRange &range, StringRef text, int priority); /** AST in analysis. */ ASTUnit *AST; @@ -284,4 +287,10 @@ class SymbolExternalizer /** Log of changed names. */ std::vector Log; + + /** Defines the method that a private symbol will be searched. */ + bool Ibt; + + /* Name of the object that will be patched. */ + std::string PatchObject; }; diff --git a/testsuite/linux/Modules.symvers b/testsuite/linux/Modules.symvers new file mode 100644 index 0000000..141e99d --- /dev/null +++ b/testsuite/linux/Modules.symvers @@ -0,0 +1 @@ +0x00000000 crc32c lib/libcrc32c EXPORT_SYMBOL diff --git a/testsuite/linux/ibt-1.c b/testsuite/linux/ibt-1.c new file mode 100644 index 0000000..bbe9ee9 --- /dev/null +++ b/testsuite/linux/ibt-1.c @@ -0,0 +1,19 @@ +/* { dg-options "-DCE_EXTRACT_FUNCTIONS=f -DCE_SYMVERS_PATH=../testsuite/linux/Modules.symvers -DCE_RENAME_SYMBOLS -nostdinc -I../testsuite/linux -DKBUILD_MODNAME=libcrc32c -D__USE_IBT__ -D__KERNEL__ -DCE_KEEP_INCLUDES" } */ + +typedef unsigned int u32; + +u32 crc32c(u32 crc, const void *address, unsigned int length); + +int f(void) +{ + u32 lcrc = 0; + void *addr = 0; + unsigned int len = 0; + + (void)crc32c(lcrc, addr, len); + return 0; +} + +/* { dg-final { scan-tree-dump "u32 klpe_crc32c|u32 \(klpe_crc32c\)" } } */ +/* { dg-final { scan-tree-dump "KLP_RELOC_SYMBOL\(libcrc32c, libcrc32c, crc32c\)" } } */ +/* { dg-final { scan-tree-dump-not "\(\*klpe_crc32c\)" } } */ diff --git a/testsuite/linux/ibt-2.c b/testsuite/linux/ibt-2.c new file mode 100644 index 0000000..a47acae --- /dev/null +++ b/testsuite/linux/ibt-2.c @@ -0,0 +1,23 @@ +/* { dg-options "-DCE_EXTRACT_FUNCTIONS=f -DCE_SYMVERS_PATH=../testsuite/linux/Modules.symvers -DCE_RENAME_SYMBOLS -nostdinc -I../testsuite/linux -DKBUILD_MODNAME=libcrc32c -D__USE_IBT__ -D__KERNEL__ -DCE_KEEP_INCLUDES" } */ + +/* Check why the include is not being output. */ +/* { dg-xfail }*/ + +typedef unsigned int u32; + +u32 crc32c(u32 crc, const void *address, unsigned int length); + +int f(void) +{ + u32 lcrc = 0; + void *addr = 0; + unsigned int len = 0; + + (void)crc32c(lcrc, addr, len); + return 0; +} + +/* { dg-final { scan-tree-dump "#include " } } */ +/* { dg-final { scan-tree-dump "u32 klpe_crc32c|u32 \(klpe_crc32c\)" } } */ +/* { dg-final { scan-tree-dump "KLP_RELOC_SYMBOL\(libcrc32c, libcrc32c, crc32c\)" } } */ +/* { dg-final { scan-tree-dump-not "\(\*klpe_crc32c\)" } } */ diff --git a/testsuite/linux/ibt-3.c b/testsuite/linux/ibt-3.c new file mode 100644 index 0000000..66e706c --- /dev/null +++ b/testsuite/linux/ibt-3.c @@ -0,0 +1,22 @@ +/* { dg-options "-DCE_EXTRACT_FUNCTIONS=f -DCE_SYMVERS_PATH=../testsuite/linux/Modules.symvers -DCE_RENAME_SYMBOLS -nostdinc -I../testsuite/linux -DKBUILD_MODNAME=libcrc32c -D__USE_IBT__ -D__KERNEL__ -DCE_KEEP_INCLUDES" } */ +/* { dg-xfail } */ + +/* Check why parenthesis are being output in the redeclaration. */ + +typedef unsigned int u32; + +u32 crc32c(u32 crc, const void *address, unsigned int length); + +int f(void) +{ + u32 lcrc = 0; + void *addr = 0; + unsigned int len = 0; + + (void)crc32c(lcrc, addr, len); + return 0; +} + +/* { dg-final { scan-tree-dump "u32 klpe_crc32c" } } */ +/* { dg-final { scan-tree-dump "KLP_RELOC_SYMBOL\(libcrc32c, libcrc32c, crc32c\)" } } */ +/* { dg-final { scan-tree-dump-not "\(\*klpe_crc32c\)" } } */ diff --git a/testsuite/linux/linux/livepatch.h b/testsuite/linux/linux/livepatch.h new file mode 100644 index 0000000..b2d0166 --- /dev/null +++ b/testsuite/linux/linux/livepatch.h @@ -0,0 +1,18 @@ +/** + * KLP_RELOC_SYMBOL_POS - define relocation for external symbols + * + * @LP_OBJ_NAME: name of the livepatched object where the symbol is needed + * @SYM_OBJ_NAME: name of the object where the symbol exists + * @SYM_NAME: symbol name + * @SYM_POS: position of the symbol in SYM_OBJ when there are more + * symbols of the same name. + * + * Use for annotating external symbols used in livepatches which are + * not exported in vmlinux or are in livepatched modules, see + * Documentation/livepatch/module-elf-format.rst + */ +#define KLP_RELOC_SYMBOL_POS(LP_OBJ_NAME, SYM_OBJ_NAME, SYM_NAME, SYM_POS) \ + asm("\".klp.sym.rela." #LP_OBJ_NAME "." #SYM_OBJ_NAME "." #SYM_NAME "," #SYM_POS "\"") + +#define KLP_RELOC_SYMBOL(LP_OBJ_NAME, SYM_OBJ_NAME, SYM_NAME) \ + KLP_RELOC_SYMBOL_POS(LP_OBJ_NAME, SYM_OBJ_NAME, SYM_NAME, 0) diff --git a/testsuite/linux/meson.build b/testsuite/linux/meson.build new file mode 100644 index 0000000..e69de29 diff --git a/testsuite/meson.build b/testsuite/meson.build index ed5dcd0..ffbcbee 100644 --- a/testsuite/meson.build +++ b/testsuite/meson.build @@ -14,7 +14,7 @@ # Author: Giuliano Belinassi runtest = find_program('lib/runtest.py') -ordinary_test_dirs = [ 'small/', 'includes/', 'ccp/' ] +ordinary_test_dirs = [ 'small/', 'includes/', 'ccp/', 'linux' ] returncode_to_bool = [ true, false ]