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 ;
1519int soname_only = 0 ;
1620int fake_soname = 1 ;
1721int 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
98149static 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