diff --git a/tools/elfdeps.c b/tools/elfdeps.c index 9cb7b68ff3..b54e7a009a 100644 --- a/tools/elfdeps.c +++ b/tools/elfdeps.c @@ -9,9 +9,13 @@ #include #include +#include +#include + #include #include +int libtool_version_fallback = 0; int soname_only = 0; int fake_soname = 1; int filter_soname = 1; @@ -33,6 +37,53 @@ typedef struct elfInfo_s { ARGV_t provides; } elfInfo; +/* + * If filename contains ".so" followed by a version number, return + * a copy of the version number. + */ +static char *getLibtoolVer(const char *filename) +{ + const char *so; + int found_digit, found_dot = 0; + // Start from the end of the string. Verify that it ends with + // numbers and dots, preceded by ".so.". + so = filename + strlen(filename); + while (so > filename+2) { + if (*so == '.') { + found_dot++; + so--; + continue; + } else if (strchr("0123456789", *so)) { + found_digit++; + so--; + continue; + } else if (strncmp(so-2, ".so.", 4) == 0) { + so+=2; + if (found_digit && found_dot > 1) { + return strdup(so); + } + break; + } else { + return NULL; + } + } + return NULL; +} + +static char *getLibtoolVerFromShLink(const char *filename) +{ + void *dl_handle; + struct link_map *linkmap; + char *version = NULL; + dl_handle = dlmopen(LM_ID_NEWLM, filename, RTLD_LAZY); + if (dl_handle == NULL) return NULL; + if (dlinfo(dl_handle, RTLD_DI_LINKMAP, &linkmap) != -1) { + version = getLibtoolVer(linkmap->l_name); + } + dlclose(dl_handle); + return version; +} + /* * Rough soname sanity filtering: all sane soname's dependencies need to * contain ".so", and normal linkable libraries start with "lib", @@ -96,14 +147,21 @@ static const char *mkmarker(GElf_Ehdr *ehdr) } static void addDep(ARGV_t *deps, - const char *soname, const char *ver, const char *marker) + const char *soname, const char *ver, const char *marker, + const char *compare_op, const char *fallback_ver) { char *dep = NULL; if (skipSoname(soname)) return; - if (ver || marker) { + if (compare_op && fallback_ver) { + // when versioned symbols aren't available, the libtool version + // might be used to generate a minimum dependency version. + rasprintf(&dep, + "%s()%s %s %s", soname, marker ? marker : "", + compare_op, fallback_ver); + } else if (ver || marker) { rasprintf(&dep, "%s(%s)%s", soname, ver ? ver : "", marker ? marker : ""); } @@ -143,10 +201,10 @@ static void processVerDef(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei) auxoffset += aux->vda_next; continue; } else if (soname && !soname_only) { - addDep(&ei->provides, soname, s, ei->marker); + addDep(&ei->provides, soname, s, ei->marker, NULL, NULL); } } - + } } rfree(soname); @@ -182,7 +240,7 @@ static void processVerNeed(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei) break; if (genRequires(ei) && soname && !soname_only) { - addDep(&ei->requires, soname, s, ei->marker); + addDep(&ei->requires, soname, s, ei->marker, NULL, NULL); } auxoffset += aux->vna_next; } @@ -222,8 +280,14 @@ static void processDynamic(Elf_Scn *scn, GElf_Shdr *shdr, elfInfo *ei) case DT_NEEDED: if (genRequires(ei)) { s = elf_strptr(ei->elf, shdr->sh_link, dyn->d_un.d_val); - if (s) - addDep(&ei->requires, s, NULL, ei->marker); + if (s) { + char *libtool_ver = NULL; + if (libtool_version_fallback) { + libtool_ver = getLibtoolVerFromShLink(s); + } + addDep(&ei->requires, s, NULL, ei->marker, ">=", libtool_ver); + free(libtool_ver); + } } break; } @@ -322,8 +386,14 @@ static int processFile(const char *fn, int dtype) const char *bn = strrchr(fn, '/'); ei->soname = rstrdup(bn ? bn + 1 : fn); } - if (ei->soname) - addDep(&ei->provides, ei->soname, NULL, ei->marker); + if (ei->soname) { + char *libtool_ver = NULL; + if (libtool_version_fallback) { + libtool_ver = getLibtoolVer(fn); + } + addDep(&ei->provides, ei->soname, NULL, ei->marker, "=", libtool_ver); + free(libtool_ver); + } } /* If requested and present, add dep for interpreter (ie dynamic linker) */ @@ -359,11 +429,12 @@ int main(int argc, char *argv[]) struct poptOption opts[] = { { "provides", 'P', POPT_ARG_VAL, &provides, -1, NULL, NULL }, { "requires", 'R', POPT_ARG_VAL, &requires, -1, NULL, NULL }, + { "libtool-version-fallback", 0, POPT_ARG_VAL, &libtool_version_fallback, -1, NULL, NULL }, { "soname-only", 0, POPT_ARG_VAL, &soname_only, -1, NULL, NULL }, { "no-fake-soname", 0, POPT_ARG_VAL, &fake_soname, 0, NULL, NULL }, { "no-filter-soname", 0, POPT_ARG_VAL, &filter_soname, 0, NULL, NULL }, { "require-interp", 0, POPT_ARG_VAL, &require_interp, -1, NULL, NULL }, - POPT_AUTOHELP + POPT_AUTOHELP POPT_TABLEEND };