Skip to content

Commit 77efcf2

Browse files
committed
Merge pull request #85 from njsmith/master
Teach --replace-needed to update .gnu.version_r table
2 parents 44b7f95 + 907c020 commit 77efcf2

File tree

1 file changed

+59
-6
lines changed

1 file changed

+59
-6
lines changed

src/patchelf.cc

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ off_t fileSize, maxSize;
5353
unsigned char * contents = 0;
5454

5555

56-
#define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym
57-
#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym
56+
#define ElfFileParams class Elf_Ehdr, class Elf_Phdr, class Elf_Shdr, class Elf_Addr, class Elf_Off, class Elf_Dyn, class Elf_Sym, class Elf_Verneed
57+
#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed
5858

5959

6060
static unsigned int getPageSize(){
@@ -1260,6 +1260,8 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(map<string, string>& libs)
12601260

12611261
Elf_Dyn * dyn = (Elf_Dyn *) (contents + rdi(shdrDynamic.sh_offset));
12621262

1263+
unsigned int verNeedNum = 0;
1264+
12631265
unsigned int dynStrAddedBytes = 0;
12641266

12651267
for ( ; rdi(dyn->d_tag) != DT_NULL; dyn++) {
@@ -1272,13 +1274,13 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(map<string, string>& libs)
12721274

12731275
// technically, the string referred by d_val could be used otherwise, too (although unlikely)
12741276
// we'll therefore add a new string
1275-
debug("resizing .dynstr ...");
1277+
debug("resizing .dynstr ...\n");
12761278

12771279
string & newDynStr = replaceSection(".dynstr",
12781280
rdi(shdrDynStr.sh_size) + replacement.size() + 1 + dynStrAddedBytes);
12791281
setSubstr(newDynStr, rdi(shdrDynStr.sh_size) + dynStrAddedBytes, replacement + '\0');
12801282

1281-
dyn->d_un.d_val = shdrDynStr.sh_size + dynStrAddedBytes;
1283+
wri(dyn->d_un.d_val, rdi(shdrDynStr.sh_size) + dynStrAddedBytes);
12821284

12831285
dynStrAddedBytes += replacement.size() + 1;
12841286

@@ -1287,6 +1289,57 @@ void ElfFile<ElfFileParamNames>::replaceNeeded(map<string, string>& libs)
12871289
debug("keeping DT_NEEDED entry `%s'\n", name);
12881290
}
12891291
}
1292+
if (rdi(dyn->d_tag) == DT_VERNEEDNUM) {
1293+
verNeedNum = rdi(dyn->d_un.d_val);
1294+
}
1295+
}
1296+
1297+
// If a replaced library uses symbol versions, then there will also be
1298+
// references to it in the "version needed" table, and these also need to
1299+
// be replaced.
1300+
1301+
if (verNeedNum) {
1302+
Elf_Shdr & shdrVersionR = findSection(".gnu.version_r");
1303+
// The filename strings in the .gnu.version_r are different from the
1304+
// ones in .dynamic: instead of being in .dynstr, they're in some
1305+
// arbitrary section and we have to look in ->sh_link to figure out
1306+
// which one.
1307+
Elf_Shdr & shdrVersionRStrings = shdrs[rdi(shdrVersionR.sh_link)];
1308+
// this is where we find the actual filename strings
1309+
char * verStrTab = (char *) contents + rdi(shdrVersionRStrings.sh_offset);
1310+
// and we also need the name of the section containing the strings, so
1311+
// that we can pass it to replaceSection
1312+
string versionRStringsSName = getSectionName(shdrVersionRStrings);
1313+
1314+
debug("found .gnu.version_r with %i entries, strings in %s\n", verNeedNum, versionRStringsSName.c_str());
1315+
1316+
unsigned int verStrAddedBytes = 0;
1317+
1318+
Elf_Verneed * need = (Elf_Verneed *) (contents + rdi(shdrVersionR.sh_offset));
1319+
while (verNeedNum > 0) {
1320+
char * file = verStrTab + rdi(need->vn_file);
1321+
if (libs.find(file) != libs.end()) {
1322+
const string & replacement = libs[file];
1323+
1324+
debug("replacing .gnu.version_r entry `%s' with `%s'\n", file, replacement.c_str());
1325+
debug("resizing string section %s ...\n", versionRStringsSName.c_str());
1326+
1327+
string & newVerDynStr = replaceSection(versionRStringsSName,
1328+
rdi(shdrVersionRStrings.sh_size) + replacement.size() + 1 + verStrAddedBytes);
1329+
setSubstr(newVerDynStr, rdi(shdrVersionRStrings.sh_size) + verStrAddedBytes, replacement + '\0');
1330+
1331+
wri(need->vn_file, rdi(shdrVersionRStrings.sh_size) + verStrAddedBytes);
1332+
1333+
verStrAddedBytes += replacement.size() + 1;
1334+
1335+
changed = true;
1336+
} else {
1337+
debug("keeping .gnu.version_r entry `%s'\n", file);
1338+
}
1339+
// the Elf_Verneed structures form a linked list, so jump to next entry
1340+
need = (Elf_Verneed *) (contents + rdi(shdrVersionR.sh_offset) + rdi(need->vn_next));
1341+
--verNeedNum;
1342+
}
12901343
}
12911344
}
12921345

@@ -1474,13 +1527,13 @@ static void patchElf()
14741527
if (contents[EI_CLASS] == ELFCLASS32 &&
14751528
contents[EI_VERSION] == EV_CURRENT)
14761529
{
1477-
ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym> elfFile;
1530+
ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed> elfFile;
14781531
patchElf2(elfFile);
14791532
}
14801533
else if (contents[EI_CLASS] == ELFCLASS64 &&
14811534
contents[EI_VERSION] == EV_CURRENT)
14821535
{
1483-
ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym> elfFile;
1536+
ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed> elfFile;
14841537
patchElf2(elfFile);
14851538
}
14861539
else {

0 commit comments

Comments
 (0)