C Bibliotecas (.a e .so)
Uma biblioteca é um pacote de funções já compiladas, pronto para ser usado por outros programas. Você usa bibliotecas desde o primeiro printf, que mora na biblioteca C do sistema, e desde o primeiro -lm, que liga a biblioteca de matemática. Nesta aula, a Ana transforma o código da lojinha numa biblioteca dela, nos dois formatos do Linux: a estática (.a), copiada para dentro do programa, e a compartilhada (.so), carregada quando o programa roda.
- O que são
libcelibm, e como olddmostra as bibliotecas de um programa. - A criar uma biblioteca estática com
ar rcs libloja.a. - A ligar com
-I(onde estão os.h),-L(onde está a biblioteca) e-l(qual biblioteca), e por que a ordem no comando importa. - A criar uma biblioteca compartilhada com
-fPICe-shared. - A fazer o programa achar a
.so:LD_LIBRARY_PATHe-Wl,-rpath. - Os erros reais:
não foi possível localizar -lloja,error while loading shared librarieserecompilar com -fPIC.
Esta aula usa o que você viu em Vários Arquivos (gcc -c, arquivos .o, nm) e em static e extern. Todos os comandos rodaram na máquina fictícia da Ana, na pasta ~/c/biblio, com o gcc 15.2 e a glibc 2.43. O ligador (ld) fala português no sistema dela; o inglês vai citado ao lado. O editor do guia não cria bibliotecas (lá, todas as abas são compiladas e ligadas juntas a cada Rodar), mas você vai praticar nele o código e os erros de uma.
A ideia: a fotocópia e o empréstimo
A Ana precisa de três capítulos de um livro da biblioteca da escola. Ela tem dois jeitos de usá-los:
- Tirar fotocópia e colar no caderno. O caderno fica um pouco mais grosso, mas funciona em qualquer lugar, mesmo se a biblioteca fechar. Se o livro ganhar uma edição corrigida, a cópia dela continua com os erros antigos.
- Pegar emprestado toda vez que precisa. O caderno fica fino, e ela sempre lê a edição mais nova. Mas, se o livro não estiver na estante, ela fica sem.
Estática (.a): a fotocópia | Compartilhada (.so): o empréstimo | |
|---|---|---|
| Quando o código entra no programa | Na ligação: o ligador copia as funções para dentro do executável | Quando o programa roda: o carregador do sistema abre a .so |
| Depois de ligar, a biblioteca precisa existir? | Não | Sim, em todo computador onde o programa rodar |
| Corrigir um erro na biblioteca | Ligar o programa de novo | Trocar a .so; o programa usa a nova na próxima vez que rodar |
| Nome do arquivo | libnome.a (de archive, "arquivo") | libnome.so (de shared object, "objeto compartilhado") |
No Windows, os nomes são outros (.lib e .dll), mas a ideia é a mesma.
1. Você já usa bibliotecas
Este programa usa duas bibliotecas: a biblioteca C (onde moram o printf e o resto do stdio.h) e a de matemática (o sqrt):
#include <stdio.h>
#include <math.h>
int main(void) {
double area = 49.0;
printf("Lado do quadrado: %.1f m\n", sqrt(area));
return 0;
}
$ gcc -std=c23 -Wall -Wextra raiz.c -o raiz -lm $ ./raiz Lado do quadrado: 7.0 m $ ldd ./raiz linux-vdso.so.1 (0x00007f26fc6ff000) libm.so.6 => /usr/lib/x86_64-linux-gnu/libm.so.6 (0x00007f26fc5cb000) libc.so.6 => /usr/lib/x86_64-linux-gnu/libc.so.6 (0x00007f26fc200000) /lib64/ld-linux-x86-64.so.2 (0x00007f26fc701000)
O ldd (de list dynamic dependencies, "listar as dependências dinâmicas") mostra as bibliotecas compartilhadas de que um programa precisa para rodar, e onde o sistema as achou:
- libm.so.6 => /usr/lib/…/libm.so.6
- A biblioteca de matemática, que entrou por causa do
-lm. O.6no fim é o número da versão. - libc.so.6
- A biblioteca C (no Linux, a glibc). Todo programa C a recebe sem precisar de
-l. - /lib64/ld-linux-x86-64.so.2
- O carregador (dynamic loader): o programa do sistema que, a cada
./raiz, acha e abre as.soantes domaincomeçar. - linux-vdso.so.1
- Um pedacinho do próprio núcleo do Linux, que todo programa recebe. Não é um arquivo; pode ignorar.
- (0x00007f…)
- O endereço da memória onde cada biblioteca foi carregada. Muda a cada execução.
Os arquivos da biblioteca de matemática ficam numa pasta do sistema:
$ ls /usr/lib/x86_64-linux-gnu/libm.* /usr/lib/x86_64-linux-gnu/libm.a /usr/lib/x86_64-linux-gnu/libm.so /usr/lib/x86_64-linux-gnu/libm.so.6
Repare no padrão dos nomes: lib + m + .a ou .so. A opção -lm quer dizer "procure a biblioteca libm": o gcc acrescenta o lib na frente e o .so (ou .a) no fim. Para uma biblioteca chamada libloja, a opção será -lloja.
2. O projeto: a biblioteca da lojinha
A Ana quer usar as contas da lojinha em vários programas (o caixa, o site, a planilha de estoque). Ela organizou o código numa pasta com o cabeçalho em include/ e as implementações em src/, como fazem as bibliotecas de verdade:
$ ls -R .: include main.c src ./include: loja.h ./src: frete.c total.c
#ifndef LOJA_H #define LOJA_H const char *loja_versao(void); int loja_total(int preco, int quantidade); int loja_frete(int km); #endif
#include "loja.h"
const char *loja_versao(void) {
return "1.0";
}
int loja_total(int preco, int quantidade) {
return preco * quantidade;
}
#include "loja.h"
int loja_frete(int km) {
if (km <= 10) {
return 0;
}
return 5 + (km - 10);
}
O frete é grátis até 10 km; depois, custa R$ 5 mais R$ 1 por quilômetro a mais. Todas as funções públicas começam com loja_: como o C não tem "sobrenome" para funções, o prefixo evita que elas briguem com funções de mesmo nome no programa de quem usa a biblioteca (você vai ver essa briga na seção 3). O programa de teste:
#include <stdio.h>
#include "loja.h"
int main(void) {
printf("Biblioteca da loja, versão %s\n", loja_versao());
printf("3 cadernos: R$ %d\n", loja_total(12, 3));
printf("Frete (8 km): R$ %d\n", loja_frete(8));
printf("Frete (23 km): R$ %d\n", loja_frete(23));
return 0;
}
-I: onde estão os .h
O main.c está numa pasta, e o loja.h em outra. O #include "loja.h" procura na pasta do próprio arquivo e não acha:
$ gcc -std=c23 -Wall -Wextra -c main.c
main.c:2:10: fatal error: loja.h: Arquivo ou diretório inexistente
2 | #include "loja.h"
| ^~~~~~~~
compilation terminated.
A opção -I (de include) acrescenta uma pasta à busca dos .h (aula Pré-processador):
$ gcc -std=c23 -Wall -Wextra -Iinclude -c main.c $ gcc -std=c23 -Wall -Wextra -Iinclude -c src/total.c src/frete.c $ ls frete.o include main.c main.o src total.o
Os .o saem na pasta onde a Ana está (~/c/biblio), não em src/.
3. A biblioteca estática (.a)
Uma biblioteca estática é só uma caixa de arquivos .o, feita com o programa ar (de archiver, "arquivador"):
$ ar rcs libloja.a total.o frete.o $ ar t libloja.a total.o frete.o $ file libloja.a libloja.a: current ar archive
- ar rcs
- Três letras de opções:
r(replace) põe os.ona caixa, trocando os que já estiverem lá;c(create) cria a caixa sem reclamar se ela não existe;sgrava um índice dos símbolos, que deixa a ligação mais rápida. - libloja.a
- O nome da biblioteca: tem de começar com
libe terminar com.apara o-llojaachá-la. - ar t
- Lista (table) o que há dentro.
O nm funciona com a caixa inteira, mostrando os símbolos de cada .o:
$ nm libloja.a total.o: 0000000000000011 T loja_total 0000000000000000 T loja_versao frete.o: 0000000000000000 T loja_frete
Ligar: -L e -l
$ gcc main.o -L. -lloja -o app $ ./app Biblioteca da loja, versão 1.0 3 cadernos: R$ 36 Frete (8 km): R$ 0 Frete (23 km): R$ 18
- main.o
- O seu programa. Vem antes das bibliotecas (veja abaixo por quê).
- -L.
- "Procure bibliotecas também nesta pasta" (
.é a pasta atual). Sem espaço entre o-Le a pasta. As pastas do sistema, como/usr/lib/x86_64-linux-gnu, já estão na lista; por isso o-lmnão precisa de-L. - -lloja
- "Ligue a biblioteca
libloja": o gcc procuralibloja.soelibloja.aem cada pasta da lista.
O ligador copiou as três funções para dentro do app. O nm as encontra no executável, e o ldd não lista nenhuma libloja, porque o programa não precisa dela para rodar:
$ nm app | grep loja 000000000000120a T loja_frete 00000000000011f3 T loja_total 00000000000011e2 T loja_versao $ ldd ./app linux-vdso.so.1 (0x00007f0174d62000) libc.so.6 => /usr/lib/x86_64-linux-gnu/libc.so.6 (0x00007f0174a00000) /lib64/ld-linux-x86-64.so.2 (0x00007f0174d64000) $ rm libloja.a $ ./app Biblioteca da loja, versão 1.0 3 cadernos: R$ 36 Frete (8 km): R$ 0 Frete (23 km): R$ 18
Mesmo com a libloja.a apagada, o programa roda: ele levou a fotocópia. (O | grep loja filtra a saída do nm e mostra só as linhas que contêm "loja".) Também dá para passar o arquivo direto, sem -L nem -l: gcc main.o libloja.a -o app.
Os dois erros de ligação mais comuns
Sem o -L., o ligador procura só nas pastas do sistema:
$ gcc main.o -lloja -o app /usr/bin/x86_64-linux-gnu-ld.bfd: não foi possível localizar -lloja: Arquivo ou diretório inexistente collect2: error: ld returned 1 exit status
(Em inglês: cannot find -lloja: No such file or directory.) E com a biblioteca antes do main.o:
$ gcc -L. -lloja main.o -o app /usr/bin/x86_64-linux-gnu-ld.bfd: main.o: na função "main": main.c:(.text+0x9): referência não definida para "loja_versao" /usr/bin/x86_64-linux-gnu-ld.bfd: main.c:(.text+0x32): referência não definida para "loja_total" /usr/bin/x86_64-linux-gnu-ld.bfd: main.c:(.text+0x54): referência não definida para "loja_frete" /usr/bin/x86_64-linux-gnu-ld.bfd: main.c:(.text+0x76): referência não definida para "loja_frete" collect2: error: ld returned 1 exit status
A biblioteca existe, e mesmo assim "referência não definida". O ligador lê o comando da esquerda para a direita. Quando chegou na libloja.a, ninguém ainda tinha pedido nenhuma loja_…, e ele não tirou nada da caixa. Depois veio o main.o, pedindo as três, mas a caixa já tinha ficado para trás. A regra: primeiro os seus arquivos, depois as bibliotecas. (É por isso que o -lm vai no fim do comando, e o LDLIBS no fim da receita do Makefile.)
Nomes internos da biblioteca
Uma biblioteca tem funções públicas (as do .h) e, muitas vezes, auxiliares. Na pasta ~/c/biblio2, a Ana pôs no frete.c uma auxiliar arredondar, que arredonda os quilômetros para o múltiplo de 5 de cima, e se esqueceu do static. Um programa que tem a sua própria arredondar (para notas) não liga:
$ gcc -std=c23 -Wall -Wextra -Iinclude main.c libloja.a -o app_a /usr/bin/x86_64-linux-gnu-ld.bfd: libloja.a(frete.o): na função "arredondar": frete.c:(.text+0x0): múltiplas definições de "arredondar"; /tmp/ccgh3i4y.o:main.c:(.text+0x0): definido primeiro aqui collect2: error: ld returned 1 exit status
O libloja.a(frete.o) diz: o frete.o de dentro da libloja.a. O conserto é o da aula static e extern: tudo o que não está no .h da biblioteca leva static. Quem usa a biblioteca não pode tropeçar nos nomes internos dela.
Missão: no editor, a biblioteca é só mais uma aba, e o ligador junta tudo, como numa biblioteca estática. Rode e leia o erro (duplicate symbol: arredondar). Conserte só a aba frete.c, da biblioteca: a arredondar dela é um detalhe interno. O programa tem de mostrar Frete (23 km): R$ 20 (13 km a mais, arredondados para 15) e Nota 7 vira 10.
#include <stdio.h>
#include "loja.h"
int arredondar(int nota) {
return nota >= 5 ? 10 : 0;
}
int main(void) {
printf("Frete (23 km): R$ %d\n", loja_frete(23));
printf("Nota 7 vira %d\n", arredondar(7));
return 0;
}
#include "loja.h"
int arredondar(int km) {
return (km + 4) / 5 * 5;
}
int loja_frete(int km) {
if (km <= 10) {
return 0;
}
return 5 + arredondar(km - 10);
}
#ifndef LOJA_H #define LOJA_H const char *loja_versao(void); int loja_total(int preco, int quantidade); int loja_frete(int km); #endif
#include "loja.h"
static int arredondar(int km) {
return (km + 4) / 5 * 5;
}
int loja_frete(int km) {
if (km <= 10) {
return 0;
}
return 5 + arredondar(km - 10);
}
4. A biblioteca compartilhada (.so)
A Ana volta ao frete.c sem a arredondar, na pasta ~/c/biblio. Uma .so é feita em dois passos: compilar os .o com -fPIC e juntá-los com -shared:
$ gcc -std=c23 -Wall -Wextra -Iinclude -fPIC -c src/total.c src/frete.c $ gcc -shared total.o frete.o -o libloja.so $ file libloja.so libloja.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=438c9650823dbca86e8990b7667bfad3814ff9f3, not stripped
- -fPIC
- Position Independent Code, "código independente de posição". A
.sovai ser carregada num endereço diferente em cada programa (você viu os endereços mudando noldd), e o código tem de funcionar em qualquer um. Vai na etapa de compilar (-c). - -shared
- "Gere uma biblioteca compartilhada em vez de um executável." Vai na etapa de ligar.
- shared object
- O que o
filediz: um objeto compartilhado. Um executável apareceria comopie executable, e um.ocomorelocatable.
Para ver as funções que uma .so oferece, use nm -D (de dynamic):
$ nm -D libloja.so
w __cxa_finalize
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000001121 T loja_frete
000000000000110a T loja_total
00000000000010f9 T loja_versao
As três T são as da Ana. As linhas com w (weak, "fraco") são ganchos que o gcc põe em toda .so; pode ignorar.
Ligar funciona; rodar, não
O comando de ligação é o mesmo da estática:
$ gcc -std=c23 -Wall -Wextra -Iinclude main.c -L. -lloja -o app $ ./app ./app: error while loading shared libraries: libloja.so: cannot open shared object file: No such file or directory $ echo $? 127
"Erro ao carregar as bibliotecas compartilhadas: não foi possível abrir o arquivo libloja.so." Esta mensagem é do carregador, não do gcc, e sai em inglês mesmo no sistema em português. O programa nem começou: o código de saída 127 é o mesmo de um comando que não existe.
O -L. só vale na hora de ligar: ele disse ao ligador onde achar a biblioteca para conferir os nomes. O programa gravou só o nome libloja.so. Na hora de rodar, o carregador procura esse nome nas pastas do sistema, e não na pasta atual. O ldd mostra o buraco:
$ ldd ./app linux-vdso.so.1 (0x00007f8271677000) libloja.so => not found libc.so.6 => /usr/lib/x86_64-linux-gnu/libc.so.6 (0x00007f8271400000) /lib64/ld-linux-x86-64.so.2 (0x00007f8271679000)
Solução 1: LD_LIBRARY_PATH
A variável de ambiente LD_LIBRARY_PATH (aula Variáveis de Ambiente) acrescenta pastas à busca do carregador. Escrita antes do comando, ela vale só para ele:
$ LD_LIBRARY_PATH=. ./app Biblioteca da loja, versão 1.0 3 cadernos: R$ 36 Frete (8 km): R$ 0 Frete (23 km): R$ 18 $ LD_LIBRARY_PATH=. ldd ./app linux-vdso.so.1 (0x00007efe23cfe000) libloja.so => ./libloja.so (0x00007efe23cee000) libc.so.6 => /usr/lib/x86_64-linux-gnu/libc.so.6 (0x00007efe23a00000) /lib64/ld-linux-x86-64.so.2 (0x00007efe23d00000)
Bom para testar, ruim para o dia a dia: você teria de lembrar dela toda vez.
Solução 2: gravar a pasta no programa (-Wl,-rpath)
$ gcc -std=c23 -Wall -Wextra -Iinclude main.c -L. -lloja -Wl,-rpath,'$ORIGIN' -o app $ ./app | head -1 Biblioteca da loja, versão 1.0 $ ldd ./app linux-vdso.so.1 (0x00007f73c4df5000) libloja.so => /home/ana/c/biblio/libloja.so (0x00007f73c4de5000) libc.so.6 => /usr/lib/x86_64-linux-gnu/libc.so.6 (0x00007f73c4a00000) /lib64/ld-linux-x86-64.so.2 (0x00007f73c4df7000) $ readelf -d app | grep -i path 0x000000000000001d (RUNPATH) Biblioteca runpath: [$ORIGIN]
- -Wl,…
- "Passe isto ao ligador" (o
lé de linker). O gcc não entende o-rpath; ele o repassa aold. As vírgulas separam as palavras: o ligador recebe-rpath $ORIGIN. - -rpath
- Run path, "caminho de execução": uma pasta gravada dentro do executável, onde o carregador vai procurar as
.so. - '$ORIGIN'
- Quer dizer "a pasta onde está o próprio executável". Assim, a dupla
app+libloja.sopode ser copiada junta para qualquer pasta. As aspas simples impedem o Bash de tentar trocar o$ORIGINpor uma variável. - readelf -d
- Mostra a parte "dinâmica" do executável. A linha
RUNPATHé o-rpathgravado.
apt ficam em /usr/lib/x86_64-linux-gnu, onde o carregador já procura. As suas costumam ir para /usr/local/lib (copiar a .so para lá e rodar sudo ldconfig, que atualiza o índice do carregador). Isso precisa de permissão de administrador e muda o sistema todo; para estudar, fique com o -rpath.
A vantagem: trocar a biblioteca sem recompilar o programa
A Ana lança a versão 2.0: o frete agora é grátis até 15 km. Ela muda o total.c (a versão) e o frete.c, refaz só a libloja.so e roda o app antigo, sem recompilá-lo:
#include "loja.h"
int loja_frete(int km) {
if (km <= 15) {
return 0;
}
return 5 + (km - 15);
}
$ ./app Biblioteca da loja, versão 1.0 3 cadernos: R$ 36 Frete (8 km): R$ 0 Frete (23 km): R$ 18 $ gcc -std=c23 -Wall -Wextra -Iinclude -fPIC -c src/total.c src/frete.c $ gcc -shared total.o frete.o -o libloja.so $ ./app Biblioteca da loja, versão 2.0 3 cadernos: R$ 36 Frete (8 km): R$ 0 Frete (23 km): R$ 13
O mesmo app agora diz 2.0. É assim que uma atualização do sistema corrige um erro na libc de todos os programas de uma vez, sem recompilar nenhum. O cuidado: a .so nova tem de continuar oferecendo as mesmas funções, com os mesmos parâmetros. Se a Ana mudasse o loja_frete para receber um double, os programas antigos passariam um int e receberiam lixo, sem aviso nenhum.
Com .a e .so na mesma pasta
Quando as duas existem, o -lloja escolhe a compartilhada. Para usar a estática, passe o arquivo pelo nome:
$ ar rcs libloja.a total.o frete.o $ gcc -std=c23 -Wall -Wextra -Iinclude main.c -L. -lloja -o app_qual $ ldd ./app_qual | grep loja libloja.so => not found $ gcc -std=c23 -Wall -Wextra -Iinclude main.c libloja.a -o app_estatico $ ldd ./app_estatico | grep loja $ ./app_estatico | head -1 Biblioteca da loja, versão 2.0
O app_qual precisa da libloja.so (e, sem -rpath, não a acha); o ldd do app_estatico não mostra nenhuma linha com "loja". Com uma biblioteca tão pequena, os tamanhos quase não mudam:
$ ls -l app app_estatico -rwxrwxr-x 1 ana ana 16056 out 17 11:30 app -rwxrwxr-x 1 ana ana 16120 out 17 11:30 app_estatico
Com bibliotecas grandes, a diferença aparece. A opção -static pede a versão estática de todas as bibliotecas, até da libc e da libm. O programa da raiz quadrada da seção 1:
$ gcc -std=c23 -Wall -Wextra raiz.c -o raiz -lm $ gcc -std=c23 -Wall -Wextra -static raiz.c -o raiz_estatico -lm $ ls -l raiz raiz_estatico -rwxrwxr-x 1 ana ana 15992 out 17 11:34 raiz -rwxrwxr-x 1 ana ana 817064 out 17 11:34 raiz_estatico $ ldd ./raiz_estatico não é um executável dinâmico $ ./raiz_estatico Lado do quadrado: 7.0 m
De 16 KB para 800 KB, por causa das fotocópias da libc e da libm; em troca, o raiz_estatico roda em qualquer Linux de 64 bits, sem depender das bibliotecas instaladas. "Não é um executável dinâmico" (not a dynamic executable) é o ldd dizendo que não há nenhuma .so para carregar.
Sem -fPIC
Com código simples, como o da lojinha, esquecer o -fPIC às vezes passa. Mas basta uma variável global pública na biblioteca para o ligador recusar. Na pasta ~/c/fpic:
int loja_vendas = 0;
void loja_vender(int valor) {
loja_vendas += valor;
}
$ gcc -std=c23 -Wall -Wextra -c vendas.c $ gcc -shared vendas.o -o libvendas.so /usr/bin/x86_64-linux-gnu-ld.bfd: vendas.o: aviso: relocalização contra "loja_vendas" em secção só de leitura ".text" /usr/bin/x86_64-linux-gnu-ld.bfd: vendas.o: relocalização R_X86_64_PC32 contra símbolo "loja_vendas" não pode ser usada ao fazer um objecto partilhado; recompilar com -fPIC /usr/bin/x86_64-linux-gnu-ld.bfd: vinculação final falhou: mau valor collect2: error: ld returned 1 exit status $ gcc -std=c23 -Wall -Wextra -fPIC -c vendas.c $ gcc -shared vendas.o -o libvendas.so && echo ok ok
A frase que importa está no fim da segunda linha: "recompilar com -fPIC" (em inglês: relocation R_X86_64_PC32 against symbol `loja_vendas' can not be used when making a shared object; recompile with -fPIC). A tradução do ligador mistura português de Portugal ("objecto partilhado", "secção"), mas o recado é claro. Regra: todo .o que vai para uma .so é compilado com -fPIC.
Qual usar
| Situação | Use | Por quê |
|---|---|---|
| Um programa que você vai copiar para outro computador, sem instalar nada | Estática (.a) | Tudo vai dentro do executável |
| Código seu, dividido em partes, usado por um ou dois programas | Estática (.a) | Mais simples: nada de -fPIC, -rpath ou "not found" |
| Uma biblioteca usada por muitos programas, que recebe correções | Compartilhada (.so) | Uma cópia só no disco e na memória; corrigir a .so corrige todos |
Bibliotecas do sistema (libc, libm, as do apt) | Compartilhada, que é o padrão | É o que o gcc escolhe quando as duas existem |
Para começar: faça a sua primeira biblioteca estática. Quando ela funcionar, a compartilhada é só trocar o ar rcs por -fPIC + -shared e cuidar do -rpath. Em projetos maiores, o CMake faz as duas com add_library(loja STATIC …) ou add_library(loja SHARED …).
Quando dá errado
fatal error: loja.h: Arquivo ou diretório inexistente
O que significa: o compilador não achou o cabeçalho da biblioteca.
Como consertar: diga a pasta com -I: -Iinclude, -I../loja/include. Para bibliotecas do sistema, quase sempre falta instalar o pacote de desenvolvimento (no Debian e no Ubuntu, os que terminam em -dev).
não foi possível localizar -lloja: Arquivo ou diretório inexistente (cannot find -lloja)
O que significa: o ligador não achou libloja.so nem libloja.a nas pastas da lista.
Como consertar: acrescente a pasta com -L. Confira também o nome: o -l leva o nome sem lib e sem extensão. Com -llibloja, o ligador até dá a dica:
$ gcc -std=c23 -Wall -Wextra -Iinclude main.c -L. -llibloja -o x /usr/bin/x86_64-linux-gnu-ld.bfd: não foi possível localizar -llibloja: Arquivo ou diretório inexistente /usr/bin/x86_64-linux-gnu-ld.bfd: para vincular com ./libloja.a use -l:libloja.a ou renome-o para a liblibloja.a collect2: error: ld returned 1 exit status $ gcc -std=c23 -Wall -Wextra -Iinclude main.c -L. -lloja.so -o x /usr/bin/x86_64-linux-gnu-ld.bfd: não foi possível localizar -lloja.so: Arquivo ou diretório inexistente collect2: error: ld returned 1 exit status
referência não definida para "loja_total", com a biblioteca no comando
O que significa: quase sempre, a biblioteca veio antes dos arquivos que a usam (gcc -L. -lloja main.o). Pode ser também uma função que está no .h, mas não em nenhum .o da biblioteca (confira com nm libloja.a).
Como consertar: os seus .c e .o primeiro, as bibliotecas no fim: gcc main.o -L. -lloja -o app.
error while loading shared libraries: libloja.so: cannot open shared object file
O que significa: o programa ligou, mas o carregador não acha a .so na hora de rodar. O código de saída é 127. O ldd ./app mostra libloja.so => not found.
Como consertar: para testar, LD_LIBRARY_PATH=. ./app. Para resolver, ligue com -Wl,-rpath,'$ORIGIN' (e deixe a .so ao lado do programa) ou instale a biblioteca no sistema.
relocalização R_X86_64_PC32 … não pode ser usada ao fazer um objecto partilhado; recompilar com -fPIC
O que significa: um .o foi compilado sem -fPIC e está indo para uma .so.
Como consertar: apague os .o e compile de novo com -fPIC: gcc -fPIC -c ….
.so, liga sem erro, mas a biblioteca calcula errado
O que significa: é o caso da arredondar da seção 3, agora com a biblioteca compartilhada. Com a .a, o ligador reclamou de múltiplas definições. Com a .so, ele não reclama: o nome do programa "ganha", e a própria biblioteca passa a chamar a arredondar das notas.
$ gcc -std=c23 -Wall -Wextra -Iinclude main.c -L. -lloja -Wl,-rpath,'$ORIGIN' -o app_so $ ./app_so Frete (23 km): R$ 15 Nota 7 vira 10
O frete deveria ser R$ 20 (5 + 15), e saiu R$ 15 (5 + 10, a "nota arredondada" de 13).
Como consertar: static em toda função interna da biblioteca. Com a .so refeita, sem recompilar o programa:
$ sed -i 's/^int arredondar/static int arredondar/' src/frete.c $ gcc -std=c23 -Wall -Wextra -Iinclude -fPIC -c src/frete.c $ gcc -shared total.o frete.o -o libloja.so $ ./app_so Frete (23 km): R$ 20 Nota 7 vira 10
(O sed -i troca o texto dentro do arquivo: aqui, põe o static antes da int arredondar.)
Referência
| Comando | O que faz |
|---|---|
gcc -Iinclude -c src/a.c | Compila, procurando os .h também em include/ |
ar rcs libnome.a a.o b.o | Cria (ou atualiza) a biblioteca estática |
ar t libnome.a | Lista os .o de dentro dela |
gcc -fPIC -c a.c | Compila um .o que pode ir para uma .so |
gcc -shared a.o b.o -o libnome.so | Cria a biblioteca compartilhada |
gcc main.o -L. -lnome -o app | Liga com a libnome da pasta atual (prefere a .so); bibliotecas depois dos seus arquivos |
gcc main.o libnome.a -o app | Liga com a estática, pelo nome do arquivo |
-Wl,-rpath,'$ORIGIN' | Grava no programa: "procure as .so na minha pasta" |
LD_LIBRARY_PATH=pasta ./app | Roda o programa procurando as .so também na pasta |
ldd ./app | Lista as .so de que o programa precisa e onde estão (ou not found) |
nm libnome.a / nm -D libnome.so | Os símbolos da biblioteca |
file arquivo | current ar archive (.a), shared object (.so) |
Exercícios
1. A opção da biblioteca
O arquivo se chama libestoque.so. Complete a opção que o liga:
2. A caixa de .o
Complete o comando que cria a biblioteca estática libestoque.a:
3. Para uma .so
Que opção de compilação todo .o de uma biblioteca compartilhada deve receber?
4. O comando gcc -L. -lloja main.o -o app dá referência não definida para "loja_total", mas a libloja.a tem essa função. Por quê?
5. O programa foi ligado com uma libloja.so, e a Ana apagou a .so. O que acontece no ./app?
Missão final: lance a versão 2.0
Missão: a Ana vai lançar a versão 2.0 da biblioteca da lojinha. Mexa só nas abas da biblioteca (total.c e frete.c); o main.c é o programa de um cliente e não muda, como com uma .so trocada:
- a
loja_versaopassa a devolver"2.0"; - o frete fica grátis até 15 km; acima disso, R$ 5 mais R$ 1 por quilômetro além dos 15.
Biblioteca da loja, versão 2.0 3 cadernos: R$ 36 Frete (8 km): R$ 0 Frete (23 km): R$ 13 Frete (40 km): R$ 30
#include <stdio.h>
#include "loja.h"
int main(void) {
printf("Biblioteca da loja, versão %s\n", loja_versao());
printf("3 cadernos: R$ %d\n", loja_total(12, 3));
printf("Frete (8 km): R$ %d\n", loja_frete(8));
printf("Frete (23 km): R$ %d\n", loja_frete(23));
printf("Frete (40 km): R$ %d\n", loja_frete(40));
return 0;
}
#include "loja.h"
const char *loja_versao(void) {
return "1.0";
}
int loja_total(int preco, int quantidade) {
return preco * quantidade;
}
#include "loja.h"
int loja_frete(int km) {
if (km <= 10) {
return 0;
}
return 5 + (km - 10);
}
#ifndef LOJA_H #define LOJA_H const char *loja_versao(void); int loja_total(int preco, int quantidade); int loja_frete(int km); #endif
#include "loja.h"
const char *loja_versao(void) {
return "2.0";
}
int loja_total(int preco, int quantidade) {
return preco * quantidade;
}
#include "loja.h"
int loja_frete(int km) {
if (km <= 15) {
return 0;
}
return 5 + (km - 15);
}
Resumo
- Uma biblioteca é um pacote de funções compiladas. O nome segue o padrão
libnome.aoulibnome.so, e o-lnomea liga. Você já usa alibc(sempre) e alibm(-lm). - Estática:
ar rcs libloja.a *.o. O ligador copia as funções para o programa, que depois roda sem ela. - Compartilhada:
gcc -fPIC -cegcc -shared … -o libloja.so. O carregador a abre a cada execução; trocar a.soatualiza todos os programas, sem recompilar. -Idiz onde estão os.h;-L, onde estão as bibliotecas (na ligação);-l, qual biblioteca. As bibliotecas vão depois dos seus arquivos no comando.- Na hora de rodar, o
-Lnão vale: use-Wl,-rpath,'$ORIGIN'ouLD_LIBRARY_PATH. Olddmostra o que o programa precisa e o que estánot found. - Funções internas da biblioteca levam
static, e as públicas, um prefixo (loja_). Com uma.so, um nome repetido não dá erro: dá resultado errado.