Skip to content
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

glibc: introduce symbol version clearing #68

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions s3
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ USE_vars[USE_STAPI]=;
USE_vars[USE_STAPI5]=;
USE_vars[USE_CONFDIR]=;
USE_vars[USE_COMPRESS]=;
USE_vars[USE_GLIBC_SYMBOL_VERSION_CLEAR]=;
USE_vars[USE_OSCAMNAME]=;

s3cfg_vars[USE_TARGZ]=0
s3cfg_vars[DIRECTMENU]=0
s3cfg_vars[USE_VERBOSE]=0
s3cfg_vars[USE_COMPRESS]=0
s3cfg_vars[USE_GLIBC_SYMBOL_VERSION_CLEAR]=0
s3cfg_vars[S3_UPDATE_CHECK]=1
s3cfg_vars[SAVE_LISTSMARGO]=1
s3cfg_vars[ADD_PROFILE_NAME]=0
Expand Down
189 changes: 189 additions & 0 deletions support/configs/_patchelf_verneed_fix.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
diff --git a/src/patchelf.cc b/src/patchelf.cc
index 1a90edb..2696a0d 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -1861,6 +1861,87 @@
changed = true;
}

+/* Remove any unused dependency symbol versions from .gnu.version_r */
+template<ElfFileParams>
+void ElfFile<ElfFileParamNames>::cleanDependencySymbolVersions()
+{
+ auto shdrVersym = findSectionHeader(".gnu.version");
+ auto shdrVersymR = findSectionHeader(".gnu.version_r");
+
+ auto versyms = reinterpret_cast<Elf_Versym *>(fileContents->data() + rdi(shdrVersym.sh_offset));
+ checkPointer(fileContents, versyms, sizeof(Elf_Versym));
+ size_t count = rdi(shdrVersym.sh_size) / sizeof(Elf_Versym);
+
+ /* Set of versions actually used. */
+ std::set<Elf_Versym> allVersions;
+
+ for (size_t i = 0; i < count; i++) {
+ allVersions.insert(versyms[i]);
+ }
+
+ /* Strings associated with .gnu_version_r section: used for debug only. */
+ Elf_Shdr & shdrVersionRStrings = shdrs.at(rdi(shdrVersymR.sh_link));
+ char * verStrTab = reinterpret_cast<char *>(fileContents->data() + rdi(shdrVersionRStrings.sh_offset));
+
+ auto ver_r = reinterpret_cast<Elf_Verneed *>(fileContents->data() + rdi(shdrVersymR.sh_offset));
+ checkPointer(fileContents, ver_r, sizeof(Elf_Verneed));
+
+ while (true) {
+ auto prev = (Elf_Vernaux *)nullptr;
+ auto vern_aux = reinterpret_cast<Elf_Vernaux *>((char *)ver_r + rdi(ver_r->vn_aux));
+ checkPointer(fileContents, vern_aux, sizeof(Elf_Vernaux));
+
+ char * file = verStrTab + rdi(ver_r->vn_file);
+ for (size_t j = 0; j < ver_r->vn_cnt ; j++) {
+ char * ver_name = verStrTab + rdi(vern_aux->vna_name);
+ // FIXME: add proper check for null-terminated string
+ checkPointer(fileContents, ver_name, sizeof(char));
+
+ auto next = reinterpret_cast<Elf_Vernaux *>((char *)vern_aux + rdi(vern_aux->vna_next));
+ checkPointer(fileContents, next, sizeof(Elf_Vernaux));
+
+ if (!allVersions.count(rdi(vern_aux->vna_other) & ~0x8000)) {
+ debug("Removing version identifier %d %s@%s\n", rdi(vern_aux->vna_other), file, ver_name);
+ /* Symbol version is no longer used, unlink it. */
+ if (!prev) {
+ auto next_off = (intptr_t)(vern_aux) + rdi(vern_aux->vna_next) - (intptr_t)(ver_r);
+ wri(ver_r->vn_aux, next_off);
+ } else {
+ auto raw_next = rdi(vern_aux->vna_next);
+ if (raw_next == 0) {
+ wri(prev->vna_next, 0);
+ } else {
+ auto next_off = (intptr_t)(vern_aux) + raw_next - (intptr_t)(prev);
+ wri(prev->vna_next, next_off);
+ }
+ }
+ wri(ver_r->vn_cnt, rdi(ver_r->vn_cnt) - 1);
+ } else {
+ prev = vern_aux;
+ }
+
+ if (vern_aux == next) {
+ if (j != rdi(ver_r->vn_cnt)) {
+ debug("Section missing elements! Ended on element %d, expected %d\n", j, rdi(ver_r->vn_cnt));
+ }
+ break;
+ }
+ vern_aux = next;
+ }
+
+ /* If this was the last entry, we're done. */
+ if (!rdi(ver_r->vn_next)) {
+ break;
+ }
+
+ ver_r = reinterpret_cast<Elf_Verneed *>(((char *) ver_r) + rdi(ver_r->vn_next));
+ checkPointer(fileContents, ver_r, sizeof(Elf_Verneed));
+ }
+
+ changed = true;
+ this->rewriteSections();
+}
+
template<ElfFileParams>
void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string> & syms)
{
@@ -1886,6 +1967,10 @@
wri(versyms[i], 1);
}
}
+
+ /* Remove entries in the .gnu.versions_r table which are no;-longer required. */
+ cleanDependencySymbolVersions();
+
changed = true;
this->rewriteSections();
}
@@ -1978,9 +2063,9 @@
const std::string & outputFileName2 = outputFileName.empty() ? fileName : outputFileName;

if (getElfType(fileContents).is32Bit)
- patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Versym>(fileContents), fileContents, outputFileName2);
+ patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed, Elf32_Vernaux, Elf32_Versym>(fileContents), fileContents, outputFileName2);
else
- patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Versym>(fileContents), fileContents, outputFileName2);
+ patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, Elf64_Vernaux, Elf64_Versym>(fileContents), fileContents, outputFileName2);
}
}

diff --git a/src/patchelf.h b/src/patchelf.h
index 9fab18c..375a285 100644
--- a/src/patchelf.h
+++ b/src/patchelf.h
@@ -1,7 +1,7 @@
using FileContents = std::shared_ptr<std::vector<unsigned char>>;

-#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, class Elf_Versym
-#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed, Elf_Versym
+#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, class Elf_Vernaux, class Elf_Versym
+#define ElfFileParamNames Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Addr, Elf_Off, Elf_Dyn, Elf_Sym, Elf_Verneed, Elf_Vernaux, Elf_Versym

template<ElfFileParams>
class ElfFile
@@ -137,6 +137,8 @@

void addDebugTag();

+ void cleanDependencySymbolVersions();
+
void clearSymbolVersions(const std::set<std::string> & syms);

private:
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0b648f8..66eaf1d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -43,6 +43,7 @@
replace-needed.sh \
replace-add-needed.sh \
add-debug-tag.sh \
+ clear-symver.sh \
empty-note.sh

build_TESTS = \
diff --git a/tests/clear-symver.sh b/tests/clear-symver.sh
new file mode 100755
index 00000000..123a6f90
--- /dev/null
+++ b/tests/clear-symver.sh
@@ -0,0 +1,33 @@
+#! /bin/sh -e
+SCRATCH=scratch/$(basename $0 .sh)
+
+rm -rf ${SCRATCH}
+mkdir -p ${SCRATCH}
+
+cp main ${SCRATCH}/
+
+SYMBOL_TO_REMOVE=__libc_start_main
+VERSION_TO_REMOVE=GLIBC_2.34a
+
+readelfData=$(readelf -V ${SCRATCH}/main 2>&1)
+
+if [ $(echo "$readelfData" | grep --count "$VERSION_TO_REMOVE") -lt 2 ]; then
+ # We expect this to appear at least twice: once for the symbol entry,
+ # and once for verneed entry.
+ echo "Warning: Couldn't find expected versioned symbol."
+ echo "This is probably because you're either not using glibc, or"
+ echo "${SYMBOL_TO_REMOVE} is no longer at version ${VERSION_TO_REMOVE}"
+ echo "Reporting a pass anyway, as the test result is invalid."
+ exit 0
+fi
+
+../src/patchelf --clear-symbol-version ${SYMBOL_TO_REMOVE} ${SCRATCH}/main
+
+readelfData=$(readelf -V ${SCRATCH}/main 2>&1)
+
+if [ $(echo "$readelfData" | grep --count "$VERSION_TO_REMOVE") -ne 0 ]; then
+ # We expect this to appear at least twice: once for the symbol entry,
+ # and once for verneed entry.
+ echo "The symbol version ${SYMBOL_TO_REMOVE} remained behind!"
+ exit 1
+fi
8 changes: 4 additions & 4 deletions support/configs/plugin_update_toolchain.config.template
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# List of setup, build and config commands below. All commands are executed in the context of the current user.
# Pay attention to the SEMICOLON at the end of EACH command and the correct quoting (' or ") for or to avoid expansion of variables.
S3TUP_CONFIG_VERSION="46";
S3TUP_CONFIG_VERSION="47";

# Tokens that are replaced automatically:
# @CTNGSOURCE@ crosstool-NG source folder; support/crosstool/crosstool-ng
Expand Down Expand Up @@ -72,7 +72,7 @@ LIBS_AUTO_INTEGRATE="1"; # Automatically reintegrate libraries when rebuilding t

# OpenSSL 3.1.x
SSL_310="1";
SSL_310_beta="1";
SSL_310_beta="0";
SSL_310_name="OpenSSL";
SSL_310_version="$(curl --silent $CURL_GITHUB_TOKEN "https://api.github.com/repos/openssl/openssl/tags?page=1&per_page=100" | jq '[.[] | select(.name|test("openssl-3.1.*"))][0] | .name' | sed -e 's#openssl-##g; s#_#.#g; s#\"##g')";
SSL_310_check="openssl.pc $(printf "$SSL_310_version" | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+')";
Expand Down Expand Up @@ -198,9 +198,9 @@ SSL_102B_tasks+=('make install;');
SSL_100="1";
SSL_100_beta="0";
SSL_100_name="OpenSSL";
SSL_100_version="$(curl --silent $CURL_GITHUB_TOKEN "https://api.github.com/repos/openssl/openssl/tags?page=2&per_page=100" | jq '[.[] | select(.name|test("OpenSSL_1_0_0[a-z].*"))][0] | .name' | sed -e 's#OpenSSL_##g; s#_#.#g; s#\"##g')";
SSL_100_version="$(curl --silent $CURL_GITHUB_TOKEN "https://api.github.com/repos/openssl/openssl/tags?page=3&per_page=100" | jq '[.[] | select(.name|test("OpenSSL_1_0_0[a-z].*"))][0] | .name' | sed -e 's#OpenSSL_##g; s#_#.#g; s#\"##g')";
SSL_100_check="openssl.pc $(printf "$SSL_100_version" | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+')";
SSL_100_url="$(curl --silent $CURL_GITHUB_TOKEN "https://api.github.com/repos/openssl/openssl/tags?page=2&per_page=100" | jq '[.[] | select(.name|test("OpenSSL_1_0_0[a-z].*"))][0] | .tarball_url' | sed -e 's#\"##g')";
SSL_100_url="$(curl --silent $CURL_GITHUB_TOKEN "https://api.github.com/repos/openssl/openssl/tags?page=3&per_page=100" | jq '[.[] | select(.name|test("OpenSSL_1_0_0[a-z].*"))][0] | .tarball_url' | sed -e 's#\"##g')";
SSL_100_tasks=('export TOOLCHAIN=@TOOLCHAIN@;');
SSL_100_tasks+=('export PATH="$TOOLCHAIN/bin:$PATH";');
SSL_100_tasks+=('[ -n "@CFLAGS@" ] && export CFLAGS="@CFLAGS@";');
Expand Down
1 change: 1 addition & 0 deletions support/configs/simplebuild.config
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ S3_UPDATE_CHECK=0
USE_VERBOSE=0
USE_COMPRESS=0
USE_TARGZ=0
USE_GLIBC_SYMBOL_VERSION_CLEAR=0
delete_oscamdebugbinary=1
9 changes: 9 additions & 0 deletions support/functions/_cmd_build
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ _cmd_build(){
s3cfg_vars[TARGZ]=1;; # overwrite global
"USE_COMPRESS=1")
s3cfg_vars[COMPRESS]=1;;# overwrite global
"USE_GLIBC_SYMBOL_VERSION_CLEAR=1")
s3cfg_vars[GLIBC_SYMBOL_VERSION_CLEAR]=1;;# overwrite global
"USE_STAPI=1")
_stapi="-stapi"
[ -z "$stapi_lib_custom" ] && STAPI_LIB="STAPI_LIB=$sdir/stapi/liboscam_stapi.a" || STAPI_LIB="STAPI_LIB=$sdir/stapi/${stapi_lib_custom}"
Expand Down Expand Up @@ -240,6 +242,13 @@ fi
#show build time
printf "$g_n""\n TIME -------> $bt$re_\n\n"

#remove glibc symbol versions
if [ "${s3cfg_vars[GLIBC_SYMBOL_VERSION_CLEAR]}" == "1" ]
then
printf "$w_l"" ENABLE -----> GLIBC SYMBOL VERSION CLEARING:$y_l $txt_wait"
glibc_clear_cam "$oscam_name" "@GLIBC_"
fi;

#compress cam
if [ "${s3cfg_vars[COMPRESS]}" == "1" ]
then
Expand Down
78 changes: 78 additions & 0 deletions support/functions/_glibc_version_clear
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/bin/bash

glibc_clear_cam(){
local glibc_entries ge i csv _patchelf

cd "$bdir"
glibc_entries=( $(nm --dynamic --undefined-only --with-symbol-versions $1 | grep "$2" | awk '{print $2}') )

printf "\n$p_l\r CLEAR ------> "

for ge in "${glibc_entries[@]}";
do
let i++; [[ $i > 1 ]] && printf ","
printf "$g_n$ge"
csv+="--clear-symbol-version "$(echo $ge | cut -d'@' -f1)" "
done

_patchelf="$(make_patchelf)"
if [ $? == 0 ]
then
$_patchelf $csv $1
echo -e "\n$y_l\r ATTENTION --> ${y_n}Use this EXPERIMENTAL option very carefully, it may result in an unstable binary!"
echo -e "$w_l\r RESULT -----> $c_n${#glibc_entries[@]} glibc symbol versions removed."
else
echo -e "\n$y_l\r RESULT -----> ${r_n}patchelf binary could not be compiled! Please check logfile in $ldir."
fi;
echo -e "$rs_"
}

glibc_clear_cam_gui(){
local glibc_entries ge i csv txt _patchelf

cd "$bdir"
glibc_entries=( $(nm --dynamic --undefined-only --with-symbol-versions $1 | grep "$2" | awk '{print $2}') )

for ge in "${glibc_entries[@]}";
do
txt+="$ge\n"
csv+="--clear-symbol-version "$(echo $ge | cut -d'@' -f1)" "
done

_patchelf="$(make_patchelf)"
if [ $? == 0 ]
then
$_patchelf $csv $1
echo -en "\n$txt\n${#glibc_entries[@]} glibc symbol versions removed\n\nATTENTION: Use this EXPERIMENTAL option very carefully,\nit may result in an unstable binary!"
else
echo -en "\n$txt\npatchelf binary could not be compiled!\nPlease check logfile in\n$ldir."
fi;
}

make_patchelf(){
pever="0.17.2"
peurl="https://github.com/NixOS/patchelf/archive/refs/tags/${pever}.tar.gz"
patchfile="$configdir/_patchelf_verneed_fix.patch"
pedir="/tmp/patchelf-${pever}"
pebin="/tmp/patchelf-${pever}/src/patchelf"

if [ ! -x $pebin ]
then
(
[ -d $pedir ] && rm -rf $pedir
cd /tmp
wget -q -c $peurl -O - | tar -xz
cd $pedir
cp -f $patchfile $pedir
patch -p1 < $patchfile
if [ $? == 0 ]
then
./bootstrap.sh
./configure
make -j$(nproc)
fi;
) &>"$ldir/$(date +%F.%H%M%S)_patchelf_compile.log"
fi;

[ -x $pebin ] && printf "$pebin" || return 1
}
7 changes: 7 additions & 0 deletions support/functions/_gui_build
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ _gui_build(){
fi;) | "$gui" "$st_" --colors --title " -[ Build ]- " "$pb_" "$_lines" "$_cols"
sleep 2

#remove glibc symbol versions
if [ "${s3cfg_vars[GLIBC_SYMBOL_VERSION_CLEAR]}" == "1" ] || [ "${USE_vars[USE_GLIBC_SYMBOL_VERSION_CLEAR]}" == "USE_GLIBC_SYMBOL_VERSION_CLEAR=1" ]
then
(glibc_clear_cam_gui "$oscam_name" "@GLIBC_") |tee -a "$ldir/$log_name"| "$gui" "$st_" --backtitle "Please wait..." --title " -[ GLIBC SYMBOL VERSION CLEARING ]- " "$pb_" 25 60
sleep 10
fi;

#COMPRESS
if [ ! "$stapi_allowed" == "1" ]
then
Expand Down
Loading