Skip to content

Add --print-os-abi and --set-os-abi options #381

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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