Linkování
Když napíšeš program ve vyšším jazyce (např. C), překladač ho nejprve přeloží do objektových souborů (.o).
Každý objektový soubor obsahuje strojový kód, ale ještě není kompletní – mohou v něm chybět odkazy na jiné funkce nebo proměnné.
Při psaní programu je praktické rozdělit kód do více souborů.
Každý soubor může řešit jinou část programu – například jeden obsahuje hlavní funkci main, jiný matematické výpočty a další třeba ovládání hardwaru.
Díky tomu je kód přehlednější, snadněji se udržuje a více programátorů na něm může pracovat současně.
Aby se z těchto částí stal fungující program, je potřeba je na konci spojit dohromady. Úkolem linkeru (spojovače) je tyto části poskládat dohromady a vytvořit spustitelný program.
Typy knihoven při linkování
-
Statické knihovny (
.a)
Kód se zkopíruje přímo do výsledného programu.
Výhoda: není potřeba nic externího. Nevýhoda: větší velikost. -
Dynamické knihovny (
.so,.dll)
Kód je načítán až při spuštění.
Výhoda: menší velikost, sdílení knihoven. Nevýhoda: závislost na externím souboru.
Jak funguje linkování
-
Překlad (compile)
Zdrojový kód (např.main.c) → objektový soubor (main.o).
V objektovém souboru mohou být:- definované funkce (např.
main), - jen deklarované funkce (např.
printf– víme, že existuje, ale není tam implementace).
- definované funkce (např.
-
Linkování (link)
Linker vezme všechny objektové soubory a knihovny (.o,.a,.so) a:- spojí je dohromady,
- nahradí všechny odkazy správnými adresami,
- vyhodí nevyužitý kód,
- vytvoří výsledný spustitelný soubor (
.elf,.exe).
-
Výsledek
Kompletní binární soubor, který se dá spustit nebo nahrát do mikrokontroléru.
U MCU (bare-metal programování) linker navíc:
- rozhoduje, kam se v paměti nahraje kód a data (flash, RAM, periferie),
- řídí umístění přerušovacích tabulek,
- vytváří výsledný
.hexnebo.binsoubor pro nahrání do čipu.
To se určuje pomocí linker skriptů (.ld), které popisují rozložení paměti.
Mějme dva zdrojové soubory:
#include <stdio.h>
void hello(void); // deklarace funkce z jiného souboru
int main(void) {
hello();
return 0;
}
#include <stdio.h>
void hello(void) {
printf("Ahoj světe!\n");
}
1) Překlad
gcc -c main.c # vytvoří main.o
gcc -c hello.c # vytvoří hello.o
main.oobsahuje voláníhello, ale nezná jeho adresu.hello.oobsahuje definicihello, ale ta ještě není propojená.
2) Linkování
gcc main.o hello.o -o program
- najde symbol
hellovhello.o, - propojí ho s voláním v
main.o, - výsledek je spustitelný program
program.
Header soubory (.h)
Když rozdělíme program do více souborů, je potřeba nějak sdílet informace mezi nimi.
Například když v jednom souboru definujeme funkci hello(), musí o ní ostatní soubory vědět – jinak překladač vyhlásí chybu.
K tomu slouží header soubory (.h):
- Obsahují deklarace funkcí (říkají překladači, že funkce existuje někde jinde).
- Mohou obsahovat i definice konstant, maker nebo struktur.
- Při překladu se vkládají pomocí
#include.
hello.h
#ifndef HELLO_H
#define HELLO_H
void hello(void); // deklarace funkce
#endif
hello.c
#include <stdio.h>
#include "hello.h"
void hello(void) {
printf("Ahoj světe!\n");
}
main.c
#include "hello.h"
int main(void) {
hello(); // kompilátor ví, že funkce existuje díky deklaraci v hello.h
return 0;
}