Static linking y archives (.a)
Una static library (biblioteca estática) no es un formato binario especial con un único
cuerpo de código mergeado. Es un archive: una tabla de miembros .o relocatable más un
índice de símbolos que esos miembros definen. Durante un link estático, el linker escanea
objects y archives, extrae solo los miembros que satisfacen símbolos unresolved que ya vio,
y después trata esos miembros extraídos como object files comunes. Esa regla de extracción
es la razón por la que el orden main.o -lthing vs -lthing main.o puede decidir si el
link tiene éxito.
El reset: un archive se busca, no se linkea entero por default. Los object files se incluyen siempre cuando los nombrás; los miembros de un archive se incluyen solo cuando responden una necesidad actual.
Cómo funciona realmente
El static linking consume objects relocatable y produce un ejecutable que ya contiene una
copia del código de biblioteca requerido. Para un object file común, la regla es simple: si
nombrás helper.o en la línea de comandos, el linker lo incluye. Para un archive como
libcalc.a, el linker abre el índice del archive y extrae solo miembros cuyas definiciones
resuelven símbolos externos undefined actuales.
La convención Unix usual es libNAME.a, usada desde el driver como -lNAME. El driver pasa
el search path de bibliotecas con -Ldir; el linker busca libNAME.so o libNAME.a según
los defaults y flags de la plataforma. Cuando se selecciona el static archive, el índice de
símbolos deja que el linker salte al miembro que define un símbolo necesario en vez de leer
todos los objects a ciegas.
La regla left-to-right es la parte que muerde. Si el linker ve main.o primero, registra
referencias undefined como calc_add y calc_mul. Cuando después escanea libcalc.a,
extrae add.o y mul.o porque esos miembros definen los símbolos pendientes. Si ve
-lcalc antes de main.o, todavía no hay referencias pendientes, entonces el archive no
aporta nada; después aparece main.o, los símbolos quedan unresolved y el archive no se
vuelve a escanear automáticamente.
| Tipo de input | Comportamiento del linker |
|---|---|
main.o |
siempre se incluye cuando lo nombrás |
libcalc.a(add.o) |
se extrae solo si resuelve un símbolo undefined actual |
libcalc.a(mul.o) |
misma regla de miembro de archive |
libc |
normalmente la agrega el compiler driver para C hosted |
El static linking te da una copia autocontenida del código de biblioteca seleccionado, pero no borra las reglas de ABI. Los objects todavía tienen que coincidir en arquitectura target, formato de object, calling convention, modelo de relocation y supuestos de runtime. Un archive Linux/ELF no se puede linkear dentro de un ejecutable macOS/Mach-O solo porque ambos contengan machine code x86-64.
Artefacto ejecutable: extracción de archive y orden
El ejemplo vive en
examples/toolchain-and-linking/static-linking-and-archives/. Construye dos objects de
biblioteca, los empaqueta en libcalc.a, muestra los símbolos del archive y corre el
programa nativo. El script también chequea el orden no portable -lcalc main.o en este host
para que la nota sea honesta sobre el comportamiento de la plataforma.
main.c usa declaraciones desde calc.h:
#include <stdio.h>
#include "calc.h"
int main(void) {
int sum = calc_add(2, 5);
int product = calc_mul(sum, 3);
printf("static archive result: %d\n", product);
return 0;
}
Los miembros del archive definen los símbolos:
#include "calc.h"
int calc_add(int left, int right) {
return left + right;
}
#include "calc.h"
int calc_mul(int left, int right) {
return left * right;
}
Corrélo:
cd examples/toolchain-and-linking/static-linking-and-archives
./run.sh
Salida real de esta máquina:
== native archive and run ==
__.SYMDEF SORTED
add.o
mul.o
== symbols inside archive members ==
add.o:
0000000000000000 T _calc_add
mul.o:
0000000000000000 T _calc_mul
== library before object on this Apple ld ==
static archive result: 21
== portable order: object before library ==
static archive result: 21
== portability note ==
GNU/ELF linkers commonly require the portable order above for static archives.
Esta corrida es en macOS/Mach-O, así que los símbolos C tienen guion bajo inicial y Apple
ld acepta este caso chico -lcalc main.o. No generalices eso a Linux/ELF. La búsqueda de
archives estilo GNU es la regla más estricta descrita arriba: poné los objects que crean
referencias unresolved antes de los archives que las satisfacen. En Linux/ELF, el mismo
nm normalmente imprime calc_add y calc_mul sin guion bajo, y el orden no portable
suele fallar con un diagnostic de undefined reference.
Modos de falla y trade-offs
- Orden incorrecto de bibliotecas. La extracción desde static archives depende del orden. Poné los objects que crean referencias antes de los archives que las satisfacen.
- Dependencias circulares entre archives. Si
liba.aylibb.ase necesitan entre sí, un escaneo puede no alcanzar. Linkers GNU tienen opciones de grupo como--start-group/--end-groupcon costo; otra solución es diseñar bordes de archive más limpios. - Definiciones strong duplicadas. Si dos objects incluidos definen el mismo símbolo externo, el link falla. Los archives pueden esconder esto hasta que se extrae un miembro.
- Ejecutables más grandes. Static linking copia código de biblioteca seleccionado en cada programa. Puede ayudar al deploy, pero también duplica código entre muchos procesos.
- Fricción para updates. Una biblioteca dynamic puede parchearse en un lugar; un programa linkeado estáticamente debe relinkearse y redistribuirse para tomar fixes.
- Restricciones de licencia y ABI. Algunas bibliotecas imponen obligaciones de distribución o esperan dynamic linking. Static no significa "libre de política."
En la práctica
- Leé líneas de link de izquierda a derecha. Cuando falla un static link, mové
-lfoodespués de los objects que referencianfoo, y después inspeccioná connmantes de cambiar fuente. - Usá
ar -tynm libx.a. Te dicen qué miembros existen y qué símbolos definen realmente. - Preferí ownership explícito de objects. Los headers declaran; un
.ces dueño de cada definición; los archives empaquetan objects sin cambiar esa regla. - Usá static linking deliberadamente. Es excelente para runtimes freestanding, envelopes de deploy chicos, rescue tools y bibliotecas de soporte para proyectos de OS. Es menos cómodo para updates de seguridad rápidos y bibliotecas del sistema compartidas.
- Dejá que el compiler driver invoque el linker.
gcc main.o -L. -lcalcagrega las piezas hosted del runtime C; conlddirecto tenés que pasarlas vos.
Conecta con: Toolchain y Linking · Símbolos: definición, referencia y resolución · Object files y qué tienen adentro · Translation units, declaraciones vs definiciones y linkage
Fuentes
- GNU binutils — manual de
ar— creación de archives, listado de miembros y comportamiento del índice de símbolos para static libraries. https://sourceware.org/binutils/docs/binutils/ar.html - GNU
ldmanual — búsqueda de archives — cómo los miembros de archives se extraen para resolver símbolos undefined y por qué importa el orden. https://sourceware.org/binutils/docs/ld/ - Ian Lance Taylor — serie Linkers — vista de implementación de linker sobre escaneo de archives y resolución de símbolos. https://www.airs.com/blog/archives/38
- David Drysdale — Beginner's Guide to Linkers — explicación práctica de static libraries y la regla left-to-right del link. https://www.lurklurk.org/linkers/linkers.html
- John R. Levine — Linkers and Loaders — static linking, bibliotecas, resolución de símbolos y contexto de loader. https://linker.iecc.com/
- Bryant & O'Hallaron — Computer Systems: A Programmer's Perspective (CS:APP), cap. 7 — archivos archive y static linking desde el lado visible para programadores. https://csapp.cs.cmu.edu/