You are not logged in.

#1 2023-08-05 19:11:37

JATothrim
Member
Registered: 2019-01-31
Posts: 15

depcheck - check for broken binary dependecies

I wrote following script snippet after spending +6h on the binutils 2.41 vs. perf bug I reported.
It would have instantly pointed me at right direction(s) and saved multiple hours of trouble shooting.

See updated version here

#!/bin/bash
# force C locale to get stable command output.
export LANG=C
function depcheck() {
  readarray -t results < <(ldd $1 | grep 'not found')
  if [[ ${#results[@]} -gt 0 ]]; then 
    echo "depcheck $1 failed:";
    for dep in "${results[@]}"; do
      # trim white space from the ldd line:
      dep="${dep#"${dep%%[![:space:]]*}"}"
      dep="${dep%"${dep##*[![:space:]]}"}"
      echo "${dep}"
      # try find who owns the missing library file:
      libname="$(echo $dep | sed -re 's#(.*)\.so.*#\1.so#')"
      echo "pacman -Qo /lib/${libname}*"
      pacman -Qo /lib/${libname}*
     done
  fi
}
# test single binary:
if [[ -n $1 ]]; then
  depcheck $1
  exit
fi
# else test all of /bin
for fbin in $(find /bin/ -executable -type f); do
  # test only ELF executables, not 100% fool proof
  # and ldd may still complain on some files.
  if head -c 4 $fbin | grep -qc 'ELF'; then
    depcheck $fbin;
  fi
done

No elevated privileges should be required to run above script.
What the script does is scan /bin for any ELF binaries (shebangs with +x should not count)
and pass them to ldd command and report any errors that ldd outputs.
If ldd shows any missing dynamic (.so) libraries for binary, it feeds these names to

pacman -Qo

to try see if these files are owned by any package.

My system does not "pass" the check and I found out that bunch things are broken at the moment:

depcheck /bin/sensord failed:
librrd.so.8 => not found
pacman -Qo /lib/librrd.so*
error: No package owns /lib/librrd.so*
depcheck /bin/mpeg2dec failed:
libSDL-1.2.so.0 => not found
pacman -Qo /lib/libSDL-1.2.so*
error: No package owns /lib/libSDL-1.2.so*
... list goes on ...

If binary is reported by this script that binary can't be run on your system.
If pacman reports  "No package owns nnn" the shared library is likely totally missing from the system.
pkgfile might find package that provides this file.

In case of perf tool from linux-tools the result got interesting: "/usr/lib/libsframe.so is owned by binutils 2.41-1"
This result tells the package that owns perf binary is now broken: its dependency has changed in incompatible way, but pacman has upgraded the dependency regardless.
Likely only way to cleanly fix such situation is to re-build the (perf,linux-tools) package yourself and report it.

I'm kind of curious of what and how many things this script would report on other user systems?
Also I don't know if tool like this exists already, I'm just glad to drop this here.
Suggestions to improve the script are also welcome.

Last edited by JATothrim (2023-08-06 20:20:09)


If it ain't broken yet, some time passes and I'm doomed to learn its secrets.
Now it is half broken but twice as fast than before.

Offline

#2 2023-08-05 19:23:55

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,879
Website

Re: depcheck - check for broken binary dependecies

There seems to be a lot of grepping, redirecting, then other parsing which could be simplified.  I have a related script that just lists the dependencies for a given binary (regardless of whether they are "missing"):

#!/bin/sh

readelf -d $1 \
	| sed -n 's|.*Shared library: \[\([^\]*\)\]|/usr/lib/\1|p' \
	| pacman -F - | sed 's|^|/|;s| .*||' | pacman -Qqo - \
	| sort -u

"UNIX is simple and coherent" - Dennis Ritchie; "GNU's Not Unix" - Richard Stallman

Offline

#3 2023-08-05 21:00:23

yochananmarqos
Member
Registered: 2020-02-05
Posts: 203

Re: depcheck - check for broken binary dependecies

@JATothrim: Er?

	not a dynamic executable
head: cannot open '/bin/mount.nfs' for reading: Permission denied
head: cannot open '/bin/cdda2wav' for reading: Permission denied
	not a dynamic executable
	not a dynamic executable
depcheck /bin/hdspconf failed:
libfltk.so.1.3 => not found
pacman -Qo /lib/libfltk.so*
error: No package owns /lib/libfltk.so*
depcheck /bin/sensord failed:
librrd.so.8 => not found
pacman -Qo /lib/librrd.so*
error: No package owns /lib/librrd.so*
head: cannot open '/bin/readcd' for reading: Permission denied
	not a dynamic executable
depcheck /bin/hdspmixer failed:
libfltk.so.1.3 => not found
pacman -Qo /lib/libfltk.so*
error: No package owns /lib/libfltk.so*
	not a dynamic executable
depcheck /bin/makemhr failed:
libmysofa.so.1 => not found
pacman -Qo /lib/libmysofa.so*
error: No package owns /lib/libmysofa.so*
head: cannot open '/bin/cdrecord' for reading: Permission denied
	not a dynamic executable
	not a dynamic executable
head: cannot open '/bin/rscsi' for reading: Permission denied

Last edited by yochananmarqos (2023-08-05 21:02:24)

Offline

#4 2023-08-05 21:06:00

JATothrim
Member
Registered: 2019-01-31
Posts: 15

Re: depcheck - check for broken binary dependecies

Trilby wrote:

There seems to be a lot of grepping, redirecting, then other parsing which could be simplified.  I have a related script that just lists the dependencies for a given binary (regardless of whether they are "missing"):

#!/bin/sh

readelf -d $1 \
	| sed -n 's|.*Shared library: \[\([^\]*\)\]|/usr/lib/\1|p' \
	| pacman -F - | sed 's|^|/|;s| .*||' | pacman -Qqo - \
	| sort -u

Had to add "export LANG=C" and do pacman -Fy to run above... wink
I'm not too good on bash scripting and I assembled this thing in one evening.. I mostly do C++.

Idea was to find + detect what installed binaries can not run on the system and point to the potential culprit if any.

Last edited by JATothrim (2023-08-06 00:10:57)


If it ain't broken yet, some time passes and I'm doomed to learn its secrets.
Now it is half broken but twice as fast than before.

Offline

#5 2023-08-05 21:48:34

JATothrim
Member
Registered: 2019-01-31
Posts: 15

Re: depcheck - check for broken binary dependecies

yochananmarqos wrote:

@JATothrim: Er?

	not a dynamic executable
head: cannot open '/bin/mount.nfs' for reading: Permission denied
head: cannot open '/bin/cdda2wav' for reading: Permission denied
	not a dynamic executable
	not a dynamic executable
depcheck /bin/hdspconf failed:
libfltk.so.1.3 => not found
pacman -Qo /lib/libfltk.so*
error: No package owns /lib/libfltk.so*
depcheck /bin/sensord failed:
librrd.so.8 => not found
pacman -Qo /lib/librrd.so*
error: No package owns /lib/librrd.so*
head: cannot open '/bin/readcd' for reading: Permission denied
	not a dynamic executable
depcheck /bin/hdspmixer failed:
libfltk.so.1.3 => not found
pacman -Qo /lib/libfltk.so*
error: No package owns /lib/libfltk.so*
	not a dynamic executable
depcheck /bin/makemhr failed:
libmysofa.so.1 => not found
pacman -Qo /lib/libmysofa.so*
error: No package owns /lib/libmysofa.so*
head: cannot open '/bin/cdrecord' for reading: Permission denied
	not a dynamic executable
	not a dynamic executable
head: cannot open '/bin/rscsi' for reading: Permission denied

Thanks for test run: The hdspconf, sensord, hdspmixer, makemhr are likely unable to run if you try them.

the script just spews errors out on few of files though...

  • "not a dynamic executable" lines are ldd command complaining because my script fails to ignore these files.

  • "head: cannot open ..." lines are a bit weird: Do you have some files in /bin that have only execute bit but no read bit set? o__O


If it ain't broken yet, some time passes and I'm doomed to learn its secrets.
Now it is half broken but twice as fast than before.

Offline

#6 2023-08-05 21:54:29

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,879
Website

Re: depcheck - check for broken binary dependecies

JATothrim wrote:

Listing all the package dependencies of program is a bit pointless

That's what you said your script was trying to do.

But if you really just want to detect programs with missing dynamic links that's much easier:

#!/bin/sh

ldd /bin/* 2>/dev/null | awk '
	/^\// { exe = $1 }
	/not found/ { list[exe] = list[exe] " " $1 }
	END { for (key in list) print key list[key] }
'

Last edited by Trilby (2023-08-05 21:55:47)


"UNIX is simple and coherent" - Dennis Ritchie; "GNU's Not Unix" - Richard Stallman

Offline

#7 2023-08-05 22:48:01

yochananmarqos
Member
Registered: 2020-02-05
Posts: 203

Re: depcheck - check for broken binary dependecies

JATothrim wrote:

The hdspconf, sensord, hdspmixer, makemhr are likely unable to run if you try them.

Oh, I see. Those are all binaries of packages I do have installed, however I do not have the corresponding optional dependencies installed.

Offline

#8 2023-08-06 00:09:47

JATothrim
Member
Registered: 2019-01-31
Posts: 15

Re: depcheck - check for broken binary dependecies

Trilby wrote:
JATothrim wrote:

Listing all the package dependencies of program is a bit pointless

That's what you said your script was trying to do.

Ah. excuse for my bad english.. *facepalm*
Your awk version is much better than mine doing the searching part.
Just do "pacman -F" for each of these missing dynamic links and that is it.

yochananmarqos wrote:

Those are all binaries of packages I do have installed, however I do not have the corresponding optional dependencies installed.

Oh, that may explain some of the cases.


If it ain't broken yet, some time passes and I'm doomed to learn its secrets.
Now it is half broken but twice as fast than before.

Offline

#9 2023-08-06 05:05:18

Allan
Pacman
From: Brisbane, AU
Registered: 2007-06-09
Posts: 11,439
Website

Re: depcheck - check for broken binary dependecies

There are versions here using "ldd" and "readelf -d".   There is a slight difference.  Using "ldd" does a recursive lookup, were "readelf -d" just looks for directly linked libraries, so you may get slightly different results.

Offline

#10 2023-08-06 08:49:59

JATothrim
Member
Registered: 2019-01-31
Posts: 15

Re: depcheck - check for broken binary dependecies

Allan wrote:

There are versions here using "ldd" and "readelf -d".   There is a slight difference.  Using "ldd" does a recursive lookup, were "readelf -d" just looks for directly linked libraries, so you may get slightly different results.

Do you have some examples that do recursive lookup?

What I understand ldd is invoking the standard system linker and stopping before the program is run.
I think "readelf -d" is good for inspecting the directly linked dependencies, but it may miss stuff deeper the linking dependency chain.


If it ain't broken yet, some time passes and I'm doomed to learn its secrets.
Now it is half broken but twice as fast than before.

Offline

#11 2023-08-06 13:59:40

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,879
Website

Re: depcheck - check for broken binary dependecies

Another difference I realized in hindsight is I'm not sure whether readelf reports missing libraries any differently than ones that are present.  My first code block in this thread was what I use for a different purpose than yours: I use it on built binaries that I intend to package (my own version of namcap) to ensure I list all the needed dependencies in the PKGBUILD.  For that use one would not want a recursive look-up (which is one of the main reasons I use readelf for that rather than ldd).

Last edited by Trilby (2023-08-06 14:01:43)


"UNIX is simple and coherent" - Dennis Ritchie; "GNU's Not Unix" - Richard Stallman

Offline

#12 2023-08-06 20:10:18

JATothrim
Member
Registered: 2019-01-31
Posts: 15

Re: depcheck - check for broken binary dependecies

I wrote upgraded version that does not use grep or ldd at all. smile
This is script is intended for users that have to troubleshoot why some installed binary fails with missing libraries.

The script may report false positives if it can't locate the library on the file system.
Please verify the reported binary/program fails to execute:
If the reported binary/program does run report an bug here.

depcheck: Recursively parse given binary/program dependency chain with "readelf -d" and awk.
If the script can't find a library file on the system it reports them with "pacman -F "
In "scan all mode" without arguments it processes all of /bin and this may take awhile...

Bugs fixed: strip off any hard coded path-names from libraries.

#!/bin/bash
# depcheck V2: find system binaries that are unable to run
# requires bash 4.3, find, readelf, awk, sort, pacman
# --Any NOTE(s) reported by this tool means an installed binary
#   is missing optional dependency.
#   Such binaries/programs cannot run until the dependency is installed.
# --Any WARN(s) reported by this tool means: an installed binary
#   is missing dependency *and* the dependency is also installed.
#   Such binaries/programs cannot run until the dependency is fixed.
#   Fixing such dependency problem may be hard.
#   You should also verify that the binary really does not execute: If it runs, please report an bug for this script.

# force C locale to get stable command output.
export LANG=C
export LC_ALL=C

checkpassed=0
function pacman_query() {
  local -n _query=$1
  readarray _query < <(pacman -F $2 | awk '
  { line[NR] = $0 }
  /installed/ { wasinstalled = 1
     line[NR] = $0 }
  END { print wasinstalled
    for (i in line)
      print "==>" line[i]
  }')
}
function checkissue() {
  # display pacman -F result for $1
  depname=$(basename "$(echo $1 | sed -re 's#(.*)\.so.*#\1.so#')" )
  local pkginfo=()
  pacman_query pkginfo "$depname"
  if [[ "${pkginfo[0]}" =~ "1" ]]; then
    echo "==>This dependency is currently installed via"
    for info in "${pkginfo[@]:1}"; do
      echo -ne "$info"
    done
    local baseprog=$(basename "$2")
    pacman_query pkginfo "$baseprog"
    echo "==>WARN: Following package may have an problem:"
    for info in "${pkginfo[@]:1}"; do
      echo -ne "$info"
    done
    checkpassed=1
    return 1
  else
    echo "==>NOTE: This dependency may be installed via"
    for info in "${pkginfo[@]:1}"; do
      echo -ne "$info"
    done
    return 1
  fi
}
function unique_arr() {
  local -n arrayref=$1
  readarray -td '' arrayref < <(printf '%s\0' "${arrayref[@]}" | sort -zus)
}
# accelerate recursive dependency chain checking:
# any library added here is ignored by depgraph.
passeddeps=('libc.so')

# build the recursive dependency graph with readelf -d and awk
# (avoid using ldd for security reasons)
function depgraph() {
  # collect RPATH/RUNPATH and NEEDED from the binary
  # awk filters out anything that is in skip array.
  local skip=(${@:3} ${passeddeps[*]} )
  echo -ne "\033[0Kprocessing...\r"
  readarray -t depinfo < <(readelf -d $1 | awk -v fskip="${skip[*]// /:}" '
  /RPATH/ { rpath = gensub(/.*\[(.+)\].*/, "\\1", "g", $0) }
  /RUNPATH/ { rpath = gensub(/.*\[(.+)\].*/, "\\1", "g", $0) }
  /NEEDED/ { list[NR] = gensub(/.*\[(.+)\].*/, "\\1", "g", $0) }
  END { print rpath
    for (i in list)
      if (index(fskip, list[i]) == 0)
        print list[i] } ' )
  [[ $? != 0 ]] && return 0
  local deps=("${depinfo[@]:1}")
  # assemble array of unique library search paths
  local rpath="${depinfo[0]}"
  local inpaths="$2"
  local paths=(${rpath//:/ } ${inpaths//:/ } /lib /usr/lib32)
  unique_arr paths
  #declare -p paths
  #declare -p deps
  for dep in ${deps[@]}; do
    dep="$(basename $dep)"
    local ok=0
    for path in ${paths[@]}; do
      if [[ -r "$path/$dep" ]]; then
        ok=1
        depgraph "$path/$dep" "${paths[*]// /:}" ${skip[*]} ${deps[*]}
        break
      fi
    done
    if [[ $ok == 0 ]]; then
      # file likely not found on the system
      echo "Unable to find [$dep] from the system. This library is needed to run [$1]"
      checkissue $dep $1
    fi
    if [[ $? == 0 ]]; then
      passeddeps+=("$dep")
    fi
  done
  unique_arr passeddeps
  return 0
}
# test single files from command line
cmdline=("$@")
if [[ -n "${cmdline[*]}" ]]; then
  for fbin in ${cmdline[@]}; do
    echo "depcheck $fbin"
    depgraph "$fbin"
  done
  exit $checkpassed
fi
# else test all of /bin
for fbin in $(find /bin/ -readable -type f); do
  depgraph $fbin 2>/dev/null
done
echo "${#passeddeps[@]} libraries checked total."
exit $checkpassed

The script output now looks like:

~$depcheck /bin/pvtkpython /bin/bpf_jit_disasm
depcheck /bin/pvtkpython
Unable to find [libmpi.so.40] from the system. This library is needed to run [/bin/pvtkpython]
==>NOTE: This dependency may be installed via
==>extra/intel-oneapi-basekit 2023.1.0.46401-3
==>    opt/intel/oneapi/mpi/2021.9.0/lib/debug/libmpi.so
==>    opt/intel/oneapi/mpi/2021.9.0/lib/release/libmpi.so
==>extra/openmpi 4.1.5-2
==>    usr/lib/libmpi.so
depcheck /bin/bpf_jit_disasm
Unable to find [libsframe.so.0] from the system. This library is needed to run [/bin/bpf_jit_disasm]
==>This dependency is currently installed via
==>core/binutils 2.41-3 [installed: 2.41-1]
==>    usr/lib/libsframe.so
==>WARN: Following package may have an problem:
==>extra/bpf 6.3-2 (linux-tools) [installed]
==>    usr/bin/bpf_jit_disasm

Trickiest part was stuffing the bash "passeddeps" array into awk and recursively processing the dependency chain only once.

Trilby wrote:

Another difference I realized in hindsight is I'm not sure whether readelf reports missing libraries any differently than ones that are present.

readelf -d reads the ELF file (i.e. binary or shared library) and dumps its contents out in human readable form.
The library name strings "readelf -d -> (NEEDED)" dumps are stored in the ELF file. readelf doesn't interpret these strings in anyway. smile

Last edited by JATothrim (2023-08-07 11:57:44)


If it ain't broken yet, some time passes and I'm doomed to learn its secrets.
Now it is half broken but twice as fast than before.

Offline

#13 2023-08-07 01:42:49

yochananmarqos
Member
Registered: 2020-02-05
Posts: 203

Re: depcheck - check for broken binary dependecies

I don't get it. mpv runs and the dependency is satisfied.

depcheck /usr/bin/mpv
Unable to find [/usr/lib/libmujs.so] from the system. This library is needed to run [/usr/bin/mpv]
==>This dependency is currently installed via
==>extra/mujs 1.3.3-1 [installed]
==>    usr/lib/libmujs.so
==>WARN: Following package may have an problem:
==>extra/mpv 1:0.36.0-1 [installed]
==>    usr/bin/mpv
==>    usr/share/bash-completion/completions/mpv

Offline

#14 2023-08-07 10:31:47

JATothrim
Member
Registered: 2019-01-31
Posts: 15

Re: depcheck - check for broken binary dependecies

yochananmarqos wrote:

I don't get it. mpv runs and the dependency is satisfied.

depcheck /usr/bin/mpv
Unable to find [/usr/lib/libmujs.so] from the system. This library is needed to run [/usr/bin/mpv]
==>This dependency is currently installed via
==>extra/mujs 1.3.3-1 [installed]
==>    usr/lib/libmujs.so
==>WARN: Following package may have an problem:
==>extra/mpv 1:0.36.0-1 [installed]
==>    usr/bin/mpv
==>    usr/share/bash-completion/completions/mpv

Thanks for testing. smile

This looks like a bug:
The library was reported as "[/usr/lib/libmujs.so]" this looks like an hard coded library filename.
It should read just "[libmujs.so]" as the combined "</lib>/</usr/lib/libmujs.so>" filename obviously can't be found...

@yochananmarqos: I fixed the bug. Can you test again?

Side note:
I'm not sure exactly what the dynamic linker search paths are and how it discovers them?
Currently only hard coded search paths are "/lib",  "/usr/lib32" and extra library paths are discovered from
the RPATH and RUNPATH info of the binaries+libraries.
If the script misses any of them false positive reports are possible...

ldd would not have this problem, because it is the dynamic linker.
So If the binary actually does run, it is an bogus result.

Last edited by JATothrim (2023-08-07 12:11:59)


If it ain't broken yet, some time passes and I'm doomed to learn its secrets.
Now it is half broken but twice as fast than before.

Offline

#15 2023-08-07 14:03:11

yochananmarqos
Member
Registered: 2020-02-05
Posts: 203

Re: depcheck - check for broken binary dependecies

I see. Now there's no output for mpv.

Offline

Board footer

Powered by FluxBB