NOTE: below is gcc specific.
It's always hard to come up with examples, let's say we have a processing item
struct __item {
int flags;
int prio;
void *priv;
};
a number of possible types
#define IT_TYPE_A (1 << 4)
#define IT_TYPE_B (1 << 3)
#define IT_TYPE_C (1 << 2)
#define IT_TYPE_ANY (1 << 1)
and a usage example:
struct __item it = {
.flags = IT_TYPE_ANY | IT_TYPE_C,
.prio = 0,
.priv = "cron item processing",
};
[..]
process_item(&it);
Let's say, we deprecate (
you know, because of reasons(tm))
IT_TYPE_ANY field.
Now we need to check and correct passed items each time
process_item() called,
even for compile-time known flag values, like the above one. In a trivial situation
this might be almost free operation, but it depends.
A solution could be to extract a
happy path -- for compile time known
values, and a
slow path -- for run-time checks and adjustments.
IOW, we need to hide our original
process_item() and define new function
__fortify_function int process_item(struct __item *it)
{
if (__builtin_constant_p (it->flags)) {
if ((it->flags & IT_TYPE_ANY) != 0) {
__warn_type_any_deprecated();
it->flags &= ~IT_TYPE_ANY;
it->flags |= IT_TYPE_A;
/** something important **/
}
}
return __process_item(it);
}
that'd be able to test and correct passed items.
__builtin_constant_p() is a
gcc internal, that returns 1 if compiler can prove that value is correct and known
at compile time (with some exceptions), and
__fortify_function is
__extern_always_inline __attribute_artificial__.
We also might be interested in informing programmer about usage of a
deprecated flag value, therefore we define empty function
extern void __attribute__((deprecated)) __warn_type_any_deprecated() {};
Now compiler fires a warning each time it sees
__warn_type_any_deprecated():
gcc -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -O2 test.c -o a.out
In file included from test.c:21:0:
item.h: In function ‘process_item’:
item.h:34:4: warning: ‘__warn_type_any_deprecated’ is deprecated (declared at item.h:25) [-Wdeprecated-declarations]
The good thing about this is that for
__builtin_constant_p() case compiler will try
to do all checks and flag adjustments behind the scene, producing code directly
for "
IT_TYPE_A | IT_TYPE_C" case:
movl $0x0,0x4(%rsp)
movq $0x4006be,0x8(%rsp)
movl $0x14,(%rsp)
callq 0x400580 <__process_item>
note
movl $0x14,(%rsp) instead of
movl $0x6,(%rsp).
Developer, however, is still able to ignore (or simply miss) warning, so
for radical cases we can fail build process.
sys/cdefs.h header file contains several defines that could be useful
145 #if __GNUC_PREREQ (4,3)
146 # define __warndecl(name, msg) \
147 extern void name (void) __attribute__((__warning__ (msg)))
148 # define __warnattr(msg) __attribute__((__warning__ (msg)))
149 # define __errordecl(name, msg) \
150 extern void name (void) __attribute__((__error__ (msg)))
151 #else
152 # define __warndecl(name, msg) extern void name (void)
153 # define __warnattr(msg)
154 # define __errordecl(name, msg) extern void name (void)
155 #endif
Changing
__warn_type_any_deprecated() prototype to
__warndecl (__warn_type_any_deprecated, "\n\tWARNING: TYPE ANY has been deprecated");
will change output to:
gcc -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -O2 test.c -o a.out
In file included from test.c:21:0:
In function ‘process_item’,
inlined from ‘main’ at test.c:63:14:
item.h:34:30: warning: call to ‘__warn_type_any_deprecated’ declared with attribute warning:
WARNING: TYPE ANY has been deprecated [enabled by default]
and fail linkage (pretty hard to ignore), because
__warndecl() declares function
w/o body:
test.c:(.text.startup+0x1d): undefined reference to `__warn_type_any_deprecated'