Skip to content

Commit

Permalink
Merge pull request #381 from crystax/master
Browse files Browse the repository at this point in the history
Add --print-os-abi and --set-os-abi options
  • Loading branch information
Mic92 authored Sep 28, 2022
2 parents 7281d99 + 5faf453 commit 683e41d
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 0 deletions.
10 changes: 10 additions & 0 deletions patchelf.1
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
95 changes: 95 additions & 0 deletions src/patchelf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ static bool hasAllowedPrefix(const std::string & s, const std::vector<std::strin
return std::any_of(allowedPrefixes.begin(), allowedPrefixes.end(), [&](const std::string & i) { return !s.compare(0, i.size(), i); });
}

static std::string trim(std::string s)
{
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());

return s;
}

static std::string downcase(std::string s)
{
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::tolower(c); });
return s;
}

/* !!! G++ creates broken code if this function is inlined, don't know
why... */
template<ElfFileParams>
Expand Down Expand Up @@ -1104,6 +1118,68 @@ std::string ElfFile<ElfFileParamNames>::getInterpreter()
return std::string((char *) fileContents->data() + rdi(shdr.sh_offset), rdi(shdr.sh_size) - 1);
}

template<ElfFileParams>
void ElfFile<ElfFileParamNames>::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<ElfFileParams>
void ElfFile<ElfFileParamNames>::modifySoname(sonameMode op, const std::string & newSoname)
{
Expand Down Expand Up @@ -1739,6 +1815,9 @@ void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string>
}

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;
Expand All @@ -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, "");

Expand Down Expand Up @@ -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\
Expand Down Expand Up @@ -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;
}
Expand Down
4 changes: 4 additions & 0 deletions src/patchelf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
44 changes: 44 additions & 0 deletions tests/change-abi.sh
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 683e41d

Please sign in to comment.