Skip to content

Instantly share code, notes, and snippets.

@toasterparty
Last active August 12, 2022 18:31
Show Gist options
  • Save toasterparty/c39ef97787154d53c7ba9f77e61b5f4c to your computer and use it in GitHub Desktop.
Save toasterparty/c39ef97787154d53c7ba9f77e61b5f4c to your computer and use it in GitHub Desktop.
Abuse GCC's section attribute to iterate through compile-defined (but dynamically included/excluded) data.
// If you want to play with this code, I reccomend https://jdoodle.com/ia/tPT
// Most other online compilers fail to respect the section definitions
//
// This file is a demonstration on how you can use macros and gcc section attributes to
// arbitrarily access functions/memory from another file's static scope at *link time*
// and without the use of runtime function pointers. It ONLY works with projects linked using gcc.
//
// Real world usage of this include:
// - Implementing a component-oriented API where each components adds support for their
// specific command(s) without having to maintain a large API manifest.
// - Callback handlers + handler data without consuming RAM
// - Executing tests on non-production code without polluting non-test code
// - Kernel Security Magic that goes above my head
#include <stdio.h>
/*** Magical Tools ***/
/* Neatly define a function pointer with no arguments and no return value */
typedef void (*magic_function_t)(void);
/* Define struct to contain smuggled data */
typedef struct {
magic_function_t function;
} magic_struct_t;
/* Helper macro for instantiating a unique instance of the above struct */
#define DECLARE_HELPER_STRUCT(fn) static const magic_struct_t magic_struct_##fn __attribute__((unused)) = {.function = fn}
/* Macro to define a constant function pointer inside of a constant struct, and then
define a pointer to that struct inside of our custom section which we use to smuggle data.
If we don't use a pointer to a struct, discrepancies in .text and .data/.bss memory alignment
can cause severe problems. Ideally the compiler chooses to place these constants in .text to
save memory, but we don't have direct control of that as the programmer. */
#define DECLARE_FUNCTION_MAGICAL(fn) \
DECLARE_HELPER_STRUCT(fn); \
static const magic_struct_t* magic_struct_ptr##fn __attribute__((section("magic"))) __attribute__((unused)) = &(magic_struct_##fn)
/* GCC automatically defines these during link-time (__start_<section-name-here> and __stop_<section-name-here>). As such, this trick only works
with GCC compilers. */
extern magic_struct_t* __start_magic;
extern magic_struct_t* __stop_magic;
/*** Magical Instantiation ***/
/* From this section's perspective, what goes on appears like magic */
static void magic_function_a(void) {
printf("Compiler Magic Occured (a)\n");
}
static void magic_function_b(void) {
printf("Compiler Magic Occured (b)\n");
}
DECLARE_FUNCTION_MAGICAL(magic_function_a);
DECLARE_FUNCTION_MAGICAL(magic_function_b);
/*** Magical Invocation ***/
/* This could be litterally anywhere as long as it gets linked at the same time as the above */
int main(void) {
/* Iterate through struct-sized chunks of our custom
segment until we reach the linker-provided end of
segment address */
magic_struct_t **iter = &__start_magic;
for ( ; iter < &__stop_magic; ++iter) {
magic_struct_t* magic_struct = *iter;
magic_struct->function();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment