1 https://github.com/AOSC-Dev/liblol/blob/v0.1.4/autobuild/patches/patchelf/0001-add-remap-symvers.patch
3 From: Miao Wang <shankerwangmiao@gmail.com>
4 Date: Fri, 12 Jan 2024 16:56:07 +0800
5 Subject: [PATCH] add remap-symvers
9 @@ -55,6 +55,8 @@ typedef uint16_t Elf64_Section;
10 typedef Elf32_Half Elf32_Versym;
11 typedef Elf64_Half Elf64_Versym;
13 +#define VERSYM_HIDDEN 0x8000
14 +#define VERSYM_VERSION 0x7fff
16 /* The ELF file header. This appears at the start of every ELF file. */
20 @@ -1234,8 +1234,14 @@ void ElfFile<ElfFileParamNames>::rewriteHeaders(Elf_Addr phdrAddress)
22 else if (d_tag == DT_VERNEED)
23 dyn->d_un.d_ptr = findSectionHeader(".gnu.version_r").sh_addr;
24 + else if (d_tag == DT_VERNEEDNUM)
25 + dyn->d_un.d_val = findSectionHeader(".gnu.version_r").sh_info;
26 else if (d_tag == DT_VERSYM)
27 dyn->d_un.d_ptr = findSectionHeader(".gnu.version").sh_addr;
28 + else if (d_tag == DT_VERDEF)
29 + dyn->d_un.d_ptr = findSectionHeader(".gnu.version_d").sh_addr;
30 + else if (d_tag == DT_VERDEFNUM)
31 + dyn->d_un.d_val = findSectionHeader(".gnu.version_d").sh_info;
32 else if (d_tag == DT_MIPS_RLD_MAP_REL) {
33 /* the MIPS_RLD_MAP_REL tag stores the offset to the debug
34 pointer, relative to the address of the tag */
35 @@ -2016,7 +2022,7 @@ auto ElfFile<ElfFileParamNames>::parseGnuHashTable(span<char> sectionData) -> Gn
38 template<ElfFileParams>
39 -void ElfFile<ElfFileParamNames>::rebuildGnuHashTable(span<char> strTab, span<Elf_Sym> dynsyms)
40 +void ElfFile<ElfFileParamNames>::rebuildGnuHashTable(span<char> strTab, span<Elf_Sym> dynsyms, span<Elf_Versym> versyms)
42 auto sectionData = tryGetSectionSpan<char>(".gnu.hash");
44 @@ -2028,12 +2034,20 @@ void ElfFile<ElfFileParamNames>::rebuildGnuHashTable(span<char> strTab, span<Elf
45 if (ght.m_table.size() == 0)
48 + if (ght.m_table.size() + rdi(ght.m_hdr.symndx) < dynsyms.size()){
49 + debug("gnuhash table is too small, rebuilding\n");
50 + auto & newSection = replaceSection(".gnu.hash", sectionData.size() + (dynsyms.size() - ght.m_table.size() - rdi(ght.m_hdr.symndx)) * sizeof(uint32_t));
51 + sectionData = span<char>(newSection.data(), newSection.size());
52 + ght = parseGnuHashTable(sectionData);
55 // The hash table includes only a subset of dynsyms
56 auto firstSymIdx = rdi(ght.m_hdr.symndx);
57 dynsyms = span(&dynsyms[firstSymIdx], dynsyms.end());
59 // Only use the range of symbol versions that will be changed
60 - auto versyms = tryGetSectionSpan<Elf_Versym>(".gnu.version");
62 + versyms = tryGetSectionSpan<Elf_Versym>(".gnu.version");
64 versyms = span(&versyms[firstSymIdx], versyms.end());
66 @@ -2148,7 +2162,7 @@ auto ElfFile<ElfFileParamNames>::parseHashTable(span<char> sectionData) -> HashT
69 template<ElfFileParams>
70 -void ElfFile<ElfFileParamNames>::rebuildHashTable(span<char> strTab, span<Elf_Sym> dynsyms)
71 +void ElfFile<ElfFileParamNames>::rebuildHashTable(span<char> strTab, span<Elf_Sym> dynsyms, int moreSyms)
73 auto sectionData = tryGetSectionSpan<char>(".hash");
75 @@ -2156,6 +2170,15 @@ void ElfFile<ElfFileParamNames>::rebuildHashTable(span<char> strTab, span<Elf_Sy
77 auto ht = parseHashTable(sectionData);
81 + auto & newSection = replaceSection(".hash", sectionData.size() + moreSyms * sizeof(uint32_t));
82 + sectionData = span<char>(newSection.data(), newSection.size());
83 + auto hdr = (typename HashTable::Header*)sectionData.begin();
84 + wri(hdr->nchain, rdi(hdr->nchain) + moreSyms);
85 + ht = parseHashTable(sectionData);
88 std::fill(ht.m_buckets.begin(), ht.m_buckets.end(), 0);
89 std::fill(ht.m_chain.begin(), ht.m_chain.end(), 0);
91 @@ -2320,6 +2343,281 @@ void ElfFile<ElfFileParamNames>::modifyExecstack(ExecstackMode op)
92 printf("execstack: %c\n", result);
95 +template<ElfFileParams>
96 +void ElfFile<ElfFileParamNames>::remapSymvers(const std::string & mapTo, const std::vector<std::string> & mapFrom, bool alsoPatchVerNeed)
98 + auto shdrDynStr = findSectionHeader(".dynstr");
99 + auto shdrDynsym = findSectionHeader(".dynsym");
100 + auto shdrVersym = findSectionHeader(".gnu.version");
102 + auto strTab = (char *)fileContents->data() + rdi(shdrDynStr.sh_offset);
103 + auto strTabSize = rdi(shdrDynStr.sh_size);
104 + auto dynsyms = (Elf_Sym *)(fileContents->data() + rdi(shdrDynsym.sh_offset));
105 + auto versyms = (Elf_Versym *)(fileContents->data() + rdi(shdrVersym.sh_offset));
106 + const size_t count = rdi(shdrDynsym.sh_size) / sizeof(Elf_Sym);
108 + if (count != rdi(shdrVersym.sh_size) / sizeof(Elf_Versym))
109 + error("versym size mismatch");
111 + auto &shdrVerdef = findSectionHeader(".gnu.version_d");
112 + auto verdef = (char *)(fileContents->data() + rdi(shdrVerdef.sh_offset));
114 + std::map<int, std::string> verdefMap;
116 + debug("Parsing .gnu.version_d\n");
118 + int verdef_entries = rdi(shdrVerdef.sh_info);
119 + debug(".gnu.version_d: %d entries\n", verdef_entries);
121 + auto verdef_end = verdef + rdi(shdrVerdef.sh_size);
124 + int map_to_ndx = -1;
126 + off_t last_verdef_off = 0;
127 + off_t mapToStrOff = 0;
128 + bool mapToAdded = false;
130 + for(int i = 0; i < verdef_entries; i++){
131 + Elf_Verdef *vd = (Elf_Verdef *) (verdef + curoff);
132 + if ((char *)vd + sizeof(Elf_Verdef) > verdef_end)
133 + error(fmt("verdef entry overflow: idx=", i));
134 + auto ndx = rdi(vd->vd_ndx);
135 + if ((char *)vd + rdi(vd->vd_aux) >= verdef_end)
136 + error(fmt("verdef entry aux out of bounds: idx=", i));
137 + auto aux = (Elf_Verdaux *) ((char *)vd + rdi(vd->vd_aux));
138 + if ((char *)aux + sizeof(Elf_Verdaux) > verdef_end)
139 + error(fmt("verdef entry aux overflow: idx=", i));
140 + std::string_view name = &strTab[rdi(aux->vda_name)];
141 + debug("verdef entry %d: %s, ndx=%d\n", i, name.data(), ndx);
144 + if (name == mapTo) {
146 + mapToStrOff = rdi(aux->vda_name);
148 + if(ndx != 0 && ndx != 1){
149 + verdefMap[ndx] = name;
152 + if(rdi(vd->vd_next) == 0){
153 + if(i == verdef_entries - 1){
154 + last_verdef_off = curoff;
158 + error(fmt("verdef entry should have next entry: idx=", i));
160 + if((char *)vd + rdi(vd->vd_next) >= verdef_end)
161 + error(fmt("verdef entry next out of bounds: idx=", i));
162 + curoff += rdi(vd->vd_next);
164 + if (map_to_ndx == -1){
165 + debug("no version index for %s, adding\n", mapTo.c_str());
166 + auto & newDynStr = replaceSection(".dynstr", rdi(shdrDynStr.sh_size) + mapTo.size() + 1);
167 + mapToStrOff = rdi(shdrDynStr.sh_size);
168 + setSubstr(newDynStr, mapToStrOff, mapTo + '\0');
169 + strTab = newDynStr.data();
170 + strTabSize = newDynStr.size();
172 + debug("parsing verneed entries\n", mapTo.c_str());
173 + auto verneedhdr = tryFindSectionHeader(".gnu.version_r");
174 + std::map<int, int> verneedMap;
176 + auto &shdrVerNeed = verneedhdr->get();
177 + auto verneed = (char *)(fileContents->data() + rdi(shdrVerNeed.sh_offset));
179 + debug("found .gnu.version_r, parsing\n");
180 + int verneed_entries = rdi(shdrVerNeed.sh_info);
181 + debug(".gnu.version_r: %d entries\n", verdef_entries);
183 + auto verneed_end = verneed + rdi(shdrVerNeed.sh_size);
185 + for(int i = 0; i < verneed_entries; i++){
186 + Elf_Verneed *vn = (Elf_Verneed *) (verneed + curoff);
187 + if ((char *)vn + sizeof(Elf_Verneed) > verneed_end)
188 + error(fmt("verneed entry overflow: idx=", i));
189 + auto aux_cnt = rdi(vn->vn_cnt);
190 + debug("file: %s, %d versions\n", &strTab[rdi(vn->vn_file)], aux_cnt);
191 + off_t aux_off = rdi(vn->vn_aux);
192 + if ((char *)vn + aux_off >= verneed_end)
193 + error(fmt("verneed entry aux out of bounds: idx=", i));
194 + for(int j = 0; j < aux_cnt; j++){
195 + auto aux = (Elf_Vernaux *) ((char *)vn + aux_off);
196 + if ((char *)aux + sizeof(Elf_Vernaux) > verneed_end)
197 + error(fmt("verneed entry aux overflow: idx=", i, "aux idx=", j));
198 + auto ndx = rdi(aux->vna_other) & VERSYM_VERSION;
199 + debug(" %s, ndx=%d\n", &strTab[rdi(aux->vna_name)], ndx);
200 + if(alsoPatchVerNeed){
201 + for (auto it : mapFrom){
202 + if (it == &strTab[rdi(aux->vna_name)]){
203 + debug(" found %s, changing to %s\n", it.c_str(), mapTo.c_str());
204 + wri(aux->vna_name, mapToStrOff);
205 + wri(aux->vna_hash, sysvHash(mapTo));
210 + if(map_to_ndx == -1 && ndx >= max_ndx + 1){
211 + verneedMap[ndx] = ndx + 1;
213 + wri(aux->vna_other, (rdi(aux->vna_other) & ~VERSYM_VERSION) | (ndx & VERSYM_VERSION));
214 + debug(" changing ndx to %d\n", ndx);
216 + if (rdi(aux->vna_next) == 0){
217 + if (j == aux_cnt - 1)
220 + error(fmt("verneed entry should have next entry: idx=", i, "aux idx=", j));
222 + if ((char *)aux + rdi(aux->vna_next) >= verneed_end)
223 + error(fmt("verneed entry next out of bounds: idx=", i, "aux idx=", j));
224 + aux_off += rdi(aux->vna_next);
226 + if (rdi(vn->vn_next) == 0){
227 + if (i == verneed_entries - 1)
230 + error(fmt("verneed entry should have next entry: idx=", i));
232 + if ((char *)vn + rdi(vn->vn_next) >= verneed_end)
233 + error(fmt("verneed entry next out of bounds: idx=", i));
234 + curoff += rdi(vn->vn_next);
237 + debug("no .gnu.version_r found\n");
239 + if (map_to_ndx == -1){
240 + map_to_ndx = max_ndx + 1;
241 + debug("decided to use %d for %s\n", map_to_ndx, mapTo.c_str());
242 + if(map_to_ndx > VERSYM_VERSION){
243 + error(fmt("version index %d is too large", map_to_ndx));
245 + verdefMap[map_to_ndx] = mapTo;
246 + auto & newVerdef = replaceSection(".gnu.version_d", rdi(shdrVerdef.sh_size) + sizeof(Elf_Verdef) + sizeof(Elf_Verdaux));
247 + char * newVerdefData = newVerdef.data();
248 + Elf_Verdef *lastVd = (Elf_Verdef *)(newVerdefData + last_verdef_off);
249 + Elf_Verdef *newVd = (Elf_Verdef *)(newVerdefData + rdi(shdrVerdef.sh_size));
250 + wri(lastVd->vd_next, (char *)newVd - (char *)lastVd);
251 + wri(newVd->vd_version, 1);
252 + wri(newVd->vd_flags, 0);
253 + wri(newVd->vd_ndx, map_to_ndx);
254 + wri(newVd->vd_cnt, 1);
255 + wri(newVd->vd_hash, sysvHash(mapTo));
256 + wri(newVd->vd_aux, sizeof(Elf_Verdef));
257 + wri(newVd->vd_next, 0);
258 + Elf_Verdaux *newVda = (Elf_Verdaux *)((char *)newVd + sizeof(Elf_Verdef));
259 + wri(newVda->vda_next, 0);
260 + wri(((Elf_Shdr *)(&shdrVerdef))->sh_info, rdi(shdrVerdef.sh_info) + 1);
261 + verdef_entries += 1;
263 + wri(newVda->vda_name, mapToStrOff);
266 + debug("verdef entry for %s found at ndx=%d\n", mapTo.c_str(), map_to_ndx);
268 + std::map<std::string, std::map<std::string, int>> symVersionMap;
270 + debug("Parsing .dynsym\n");
271 + for(size_t i = 0; i < count; i++){
272 + auto dynsym = &dynsyms[i];
273 + std::string name = strTab + rdi(dynsym->st_name);
274 + auto verndx = rdi(versyms[i]);
275 + auto verdef_ndx = verndx & VERSYM_VERSION;
277 + if(verneedMap.find(verdef_ndx) != verneedMap.end()){
278 + debug("verneed entry remapping for %s found at ndx=%d\n", name.c_str(), verdef_ndx);
279 + verdef_ndx = verneedMap[verdef_ndx];
280 + wri(versyms[i], (verndx & ~VERSYM_VERSION) | (verdef_ndx & VERSYM_VERSION));
285 + debug("dynsym entry %d: %s ", i, name.c_str());
286 + auto shndx = rdi(dynsym->st_shndx);
287 + if(shndx == SHN_UNDEF){
288 + debug("(undefined)\n");
290 + }else if(shndx == SHN_ABS){
291 + debug("(absolute)\n");
293 + }else if(shndx == SHN_COMMON){
294 + debug("(common)\n");
298 + debug("(local)\n");
300 + }else if(verndx == 1){
301 + debug("(global)\n");
304 + if(verdefMap.find(verdef_ndx) == verdefMap.end()){
305 + debug("(verdef %d not found)\n", verdef_ndx);
308 + debug("(ver: %s)\n", verdefMap[verdef_ndx].c_str());
309 + symVersionMap[verdefMap[verdef_ndx]][name] = i;
312 + debug("Generating new dsyms list\n");
313 + std::map<std::string, int> newDsyms;
314 + for(const auto &fromVer : mapFrom){
315 + if(symVersionMap.find(fromVer) == symVersionMap.end()){
316 + debug("No symbols with version %s found\n", fromVer.c_str());
319 + for(auto sym : symVersionMap[fromVer]){
320 + debug("Adding %s@%s to new dsyms list\n", sym.first.c_str(), fromVer.c_str());
321 + newDsyms[sym.first] = sym.second;
324 + for(const auto &syms : symVersionMap[mapTo]){
325 + debug("removing %s@%s from new dsyms list\n", syms.first.c_str(), mapTo.c_str());
326 + newDsyms.erase(syms.first);
328 + auto newDynsymSize = (newDsyms.size() + (mapToAdded ? 1 : 0)) * sizeof(Elf_Sym) + rdi(shdrDynsym.sh_size);
329 + auto newVersymSize = (newDsyms.size() + (mapToAdded ? 1 : 0)) * sizeof(Elf_Versym) + rdi(shdrVersym.sh_size);
331 + auto& newDynsym = replaceSection(".dynsym", newDynsymSize);
332 + auto& newVersym = replaceSection(".gnu.version", newVersymSize);
334 + auto newDynsymSpan = span<Elf_Sym>((Elf_Sym *)newDynsym.data(), newDynsymSize / sizeof(Elf_Sym));
335 + auto newVersymSpan = span<Elf_Versym>((Elf_Versym *)newVersym.data(), newVersymSize / sizeof(Elf_Versym));
339 + for(auto it = newDsyms.cbegin(); it != newDsyms.cend(); ++it){
340 + auto sym = it->second;
341 + debug("Adding %s@%s to dynsym list\n", it->first.c_str(), mapTo.c_str());
342 + newDynsymSpan[i] = dynsyms[sym];
343 + bool is_hidden = rdi(newVersymSpan[sym]) & VERSYM_HIDDEN;
344 + wri(newVersymSpan[i], map_to_ndx | (is_hidden ? VERSYM_HIDDEN : 0));
345 + wri(newVersymSpan[sym], rdi(newVersymSpan[sym]) | VERSYM_HIDDEN);
349 + debug("Adding %s@%s to dynsym list\n", mapTo.c_str(), mapTo.c_str());
350 + wri(newDynsymSpan[i].st_name, mapToStrOff);
351 + wri(newDynsymSpan[i].st_info, STB_GLOBAL << 4 | STT_OBJECT);
352 + wri(newDynsymSpan[i].st_other, STV_DEFAULT);
353 + wri(newDynsymSpan[i].st_shndx, SHN_ABS);
354 + wri(newDynsymSpan[i].st_value, 0);
355 + wri(newDynsymSpan[i].st_size, 0);
356 + wri(newVersymSpan[i], map_to_ndx);
360 + debug("Rebuilding hash tables\n");
362 + rebuildGnuHashTable(span(strTab, strTabSize), newDynsymSpan, newVersymSpan);
363 + rebuildHashTable(span(strTab, strTabSize), newDynsymSpan, newDsyms.size() + (mapToAdded ? 1 : 0));
365 + this->rewriteSections();
370 template<ElfFileParams>
371 template<class StrIdxCallback>
372 void ElfFile<ElfFileParamNames>::forAllStringReferences(const Elf_Shdr& strTabHdr, StrIdxCallback&& fn)
373 @@ -2384,6 +2682,10 @@ static bool noDefaultLib = false;
374 static bool printExecstack = false;
375 static bool clearExecstack = false;
376 static bool setExecstack = false;
377 +static bool remapSymvers = false;
378 +static bool remapVerneed = false;
379 +static std::string symverMapTo;
380 +static std::vector<std::string> symverMapFrom;
382 template<class ElfFile>
383 static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, const std::string & fileName)
384 @@ -2441,6 +2743,9 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
385 if (renameDynamicSymbols)
386 elfFile.renameDynamicSymbols(symbolsToRename);
389 + elfFile.remapSymvers(symverMapTo, symverMapFrom, remapVerneed);
391 if (elfFile.isChanged()){
392 writeFile(fileName, elfFile.fileContents);
393 } else if (alwaysWrite) {
394 @@ -2505,6 +2810,8 @@ static void showHelp(const std::string & progName)
395 [--clear-execstack]\n\
397 [--rename-dynamic-symbols NAME_MAP_FILE]\tRenames dynamic symbols. The map file should contain two symbols (old_name new_name) per line\n\
398 + [--remap-symvers TO=FROM1,FROM2...]\n\
399 + [--also-remap-verneed]\n\
403 @@ -2661,6 +2968,44 @@ static int mainWrapped(int argc, char * * argv)
404 symbolsToRename[*symbolsToRenameKeys.insert(from).first] = to;
407 + else if (arg == "--remap-symvers") {
408 + remapSymvers = true;
409 + if (++i == argc) error("missing argument");
411 + const char* mapping = argv[i];
412 + for(int i = 0; mapping[i]; ++i)
414 + if (mapping[i] == '=')
416 + char *mapto = strndup(mapping, i);
417 + symverMapTo = mapto;
423 + if (symverMapTo.empty())
424 + error(fmt("Invalid symver mapping, must contains =: ", mapping));
425 + for(int i = 0; mapping[i]; ++i)
427 + if (mapping[i] == ',')
429 + char *mapfrom = strndup(mapping, i);
430 + if(strlen(mapfrom) != 0)
431 + symverMapFrom.push_back(mapfrom);
437 + if (strlen(mapping) != 0)
438 + symverMapFrom.push_back(mapping);
439 + if (symverMapFrom.empty())
440 + error(fmt("Invalid symver mapping, must contains at least one from: ", mapping));
442 + else if (arg == "--also-remap-verneed") {
443 + remapVerneed = true;
445 else if (arg == "--help" || arg == "-h" ) {
450 @@ -175,6 +175,8 @@ public:
452 void modifyExecstack(ExecstackMode op);
454 + void remapSymvers(const std::string & mapTo, const std::vector<std::string> & mapFrom, bool alsoRemapVerneed);
457 struct GnuHashTable {
458 using BloomWord = Elf_Addr;
459 @@ -194,8 +196,8 @@ private:
461 HashTable parseHashTable(span<char> gh);
463 - void rebuildGnuHashTable(span<char> strTab, span<Elf_Sym> dynsyms);
464 - void rebuildHashTable(span<char> strTab, span<Elf_Sym> dynsyms);
465 + void rebuildGnuHashTable(span<char> strTab, span<Elf_Sym> dynsyms, span<Elf_Versym> versyms = {nullptr, nullptr});
466 + void rebuildHashTable(span<char> strTab, span<Elf_Sym> dynsyms, int moreSyms = 0);
468 using Elf_Rel_Info = decltype(Elf_Rel::r_info);