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 */
cdecl_func(getopt)
cdecl_var(optarg)
cdecl_var(opterr)
cdecl_var(optind)
cdecl_var(optopt)
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
gcc.set_asm_file_name(gcc.HOST_BIT_BUCKET)
-- invoke Lua function after translation unit has been parsed
gcc.register_callback(gcc.PLUGIN_FINISH_UNIT, function()
-- 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:
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 */
cdecl_func(basename)
/* https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirname.html */
cdecl_func(dirname)
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
gcc.set_asm_file_name(gcc.HOST_BIT_BUCKET)
-- invoke Lua function after translation unit has been parsed
gcc.register_callback(gcc.PLUGIN_FINISH_UNIT, function()
-- 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;
cdecl_type(clockid_t)
cdecl_type(time_t)
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
gcc.set_asm_file_name(gcc.HOST_BIT_BUCKET)
-- invoke Lua function after translation unit has been parsed
gcc.register_callback(gcc.PLUGIN_FINISH_UNIT, function()
-- 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:
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;
cdecl_struct(timespec)
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
gcc.set_asm_file_name(gcc.HOST_BIT_BUCKET)
-- invoke Lua function after translation unit has been parsed
gcc.register_callback(gcc.PLUGIN_FINISH_UNIT, function()
-- 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:
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 */
cdecl_const(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)
Create a file const.lua
with the following Lua source code:
local gcc = require("gcc")
-- send assembler output to /dev/null
gcc.set_asm_file_name(gcc.HOST_BIT_BUCKET)
-- invoke Lua function after translation unit has been parsed
gcc.register_callback(gcc.PLUGIN_FINISH_UNIT, function()
-- 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: