From cc3166e00f52d518736837cfb709fbbd42427657 Mon Sep 17 00:00:00 2001 From: Giuliano Belinassi Date: Fri, 8 Mar 2024 17:31:52 -0300 Subject: [PATCH] Add new option to create a header with foward declaration of all externalized functions This commit adds a way for clang-extract to generate a header file containing foward declarations of all externalized functions. This can be enabled by using -DCE_OUTPUT_FUNCTION_PROTOTYPE_HEADER=. The generated file is not self-compilable. Signed-off-by: Giuliano Belinassi --- libcextract/ArgvParser.cpp | 8 ++++ libcextract/ArgvParser.hh | 7 +++ libcextract/FunctionDepsFinder.cpp | 8 ++++ libcextract/FunctionDepsFinder.hh | 6 +++ libcextract/HeaderGenerate.cpp | 74 ++++++++++++++++++++++++++++++ libcextract/HeaderGenerate.hh | 42 +++++++++++++++++ libcextract/Passes.cpp | 35 ++++++++++++++ libcextract/Passes.hh | 4 ++ libcextract/meson.build | 3 +- 9 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 libcextract/HeaderGenerate.cpp create mode 100644 libcextract/HeaderGenerate.hh diff --git a/libcextract/ArgvParser.cpp b/libcextract/ArgvParser.cpp index b7e6ec3..d824d4a 100644 --- a/libcextract/ArgvParser.cpp +++ b/libcextract/ArgvParser.cpp @@ -130,6 +130,9 @@ void ArgvParser::Print_Usage_Message(void) " -DCE_SYMVERS_PATH= Path to kernel Modules.symvers file. Only used when\n" " -D__KERNEL__ is specified.\n" " -DCE_DSC_OUTPUT= Libpulp .dsc file output, used for userspace livepatching.\n" +" -DCE_OUTPUT_FUNCTION_PROTOTYPE_HEADER=\n" +" Outputs a header file with a foward declaration of all\n" +" functions. This header is not self-compilable.\n" "\n"; llvm::outs() << "The following arguments are ignored by clang-extract:\n"; @@ -215,6 +218,11 @@ bool ArgvParser::Handle_Clang_Extract_Arg(const char *str) return true; } + if (prefix("-DCE_OUTPUT_FUNCTION_PROTOTYPE_HEADER=", str)) { + OutputFunctionPrototypeHeader = Extract_Single_Arg_C(str); + + return true; + } if (!strcmp("-DCE_RENAME_SYMBOLS", str)) { RenameSymbols = true; diff --git a/libcextract/ArgvParser.hh b/libcextract/ArgvParser.hh index 6618712..81b912e 100644 --- a/libcextract/ArgvParser.hh +++ b/libcextract/ArgvParser.hh @@ -114,6 +114,11 @@ class ArgvParser return IncExpansionPolicy; } + inline const char *Get_Output_Path_To_Prototype_Header(void) + { + return OutputFunctionPrototypeHeader; + } + const char *Get_Input_File(void); /** Print help usage message. */ @@ -143,4 +148,6 @@ class ArgvParser const char *DescOutputPath; const char *IncExpansionPolicy; + + const char *OutputFunctionPrototypeHeader; }; diff --git a/libcextract/FunctionDepsFinder.cpp b/libcextract/FunctionDepsFinder.cpp index 907c4a9..66d8bd0 100644 --- a/libcextract/FunctionDepsFinder.cpp +++ b/libcextract/FunctionDepsFinder.cpp @@ -137,6 +137,11 @@ class DeclClosureVisitor : public RecursiveASTVisitor inline void Mark_As_Analyzed(Decl *decl) { + /* + if (TypedefNameDecl *ndecl = dynamic_cast(decl)) { + llvm::outs() << ndecl->getNameAsString() << '\n'; + } + */ AnalyzedDecls.insert(decl); } @@ -303,6 +308,9 @@ class DeclClosureVisitor : public RecursiveASTVisitor /* Not called automatically by Transverse. */ bool VisitTypedefNameDecl(TypedefNameDecl *decl) { + if (decl->getNameAsString() == "streampos") { + llvm::outs() << "debug me\n"; + } TRY_TO(TraverseType(decl->getUnderlyingType())); Closure.Add_Decl_And_Prevs(decl); diff --git a/libcextract/FunctionDepsFinder.hh b/libcextract/FunctionDepsFinder.hh index 5d9fd07..928e859 100644 --- a/libcextract/FunctionDepsFinder.hh +++ b/libcextract/FunctionDepsFinder.hh @@ -37,6 +37,12 @@ class ClosureSet /** Mark decl as dependencies and all its previous decls versions. */ bool Add_Decl_And_Prevs(Decl *decl); + /** Add a single decl to the set. */ + void Add_Single_Decl(Decl *decl) + { + Dependencies.insert(decl); + } + inline std::unordered_set &Get_Set(void) { return Dependencies; diff --git a/libcextract/HeaderGenerate.cpp b/libcextract/HeaderGenerate.cpp new file mode 100644 index 0000000..3919cef --- /dev/null +++ b/libcextract/HeaderGenerate.cpp @@ -0,0 +1,74 @@ +//===- HeaderGenerate.hh - Output a header file for generated output. *- C++ -*-===// +// +// This project is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Remove the body of all functions and only outputs the foward declaration. +// +//===----------------------------------------------------------------------===// + +/* Author: Giuliano Belinassi */ + +#include "HeaderGenerate.hh" +#include "FunctionDepsFinder.hh" +#include "IncludeTree.hh" +#include "PrettyPrint.hh" + +HeaderGeneration::HeaderGeneration(PassManager::Context *ctx) + : AST(ctx->AST.get()) +{ + Run_Analysis(ctx->NamesLog); +} + +void HeaderGeneration::Print(void) +{ + IncludeTree IT(AST); // Not necessary, just to use the RecursivePrint class. + RecursivePrint(AST, Closure.Get_Set(), IT, false).Print(); +} + +static bool Contains(const std::vector &v, const std::string &name) +{ + for (const ExternalizerLogEntry &x : v) { + if (x.NewName == name) { + return true; + } + } + + return false; +} + +bool HeaderGeneration::Run_Analysis(const std::vector &set) +{ + ASTUnit::top_level_iterator it; + for (it = AST->top_level_begin(); it != AST->top_level_end(); ++it) { + Decl *decl = *it; + + if (FunctionDecl *fdecl = dyn_cast(decl)) { + if (Contains(set, fdecl->getNameAsString())) { + if (fdecl->hasBody()) { + Stmt *body = fdecl->getBody(); + fdecl->setRangeEnd(body->getBeginLoc().getLocWithOffset(-1)); + fdecl->setBody(nullptr); + } + Closure.Add_Single_Decl(fdecl); + } + } + } + + /* Do not output any macros. */ + MacroWalker mw(AST->getPreprocessor()); + PreprocessingRecord *rec = AST->getPreprocessor().getPreprocessingRecord(); + for (PreprocessedEntity *entity : *rec) { + if (MacroDefinitionRecord *def = dyn_cast(entity)) { + if (MacroInfo *info = mw.Get_Macro_Info(def)) { + info->setIsUsed(false); + } + } + } + + return true; +} diff --git a/libcextract/HeaderGenerate.hh b/libcextract/HeaderGenerate.hh new file mode 100644 index 0000000..a5ae575 --- /dev/null +++ b/libcextract/HeaderGenerate.hh @@ -0,0 +1,42 @@ +//===- HeaderGenerate.hh - Output a header file for generated output. *- C++ -*-===// +// +// This project is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Remove the body of all functions and only outputs the foward declaration. +// +//===----------------------------------------------------------------------===// + +/* Author: Giuliano Belinassi */ + +#pragma once + +#include "Passes.hh" +#include "FunctionDepsFinder.hh" +#include "SymbolExternalizer.hh" + + +using namespace clang; + +/** Outputs a header file with a foward declarations of all functions in the current + * AST. + * + * WARNING: This class modifies the AST. + */ +class HeaderGeneration +{ + public: + HeaderGeneration(PassManager::Context *); + + bool Run_Analysis(const std::vector &set); + + void Print(void); + + protected: + ASTUnit *AST; + ClosureSet Closure; +}; diff --git a/libcextract/Passes.cpp b/libcextract/Passes.cpp index 2a594ad..bd5a562 100644 --- a/libcextract/Passes.cpp +++ b/libcextract/Passes.cpp @@ -25,6 +25,7 @@ #include "DscFileGenerator.hh" #include "NonLLVMMisc.hh" #include "Error.hh" +#include "HeaderGenerate.hh" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" @@ -513,6 +514,39 @@ class GenerateDscPass : public Pass } }; +class HeaderGenerationPass : public Pass +{ + public: + HeaderGenerationPass() + { + PassName = "HeaderGenerationPass"; + } + + virtual bool Gate(PassManager::Context *ctx) + { + /* Only runs if the user requested the header. */ + return ctx->OutputFunctionPrototypeHeader; + } + + virtual bool Run_Pass(PassManager::Context *ctx) + { + std::error_code ec; + llvm::raw_fd_ostream out(ctx->OutputFunctionPrototypeHeader, ec); + PrettyPrint::Set_Output_Ostream(&out); + + HeaderGeneration HGen(ctx); + HGen.Print(); + + PrettyPrint::Set_Output_Ostream(nullptr); + return true; + } + + virtual void Dump_Result(PassManager::Context *ctx) + { + /* The dump is the generated file itself. */ + } +}; + PassManager::PassManager() { /* Declare the pass list. Passes will run in this order. */ @@ -524,6 +558,7 @@ PassManager::PassManager() new FunctionExternalizerPass(), new GenerateDscPass(), new ClosurePass(/*PrintToFile=*/true), + new HeaderGenerationPass(), }; } diff --git a/libcextract/Passes.hh b/libcextract/Passes.hh index 19711f6..41a5ccb 100644 --- a/libcextract/Passes.hh +++ b/libcextract/Passes.hh @@ -59,6 +59,7 @@ class PassManager { IpaclonesPath(args.Get_Ipaclones_Path()), SymversPath(args.Get_Symvers_Path()), DscOutputPath(args.Get_Dsc_Output_Path()), + OutputFunctionPrototypeHeader(args.Get_Output_Path_To_Prototype_Header()), IncExpansionPolicy(IncludeExpansionPolicy::Get_Overriding( args.Get_Include_Expansion_Policy(), Kernel)), NamesLog(), @@ -119,6 +120,9 @@ class PassManager { /* Path to libpulp .dsc file for output. */ const char *DscOutputPath; + /* Output path to a file containing foward declarations of all functions. */ + const char *OutputFunctionPrototypeHeader; + /* Policy used to expand includes. */ IncludeExpansionPolicy::Policy IncExpansionPolicy; diff --git a/libcextract/meson.build b/libcextract/meson.build index 8e29d40..434fea5 100644 --- a/libcextract/meson.build +++ b/libcextract/meson.build @@ -32,7 +32,8 @@ libcextract_sources = [ 'SymbolExternalizer.cpp', 'SymversParser.cpp', 'TopLevelASTIterator.cpp', - 'ExpansionPolicy.cpp' + 'ExpansionPolicy.cpp', + 'HeaderGenerate.cpp' ] libcextract_static = static_library('cextract', libcextract_sources)