Skip to content

Commit a01b715

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 a01b715

File tree

1 file changed

+81
-10
lines changed

1 file changed

+81
-10
lines changed

tools/elfdeps.c

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@
99
#include <popt.h>
1010
#include <gelf.h>
1111

12+
#include <link.h>
13+
#include <dlfcn.h>
14+
1215
#include <rpm/rpmstring.h>
1316
#include <rpm/argv.h>
1417

18+
int libtool_version_fallback = 0;
1519
int soname_only = 0;
1620
int fake_soname = 1;
1721
int filter_soname = 1;
@@ -33,6 +37,53 @@ typedef struct elfInfo_s {
3337
ARGV_t provides;
3438
} elfInfo;
3539

40+
/*
41+
* If filename contains ".so" followed by a version number, return
42+
* a copy of the version number.
43+
*/
44+
static char *getLibtoolVer(const char *filename)
45+
{
46+
const char *so;
47+
int found_digit, found_dot = 0;
48+
// Start from the end of the string. Verify that it ends with
49+
// numbers and dots, preceded by ".so.".
50+
so = filename + strlen(filename);
51+
while (so > filename+2) {
52+
if (*so == '.') {
53+
found_dot++;
54+
so--;
55+
continue;
56+
} else if (strchr("0123456789", *so)) {
57+
found_digit++;
58+
so--;
59+
continue;
60+
} else if (strncmp(so-2, ".so.", 4) == 0) {
61+
so+=2;
62+
if (found_digit && found_dot > 1) {
63+
return strdup(so);
64+
}
65+
break;
66+
} else {
67+
return NULL;
68+
}
69+
}
70+
return NULL;
71+
}
72+
73+
static char *getLibtoolVerFromShLink(const char *filename)
74+
{
75+
void *dl_handle;
76+
struct link_map *linkmap;
77+
char *version = NULL;
78+
dl_handle = dlmopen(LM_ID_NEWLM, filename, RTLD_LAZY);
79+
if (dl_handle == NULL) return NULL;
80+
if (dlinfo(dl_handle, RTLD_DI_LINKMAP, &linkmap) != -1) {
81+
version = getLibtoolVer(linkmap->l_name);
82+
}
83+
dlclose(dl_handle);
84+
return version;
85+
}
86+
3687
/*
3788
* Rough soname sanity filtering: all sane soname's dependencies need to
3889
* contain ".so", and normal linkable libraries start with "lib",
@@ -96,14 +147,21 @@ static const char *mkmarker(GElf_Ehdr *ehdr)
96147
}
97148

98149
static void addDep(ARGV_t *deps,
99-
const char *soname, const char *ver, const char *marker)
150+
const char *soname, const char *ver, const char *marker,
151+
const char *compare_op, const char *fallback_ver)
100152
{
101153
char *dep = NULL;
102154

103155
if (skipSoname(soname))
104156
return;
105157

106-
if (ver || marker) {
158+
if (compare_op && fallback_ver) {
159+
// when versioned symbols aren't available, the libtool version
160+
// might be used to generate a minimum dependency version.
161+
rasprintf(&dep,
162+
"%s()%s %s %s", soname, marker ? marker : "",
163+
compare_op, fallback_ver);
164+
} else if (ver || marker) {
107165
rasprintf(&dep,
108166
"%s(%s)%s", soname, ver ? ver : "", marker ? marker : "");
109167
}
@@ -143,10 +201,10 @@ static void processVerDef(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
143201
auxoffset += aux->vda_next;
144202
continue;
145203
} else if (soname && !soname_only) {
146-
addDep(&ei->provides, soname, s, ei->marker);
204+
addDep(&ei->provides, soname, s, ei->marker, NULL, NULL);
147205
}
148206
}
149-
207+
150208
}
151209
}
152210
rfree(soname);
@@ -182,7 +240,7 @@ static void processVerNeed(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
182240
break;
183241

184242
if (genRequires(ei) && soname && !soname_only) {
185-
addDep(&ei->requires, soname, s, ei->marker);
243+
addDep(&ei->requires, soname, s, ei->marker, NULL, NULL);
186244
}
187245
auxoffset += aux->vna_next;
188246
}
@@ -222,8 +280,14 @@ static void processDynamic(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei)
222280
case DT_NEEDED:
223281
if (genRequires(ei)) {
224282
s = elf_strptr(ei->elf, shdr->sh_link, dyn->d_un.d_val);
225-
if (s)
226-
addDep(&ei->requires, s, NULL, ei->marker);
283+
if (s) {
284+
char *libtool_ver = NULL;
285+
if (libtool_version_fallback) {
286+
libtool_ver = getLibtoolVerFromShLink(s);
287+
}
288+
addDep(&ei->requires, s, NULL, ei->marker, ">=", libtool_ver);
289+
free(libtool_ver);
290+
}
227291
}
228292
break;
229293
}
@@ -322,8 +386,14 @@ static int processFile(const char *fn, int dtype)
322386
const char *bn = strrchr(fn, '/');
323387
ei->soname = rstrdup(bn ? bn + 1 : fn);
324388
}
325-
if (ei->soname)
326-
addDep(&ei->provides, ei->soname, NULL, ei->marker);
389+
if (ei->soname) {
390+
char *libtool_ver = NULL;
391+
if (libtool_version_fallback) {
392+
libtool_ver = getLibtoolVer(fn);
393+
}
394+
addDep(&ei->provides, ei->soname, NULL, ei->marker, "=", libtool_ver);
395+
free(libtool_ver);
396+
}
327397
}
328398

329399
/* If requested and present, add dep for interpreter (ie dynamic linker) */
@@ -359,11 +429,12 @@ int main(int argc, char *argv[])
359429
struct poptOption opts[] = {
360430
{ "provides", 'P', POPT_ARG_VAL, &provides, -1, NULL, NULL },
361431
{ "requires", 'R', POPT_ARG_VAL, &requires, -1, NULL, NULL },
432+
{ "libtool-version-fallback", 0, POPT_ARG_VAL, &libtool_version_fallback, -1, NULL, NULL },
362433
{ "soname-only", 0, POPT_ARG_VAL, &soname_only, -1, NULL, NULL },
363434
{ "no-fake-soname", 0, POPT_ARG_VAL, &fake_soname, 0, NULL, NULL },
364435
{ "no-filter-soname", 0, POPT_ARG_VAL, &filter_soname, 0, NULL, NULL },
365436
{ "require-interp", 0, POPT_ARG_VAL, &require_interp, -1, NULL, NULL },
366-
POPT_AUTOHELP
437+
POPT_AUTOHELP
367438
POPT_TABLEEND
368439
};
369440

0 commit comments

Comments
 (0)