Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions tools/cage/include/cage/generator/CallGraphEmbedder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* File: CallGraphEmbedder.h
* License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at
* https://github.com/tudasc/metacg/LICENSE.txt
*/
#ifndef METACG_CALLGRAPHEMBEDDER_H
#define METACG_CALLGRAPHEMBEDDER_H

#include "cage/interface/CaGePlugin.h"

#include <string>

namespace llvm {
class Module;
}

namespace cage {

class GraphEmbedder : public Plugin {
public:
explicit GraphEmbedder(llvm::Module& M) : GraphEmbedder(M, "metacg") {}
GraphEmbedder(llvm::Module& M, const std::string& sectionName) : M(M), sectionName(sectionName) {}
void consumeCallGraph(const metacg::Callgraph&) override;

private:
llvm::Module& M;
std::string sectionName;
};

} // namespace cage

#endif // METACG_CALLGRAPHEMBEDDER_H
25 changes: 22 additions & 3 deletions tools/cage/src/CaGe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "cage/CaGe.h"
#include "cage/interface/CaGePlugin.h"

#include "cage/generator/CallGraphEmbedder.h"
#include "cage/generator/CallgraphGenerator.h"
#include "cage/generator/FileExporter.h"

Expand All @@ -25,6 +26,13 @@ static opt<cage::PTAType> pta(
"Treat all available valid function signatures for a given function pointer as potential call target ")),
cat(cageOpts), init(cage::PTAType::No));

enum class OutputTypes { File, Embed };
static bits<OutputTypes> outputTypeBits(desc("Where to output the graph"),
values(clEnumValN(OutputTypes::File, "file", " Output into separate file"),
clEnumValN(OutputTypes::Embed, "embed",
"Embed the graph into the metacg section of the binary")),
cat(cageOpts));

static opt<std::string> cgout("cg-file", desc("Output file for the generated call graph"), cat(cageOpts), init(""));

static list<std::string> pluginPaths("plugin-paths", desc("option list"), cat(cageOpts), CommaSeparated);
Expand Down Expand Up @@ -58,9 +66,15 @@ PreservedAnalyses CaGe::run(Module& M, ModuleAnalysisManager& MA) {
outs() << "Running CaGe in verbose mode\n";
}

// First check explicit option
// Write to file if:
// (1) Output file name is explicitly given
// (2) Option for file output is set, or
// (3) No alternative output option is specified.
bool writeToFile = !cgout.empty() || outputTypeBits.isSet(OutputTypes::File) || outputTypeBits.getBits() == 0;

// To determine output file, first check explicit option
std::string outfile = cgout.getValue();
if (outfile.empty()) {
if (writeToFile && outfile.empty()) {
// If empty, check environment variable
if (const auto* cgNameEnv = std::getenv("CAGE_CG")) {
outfile = cgNameEnv;
Expand All @@ -71,7 +85,12 @@ PreservedAnalyses CaGe::run(Module& M, ModuleAnalysisManager& MA) {
}

Generator gen(pta);
gen.addPlugin(std::make_unique<FileExporter>(outfile));
if (writeToFile) {
gen.addPlugin(std::make_unique<FileExporter>(outfile));
}
if (outputTypeBits.isSet(OutputTypes::Embed)) {
gen.addPlugin(std::make_unique<GraphEmbedder>(M));
}

// Load external plugins
for (const auto& pluginPath : pluginPaths) {
Expand Down
7 changes: 6 additions & 1 deletion tools/cage/src/generator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
add_library(cage STATIC CallGraphGenerator.cpp FileExporter.cpp)
add_library(
cage STATIC
CallGraphEmbedder.cpp
CallGraphGenerator.cpp
FileExporter.cpp
)
set_target_properties(cage PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(cage PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/tools/cage/include>)
target_include_directories(cage PUBLIC $<BUILD_INTERFACE:${LLVM_INCLUDE_DIRS}>)
Expand Down
52 changes: 52 additions & 0 deletions tools/cage/src/generator/CallGraphEmbedder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* File: CallGraphEmbedder.cpp
* License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at
* https://github.com/tudasc/metacg/LICENSE.txt
*/

#include "cage/generator/CallGraphEmbedder.h"

#include "llvm/IR/Constants.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"

#include "metacg/config.h"
#include "metacg/io/MCGWriter.h"
#include "metacg/io/VersionFourMCGWriter.h"

namespace cage {

using namespace llvm;

static void embed(Module& M, const std::string& sectionName, const std::string& cgStr) {
LLVMContext& C = M.getContext();

auto* CgStrData = ConstantDataArray::getString(C, cgStr);

auto* GV = new GlobalVariable(M, CgStrData->getType(), true, GlobalValue::PrivateLinkage, CgStrData, "__cage_cg");

GV->setSection(sectionName);
GV->setAlignment(Align(1));

appendToCompilerUsed(M, GV);
}

void GraphEmbedder::consumeCallGraph(const metacg::Callgraph& graph) {
metacg::io::JsonSink jsSink;
metacg::io::VersionFourMCGWriter mcgw({{4, 0}, {"CaGe", 0, 1, MetaCG_GIT_SHA}}, true, true);
mcgw.write(&graph, jsSink);

llvm::outs() << "Embedding generated call graph into ELF section " << sectionName << "\n";

std::stringstream ss;

ss << jsSink.getJson().dump();
ss.flush();
embed(M, sectionName, ss.str());
}

} // namespace cage
21 changes: 19 additions & 2 deletions tools/cage/test/runCaGeTests.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,24 @@ mcgconfig="@METACG_CONFIG@"
build_dir="@CMAKE_BINARY_DIR@"
cgdiff="$build_dir/tools/cgdiff/cgdiff"
cgmerge="$build_dir/tools/cgmerge2/cgmerge2"
extract_mcg="@CMAKE_SOURCE_DIR@/utils/extract_metacg"

suite_pattern=".*"
verbose=0

mkdir -p log


# Check whether embedded CG matches the exported file.
# Param 1: Binary with embedded CG
# Param 2: GT file
function checkEmbedded {
if [[ $verbose -eq 1 ]]; then
echo "Checking embedded graph using $extract_mcg"
fi
"$extract_mcg" "$1" | cmp -s <(jq -S . /dev/stdin) <(jq -S . "$2")
}

# Run single source test case.
# Param 1: Name of the test case.
# Param 2: Path to the test case.
Expand Down Expand Up @@ -63,9 +75,9 @@ function runTestCase {
failed=0
tfile_out="$run_dir/$testCaseName.exe"
rm -f "$tfile_out"
clang++ -g `$mcgconfig --cage-ldflags --cage-pass-option "-cg-file=${cg_outfile}" ${addOptions}` ${addFlags} -fno-exceptions ${o_files[@]} -o "$tfile_out" >> log/testrun.log 2>&1 || failed=1
clang++ -g `$mcgconfig --cage-ldflags --cage-pass-option -embed --cage-pass-option "-cg-file=${cg_outfile}" ${addOptions}` ${addFlags} -fno-exceptions ${o_files[@]} -o "$tfile_out" >> log/testrun.log 2>&1 || failed=1
if [[ $failed -ne 0 ]] || [[ $verbose -eq 1 ]]; then
echo "Link command: clang++ -g `$mcgconfig --cage-ldflags --cage-pass-option "-cg-file=${cg_outfile}" ${addOptions}` ${addFlags} -fno-exceptions ${o_files[@]} -o $tfile_out"
echo "Link command: clang++ -g `$mcgconfig --cage-ldflags --cage-pass-option -embed --cage-pass-option "-cg-file=${cg_outfile}" ${addOptions}` ${addFlags} -fno-exceptions ${o_files[@]} -o $tfile_out"
fi
if [[ $failed -ne 0 ]]; then
if [[ ! -f "$cg_outfile" ]]; then
Expand All @@ -75,6 +87,11 @@ function runTestCase {
echo "Linking failed but call graph was generated."
fi

if ! checkEmbedded "$tfile_out" "$cg_outfile"; then
echo "Embedded call graph does not match the exported version!"
return 1
fi

if [[ $regenerate_gt -eq 1 ]]; then
echo "Using $cg_outfile as new ground truth"
cp "$cg_outfile" "$cg_gtfile"
Expand Down
2 changes: 2 additions & 0 deletions utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
add_subdirectory(config)

install(PROGRAMS extract_metacg DESTINATION bin)
2 changes: 2 additions & 0 deletions utils/extract_metacg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
objcopy -O binary --only-section=metacg "$1" /dev/stdout | strings -n 1
Comment thread
TimHeldmann marked this conversation as resolved.
Loading