Skip to content

Commit

Permalink
glibc: introduce symbol version clearing
Browse files Browse the repository at this point in the history
  • Loading branch information
WXbet committed Mar 19, 2023
1 parent 9981f0d commit a5bc641
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 6 deletions.
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
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
12 changes: 7 additions & 5 deletions support/functions/_misc
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ _pre_build(){
printf $WH
ologo
_tcfile=""
dln="$(basename $(decode "$_toolchainfilename"))"
[ -n "$_toolchainfilename" ] && dln="$(basename $(decode "$_toolchainfilename"))" || dln=""
tc_dl="$dldir/$dln"
if [ -f "$tcdir/$_toolchainname/bin/$_compiler""gcc" ]
then
Expand Down Expand Up @@ -289,7 +289,7 @@ _oscam_extra_menu(){

for e in "${!USE_vars[@]}"
do
if [ ! "$e" == "USE_TARGZ" ] && [ ! "$e" == "USE_COMPRESS" ] && [ ! "$e" == "USE_PATCH" ] && [ ! "$e" == "USE_LIBUSB" ]
if [ ! "$e" == "USE_TARGZ" ] && [ ! "$e" == "USE_COMPRESS" ] && [ ! "$e" == "USE_GLIBC_SYMBOL_VERSION_CLEAR" ] && [ ! "$e" == "USE_PATCH" ] && [ ! "$e" == "USE_LIBUSB" ]
then
if [ "${#USE_vars[$e]}" -gt "4" ]
then
Expand All @@ -305,7 +305,7 @@ _oscam_extra_menu(){

for e in "${!USE_vars[@]}"
do
if [ ! "$e" == "USE_TARGZ" ] && [ ! "$e" == "USE_COMPRESS" ] && [ ! "$e" == "USE_PATCH" ] && [ ! "$e" == "USE_LIBUSB" ]
if [ ! "$e" == "USE_TARGZ" ] && [ ! "$e" == "USE_COMPRESS" ] && [ ! "$e" == "USE_GLIBC_SYMBOL_VERSION_CLEAR" ] && [ ! "$e" == "USE_PATCH" ] && [ ! "$e" == "USE_LIBUSB" ]
then
ok=0
for oex in $oextra
Expand All @@ -331,15 +331,16 @@ _build_extra_menu(){
us=
check_smargo
unset MENU_OPTIONS
for e in USE_COMPRESS USE_TARGZ USE_PATCH
for e in USE_COMPRESS USE_TARGZ USE_PATCH USE_GLIBC_SYMBOL_VERSION_CLEAR
do
[ "${#USE_vars[$e]}" -gt "4" ] && MENU_OPTIONS+=( "$e" "$e=1" on ) || MENU_OPTIONS+=( "$e" "$e=1" off )
done
bextra=$("$gui" "$st_" "$nc_" "$bt_" "$title_" --title "-[ Build EXTRA ]-" "$cl_" " OPTIONS " 10 40 3 ${MENU_OPTIONS[@]})
bextra=$("$gui" "$st_" "$nc_" "$bt_" "$title_" --title "-[ Build EXTRA ]-" "$cl_" " OPTIONS " 12 80 3 ${MENU_OPTIONS[@]})
[ $? = 255 ] && _build_extra_menu
USE_vars[USE_COMPRESS]=
USE_vars[USE_PATCH]=
USE_vars[USE_TARGZ]=
USE_vars[USE_GLIBC_SYMBOL_VERSION_CLEAR]=
for bex in $bextra
do
b=$(echo "$bex" | tr -d '"')
Expand Down Expand Up @@ -623,6 +624,7 @@ load_config(){
_reset_config
[ "${s3cfg_vars[USE_TARGZ]}" == "1" ] && USE_vars[USE_TARGZ]="USE_TARGZ=1"
[ "${s3cfg_vars[USE_COMPRESS]}" == "1" ] && USE_vars[USE_COMPRESS]="USE_COMPRESS=1"
[ "${s3cfg_vars[USE_GLIBC_SYMBOL_VERSION_CLEAR]}" == "1" ] && USE_vars[USE_GLIBC_SYMBOL_VERSION_CLEAR]="USE_GLIBC_SYMBOL_VERSION_CLEAR=1"
for e in $default_use
do
USE_vars[$e]="$e=1"
Expand Down
2 changes: 1 addition & 1 deletion support/profiles/complete.box.profile
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ all_off readers_on
WEBIF LIVELOG JQUERY SSL DVBAPI READ_SDT ANTICASC LB CACHEEX CACHEEX_AIO CWCC LCD LED IPV6
CAMD35 CAMD35_TCP NEWCAMD CCCAM CCCSHARE GBOX SCAM CONSTCW
PHOENIX INTERNAL SC8IN1 MP35 STINGER
USE_UTF8 USE_COMPRESS
USE_UTF8 USE_COMPRESS USE_GLIBC_SYMBOL_VERSION_CLEAR

0 comments on commit a5bc641

Please sign in to comment.