@@ -2215,6 +2215,130 @@ void ElfFile<ElfFileParamNames>::renameDynamicSymbols(const std::unordered_map<s
22152215 this ->rewriteSections ();
22162216}
22172217
2218+ template <ElfFileParams>
2219+ void ElfFile<ElfFileParamNames>::removeNeededVersion(const std::map<std::string, std::set<std::string>> & verMap)
2220+ {
2221+ if (verMap.empty ()) return ;
2222+
2223+ auto idxVersionR = getSectionIndex (" .gnu.version_r" );
2224+ if (!idxVersionR) {
2225+ error (" no .gnu.version_r section found\n " );
2226+ return ;
2227+ }
2228+ auto & shdrVersionR = shdrs.at (idxVersionR);
2229+ Elf_Shdr & shdrVersionRStrings = shdrs.at (rdi (shdrVersionR.sh_link ));
2230+
2231+ auto spanVersyms = tryGetSectionSpan<Elf_Versym>(" .gnu.version" );
2232+
2233+ size_t verNeedNum = rdi (shdrVersionR.sh_info );
2234+ std::set<size_t > removedVersionIds;
2235+
2236+ // file names and versions here
2237+ char * verStrTab = (char *) fileContents->data () + rdi (shdrVersionRStrings.sh_offset );
2238+
2239+ debug (" found .gnu.version_r with %i entries\n " , verNeedNum);
2240+
2241+ auto vn = (Elf_Verneed *)(fileContents->data () + rdi (shdrVersionR.sh_offset ));
2242+ Elf_Verneed * prevVn = nullptr ;
2243+ for (size_t i = 0 ; i < verNeedNum; ++i) {
2244+ char * file = verStrTab + rdi (vn->vn_file );
2245+
2246+ if (verMap.count (file)) {
2247+ size_t verNauxNum = rdi (vn->vn_cnt );
2248+ size_t newVerNauxNum = 0 ;
2249+
2250+ auto va = (follow<Elf_Vernaux>(vn, rdi (vn->vn_aux )));
2251+ Elf_Vernaux * prevVa = nullptr ;
2252+
2253+ auto & versions = verMap.at (file);
2254+
2255+ for (size_t j = 0 ; j < verNauxNum; ++j) {
2256+ char * name = verStrTab + rdi (va->vna_name );
2257+
2258+ auto version = rdi (va->vna_other );
2259+ off_t next = rdi (va->vna_next );
2260+ if (versions.count (name)) {
2261+ debug (" removing symbol %s with version %d in entry %s\n " , name, version, file);
2262+ // 1 for unversioned symbol
2263+ removedVersionIds.insert (version);
2264+
2265+ if (prevVa) {
2266+ wri (prevVa->vna_next , next ? rdi (prevVa->vna_next ) + next : 0 );
2267+ } else {
2268+ wri (vn->vn_aux , next ? rdi (vn->vn_aux ) + next : 0 );
2269+ }
2270+ changed = true ;
2271+
2272+ auto nextVa = follow<Elf_Vernaux>(va, next);
2273+ // remove the version data
2274+ memset (va, 0 , sizeof (Elf_Vernaux));
2275+ va = nextVa;
2276+ } else {
2277+ debug (" keeping symbol %s with version %d in entry %s\n " , name, version, file);
2278+ ++newVerNauxNum;
2279+ prevVa = va;
2280+ va = follow<Elf_Vernaux>(va, next);
2281+ }
2282+ }
2283+
2284+ off_t next = rdi (vn->vn_next );
2285+ // there are versions left
2286+ if (prevVa) {
2287+ wri (vn->vn_cnt , newVerNauxNum);
2288+ prevVn = vn;
2289+ vn = follow<Elf_Verneed>(vn, next);
2290+ } else {
2291+ // remove entire file entry
2292+ if (prevVn) {
2293+ wri (prevVn->vn_next , next ? rdi (prevVn->vn_next ) + next : 0 );
2294+ } else {
2295+ // there are file entries left
2296+ if (next) {
2297+ wri (shdrVersionR.sh_offset , rdi (shdrVersionR.sh_offset ) + next);
2298+ } else {
2299+ // FIXME: remove entire section?
2300+ wri (shdrVersionR.sh_offset , 0 );
2301+ wri (shdrVersionR.sh_size , 0 );
2302+ }
2303+ }
2304+ wri (shdrVersionR.sh_info , rdi (shdrVersionR.sh_info ) - 1 );
2305+
2306+ Elf_Verneed * nextVn = follow<Elf_Verneed>(vn, next);
2307+ memset (vn, 0 , sizeof (Elf_Verneed));
2308+ vn = nextVn;
2309+ }
2310+ } else {
2311+ off_t next = rdi (vn->vn_next );
2312+ prevVn = vn;
2313+ vn = follow<Elf_Verneed>(vn, next);
2314+ }
2315+ }
2316+ // virtual address and file offset need to be the same for .gnu.version_r
2317+ shdrVersionR.sh_addr = shdrVersionR.sh_offset ;
2318+
2319+ if (auto shdrDynamic = tryFindSectionHeader (" .dynamic" )) {
2320+ auto dyn = (Elf_Dyn *)(fileContents->data () + rdi (shdrDynamic->get ().sh_offset ));
2321+
2322+ // keep DT_VERNEED and DT_VERNEEDNUM in sync, DT_VERNEEDNUM handled by rewriteHeaders
2323+ for ( ; rdi (dyn->d_tag ) != DT_NULL; dyn++) {
2324+ if (rdi (dyn->d_tag ) == DT_VERNEEDNUM) {
2325+ dyn->d_un .d_val = shdrVersionR.sh_info ;
2326+ }
2327+ }
2328+ }
2329+
2330+ if (spanVersyms) {
2331+ for (auto & versym : spanVersyms) {
2332+ if (removedVersionIds.count (versym)) {
2333+ wri (versym, 1 );
2334+ }
2335+ }
2336+ }
2337+
2338+ debug (" remaining entries in .gnu.version_r: %i\n " , rdi (shdrVersionR.sh_info ));
2339+ this ->rewriteSections (true );
2340+ }
2341+
22182342template <ElfFileParams>
22192343void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string> & syms)
22202344{
@@ -2376,6 +2500,7 @@ static bool addDebugTag = false;
23762500static bool renameDynamicSymbols = false ;
23772501static bool printRPath = false ;
23782502static std::string newRPath;
2503+ static std::map<std::string, std::set<std::string>> neededVersionsToRemove;
23792504static std::set<std::string> neededLibsToRemove;
23802505static std::map<std::string, std::string> neededLibsToReplace;
23812506static std::set<std::string> neededLibsToAdd;
@@ -2430,6 +2555,7 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
24302555
24312556 if (printNeeded) elfFile.printNeededLibs ();
24322557
2558+ elfFile.removeNeededVersion (neededVersionsToRemove);
24332559 elfFile.removeNeeded (neededLibsToRemove);
24342560 elfFile.replaceNeeded (neededLibsToReplace);
24352561 elfFile.addNeeded (neededLibsToAdd);
@@ -2505,6 +2631,7 @@ static void showHelp(const std::string & progName)
25052631 [--clear-symbol-version SYMBOL]\n \
25062632 [--add-debug-tag]\n \
25072633 [--print-execstack]\t\t Prints whether the object requests an executable stack\n \
2634+ [--remove-needed-version LIBRARY VERSION_SYMBOL]\n \
25082635 [--clear-execstack]\n \
25092636 [--set-execstack]\n \
25102637 [--rename-dynamic-symbols NAME_MAP_FILE]\t Renames dynamic symbols. The map file should contain two symbols (old_name new_name) per line\n \
@@ -2613,6 +2740,11 @@ static int mainWrapped(int argc, char * * argv)
26132740 neededLibsToReplace[ argv[i+1 ] ] = argv[i+2 ];
26142741 i += 2 ;
26152742 }
2743+ else if (arg == " --remove-needed-version" ) {
2744+ if (i+2 >= argc) error (" missing argument(s)" );
2745+ neededVersionsToRemove[ argv[i+1 ] ].insert (resolveArgument (argv[i+2 ]));
2746+ i += 2 ;
2747+ }
26162748 else if (arg == " --clear-symbol-version" ) {
26172749 if (++i == argc) error (" missing argument" );
26182750 symbolsToClearVersion.insert (resolveArgument (argv[i]));
0 commit comments