diff --git a/patchelf.1 b/patchelf.1 index fdf16fde..7c8f402c 100644 --- a/patchelf.1 +++ b/patchelf.1 @@ -36,6 +36,16 @@ INTERPRETER. .IP --print-interpreter Prints the ELF interpreter of the executable. +.IP --print-os-abi +Prints the OS ABI of the executable (EI_OSABI field of an ELF file). + +.IP "--set-os-abi ABI" +Changes the OS ABI of the executable (EI_OSABI field of an ELF file). +The ABI parameter is pretty flexible. For example, you can specify it +as a "Linux", "linux", or even "lInUx" - all those names will set EI_OSABI +field of the ELF header to the value "3", which corresponds to Linux OS ABI. +The same applies to other ABI names - System V, FreeBSD, Solaris, etc. + .IP --print-soname Prints DT_SONAME entry of .dynamic section. Raises an error if DT_SONAME doesn't exist. diff --git a/src/patchelf.cc b/src/patchelf.cc index 49accae1..505c9802 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -86,6 +86,20 @@ static bool hasAllowedPrefix(const std::string & s, const std::vector @@ -1104,6 +1118,68 @@ std::string ElfFile::getInterpreter() return std::string((char *) fileContents->data() + rdi(shdr.sh_offset), rdi(shdr.sh_size) - 1); } +template +void ElfFile::modifyOsAbi(osAbiMode op, const std::string & newOsAbi) +{ + unsigned char abi = hdr()->e_ident[EI_OSABI]; + + if (op == printOsAbi) { + switch (abi) { + case 0: printf("System V\n"); break; + case 1: printf("HP-UX\n"); break; + case 2: printf("NetBSD\n"); break; + case 3: printf("Linux\n"); break; + case 4: printf("GNU Hurd\n"); break; + case 6: printf("Solaris\n"); break; + case 7: printf("AIX\n"); break; + case 8: printf("IRIX\n"); break; + case 9: printf("FreeBSD\n"); break; + case 10: printf("Tru64\n"); break; + case 12: printf("OpenBSD\n"); break; + case 13: printf("OpenVMS\n"); break; + default: printf("0x%02X\n", (unsigned int) abi); + } + return; + } + + unsigned char newAbi; + std::string nabi = downcase(trim(newOsAbi)); + if (nabi == "system v" || nabi == "system-v" || nabi == "sysv") + newAbi = 0; + else if (nabi == "hp-ux") + newAbi = 1; + else if (nabi == "netbsd") + newAbi = 2; + else if (nabi == "linux" || nabi == "gnu") + newAbi = 3; + else if (nabi == "gnu hurd" || nabi == "gnu-hurd" || nabi == "hurd") + newAbi = 4; + else if (nabi == "solaris") + newAbi = 6; + else if (nabi == "aix") + newAbi = 7; + else if (nabi == "irix") + newAbi = 8; + else if (nabi == "freebsd") + newAbi = 9; + else if (nabi == "tru64") + newAbi = 10; + else if (nabi == "openbsd") + newAbi = 12; + else if (nabi == "openvms") + newAbi = 13; + else + error("unrecognized OS ABI"); + + if (newAbi == abi) { + debug("current and requested OS ABIs are equal\n"); + return; + } + + hdr()->e_ident[EI_OSABI] = newAbi; + changed = true; +} + template void ElfFile::modifySoname(sonameMode op, const std::string & newSoname) { @@ -1739,6 +1815,9 @@ void ElfFile::clearSymbolVersions(const std::set } static bool printInterpreter = false; +static bool printOsAbi = false; +static bool setOsAbi = false; +static std::string newOsAbi; static bool printSoname = false; static bool setSoname = false; static std::string newSoname; @@ -1764,6 +1843,12 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con if (printInterpreter) printf("%s\n", elfFile.getInterpreter().c_str()); + if (printOsAbi) + elfFile.modifyOsAbi(elfFile.printOsAbi, ""); + + if (setOsAbi) + elfFile.modifyOsAbi(elfFile.replaceOsAbi, newOsAbi); + if (printSoname) elfFile.modifySoname(elfFile.printSoname, ""); @@ -1839,6 +1924,8 @@ void showHelp(const std::string & progName) [--set-interpreter FILENAME]\n\ [--page-size SIZE]\n\ [--print-interpreter]\n\ + [--print-os-abi]\t\tPrints 'EI_OSABI' field of ELF header\n\ + [--set-os-abi ABI]\t\tSets 'EI_OSABI' field of ELF header to ABI.\n\ [--print-soname]\t\tPrints 'DT_SONAME' entry of .dynamic section. Raises an error if DT_SONAME doesn't exist\n\ [--set-soname SONAME]\t\tSets 'DT_SONAME' entry to SONAME.\n\ [--set-rpath RPATH]\n\ @@ -1888,6 +1975,14 @@ int mainWrapped(int argc, char * * argv) else if (arg == "--print-interpreter") { printInterpreter = true; } + else if (arg == "--print-os-abi") { + printOsAbi = true; + } + else if (arg == "--set-os-abi") { + if (++i == argc) error("missing argument"); + setOsAbi = true; + newOsAbi = resolveArgument(argv[i]); + } else if (arg == "--print-soname") { printSoname = true; } diff --git a/src/patchelf.h b/src/patchelf.h index 33ecfc3f..2e2ac6d0 100644 --- a/src/patchelf.h +++ b/src/patchelf.h @@ -109,6 +109,10 @@ class ElfFile std::string getInterpreter(); + typedef enum { printOsAbi, replaceOsAbi } osAbiMode; + + void modifyOsAbi(osAbiMode op, const std::string & newOsAbi); + typedef enum { printSoname, replaceSoname } sonameMode; void modifySoname(sonameMode op, const std::string & newSoname); diff --git a/tests/Makefile.am b/tests/Makefile.am index bbea1ba4..600639d9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -33,6 +33,7 @@ src_TESTS = \ endianness.sh \ contiguous-note-sections.sh \ no-gnu-hash.sh \ + change-abi.sh \ grow-file.sh \ no-dynamic-section.sh \ args-from-file.sh \ diff --git a/tests/change-abi.sh b/tests/change-abi.sh new file mode 100755 index 00000000..26a151dc --- /dev/null +++ b/tests/change-abi.sh @@ -0,0 +1,44 @@ +#! /bin/sh -e + +SCRATCH=scratch/$(basename $0 .sh) + +rm -rf ${SCRATCH} +mkdir -p ${SCRATCH} + +cp simple-pie ${SCRATCH}/simple-pie + +# Save the old OS ABI +OLDABI=`../src/patchelf --print-os-abi ${SCRATCH}/simple-pie` +# Ensure it's not empty +test -n "$OLDABI" + +# Change OS ABI and verify it has been changed +{ + echo "System V" + echo "HP-UX" + echo "NetBSD" + echo "Linux" + echo "GNU Hurd" + echo "Solaris" + echo "AIX" + echo "IRIX" + echo "FreeBSD" + echo "Tru64" + echo "OpenBSD" + echo "OpenVMS" +} | { + while IFS="\n" read ABI; do + echo "Set OS ABI to '$ABI'..." + ../src/patchelf --set-os-abi "$ABI" ${SCRATCH}/simple-pie + + echo "Check is OS ABI is '$ABI'..." + NEWABI=`../src/patchelf --print-os-abi ${SCRATCH}/simple-pie` + test "$NEWABI" = "$ABI" + done +} + +# Reset OS ABI to the saved one +../src/patchelf --set-os-abi "$OLDABI" ${SCRATCH}/simple-pie + +# Verify we still can run the executable +${SCRATCH}/simple-pie