ltrace.conf - Configuration file for ltrace(1).
This manual page describes ltrace.conf, a file that describes prototypes of functions in binaries for ltrace(1) to use. Ltrace needs this information to display function call arguments.
Each line of a configuration file describes at most a single item. Lines composed entirely of white space are ignored, as are lines starting with semicolon character (comment lines). Described items can be either function prototypes, or definitions of type aliases.
A prototype describes return type and parameter types of a single function. The syntax is as follows:
NAME is the (mangled) name of a symbol. In the elementary case, LENS is simply a type. Both lenses and types are described below. For example, a simple function prototype might look like this:
Despite the apparent similarity with C, ltrace.conf is really its own language that's only somewhat inspired by C.
Ltrace understands a range of primitive types. Those are interpreted according to C convention native on a given architecture. E.g. ulong is interpreted as 4-byte unsigned integer on 32-bit GNU/Linux machine, but 8-byte unsigned integer on 64-bit GNU/Linux machine.
Besides primitive types, the following composed types are possible:
Alignment is computed as customary on the architecture. Custom alignment (e.g. packed structs) and bit-fields are not supported. It's also not possible to differentiate between structs and non-POD C++ classes, for arches where it makes a difference.
Note that in C, arrays in role of function argument decay into pointers. Ltrace currently handles this automatically, but for full formal correctness, any such arguments should be described as pointers to arrays.
Lenses change the way that types are described. In the simplest case, a lens is directly a type. Otherwise a type is decorated by the lens. Ltrace understands the following lenses:
If the underlying type is integral, then bits are shown in their natural big-endian order, with LSB being bit 0. E.g. bitvec(ushort) with value 0x0102 would be displayed as <1,8>, irrespective of underlying byte order.
For other data types (notably structures and arrays), the underlying data is interpreted byte after byte. Bit 0 of first byte has number 0, bit 0 of second byte number 8, and so on. Thus bitvec(struct(int)) is endian sensitive, and will show bytes comprising the integer in their memory order. Pointers are first dereferenced, thus bitvec(array(char, 32)*) is actually a pointer to 256-bit bit vector.
string(TYPE)
string[EXPR]
string
Because simple C-like strings are pretty common, there are two shorthand forms. The first shorthand form (with brackets) means the same as string(array(char, EXPR)*). Plain string without an argument is then taken to mean the same as string[zero].
Note that char* by itself describes a pointer to a char. Ltrace will dereference the pointer, and read and display the single character that it points to.
enum(NAME[=VALUE]{,NAME[=VALUE]})
enum[TYPE](NAME[=VALUE]{,NAME[=VALUE]})
TYPE, if given, is the underlying type. It is thus possible to create enums over shorts or longs---arguments that are themselves plain, non-enum types in C, but whose values can be meaningfully described as enumerations. If omitted, TYPE is taken to be int.
A line in config file can, instead of describing a prototype, create a type alias. Instead of writing the same enum or struct on many places (and possibly updating when it changes), one can introduce a name for such type, and later just use that name:
Ltrace allows you to express recursive structures. Such structures are expanded to the depth described by the parameter -A. To declare a recursive type, you first have to introduce the type to ltrace by using forward declaration. Then you can use the type in other type definitions in the usual way:
For example, consider the following singy-linked structure and a function that takes such list as an argument:
Such declarations might lead to an output like the following:
Ltrace detects recursion and will not expand already-expanded structures. Thus a doubly-linked list would look like the following:
With output e.g. like:
The "recurse^" tokens mean that given pointer points to a structure that was expanded in the previous layer. Simple "recurse" would mean that it points back to this object. E.g. "recurse^^^" means it points to a structure three layers up. For doubly-linked list, the pointer to the previous element is of course the one that has been just expanded in the previous round, and therefore all of them are either recurse^, or nil. If the next and previous pointers are swapped, the output adjusts correspondingly:
Ltrace has support for some elementary expressions. Each expression can be either of the following:
zero
zero(EXPR)
Sometimes the actual function prototype varies slightly depending on the exact parameters given. For example, the number and types of printf parameters are not known in advance, but ltrace might be able to determine them in runtime. This feature has wider applicability, but currently the only parameter pack that ltrace supports is printf-style format string itself:
C functions often use one or more arguments for returning values back to the caller. The caller provides a pointer to storage, which the called function initializes. Ltrace has some support for this idiom.
When a traced binary hits a function call, ltrace first fetches all arguments. It then displays left portion of the argument list. Only when the function returns does ltrace display right portion as well. Typically, left portion takes up all the arguments, and right portion only contains return value. But ltrace allows you to configure where exactly to put the dividing line by means of a + operator placed in front of an argument:
Here, the first argument to asprintf is denoted as return argument, which means that displaying the whole argument list is delayed until the function returns:
It is currently not possible to have an "inout" argument that passes information in both directions.
In the following, the first is the C prototype, and following that is ltrace configuration line.
enum e_foo {RED, GREEN, BLUE};
void func_enum(enum e_foo bar);
struct S1 {float f; char a; char b;};
struct S2 {char str[6]; float f;};
struct S1 func_struct(int a, struct S2, double d);