Инъекция N-зависимостей в C - лучше, чем линеарные массивы?

Учитывая библиотечный модуль, в следующем называемом Runner, который находится в качестве повторно используемого компонента (не требуется перекомпиляции, то есть статическая библиотека ссылок) в разделе приложения архитектуры, а не в главном разделе. Обратите внимание, что он содержит только main() для демонстрационных целей.

Учитывая набор (не относящийся к делу) других модулей/объектов, называемых Callable s, т.е. Callable1, Callable2 и Callable3, которые также находятся в качестве повторно используемых компонентов в разделе приложения.

Runner имеет зависимость от времени выполнения от Callable в том, что Runner должен знать каждый из этих Callable, чтобы получить данные от них (получить доступ к структуре) или выполнить их операцию ( вызовите функцию).

Runner не должен зависеть от компиляции с Callable s. Вместо этого зависимости должны быть введены через компоновщик в фиктивный модуль Callables. Концепция должна использоваться не только в размещенной среде, но и в автономной среде. Поэтому механизмы загрузки, такие как загрузка Callable в качестве общих объектов во время выполнения, не могут быть использованы.

Как можно вводить зависимости с помощью компоновщика?

Мое рабочее решение состоит в том, чтобы указать указатели Callable в выделенном разделе, в этом случае с именем Callables, который собирал компоновщик, чтобы Runner мог получить к нему доступ, получая необходимую информацию во время соединения.

Callable.h

#ifndef CALLABLE_H
#define CALLABLE_H
typedef void (*Callable)(void);
#endif

Callables.h

#ifndef CALLABLES_H
#define CALLABLES_H
#include "Callable.h"
extern Callable Callables_start[];
extern Callable Callables_end[];
#endif

Runner.c

#include "Callables.h"
void Runner_run(void) {
    for (Callable *callables = Callables_start; callables < Callables_end; callables++)
        (*callables)();
}
int main(void) {
    Runner_run();
    return 0;
}

Callable1.c

#include <stdio.h>
#include "Callable.h"
static void Callable1_call(void) {
    printf("Callable 1\n");
}
static Callable thisCallable __attribute__((section("Callables"))) = &Callable1_call;

Callable2.c и Callable3.c выглядят одинаково, за исключением другого сообщения в printf().

Callables.ld

PROVIDE(Callables_start = LOADADDR(Callables));
PROVIDE(Callables_end = Callables_start + SIZEOF(Callables));

Makefile

CFLAGS+=--std=c99
callables:=Callable1 Callable2 Callable3
.PHONY: all
all: Runner

Runner: Callables.ld Runner.o $(addsuffix .o, $(callables))

Выход

$ make -s && ./Runner
Callable 1
Callable 2
Callable 3

Выход показывает, что решение работает, но я не удовлетворен решением. Каковы альтернативные, предпочтительней лучшие способы встраивания зависимостей с помощью компоновщика, чем найденное решение?

Примечание

Окружающая среда - GCC/ld на x86_64 с -ffreestanding, но меня очень интересуют решения, которые менее специфичны для инструментальной цепочки/более переносимы, чем решение, которое я нашел до сих пор.