#!/bin/sh -ef
#
# verify-elf - verify ELF objects.
#
# Copyright (C) 2002, 2003, 2004, 2006  Dmitry V. Levin <ldv@altlinux.org>
#
# This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#

. /usr/lib/rpm/functions
ValidateBuildRoot

: ${RPM_VERIFY_ELF_SKIPLIST:=}

elf_ldd='/usr/lib/rpm/ldd'

lookup_path()
{
	local d dir path found=
	dir="$1" && shift
	path="$1" && shift
	for d in $(printf %s "$path" |tr : ' '); do
		[ "$d" = "$dir" ] || continue
		found="$d"
		break
	done
	[ -n "$found" ] && return 0 || return 1
}

rc=0
for f in "$@"; do
	if [ ! -f "$f" ]; then
		Info "ERROR: $f: file unavailable"
		rc=1
		continue
	fi

	fname="${f#$RPM_BUILD_ROOT}"
	fname="${fname#.}"

	if [ -n "$RPM_VERIFY_ELF_SKIPLIST" ]; then
		for skip in $RPM_VERIFY_ELF_SKIPLIST; do
			if [ -z "${fname##$skip}" ]; then
				continue 2
			fi
		done
	fi

	t="$(file -b "$f")"
	[ -z "${t##ELF *}" -o -z "${t##* ELF *}" ] || continue

	objdump_info=$(objdump -p "$f") || continue

	if [ -n "$VERIFY_ELF_ARCH" -a "$RPM_TARGET_ARCH" = noarch ]; then
		[ "$VERIFY_ELF_ARCH" = relaxed ] && prefix=WARNING || prefix=ERROR
		[ "$VERIFY_ELF_ARCH" = relaxed ] || rc=1
		Info "$prefix: $f: ELF object for \"$RPM_TARGET_ARCH\" architecture"
	fi

	if [ -n "$VERIFY_ELF_FHS" ] && [ -z "${fname##/usr/share/*}" -o -z "${fname##/etc/*}" ]; then
		[ "$VERIFY_ELF_FHS" = relaxed ] && prefix=WARNING || prefix=ERROR
		[ "$VERIFY_ELF_FHS" = relaxed ] || rc=1
		Info "$prefix: $f: ELF object out of allowed directory tree"
	fi

	if [ -n "$VERIFY_ELF_RPATH" ]; then
		rpath="$(printf %s "$objdump_info" |awk '{if ($1=="RPATH") print $2}')"
		if [ -n "$rpath" ]; then
			prefix=
			if [ -z "${rpath##:*}" ]; then
				Info "ERROR: $f: RPATH starts with \":\": $rpath"
				rc=1
			fi
			if [ -z "${rpath%%*:}" ]; then
				Info "ERROR: $f: RPATH ends with \":\": $rpath"
				rc=1
			fi
			if [ -z "${rpath##*::*}" ]; then
				Info "ERROR: $f: RPATH contains \"::\": $rpath"
				rc=1
			fi
			if [ rc = 0 ] && printf %s "$rpath" |fgrep -qs :; then
				[ "$VERIFY_ELF_RPATH" = relaxed ] && prefix=WARNING || prefix=ERROR
				[ "$VERIFY_ELF_RPATH" = relaxed ] || rc=1
				Info "$prefix: $f: RPATH entry contains \":\": $rpath"
			fi

			for p in $RPM_BUILD_ROOT $RPM_BUILD_DIR $RPM_SOURCE_DIR; do
				if printf %s "$rpath" |fgrep -qs "$p"; then
					Info "ERROR: $f: RPATH entry contains \"$p\": $rpath"
					rc=1
				fi
			done

			if [ -z "$prefix" -a rc = 0 ]; then
				[ "$VERIFY_ELF_RPATH" = strict ] && prefix=ERROR || prefix=WARNING
				[ "$VERIFY_ELF_RPATH" = strict ] && rc=1 ||:
				Info "$prefix: $f: RPATH entry found: $rpath"
			fi
		fi
	fi
	
	if [ -n "$VERIFY_ELF_STACK" ]; then
		if [ -z "${t##*ELF* executable*}" -o -z "${t##*ELF* shared object*}" ]; then
			stack="$(printf %s "$objdump_info" |sed -ne 's/^[[:space:]]*STACK[[:space:]]\+\([^[:space:]]\+\).*/\1/p')"
			if [ -z "$stack" ]; then
				[ "$VERIFY_ELF_STACK" = strict ] && prefix=ERROR || prefix=WARNING
				[ "$VERIFY_ELF_STACK" = strict ] && rc=1 ||:
				Info "$prefix: $f: STACK entry not found"
			elif [ "$stack" = on ]; then
				[ "$VERIFY_ELF_STACK" = strict ] && prefix=ERROR || prefix=WARNING
				[ "$VERIFY_ELF_STACK" = strict ] && rc=1 ||:
				stack="$(printf %s "$objdump_info" |sed -ne 's/^[[:space:]]*STACK[[:space:]]\+\([^[:space:]]\+.*\)/\1/p')"
				Info "$prefix: $f: found executable STACK entry: $stack"
			fi
		fi
	fi

	if [ -n "$VERIFY_ELF_TEXTREL" ]; then
		# PIE executables on ARM always has TEXTREL, do not check them.
		if [ -z "$RPM_TARGET_ARCH" -o -n "${RPM_TARGET_ARCH##arm*}" -o ! -x "$f" -o -n "${t##*ELF*shared object*}" ]; then
			textrel="$(printf %s "$objdump_info" |sed -ne 's/^[[:space:]]*TEXTREL[[:space:]]\+\([^[:space:]]\+\).*/\1/p')"
			if [ -n "$textrel" ]; then
				[ "$VERIFY_ELF_TEXTREL" = relaxed ] && prefix=WARNING || prefix=ERROR
				[ "$VERIFY_ELF_TEXTREL" = relaxed ] || rc=1
				Info "$prefix: $f: TEXTREL entry found: $textrel"
			fi
		fi
	fi

	if [ -n "$VERIFY_ELF_UNRESOLVED" ]; then
		while [ -z "${t##*ELF* executable*dynamically linked*}" -o -z "${t##*ELF* shared object*}" ]; do
			rpath="$(printf %s "$objdump_info" |awk '{if ($1=="RPATH") print $2}' |tr -s : ' ' |sed -e "s|\$ORIGIN|${fname%/*}|g")"
			if [ -n "$rpath" ]; then
				rpath="$rpath $RPM_VERIFY_ELF_LDD_RPATH"
			else
				rpath="$RPM_VERIFY_ELF_LDD_RPATH"
			fi
			rpath="$(printf %s "$rpath" |
				tr -s '[:space:]' '\n' |
				grep -v '^$' |
				LANG=C uniq |
				sed -e "s|^|$RPM_BUILD_ROOT&|" |
				tr -s '[:space:]' : |
				sed -e 's/^:\+//; s/:\+$//')"

			if ! ldd_info="$("$elf_ldd" --undefined -- "$f" "$rpath" 2>&1)"; then
				printf >&2 '%s\n' "$ldd_info"
				rc=1
				break
			fi
			case "$VERIFY_ELF_UNRESOLVED" in
				relaxed)
					ldd_rc=0
					;;
				strict)
					ldd_rc=1
					;;
				*)
					if [ -z "${t##*ELF* executable*dynamically linked*}" ] ||
					   lookup_path "${fname%/*}" "$RPM_VERIFY_ELF_LDD_RPATH"; then
						ldd_rc=1
					else
						ldd_rc=0
					fi
					;;
			esac
			printf '%s\n' "$ldd_info" |
				awk -vrc="$ldd_rc" -vprog="$PROG" -vfname="$f" -- '
BEGIN {
        if (rc == "0")
                prefix="WARNING"
        else
                prefix="ERROR"
        errors=0
}
$2 == "=>" && $3 == "not" && $4 == "found" {
        lib=$1
        printf ("%s: %s: %s: not found: %s\n", prog, prefix, fname, lib)
        errors=1
}
$1 == "undefined" && $2 == "symbol:" {
        sym=$3
        lib=$4
        sub("^[(]", "", lib)
        sub("[)]$", "", lib)
        if (lib == fname) {
                printf ("%s: %s: %s: undefined symbol: %s\n", prog, prefix, fname, sym)
                errors=1
        }
}
END {
        if (rc != "0" && errors != 0)
                exit 1
}
				' >&2 && ldd_rc=0 || ldd_rc=1
			[ "$ldd_rc" = 0 ] || rc=1
			break
		done
	fi
done

exit $rc
