Estou tentando criar uma biblioteca compartilhada, libfunc.so
que depende de outra biblioteca compartilhada (especificamente libuv.so
, mas acho que a biblioteca específica não é relevante para a questão). Ou seja, quando executo ldd libfunc.so
, quero ver a dependência de libfunc.so
para libuv.so
.
Este é o código que quero compilar libfunc.so
:
#include <uv.h>
int func() {
uv_timespec64_t now;
uv_clock_gettime(UV_CLOCK_REALTIME, &now);
return 0;
}
... eu compilo assim:
$ cc --version && cc -fpic -ggdb -Wall -c -o func.o func.c
cc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ cc -shared -o libfunc.so func.o -fpic -ggdb -Wall -luv
$
...quando executo ldd libfunc.so
não vejo a dependência desejada em libuv.so
:
$ ldd ./libfunc.so
linux-vdso.so.1 (0x00007fff827ae000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f14b291e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f14b2b54000)
$
... ou seja, eu queria ver algo como:
linux-vdso.so.1 (0x00007ffcbbdca000)
libuv.so.1 => /lib/x86_64-linux-gnu/libuv.so.1 (0x00007f781b25c000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f781b034000)
/lib64/ld-linux-x86-64.so.2 (0x00007f781b2a2000)
Minha pergunta é: por que não libuv.so
aparece uma dependência e o que preciso fazer para criar essa dependência?
Meu entendimento é provavelmente rudimentar e incompleto, mas pensei que criar um relacionamento de dependência seria algo como 1) escrever código que chama uma função de uma biblioteca (compartilhada), 2) compilar código de objeto, 3) criar biblioteca (compartilhada) de código de objeto ao vincular a uma biblioteca que define o símbolo ausente.
Inspecionando libfunc.so
, vejo símbolos indefinidos esperados para libuv
símbolos:
$ nm libfunc.so | grep U
0000000000002000 r __GNU_EH_FRAME_HDR
U __stack_chk_fail@GLIBC_2.4
U uv_clock_gettime
$
Para algum contexto, estou tentando criar um MVCE a partir de um projeto maior. Especificamente, esse projeto maior cria uma biblioteca compartilhada que depende do libuv
. Porém , quando executo ldd
a biblioteca compartilhada do projeto maior, ela mostra uma dependência libuv
(foi aí que obtive a saída para a saída ldd "desejada", acima).
O projeto maior é muito grande para eu postar aqui no Stack Overflow, mas ao inspecionar sua make
saída, acredito que meu MCVE está compilando/vinculando com os mesmos sinalizadores. Por exemplo, algumas linhas de compilação e a linha de link do projeto maior são:
cc -fpic -ggdb -Wall -c -o file1.o file1.c
cc -fpic -ggdb -Wall -c -o file2.o file2.c
cc -shared -o libplugin.so file1.o file2.o -fpic -ggdb -Wall -luv
(há mais arquivos compilados que compõem libplugin.so
, mas o subconjunto acima transmite a essência disso - os sinalizadores de compilação são uniformes para todos os arquivos compilados)
Atualização: se eu adicionar uma chamada uv_close()
no código da minha biblioteca compartilhada, o relacionamento de dependência desejado aparecerá! Ou seja:
#include <uv.h>
int func() {
uv_timespec64_t now;
uv_clock_gettime(UV_CLOCK_REALTIME, &now);
uv_close(NULL, NULL); // <= Adding this line causes the desired dependency to show up in ldd
return 0;
}
$ cc -fpic -ggdb -Wall -c -o func.o func.c
$ cc -shared -o libfunc.so func.o -fpic -ggdb -Wall -luv
$ ldd ./libfunc.so
linux-vdso.so.1 (0x00007ffc1e323000)
libuv.so.1 => /lib/x86_64-linux-gnu/libuv.so.1 (0x00007f412b761000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f412b539000)
/lib64/ld-linux-x86-64.so.2 (0x00007f412b7a1000)
$
Alguém pode me ajudar a entender essa observação? Por que chamar uv_clock_gettime()
vs. uv_close()
se comporta dessa maneira em relação ao relacionamento de dependência criado na biblioteca compartilhada?
Atualização: eu queria explorar um pouco mais o comentário de @WeatherVane sobre RE: otimização. Aqui, novamente, meu entendimento é provavelmente rudimentar e incompleto, mas pensei que se eu compilasse com -O0
, isso forçaria o compilador a não otimizar nada e, portanto, induziria a dependência mesmo quando minha biblioteca compartilhada chamasse apenas uv_clock_gettime()
. Mas a realidade não correspondeu a essa ideia: voltando func.c
a apenas chamar uv_clock_gettime()
e compilar tudo com o -O0
, ainda não vejo dependência do libuv
. Ou seja:
$ cc -fpic -ggdb -O0 -Wall -c -o func.o func.c # Note the -O0
$ cc -shared -O0 -o libfunc.so func.o -fpic -ggdb -luv # Note the -O0
$ ldd ./libfunc.so
linux-vdso.so.1 (0x00007fff8e724000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fee6f03e000)
/lib64/ld-linux-x86-64.so.2 (0x00007fee6f274000)
Eu queria explorar a sugestão do @Barmar de excluir a possibilidade de otimização imprimindo o valor de now
, mas mesmo essa versão do código resultou em não haver a dependência desejada. Ou seja:
#include <inttypes.h>
#include <stdio.h>
#include <uv.h>
int func() {
uv_timespec64_t now;
uv_clock_gettime(UV_CLOCK_REALTIME, &now);
printf("%" PRId64 "\n", now.tv_sec);
return 0;
}
$ cc -fpic -ggdb -Wall -c -o func.o func.c
$ cc -shared -o libfunc.so func.o -fpic -ggdb -luv
$ ldd ./libfunc.so
linux-vdso.so.1 (0x00007ffd72bdf000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0a89964000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0a89b9a000)
Explorando a sugestão de @EmployedRusso, o readelf
resultado é:
$ readelf -Ws /usr/local/lib/libuv.so.1 | grep uv_close
337: 0000000000013766 427 FUNC GLOBAL DEFAULT 14 uv_close
735: 0000000000013766 427 FUNC GLOBAL DEFAULT 14 uv_close
$ readelf -Ws /usr/local/lib/libuv.so.1 | grep uv_clock_gettime
341: 00000000000136a0 178 FUNC GLOBAL DEFAULT 14 uv_clock_gettime
1120: 00000000000136a0 178 FUNC GLOBAL DEFAULT 14 uv_clock_gettime
$
Estou atualizando esta postagem com algumas observações baseadas na resposta de @EmployedRusso porque são relevantes, mas difíceis de adicionar como comentário.
Com a versão da biblioteca compartilhada que chama apenas uv_clock_gettime
, o uso da -y
opção vinculador revela:
$ cc -shared -Wl,-y,uv_clock_gettime,-y,uv_close -o libfunc.so func.o -fpic -ggdb -luv
/usr/bin/ld: func.o: reference to uv_clock_gettime
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libuv.so: definition of uv_close
$
... e com a versão da biblioteca compartilhada que faz referência a ambos uv_clock_gettime
e uv_close
, a -y
opção do vinculador revela:
$ cc -shared -Wl,-y,uv_clock_gettime,-y,uv_close -o libfunc.so func.o -fpic -ggdb -luv
/usr/bin/ld: func.o: reference to uv_close
/usr/bin/ld: func.o: reference to uv_clock_gettime
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libuv.so: definition of uv_close
... que se alinha com a explicação de @EmployedRusso sobre símbolos referenciados vs. símbolos encontrados DT_NEEDED
vs.
Além disso, com a versão da biblioteca compartilhada que faz referência apenas a uv_clock_gettime
, o uso do --no-as-needed
sinalizador do vinculador realmente "forçou" a inclusão da dependência:
$ cc -shared -Wl,--no-as-needed -o libfunc.so func.o -fpic -ggdb -luv
$ ldd ./libfunc.so
linux-vdso.so.1 (0x00007ffe312cf000)
libuv.so.1 => /lib/x86_64-linux-gnu/libuv.so.1 (0x00007f36fe4f8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f36fe2d0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f36fe538000)
$
Essa é provavelmente a resposta. Eu estou supondo que
/lib/x86_64-linux-gnu/libuv.so.1
está sendo usado no momento do link, e-Wl,--as-needed
por padrão.Se ambos forem verdadeiros, então, quando você vincula
func.o
isso não faz referência auv_close
, o vinculador encontra/lib/x86_64-linux-gnu/libuv.so.1
, mas descobre que não satisfaz nenhum símbolo e, portanto, não o registra comoDT_NEEDED
forlibfoo.so
.Quando você muda
func.o
para também requireuv_close
, o vinculador observa que/lib/x86_64-linux-gnu/libuv.so.1
é necessário satisfazer esse símbolo e o registra naDT_NEEDED
tag forlibfoo.so
.Para confirmar essas suposições, vincule-se
libfoo.so
à-Wl,-y,uv_clock_gettime,-y,uv_close
bandeira. Isso deve mostrar quais binários fazem referência e quais definem os dois símbolos.Você também pode vincular
-Wl,--no-as-needed
- nesse caso,libuv.so.1
aparecerá independentemente de satisfazer algum símbolo.