Experiments with the gcc plugin mechanism
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
01_first
02_help_version
03_parameters
04_callbacks
05_my_first_pass
06_dump_gimple
07_walk_gimple
08_cfg_test
09_cfg_graphviz
10_warn_unused_result_cxx
blog_01
blog_02
blog_03
.gitignore
LICENSE
Makefile
Makefile.common
README.md

README.md

Install gcc

Download gcc and unpack it

$ wget http://ftp.gnu.org/gnu/gcc/gcc-5.2.0/gcc-5.2.0.tar.bz2
$ tar xfj gcc-5.2.0.tar.bz2

Make sure you fullfill all the gcc dependences

$ cd gcc-5.2.0
$ ./contrib/download_prerequisites

Set an installation path

$ export INSTALLDIR=$HOME/gcc/gcc-install

Create a build directory

$ cd ..            # leave gcc source directory
$ mkdir gcc-build
$ cd gcc-build

Configure the source to get a C/C++ compiler

$ ../gcc-5.2.0/configure --prefix=$INSTALLDIR --enable-languages=c,c++

Build (will take a while, like 10 min or so in a fast computer)

$ make -j$(getconf _NPROCESSORS_ONLN) 

Install

$ make install

Make sure we have plugins available

$ ${INSTALLDIR}/bin/gcc -print-file-name=plugin
<<INSTALLDIR>>/lib/gcc/x86_64-unknown-linux-gnu/5.2.0/plugin

If it just appears 'plugin' you are using the wrong compiler

Our first plugin

Create a boilerplate Makefile.common

# Common makefile to be included from all other makefiles

# Where we installed gcc and its headers
INSTALLDIR=<<INSTALLDIR>>

CC=$(INSTALLDIR)/bin/gcc
CXX=$(INSTALLDIR)/bin/g++
PLUGINDIR=$(shell $(CC) -print-file-name=plugin)

CFLAGS=-fPIC -Wall -g -fno-rtti -I$(PLUGINDIR)/include
CXXFLAGS=-fPIC -Wall -g -fno-rtti -I$(PLUGINDIR)/include
LDFLAGS=
LDADD=

END=
OBJECTS=$(patsubst %.cc,%.o,$(SOURCES))

all: $(PLUGIN)

$(PLUGIN): $(OBJECTS)
	$(CXX) $(LDFLAGS) -o $@ -shared $+ $(LDADD)

%.o: %.cc
	$(CXX) -c -o $@ $(CFLAGS) $<

.PHONY: all clean test
clean:
	rm -f $(OBJECTS) $(PLUGIN)

test: $(PLUGIN)
	$(CC) -fplugin=./$(PLUGIN) -c test.c

Create a first/Makefile

PLUGIN=my-first-gcc-plugin.so
SOURCES=\
        my-first-gcc-plugin.c \
		$(END)

include ../Makefile.common

Create a phase that just prints what it is passed by gcc

// This is the first gcc header to be included
#include "gcc-plugin.h"
#include "plugin-version.h"

#include <iostream>

// We must assert that this plugin is GPL compatible
int plugin_is_GPL_compatible;

int plugin_init (struct plugin_name_args *plugin_info,
		struct plugin_gcc_version *version)
{
	// We check the current gcc loading this plugin against the gcc we used to
	// created this plugin
	if (!plugin_default_version_check (version, &gcc_version))
    {
        std::cerr << "This GCC plugin is for version " << GCCPLUGIN_VERSION_MAJOR << "." << GCCPLUGIN_VERSION_MINOR << "\n";
		return 1;
    }

    // Let's print all the information given to this plugin!

    std::cerr << "Plugin info\n";
    std::cerr << "===========\n\n";
    std::cerr << "Base name: " << plugin_info->base_name << "\n";
    std::cerr << "Full name: " << plugin_info->full_name << "\n";
    std::cerr << "Number of arguments of this plugin:" << plugin_info->argc << "\n";

    for (int i = 0; i < plugin_info->argc; i++)
    {
        std::cerr << "Argument " << i << ": Key: " << plugin_info->argv[i].key << ". Value: " << plugin_info->argv[i].value<< "\n";

    }

    std::cerr << "\n";
    std::cerr << "Version info\n";
    std::cerr << "============\n\n";
    std::cerr << "Base version: " << version->basever << "\n";
    std::cerr << "Date stamp: " << version->datestamp << "\n";
    std::cerr << "Dev phase: " << version->devphase << "\n";
    std::cerr << "Revision: " << version->devphase << "\n";
    std::cerr << "Configuration arguments: " << version->configuration_arguments << "\n";
    std::cerr << "\n";

    std::cerr << "Plugin successfully initialized\n";

    return 0;
}

Compile

$ make
<<INSTALLDIR>>/bin/gcc -fPIC -Wall -I<<INSTALLDIR>>/lib/gcc/x86_64-unknown-linux-gnu/5.2.0/plugin/include   -c -o my-first-gcc-plugin.o my-first-gcc-plugin.c
<<INSTALLDIR>>/bin/gcc  -o my-first-gcc-plugin.so -shared my-first-gcc-plugin.o 

Run gcc enabling the plugin (you can call 'make test')

$ <<INSTALLDIR>>/bin/gcc -fplugin=./my-first-gcc-plugin.so -c test.c
Plugin info
===========

Base name: my_first_gcc_plugin
Full name: ./my_first_gcc_plugin.so
Number of arguments of this plugin:0

Version info
============

Base version: 5.2.0
Date stamp: 20150716
Dev phase: 
Revision: 
Configuration arguments: ../gcc-5.2.0/configure --prefix=<<INSTALLDIR>> --enable-languages=c,c++,fortran

Plugin successfully initialized

The phase does nothing and still lacks info but it is a step in the good direction.

Introducing ourselves to the compiler

// This is the first gcc header to be included
#include "gcc-plugin.h"
#include "plugin-version.h"

#include <iostream>

// We must assert that this plugin is GPL compatible
int plugin_is_GPL_compatible;

static struct plugin_info my_gcc_plugin_info = { "1.0", "This is a very simple plugin" };

int plugin_init (struct plugin_name_args *plugin_info,
		struct plugin_gcc_version *version)
{
	// We check the current gcc loading this plugin against the gcc we used to
	// created this plugin
	if (!plugin_default_version_check (version, &gcc_version))
    {
        std::cerr << "This GCC plugin is for version " << GCCPLUGIN_VERSION_MAJOR << "." << GCCPLUGIN_VERSION_MINOR << "\n";
		return 1;
    }

    // Register the plugin itself
    register_callback(plugin_info->base_name,
            /* event */ PLUGIN_INFO,
            /* callback */ NULL, /* user_data */ &my_gcc_plugin_info);

    return 0;
}

Call the C compiler (the gcc driver is not going to be enough) to see the plugins loaded

$ <<INSTALLDIR>>/libexec/gcc/x86_64-unknown-linux-gnu/5.2.0/cc1 -fplugin=./help-version.so --help

...
  -p                                [disabled]
  -pedantic-errors                  [disabled]
  -quiet                            [disabled]

Help for the loaded plugins:
 help-version:
    This is a very simple plugin

Being able to pass parameters

Once we have introduced ourselves to the compiler we can pass parameters using -fplugin-arg-<>-key=value

// This is the first gcc header to be included
#include "gcc-plugin.h"
#include "plugin-version.h"

#include <iostream>

// We must assert that this plugin is GPL compatible
int plugin_is_GPL_compatible;

static struct plugin_info my_gcc_plugin_info = { "1.0", "This is a very simple plugin" };

int plugin_init (struct plugin_name_args *plugin_info,
		struct plugin_gcc_version *version)
{
	// We check the current gcc loading this plugin against the gcc we used to
	// created this plugin
	if (!plugin_default_version_check (version, &gcc_version))
    {
        std::cerr << "This GCC plugin is for version " << GCCPLUGIN_VERSION_MAJOR << "." << GCCPLUGIN_VERSION_MINOR << "\n";
		return 1;
    }

    register_callback(plugin_info->base_name,
            /* event */ PLUGIN_INFO,
            /* callback */ NULL, /* user_data */ &my_gcc_plugin_info);

    std::cerr << "Number of arguments of this plugin:" << plugin_info->argc << "\n";

    for (int i = 0; i < plugin_info->argc; i++)
    {
        std::cerr << "Argument " << i << ": Key: " << plugin_info->argv[i].key << ". Value: " << plugin_info->argv[i].value<< "\n";

    }

    return 0;
}

For instance

<<INSTALLDIR>>/bin/gcc -fplugin=./my_parameters.so -c -x c /dev/null \
	-fplugin-arg-my_parameters-this=is-a-parameter \
	-fplugin-arg-my_parameters-and-this=is-another-parameter
Number of arguments of this plugin:2
Argument 0: Key: this. Value: is-a-parameter
Argument 1: Key: and-this. Value: is-another-parameter