From 7836af47d5ed5cb9e97712f7a13b1944c9955b29 Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Tue, 27 Aug 2024 17:15:19 +0100 Subject: [PATCH 1/3] lddtree.sh: Add an "ldd-mode" for when the command is executed as *ldd The output resembles the regular ldd command. Signed-off-by: James Le Cuirot --- lddtree.sh | 79 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/lddtree.sh b/lddtree.sh index e0185f4..db9bab8 100755 --- a/lddtree.sh +++ b/lddtree.sh @@ -23,6 +23,9 @@ usage() { -l Display output in a flat format -h Show this help output -V Show version information + + When called as *ldd (e.g. via a symlink), the output resembles the regular + ldd command by default. EOF exit ${1:-0} } @@ -124,45 +127,66 @@ find_elf() { } show_elf() { - local elf=$1 indent=$2 parent_elfs=$3 + local elf=$1 indent=$2 parent_elfs=$3 inputs=$4 local rlib lib libs local resolved find_elf "${elf}" resolved=${_find_elf} elf=${elf##*/} - ${LIST} || printf "%${indent}s%s => " "" "${elf}" - if [[ ,${parent_elfs}, == *,${elf},* ]] ; then - ${LIST} || printf "!!! circular loop !!!\n" "" - return + local loop=false + [[ ,${parent_elfs}, == *,${elf},* ]] && loop=true + + if ${LDD_MODE} ; then + if [[ ${indent} -gt 0 ]] ; then + printf "\t%s => " "${elf}" + elif [[ ${inputs} -gt 1 ]] ; then + printf "%s:\n" "${resolved}" + fi + elif ${LIST} ; then + : + else + printf "%${indent}s%s => " "" "${elf}" + ${loop} && printf "!!! circular loop !!!\n" fi + + ${loop} && return parent_elfs="${parent_elfs},${elf}" - if ${LIST} ; then - echo "${resolved:-$1}" + + if ${LDD_MODE} ; then + if [[ ${indent} -gt 0 ]] ; then + printf "%s" "${resolved:-not found} (0xdeadbeef)" + fi + elif ${LIST} ; then + printf "%s" "${resolved:-$1}" else - printf "${resolved:-not found}" + printf "%s" "${resolved:-not found}" fi + if [[ ${indent} -eq 0 ]] ; then - local elf_specs interp full_interp + local elf_specs full_interp root_interp base_interp elf_specs=$(elf_specs "${resolved}") - interp=$(scanelf -qF '#F%i' "${resolved}") - [[ -n ${interp} ]] && interp="${ROOT}${interp#/}" + full_interp=$(scanelf -qF '#F%i' "${resolved}") + base_interp=${full_interp##*/} + [[ -n ${full_interp} ]] && root_interp="${ROOT}${full_interp#/}" - if ${LIST} ; then - [[ -n ${interp} ]] && echo "${interp}" + # If we are in the default mode, then we want to show the "duplicate" + # interp lines -- first the header (interp=>xxx), and then the DT_NEEDED + # line to show that the ELF is directly linked against the interp. + # Otherwise, we only want to show the interp once. + if ${LDD_MODE} ; then + [[ -n ${root_interp} ]] && printf "\t%s" "${root_interp} (0xdeadbeef)" + allhits+=",${base_interp}" + elif ${LIST} ; then + [[ -n ${root_interp} ]] && printf "\n%s" "${root_interp}" + allhits+=",${base_interp}" else - printf " (interpreter => ${interp:-none})" + printf " (interpreter => %s)" "${root_interp:-none}" fi - full_interp=${interp} - interp=${interp##*/} - # If we are in non-list mode, then we want to show the "duplicate" interp - # lines -- first the header (interp=>xxx), and then the DT_NEEDED line to - # show that the ELF is directly linked against the interp. - # If we're in list mode though, we only want to show the interp once. - ${LIST} && allhits+=",${interp}" fi - ${LIST} || printf "\n" + + printf "\n" [[ -z ${resolved} ]] && return @@ -186,13 +210,13 @@ show_elf() { # the ldso won't load another copy of ldso into memory from the search # path, it'll reuse the existing copy that was loaded from the full # hardcoded path. - if [[ ${lib} == "${interp}" ]] ; then + if [[ ${lib} == "${base_interp}" ]] ; then rlib=${full_interp} else find_elf "${lib}" "${resolved}" rlib=${_find_elf} fi - show_elf "${rlib:-${lib}}" $((indent + 4)) "${parent_elfs}" + show_elf "${rlib:-${lib}}" $((indent + 4)) "${parent_elfs}" "${inputs}" done } @@ -203,6 +227,9 @@ SHOW_ALL=false SET_X=false LIST=false AUTO_ROOT=true +LDD_MODE=false + +[[ ${argv0} = *ldd ]] && LDD_MODE=true while getopts haxVR:l-: OPT ; do case ${OPT} in @@ -211,7 +238,7 @@ while getopts haxVR:l-: OPT ; do h) usage;; V) version;; R) ROOT="${OPTARG%/}/";; - l) LIST=true;; + l) LIST=true LDD_MODE=false;; -) # Long opts ftw. case ${OPTARG} in no-auto-root) AUTO_ROOT=false;; @@ -241,7 +268,7 @@ for elf ; do else allhits="" [[ ${elf} != */* ]] && elf="./${elf}" - show_elf "${elf}" 0 "" + show_elf "${elf}" 0 "" $# fi done exit ${ret} From a871611dfface788fdf59f696254a05bda0fd506 Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Tue, 27 Aug 2024 17:22:32 +0100 Subject: [PATCH 2/3] build: Always install lddtree.sh and create symlink to chosen variant Don't install lddtree.py unconditionally because it has additional dependencies. --- meson.build | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 827e7e4..a89d7e7 100644 --- a/meson.build +++ b/meson.build @@ -116,18 +116,25 @@ executable('scanmacho', install : true ) +install_data('lddtree.sh', + install_dir : get_option('bindir') +) lddtree_impl = get_option('lddtree_implementation') if lddtree_impl != 'none' if lddtree_impl == 'python' + install_data('lddtree.py', + install_dir : get_option('bindir') + ) suffix = '.py' else suffix = '.sh' endif - install_data('lddtree' + suffix, - rename : 'lddtree', + install_symlink('lddtree', + pointing_to : 'lddtree' + suffix, install_dir : get_option('bindir') ) endif + install_data('symtree.sh', rename : 'symtree', install_dir : get_option('bindir') From 7a432daeeae7388f79cdb92d9478024fde425db9 Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Tue, 27 Aug 2024 18:07:48 +0100 Subject: [PATCH 3/3] lddtree.*: Disable auto-root feature by default It's counter-intuitive and doesn't play well with other programs that expect path arguments to work normally. Signed-off-by: James Le Cuirot --- lddtree.py | 9 +++++++-- lddtree.sh | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lddtree.py b/lddtree.py index 3a41886..787cc3e 100755 --- a/lddtree.py +++ b/lddtree.py @@ -851,13 +851,18 @@ def GetParser() -> argparse.ArgumentParser: action=_NormalizePathAction, help="Search for all files/dependencies in ROOT", ) + group.add_argument( + "--auto-root", + action="store_true", + help="Automatically prefix input ELFs with ROOT", + ) group.add_argument( "--no-auto-root", dest="auto_root", action="store_false", - default=True, - help="Do not automatically prefix input ELFs with ROOT", + help=argparse.SUPPRESS, ) + group.set_defaults(auto_root=False) group.add_argument( "-C", "--cwd", diff --git a/lddtree.sh b/lddtree.sh index db9bab8..e0ab757 100755 --- a/lddtree.sh +++ b/lddtree.sh @@ -19,7 +19,7 @@ usage() { -a Show all duplicated dependencies -x Run with debugging -R Use this ROOT filesystem tree - --no-auto-root Do not automatically prefix input ELFs with ROOT + --auto-root Automatically prefix input ELFs with ROOT -l Display output in a flat format -h Show this help output -V Show version information @@ -226,7 +226,7 @@ if [[ $1 != "/../..source.lddtree" ]] ; then SHOW_ALL=false SET_X=false LIST=false -AUTO_ROOT=true +AUTO_ROOT=false LDD_MODE=false [[ ${argv0} = *ldd ]] && LDD_MODE=true @@ -241,6 +241,7 @@ while getopts haxVR:l-: OPT ; do l) LIST=true LDD_MODE=false;; -) # Long opts ftw. case ${OPTARG} in + auto-root) AUTO_ROOT=true;; no-auto-root) AUTO_ROOT=false;; *) usage 1;; esac