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 ;
1520int soname_only = 0 ;
1621int fake_soname = 1 ;
1722int 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+
98202static 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