Add LLVM-pass plugin support to LDC. Commandline flag: -plugin=.... (#2554)

This adds functionality to load plugins such as the AFL llvm-mode plugin: https://github.com/mirrorer/afl/blob/master/llvm_mode/afl-llvm-pass.so.cc
Note that such plugins developed for Clang should also work for LDC !
This commit is contained in:
Johan Engelen 2018-02-13 20:22:48 +01:00 committed by GitHub
parent b22d8cccf3
commit 16ecb3e79f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 196 additions and 0 deletions

View file

@ -321,6 +321,7 @@ set(DRV_SRC
driver/linker-gcc.cpp
driver/linker-msvc.cpp
driver/main.cpp
driver/plugins.cpp
${CMAKE_BINARY_DIR}/driver/ldc-version.cpp
)
set(DRV_HDR
@ -337,6 +338,7 @@ set(DRV_HDR
driver/ldc-version.h
driver/archiver.h
driver/linker.h
driver/plugins.h
driver/targetmachine.h
driver/toobj.h
driver/tool.h
@ -559,6 +561,24 @@ if(LDC_WITH_LLD)
endif()
endif()
# Plugin support
if(UNIX)
set(LDC_ENABLE_PLUGINS_DEFAULT ON)
else()
set(LDC_ENABLE_PLUGINS_DEFAULT OFF)
endif()
set(LDC_ENABLE_PLUGINS ${LDC_ENABLE_PLUGINS_DEFAULT} CACHE BOOL "Build LDC with plugin support (increases binary size)")
if(LDC_ENABLE_PLUGINS)
message(STATUS "Building LDC with plugin support (LDC_ENABLE_PLUGINS=ON)")
add_definitions(-DLDC_ENABLE_PLUGINS)
# For plugin support, we need to link with --export-dynamic on Unix.
if(UNIX AND NOT APPLE)
set(LDC_LINKERFLAG_LIST "${LDC_LINKERFLAG_LIST};-Wl,--export-dynamic")
endif()
else()
message(STATUS "Building LDC without plugin support (LDC_ENABLE_PLUGINS=OFF)")
endif()
set(LDC_LINK_MANUALLY OFF)
if(UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")))
# On Unix-like systems, DMD and LDC will use the C compiler for linking, but

View file

@ -29,6 +29,7 @@
#include "driver/exe_path.h"
#include "driver/ldc-version.h"
#include "driver/linker.h"
#include "driver/plugins.h"
#include "driver/targetmachine.h"
#include "gen/cl_helpers.h"
#include "gen/irstate.h"
@ -1063,6 +1064,8 @@ int cppmain(int argc, char **argv) {
opts::initializeInstrumentationOptionsFromCmdline(
*global.params.targetTriple);
loadAllPlugins();
Strings libmodules;
return mars_mainBody(files, libmodules);
}

48
driver/plugins.cpp Normal file
View file

@ -0,0 +1,48 @@
//===-- driver/plugins.cpp -------------------------------------*- C++ -*-===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// Implements functionality related to plugins (`-plugin=...`).
//
//===----------------------------------------------------------------------===//
#include "driver/plugins.h"
#if LDC_ENABLE_PLUGINS
#include "errors.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/DynamicLibrary.h"
namespace {
namespace cl = llvm::cl;
cl::list<std::string>
pluginFiles("plugin", cl::CommaSeparated, cl::desc("Plugins to load."),
cl::value_desc("<dynamic_library.so, lib2.so>"));
} // anonymous namespace
/// Loads all plugins. The static constructor of each plugin should take care of
/// the plugins registering themself with the rest of LDC/LLVM.
void loadAllPlugins() {
for (auto &filename : pluginFiles) {
std::string errorString;
if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(filename.c_str(),
&errorString)) {
error(Loc(), "Error loading plugin '%s': %s", filename.c_str(),
errorString.c_str());
}
}
}
#else // #if LDC_ENABLE_PLUGINS
void loadAllPlugins() {}
#endif // LDC_ENABLE_PLUGINS

15
driver/plugins.h Normal file
View file

@ -0,0 +1,15 @@
//===-- driver/plugins.h ---------------------------------------*- C++ -*-===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#ifndef LDC_DRIVER_PLUGINS_H
#define LDC_DRIVER_PLUGINS_H
void loadAllPlugins();
#endif // LDC_DRIVER_PLUGINS_H

View file

@ -22,6 +22,7 @@ config.llvm_targetsstr = "@LLVM_TARGETS_TO_BUILD@"
config.default_target_bits = @DEFAULT_TARGET_BITS@
config.with_PGO = True
config.dynamic_compile = @LDC_DYNAMIC_COMPILE@
config.plugins_supported = "@LDC_ENABLE_PLUGINS@" == "ON"
config.name = 'LDC'

View file

@ -0,0 +1,28 @@
# ROOT_DIR = directory where Makefile sits
MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
ROOT_DIR := $(dir $(MAKEFILE_PATH))
LLVM_CONFIG ?= llvm-config
CXXFLAGS ?= -O3
CXXFLAGS += $(shell $(LLVM_CONFIG) --cxxflags) -fno-rtti -fpic
# Remove all warning flags (they may or may not be supported by the compiler)
CXXFLAGS := $(filter-out -W%,$(CXXFLAGS))
CXXFLAGS := $(filter-out -fcolor-diagnostics,$(CXXFLAGS))
ifeq "$(shell uname)" "Darwin"
CXXFLAGS += -Wl,-flat_namespace -Wl,-undefined,suppress
endif
PASSLIB = addFuncEntryCallPass
all: $(PASSLIB)
$(PASSLIB): $(ROOT_DIR)$(PASSLIB).cpp
$(CXX) $(CXXFLAGS) -shared $< -o $@.so
.NOTPARALLEL: clean
clean:
rm -f $(PASSLIB).so

View file

@ -0,0 +1,60 @@
//===-- addFuncEntryCallPass.cpp - Optimize druntime calls ----------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the University of Illinois Open Source
// License. See the LICENSE file for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
using namespace llvm;
namespace {
class FuncEntryCallPass : public FunctionPass {
Constant *funcToCallUponEntry = nullptr;
public:
static char ID;
FuncEntryCallPass() : FunctionPass(ID) {}
bool doInitialization(Module &M) override;
bool runOnFunction(Function &F) override;
};
}
char FuncEntryCallPass::ID = 0;
bool FuncEntryCallPass::doInitialization(Module &M) {
// Add fwd declaration of the `void __test_funcentrycall(void)` function.
auto functionType = FunctionType::get(Type::getVoidTy(M.getContext()), false);
funcToCallUponEntry =
M.getOrInsertFunction("__test_funcentrycall", functionType);
return true;
}
bool FuncEntryCallPass::runOnFunction(Function &F) {
// Add call to `__test_funcentrycall(void)` at the start of _every_ function
// (this includes e.g. `ldc.register_dso`!)
llvm::BasicBlock &block = F.getEntryBlock();
IRBuilder<> builder(&block, block.begin());
builder.CreateCall(funcToCallUponEntry);
return true;
}
static void addFuncEntryCallPass(const PassManagerBuilder &,
legacy::PassManagerBase &PM) {
PM.add(new FuncEntryCallPass());
}
// Registration of the plugin's pass is done by the plugin's static constructor.
static RegisterStandardPasses
RegisterFuncEntryCallPass0(PassManagerBuilder::EP_EnabledOnOptLevel0,
addFuncEntryCallPass);

View file

@ -0,0 +1,14 @@
// REQUIRES: Plugins
// RUN: make -f %S/Makefile
// RUN: %ldc -c -output-ll -plugin=./addFuncEntryCallPass.so -of=%t.ll %s
// RUN: FileCheck %s < %t.ll
// CHECK: define {{.*}}testfunction
int testfunction(int i)
{
// CHECK-NEXT: call {{.*}}__test_funcentrycall
return i * 2;
}
// CHECK-DAG: declare {{.*}}__test_funcentrycall

View file

@ -0,0 +1,7 @@
import os
import platform
import re
if (config.plugins_supported):
config.available_features.add('Plugins')
config.environment['LLVM_CONFIG'] = os.path.join(config.llvm_tools_dir, 'llvm-config')