Creating custom GCC attributes

GNU C supports a non-standard __attribute__(()) syntax for marking declarations with additional information that may be of interest to the optimizer, and for checking the correctness of the code.

The GCC Python plugin allows you to create custom attributes, which may be of use to your scripts: you can use this to annotate C code with additional information. For example, you could create a custom attribute for functions describing the interaction of a function on mutex objects:

extern void some_function(void)
  __attribute__((claims_mutex("io")));

extern void some_other_function(void)
  __attribute__((releases_mutex("io")));

and use this in a custom code-checker.

Custom attributes can take string and integer parameters. For example, the above custom attributes take a single string parameter. A custom attribute can take more than one parameter, or none at all.

To create custom attributes from Python, you need to wire up a callback response to the gcc.PLUGIN_ATTRIBUTES event:

gcc.register_callback(gcc.PLUGIN_ATTRIBUTES,
                      register_our_attributes)

This callback should then call gcc.register_attribute() to associate the name of the attribute with a Python callback to be called when the attribute is encountered in C code.

gcc.register_attribute(name, min_length, max_length, decl_required, type_required, function_type_required, callable)

Registers a new GCC attribute with the given name , usable in C source code via __attribute__(()).

Parameters:
  • name (str) – the name of the new attribute
  • min_length (int) – the minimum number of arguments expected when the attribute is used
  • max_length (int) – the maximum number of arguments expected when the attribute is used (-1 for no maximum)
  • decl_required
  • type_required
  • function_type_required
  • callable (a callable object, such as a function) – the callback to be invoked when the attribute is seen

In this example, we can simply print when the attribute is seen, to verify that the callback mechanism is working:

def attribute_callback_for_claims_mutex(*args):
    print('attribute_callback_for_claims_mutex called: args: %s' % (args, ))

def attribute_callback_for_releases_mutex(*args):
    print('attribute_callback_for_releases_mutex called: args: %s' % (args, ))

def register_our_attributes():
    gcc.register_attribute('claims_mutex',
                           1, 1,
                           False, False, False,
                           attribute_callback_for_claims_mutex)
    gcc.register_attribute('releases_mutex',
                           1, 1,
                           False, False, False,
                           attribute_callback_for_releases_mutex)

Putting it all together, here is an example Python script for the plugin:

import gcc

# Verify that we can register custom attributes:

def attribute_callback_for_claims_mutex(*args):
    print('attribute_callback_for_claims_mutex called: args: %s' % (args, ))

def attribute_callback_for_releases_mutex(*args):
    print('attribute_callback_for_releases_mutex called: args: %s' % (args, ))

def register_our_attributes():
    gcc.register_attribute('claims_mutex',
                           1, 1,
                           False, False, False,
                           attribute_callback_for_claims_mutex)
    gcc.register_attribute('releases_mutex',
                           1, 1,
                           False, False, False,
                           attribute_callback_for_releases_mutex)

# Wire up our callback:
gcc.register_callback(gcc.PLUGIN_ATTRIBUTES,
                      register_our_attributes)

Compiling this test C source file:

/* Function declarations with custom attributes: */
extern some_function(void) __attribute__((claims_mutex("io")));

extern some_other_function(void) __attribute__((releases_mutex("io")));

extern yet_another_function(void) __attribute__((claims_mutex("db"),
                                                 claims_mutex("io"),
                                                 releases_mutex("io")));

leads to this output from the script:

attribute_callback_for_claims_mutex called: args: (gcc.FunctionDecl('some_function'), gcc.StringCst('io'))
attribute_callback_for_releases_mutex called: args: (gcc.FunctionDecl('some_other_function'), gcc.StringCst('io'))
attribute_callback_for_claims_mutex called: args: (gcc.FunctionDecl('yet_another_function'), gcc.StringCst('db'))
attribute_callback_for_claims_mutex called: args: (gcc.FunctionDecl('yet_another_function'), gcc.StringCst('io'))
attribute_callback_for_releases_mutex called: args: (gcc.FunctionDecl('yet_another_function'), gcc.StringCst('io'))

Using the preprocessor to guard attribute usage

Unfortunately, the above C code will only work when it is compiled with the Python script that adds the custom attributes.

You can avoid this by using gcc.define_macro() to pre-define a preprocessor name (e.g. “WITH_ATTRIBUTE_CLAIMS_MUTEX”) at the same time as when you define the attribute:

import gcc

def attribute_callback_for_claims_mutex(*args):
    print('attribute_callback_for_claims_mutex called: args: %s' % (args, ))

def attribute_callback_for_releases_mutex(*args):
    print('attribute_callback_for_releases_mutex called: args: %s' % (args, ))

def register_our_attributes():
    gcc.register_attribute('claims_mutex',
                           1, 1,
                           False, False, False,
                           attribute_callback_for_claims_mutex)
    gcc.define_macro('WITH_ATTRIBUTE_CLAIMS_MUTEX')

    gcc.register_attribute('releases_mutex',
                           1, 1,
                           False, False, False,
                           attribute_callback_for_releases_mutex)
    gcc.define_macro('WITH_ATTRIBUTE_RELEASES_MUTEX')

# Wire up our callback:
gcc.register_callback(gcc.PLUGIN_ATTRIBUTES,
                      register_our_attributes)

This way the user can write this C code instead, and have it work both with and without the Python script:

#if defined(WITH_ATTRIBUTE_CLAIMS_MUTEX)
 #define CLAIMS_MUTEX(x) __attribute__((claims_mutex(x)))
#else
 #define CLAIMS_MUTEX(x)
#endif

#if defined(WITH_ATTRIBUTE_RELEASES_MUTEX)
 #define RELEASES_MUTEX(x) __attribute__((releases_mutex(x)))
#else
 #define RELEASES_MUTEX(x)
#endif


/* Function declarations with custom attributes: */
extern void some_function(void)
    CLAIMS_MUTEX("io");

extern void some_other_function(void)
    RELEASES_MUTEX("io");

extern void yet_another_function(void)
    CLAIMS_MUTEX("db")
    CLAIMS_MUTEX("io")
    RELEASES_MUTEX("io");

giving this output from the script:

attribute_callback_for_claims_mutex called: args: (gcc.FunctionDecl('some_function'), gcc.StringCst('io'))
attribute_callback_for_releases_mutex called: args: (gcc.FunctionDecl('some_other_function'), gcc.StringCst('io'))
attribute_callback_for_claims_mutex called: args: (gcc.FunctionDecl('yet_another_function'), gcc.StringCst('db'))
attribute_callback_for_claims_mutex called: args: (gcc.FunctionDecl('yet_another_function'), gcc.StringCst('io'))
attribute_callback_for_releases_mutex called: args: (gcc.FunctionDecl('yet_another_function'), gcc.StringCst('io'))