Skip to content

Commit 2075c5e

Browse files
committed
Enhance requires with version information from the build root.
The --libtool-version-fallback option will cause elfdeps to try to use the version information in the shared object's filename for shared objects that don't provide versioned symbols. This additional information allows rpm to track minor-version dependencies.
1 parent a9ac172 commit 2075c5e

File tree

1 file changed

+137
-10
lines changed

1 file changed

+137
-10
lines changed

tools/elfdeps.c

Lines changed: 137 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@
22

33
#include <sys/types.h>
44
#include <sys/stat.h>
5+
#include <sys/wait.h>
56
#include <unistd.h>
67
#include <stdlib.h>
78
#include <fcntl.h>
89
#include <errno.h>
910
#include <popt.h>
1011
#include <gelf.h>
1112

13+
#include <link.h>
14+
#include <dlfcn.h>
15+
1216
#include <rpm/rpmstring.h>
1317
#include <rpm/argv.h>
1418

19+
int libtool_version_fallback = 0;
1520
int soname_only = 0;
1621
int fake_soname = 1;
1722
int filter_soname = 1;
@@ -33,6 +38,97 @@ typedef struct elfInfo_s {
3338
ARGV_t provides;
3439
} elfInfo;
3540

41+
/*
42+
* If filename contains ".so" followed by a version number, return
43+
* a copy of the version number.
44+
*/
45+
static char *getLibtoolVer(const char *filename)
46+
{
47+
const char *so;
48+
char dest[PATH_MAX];
49+
int destsize = 0;
50+
int found_digit = 0, found_dot = 0;
51+
52+
destsize = readlink(filename, dest, PATH_MAX);
53+
if (destsize > 0) {
54+
dest[destsize] = 0;
55+
filename = dest;
56+
}
57+
// Start from the end of the string. Verify that it ends with
58+
// numbers and dots, preceded by ".so.".
59+
so = filename + strlen(filename);
60+
while (so > filename+2) {
61+
if (*so == '.') {
62+
found_dot++;
63+
so--;
64+
continue;
65+
} else if (strchr("0123456789", *so)) {
66+
found_digit++;
67+
so--;
68+
continue;
69+
} else if (strncmp(so-2, ".so.", 4) == 0) {
70+
so+=2;
71+
if (found_digit && found_dot > 1) {
72+
return strdup(so);
73+
}
74+
break;
75+
} else {
76+
break;
77+
}
78+
}
79+
return NULL;
80+
}
81+
82+
/*
83+
* Rather than re-implement path searching for shared objects, use
84+
* dlmopen(). This will still perform initialization and finalization
85+
* functions, which isn't necessarily safe, so do that in a separate
86+
* process.
87+
*/
88+
static char *getLibtoolVerFromShLink(const char *filename)
89+
{
90+
char dest[PATH_MAX];
91+
int pipefd[2];
92+
pid_t cpid;
93+
94+
if (pipe(pipefd) == -1) {
95+
return NULL; // Should this be a fatal error instead?
96+
}
97+
cpid = fork();
98+
if (cpid == -1) {
99+
return NULL; // Should this be a fatal error instead?
100+
}
101+
if (cpid == 0) {
102+
void *dl_handle;
103+
struct link_map *linkmap;
104+
char *version = NULL;
105+
106+
close(pipefd[0]);
107+
dl_handle = dlmopen(LM_ID_NEWLM, filename, RTLD_LAZY);
108+
if (dl_handle == NULL) _exit(0);
109+
if (dlinfo(dl_handle, RTLD_DI_LINKMAP, &linkmap) != -1) {
110+
version = getLibtoolVer(linkmap->l_name);
111+
}
112+
(void) write(pipefd[1], version, strlen(version));
113+
close(pipefd[1]);
114+
free(version);
115+
dlclose(dl_handle);
116+
_exit(0);
117+
} else {
118+
ssize_t len;
119+
close(pipefd[1]);
120+
dest[0] = 0;
121+
len = read(pipefd[0], dest, sizeof(dest));
122+
if (len > 0) dest[len] = 0;
123+
close(pipefd[0]);
124+
wait(NULL);
125+
}
126+
if (strlen(dest) > 0)
127+
return strdup(dest);
128+
else
129+
return NULL;
130+
}
131+
36132
/*
37133
* Rough soname sanity filtering: all sane soname's dependencies need to
38134
* contain ".so", and normal linkable libraries start with "lib",
@@ -95,15 +191,30 @@ static const char *mkmarker(GElf_Ehdr *ehdr)
95191
return marker;
96192
}
97193

194+
static int findSonameInDeps(ARGV_t deps, const char *soname)
195+
{
196+
for (ARGV_t dep = deps; *dep; dep++) {
197+
if (strncmp(*dep, soname, strlen(soname)) == 0) return 1;
198+
}
199+
return 0;
200+
}
201+
98202
static void addDep(ARGV_t *deps,
99-
const char *soname, const char *ver, const char *marker)
203+
const char *soname, const char *ver, const char *marker,
204+
const char *compare_op, const char *fallback_ver)
100205
{
101206
char *dep = NULL;
102207

103208
if (skipSoname(soname))
104209
return;
105210

106-
if (ver || marker) {
211+
if (compare_op && fallback_ver) {
212+
// when versioned symbols aren't available, the libtool version
213+
// might be used to generate a minimum dependency version.
214+
rasprintf(&dep,
215+
"%s()%s %s %s", soname, marker ? marker : "",
216+
compare_op, fallback_ver);
217+
} else if (ver || marker) {
107218
rasprintf(&dep,
108219
"%s(%s)%s", soname, ver ? ver : "", marker ? marker : "");
109220
}
@@ -143,10 +254,10 @@ static void processVerDef(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
143254
auxoffset += aux->vda_next;
144255
continue;
145256
} else if (soname && !soname_only) {
146-
addDep(&ei->provides, soname, s, ei->marker);
257+
addDep(&ei->provides, soname, s, ei->marker, NULL, NULL);
147258
}
148259
}
149-
260+
150261
}
151262
}
152263
rfree(soname);
@@ -182,7 +293,7 @@ static void processVerNeed(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
182293
break;
183294

184295
if (genRequires(ei) && soname && !soname_only) {
185-
addDep(&ei->requires, soname, s, ei->marker);
296+
addDep(&ei->requires, soname, s, ei->marker, NULL, NULL);
186297
}
187298
auxoffset += aux->vna_next;
188299
}
@@ -222,8 +333,17 @@ static void processDynamic(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
222333
case DT_NEEDED:
223334
if (genRequires(ei)) {
224335
s = elf_strptr(ei->elf, shdr->sh_link, dyn->d_un.d_val);
225-
if (s)
226-
addDep(&ei->requires, s, NULL, ei->marker);
336+
if (s) {
337+
char *libtool_ver = NULL;
338+
// If soname matches an item already in the deps, then
339+
// it had versioned symbols and doesn't require fallback.
340+
if (libtool_version_fallback &&
341+
!findSonameInDeps(ei->requires, s)) {
342+
libtool_ver = getLibtoolVerFromShLink(s);
343+
}
344+
addDep(&ei->requires, s, NULL, ei->marker, ">=", libtool_ver);
345+
free(libtool_ver);
346+
}
227347
}
228348
break;
229349
}
@@ -322,8 +442,14 @@ static int processFile(const char *fn, int dtype)
322442
const char *bn = strrchr(fn, '/');
323443
ei->soname = rstrdup(bn ? bn + 1 : fn);
324444
}
325-
if (ei->soname)
326-
addDep(&ei->provides, ei->soname, NULL, ei->marker);
445+
if (ei->soname) {
446+
char *libtool_ver = NULL;
447+
if (libtool_version_fallback) {
448+
libtool_ver = getLibtoolVer(fn);
449+
}
450+
addDep(&ei->provides, ei->soname, NULL, ei->marker, "=", libtool_ver);
451+
free(libtool_ver);
452+
}
327453
}
328454

329455
/* If requested and present, add dep for interpreter (ie dynamic linker) */
@@ -359,11 +485,12 @@ int main(int argc, char *argv[])
359485
struct poptOption opts[] = {
360486
{ "provides", 'P', POPT_ARG_VAL, &provides, -1, NULL, NULL },
361487
{ "requires", 'R', POPT_ARG_VAL, &requires, -1, NULL, NULL },
488+
{ "libtool-version-fallback", 0, POPT_ARG_VAL, &libtool_version_fallback, -1, NULL, NULL },
362489
{ "soname-only", 0, POPT_ARG_VAL, &soname_only, -1, NULL, NULL },
363490
{ "no-fake-soname", 0, POPT_ARG_VAL, &fake_soname, 0, NULL, NULL },
364491
{ "no-filter-soname", 0, POPT_ARG_VAL, &filter_soname, 0, NULL, NULL },
365492
{ "require-interp", 0, POPT_ARG_VAL, &require_interp, -1, NULL, NULL },
366-
POPT_AUTOHELP
493+
POPT_AUTOHELP
367494
POPT_TABLEEND
368495
};
369496

0 commit comments

Comments
 (0)