Skip to content

Commit

Permalink
Add new option to create a header with foward declaration of all exte…
Browse files Browse the repository at this point in the history
…rnalized 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=<arg>.
The generated file is not self-compilable.

Signed-off-by: Giuliano Belinassi <[email protected]>
  • Loading branch information
giulianobelinassi committed Mar 8, 2024
1 parent db616a3 commit cc3166e
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 1 deletion.
8 changes: 8 additions & 0 deletions libcextract/ArgvParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ void ArgvParser::Print_Usage_Message(void)
" -DCE_SYMVERS_PATH=<arg> Path to kernel Modules.symvers file. Only used when\n"
" -D__KERNEL__ is specified.\n"
" -DCE_DSC_OUTPUT=<arg> Libpulp .dsc file output, used for userspace livepatching.\n"
" -DCE_OUTPUT_FUNCTION_PROTOTYPE_HEADER=<arg>\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";
Expand Down Expand Up @@ -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;

Expand Down
7 changes: 7 additions & 0 deletions libcextract/ArgvParser.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -143,4 +148,6 @@ class ArgvParser
const char *DescOutputPath;

const char *IncExpansionPolicy;

const char *OutputFunctionPrototypeHeader;
};
8 changes: 8 additions & 0 deletions libcextract/FunctionDepsFinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ class DeclClosureVisitor : public RecursiveASTVisitor<DeclClosureVisitor>

inline void Mark_As_Analyzed(Decl *decl)
{
/*
if (TypedefNameDecl *ndecl = dynamic_cast<TypedefNameDecl *>(decl)) {
llvm::outs() << ndecl->getNameAsString() << '\n';
}
*/
AnalyzedDecls.insert(decl);
}

Expand Down Expand Up @@ -303,6 +308,9 @@ class DeclClosureVisitor : public RecursiveASTVisitor<DeclClosureVisitor>
/* 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);

Expand Down
6 changes: 6 additions & 0 deletions libcextract/FunctionDepsFinder.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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<Decl *> &Get_Set(void)
{
return Dependencies;
Expand Down
74 changes: 74 additions & 0 deletions libcextract/HeaderGenerate.cpp
Original file line number Diff line number Diff line change
@@ -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<ExternalizerLogEntry> &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<ExternalizerLogEntry> &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<FunctionDecl>(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<MacroDefinitionRecord>(entity)) {
if (MacroInfo *info = mw.Get_Macro_Info(def)) {
info->setIsUsed(false);
}
}
}

return true;
}
42 changes: 42 additions & 0 deletions libcextract/HeaderGenerate.hh
Original file line number Diff line number Diff line change
@@ -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<ExternalizerLogEntry> &set);

void Print(void);

protected:
ASTUnit *AST;
ClosureSet Closure;
};
35 changes: 35 additions & 0 deletions libcextract/Passes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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. */
Expand All @@ -524,6 +558,7 @@ PassManager::PassManager()
new FunctionExternalizerPass(),
new GenerateDscPass(),
new ClosurePass(/*PrintToFile=*/true),
new HeaderGenerationPass(),
};
}

Expand Down
4 changes: 4 additions & 0 deletions libcextract/Passes.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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;

Expand Down
3 changes: 2 additions & 1 deletion libcextract/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit cc3166e

Please sign in to comment.