Jan H. schrieb:
Das heißt der Linker findet die sonst nicht?
Der Linker findet garnix.
Es gibt das Makefile, das üblicherweise von der IDE generiert wird.
In dem steht zunächst drin, welche Quelldateien vorhanden sind, das können C-Dateien sein, oder Assemblerquellcodes oder prinzipiell, wenn die Calling-Conventions der verwendeten Compiler kompatibel sind, auch ganz andere Sprachen. (Auf PC wurde das früher(TM) z.B. mit (Turbo) Pascal und C durchaus gemacht.)
Ein #include im C-File (oder H-File) macht nichts anderes, als genau an dieser Stelle die referenzierte Datei 1:1 einzufügen. Einfach wie Copy/Paste, weiter passiert da nichts. Da das mehrfach passieren kann (z.B. wenn das Headerfile auch die stdio.h einbindet), haben headerfiles üblicherweise das #ifndef BLAH_H #define BLAH_H .. Körper .. #endif Konstrukt.
Aus den so vom Präprozessor vorbereiteten C Files werden dann Objektfiles. Eines pro Datei. Das ist Maschinencode, wo aber alle Speicheradressen noch leer sind. Zusätzlich Metadaten, mit was sie gefüllt werden sollen.
Der Linker fasst dann alle Objektfiles (die ihm auch vom Makefile genannt werden) zusammen und trägt die Adressen ein und baut so das Executable.
(U.u. kommt da noch Relokationscode und Startupcode usw. dazu das will ich aber erstmal beiseite lassen, verkompliziert es nur)
Wenn im Makefile nun keine gpio.c steht, aus der dann eine gpio.o wird die dem Linker vorgesetzt wird, bindet er den aus gpio.c erzeugen Maschinencode nicht mit ein und hat damit natürlich auch keine Adresse für "gpio_set_bit".
Eine weitere Möglichkeit ist die Verwendung einer vorkompilierten Library, das ist als z.B. libc sogar der Standard. Der Linker bekommt dann gesagt, dass diese Library existiert und kopiert die nötigen Funktionen aus dieser heraus.
Eine passend konfigurierte IDE versteckt viel davon, aber wenn es mal nicht geht sollte man schon wissen wie das ganze abläuft.