User's Guide
The module gcc.cdecl
provides a function
gcc.cdecl.declare
, which formats a GCC declaration or type
node as a string of C code. In the following examples, we will learn how
to use this function to extract functions, variables and types from the
operating system’s implementation of the POSIX C
API.
1 Functions and variables
To extract function and variable declarations from a C header file, we declare specially-prefixed variables in a C source file that point to the declarations. The source file is parsed using GCC and a Lua plugin script that selects these variables by matching their common name prefix.
Create a file func.c
with the following C source
code:
#define _XOPEN_SOURCE 700
#include <unistd.h>
#define cdecl_func(name) __typeof__(name) *cdecl_func__ ## name = &name;
#define cdecl_var cdecl_func
/* https://pubs.opengroup.org/onlinepubs/9699919799/functions/optarg.html */
(getopt)
cdecl_func(optarg)
cdecl_var(opterr)
cdecl_var(optind)
cdecl_var(optopt) cdecl_var
Create a file func.lua
with the following Lua source
code:
local gcc = require("gcc")
local cdecl = require("gcc.cdecl")
-- send assembler output to /dev/null
.set_asm_file_name(gcc.HOST_BIT_BUCKET)
gcc
-- invoke Lua function after translation unit has been parsed
.register_callback(gcc.PLUGIN_FINISH_UNIT, function()
gcc-- get global variables in reverse order of declaration
local nodes = gcc.get_variables()
for i = #nodes, 1, -1 do
local name = nodes[i]:name():value()
if name:match("^cdecl_func__") then
-- get function or variable pointed to by initial value
local decl = nodes[i]:initial():operand()
-- output function or variable declaration
print(cdecl.declare(decl) .. ";")
end
end
end)
Parse the C source file using the GCC Lua plugin:
gcc -S -std=c99 -Wall func.c -fplugin=../gcc-lua/gcc/gcclua.so -fplugin-arg-gcclua-script=func.lua
The output contains the declarations captured in the C source file:
int getopt(int, char *const *, const char *) __attribute__((__nothrow__));
extern char *optarg;
extern int opterr;
extern int optind;
extern int optopt;
2 Assembler names
Many C libraries use the C preprocessor to substitute documented API
names with internal ABI names. For example, the POSIX function
basename
is implemented as __xpg_basename
in
the GNU C library.
gcc.cdecl.declare
may be passed a function as a second
argument. This function is invoked on any declaration or type
encountered upon formatting the first argument, and may return a string
that overrides the parsed name. In this example, we override the
internal ABI name of a declaration with its documented API name.
Create a file asm.c
with the following C source
code:
#define _XOPEN_SOURCE 700
#include <libgen.h>
#define cdecl_func(name) __typeof__(name) *cdecl_func__ ## name = &name;
#define cdecl_var cdecl_func
/* https://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html */
(basename)
cdecl_func/* https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirname.html */
(dirname) cdecl_func
Create a file asm.lua
with the following Lua source
code:
local gcc = require("gcc")
local cdecl = require("gcc.cdecl")
-- send assembler output to /dev/null
.set_asm_file_name(gcc.HOST_BIT_BUCKET)
gcc
-- invoke Lua function after translation unit has been parsed
.register_callback(gcc.PLUGIN_FINISH_UNIT, function()
gcc-- get global variables in reverse order of declaration
local nodes = gcc.get_variables()
for i = #nodes, 1, -1 do
local name = nodes[i]:name():value()
local name = name:match("^cdecl_func__(.+)")
if name then
-- get function or variable pointed to by initial value
local decl = nodes[i]:initial():operand()
-- output function or variable declaration with API name
print(cdecl.declare(decl, function(node)
if node == decl then return name end
end) .. ";")
end
end
end)
Parse the C source file using the GCC Lua plugin:
gcc -S -std=c99 -Wall asm.c -fplugin=../gcc-lua/gcc/gcclua.so -fplugin-arg-gcclua-script=asm.lua
The output contains the declarations captured in the C source file:
char *basename(char *) __asm__("__xpg_basename") __attribute__((__nothrow__));
char *dirname(char *) __attribute__((__nothrow__));
Declarations with a differing assembler name include a GCC asm label.
3 Type declarations
To extract type declarations, we declare variables with a corresponding pointee type. We use pointer variables to avoid warnings about missing initialisation for constant types.
Create a file type.c
with the following C source
code:
#define _XOPEN_SOURCE 700
#include <time.h>
#define cdecl_type(name) name *cdecl_type__ ## name;
(clockid_t)
cdecl_type(time_t) cdecl_type
Create a file type.lua
with the following Lua source
code:
local gcc = require("gcc")
local cdecl = require("gcc.cdecl")
-- send assembler output to /dev/null
.set_asm_file_name(gcc.HOST_BIT_BUCKET)
gcc
-- invoke Lua function after translation unit has been parsed
.register_callback(gcc.PLUGIN_FINISH_UNIT, function()
gcc-- get global variables in reverse order of declaration
local nodes = gcc.get_variables()
for i = #nodes, 1, -1 do
local name = nodes[i]:name():value()
local name = name:match("^cdecl_type__(.+)")
if name then
-- get type declaration of pointee type
local decl = nodes[i]:type():type():name()
-- output type declaration with API name
print(cdecl.declare(decl, function(node)
if node == decl then return name end
end) .. ";")
end
end
end)
Parse the C source file using the GCC Lua plugin:
gcc -S -std=c99 -Wall type.c -fplugin=../gcc-lua/gcc/gcclua.so -fplugin-arg-gcclua-script=type.lua
The output contains the declarations captured in the C source file:
typedef int clockid_t;
typedef long int time_t;
4 Struct, union and enum types
For a type declaration, gcc.cdecl.declare
only expands
anonymous struct, union and enum types, for the purpose of
allowing forward declarations, and to avoid duplicate definitions.
Instead of the type declaration, we format the type itself to define a
named struct, union or enum type.
Create a file struct.c
with the following C source
code:
#define _XOPEN_SOURCE 700
#include <time.h>
#define cdecl_struct(name) struct name *cdecl_struct__ ## name;
(timespec) cdecl_struct
Create a file struct.lua
with the following Lua source
code:
local gcc = require("gcc")
local cdecl = require("gcc.cdecl")
-- send assembler output to /dev/null
.set_asm_file_name(gcc.HOST_BIT_BUCKET)
gcc
-- invoke Lua function after translation unit has been parsed
.register_callback(gcc.PLUGIN_FINISH_UNIT, function()
gcc-- get global variables in reverse order of declaration
local nodes = gcc.get_variables()
for i = #nodes, 1, -1 do
local name = nodes[i]:name():value()
local name = name:match("^cdecl_struct__(.+)")
if name then
-- get pointee type
local type = nodes[i]:type():type()
-- output type with API name
print(cdecl.declare(type, function(node)
if node == type then return name end
end) .. ";")
end
end
end)
Parse the C source file using the GCC Lua plugin:
gcc -S -std=c99 -Wall struct.c -fplugin=../gcc-lua/gcc/gcclua.so -fplugin-arg-gcclua-script=struct.lua
The output contains the definitions of types captured in the C source file:
struct timespec {
long int tv_sec;
long int tv_nsec;
};
5 Integer constants
In this example, we extract and declare integer constants, which is the only kind of C constant supported by LuaJIT FFI as of LuaJIT version 2.0.
Create a file const.c
with the following C source
code:
#define _XOPEN_SOURCE 700
#include <sys/resource.h>
#define cdecl_const(name) __typeof__(name) cdecl_const__ ## name = name;
/* https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html */
(RLIM_INFINITY)
cdecl_const(RLIMIT_CORE)
cdecl_const(RLIMIT_CPU)
cdecl_const(RLIMIT_DATA)
cdecl_const(RLIMIT_FSIZE)
cdecl_const(RLIMIT_NOFILE)
cdecl_const(RLIMIT_STACK) cdecl_const
Create a file const.lua
with the following Lua source
code:
local gcc = require("gcc")
-- send assembler output to /dev/null
.set_asm_file_name(gcc.HOST_BIT_BUCKET)
gcc
-- invoke Lua function after translation unit has been parsed
.register_callback(gcc.PLUGIN_FINISH_UNIT, function()
gcc-- get global variables in reverse order of declaration
local nodes = gcc.get_variables()
for i = #nodes, 1, -1 do
local name = nodes[i]:name():value()
local name = name:match("^cdecl_const__(.+)")
if name then
-- extract constant value
local value = nodes[i]:initial():value()
-- output constant with API name
print(("static const int %s = %d;"):format(name, value))
end
end
end)
Parse the C source file using the GCC Lua plugin:
gcc -S -std=c99 -Wall const.c -fplugin=../gcc-lua/gcc/gcclua.so -fplugin-arg-gcclua-script=const.lua
The output contains the definitions of constants captured in the C source file:
static const int RLIM_INFINITY = -1;
static const int RLIMIT_CORE = 4;
static const int RLIMIT_CPU = 0;
static const int RLIMIT_DATA = 2;
static const int RLIMIT_FSIZE = 1;
static const int RLIMIT_NOFILE = 7;
static const int RLIMIT_STACK = 3;