#! /bin/sh
# Copyright (C) 1996-2004, 2005 Free Software Foundation, Inc.
# Copyright (C) 2006-2018  Dmitry V. Levin <ldv@altlinux.org>
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

. /usr/lib/rpm/rpmb-functions

warn=
bind_now=
debug=

while test $# -gt 0; do
  case "$1" in
  --undefined)
    warn=1
    bind_now=1
    shift
    ;;
  --bindings)
    warn=1
    bind_now=1
    debug=bindings
    shift
    ;;
  --)		# Stop option processing.
    shift; break
    ;;
  -*)
    Fatal "unrecognized option: $1"
    ;;
  *)
    break
    ;;
  esac
done

case $# in
	[01]) Fatal 'insufficient arguments' ;;
	2) ;;
	*) Fatal 'too many arguments' ;;
esac

file="$1" && shift
rpath="$1" && shift

interp=
get_interp_from_elf()
{
	local f info
	f="$1"; shift

	info="$(readelf --wide --segments "$f")" ||
		Fatal "$f: unable to fetch ELF segment headers"

	interp="$(printf '%s\n' "$info" |
		  sed -ne 's,^[[:space:]]*\[Requesting program interpreter: \(/[^]]\+\)\]$,\1,p')"

	[ -n "$interp" ]
}

rtld=
get_rtld_from_interp()
{
	if [ -n "${RPM_BUILD_ROOT-}" ] &&
	   [ -f "$RPM_BUILD_ROOT$interp" -a -x "$RPM_BUILD_ROOT$interp" ]; then
		rtld="$RPM_BUILD_ROOT$interp"
	elif [ -f "$interp" -a -x "$interp" ]; then
		rtld="$interp"
	else
		rtld=
	fi

	[ -n "$rtld" ]
}

verify_out=
rtld_verify_elf()
{
	local f
	f="$1"; shift

	verify_out="$("$rtld" --verify "$f")"
	[ "$?" = 0 -o "$?" = 2 ]
}

trace_elf()
{
	local rtld_target rtld_new_target= rtld_preload=
	rtld_target="$1"; shift

	if [ -n "$RPM_LD_PRELOAD" ] &&
	   eu-elfclassify --shared "$rtld_target"; then
		local f
		for f in $RPM_LD_PRELOAD; do
			if eu-elfclassify --executable "$f"; then
				if [ -z "$rtld_new_target" ]; then
					rtld_new_target="$f"
				else
					Warning "preloading executable: $f"
					rtld_preload="$rtld_preload $f"
				fi
			else
				rtld_preload="$rtld_preload $f"
			fi
		done
	else
		rtld_preload="$RPM_LD_PRELOAD"
	fi

	if [ -n "$rtld_new_target" ]; then
		Debug "retargeted from $rtld_target to $rtld_new_target"
		rtld_preload="$rtld_preload $rtld_target"
		rtld_target="$rtld_new_target"
	fi

	LD_TRACE_LOADED_OBJECTS=1 \
	LD_WARN=$warn \
	LD_BIND_NOW=$bind_now \
	LD_DEBUG=$debug \
	LD_LIBRARY_VERSION=$verify_out \
	LD_PRELOAD="$rtld_preload" \
		"$rtld" --library-path "$rpath" "$rtld_target" ||
		Fatal "$rtld_target: trace failed"

	exit 0
}

if get_interp_from_elf "$file"; then
	get_rtld_from_interp ||
		Fatal "$file: program interpreter $interp not found"
	rtld_verify_elf "$file" &&
	trace_elf "$file"
fi

for interp in $(cat /usr/bin/ldd 2>/dev/null |
		sed -n 's/^RTLDLIST="\([^"]\+\)"/\1/p'); do
	get_rtld_from_interp &&
	rtld_verify_elf "$file" &&
		trace_elf "$file"
done

dump_ld_config='/usr/lib/rpm/dump_ld_config'
get_interp_from_elf "$dump_ld_config" ||
	Fatal "$dump_ld_config: program interpreter not specified"

get_rtld_from_interp ||
	Fatal "$dump_ld_config: program interpreter $interp not found"

rtld_verify_elf "$file" &&
	trace_elf "$file"

Fatal "$file: failed to find the program interpreter"
