Это делается с помощью слабых псевдонимов"нестандартного" трюка с компоновщиком, который использовался с ранних версий и поддерживается всеми известными мне компиляторами / компоновщиками Unix.В основном это делается так:
void __foo(void);
void foo(void) __attribute__((weak, alias("__foo")));
часто с макросами, чтобы немного абстрагировать его.Это делает его таким, что символ foo
будет иметь тот же адрес и тип, что и символ __foo
по умолчанию, но позволяет переопределить его «сильным» определением где-то еще.