Given a library module, in the following called Runner
, which resides as a reusable component (no recompilation required, i.e. static link library) in the app partition of the architecture, not the main partition. Note that it only contains main()
for demo purposes.
Given a set (order irrelevant) of other modules / objects called Callable
s, i.e. Callable1
, Callable2
and Callable3
, which also reside as reusable components in the app partition.
Runner
has a runtime dependency on the Callable
s in that Runner
needs to know each and every of these Callable
s in order to get data from them (access a structure) or have them perform an operation (call a function).
Runner
shall not have a compile dependency on the Callable
s. Instead the dependencies shall be injected via the linker into a fictive module Callables
.
The concept shall be usable not only in a hosted environment but also a freestanding environment. Therefore, loader-based mechanisms like loading the Callable
s as shared objects at runtime cannot be used.
How can one inject the dependencies using the linker?
My working solution is by letting the Callable
s define pointers in a dedicated section, in this case named Callables
, which the linker would collect so that the Runner
can access it, gaining the necessary information at link time.
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
and Callable3.c
look the same except for a different message in 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))
Output
$ make -s && ./Runner
Callable 1
Callable 2
Callable 3
The output shows that the solution works, but I'm not satisfied with the solution. What are alternative, preferably better ways to inject dependencies using the linker than the solution that I've found?
Note
The environment is GCC / ld on x86_64 with -ffreestanding
, but I'm very much interested in solutions that are less toolchain-specific / more portable than the solution which I've found so far.