#!/bin/bash

# Check if a file is an ELF binary
#
function file_is_elf() {
	local file=${1}

	file "${file}" 2>/dev/null | grep -q "ELF"
}

# Check if a file is a script.
#   If the first line starts with #! this is sufficient.
#
function file_is_script() {
	local file=${1}

	file ${file} 2>/dev/null | egrep -q ":.* (commands|script)"
}

# Check if file is an 64-bit binary.
function file_is_64bit() {
	local file=${1}

	file -L ${file} 2>/dev/null | grep -q "ELF 64-bit"
}

# Get the interpreter of a file.
#
function file_get_interpreter() {
	local file=${1}

	if file_is_elf ${file}; then
		file_get_elf_interpreter ${file}
	elif file_is_script ${file}; then
		file_get_script_interpreter ${file}
	fi
}

# Hidden function that gets the interpreter from an ELF file.
#
function file_get_elf_interpreter() {
	local file=${1}

	local interp=$(readelf -l ${file} 2>/dev/null | \
		grep "program interpreter" | tr -d "]" | awk '{ print $NF }')

	# Only return real file names. Debugging files do not
	# have those starting with a /.
	if [ "${interp:0:1}" = "/" ]; then
		echo "${interp}"
		return
	fi
}

# Hidden fucntion that gets the interpreter from a script file.
#
function file_get_script_interpreter() {
	local file=${1}

	# If the file is not executeable, no interpreter will be needed
	[ -x "${file}" ] || return

	local first_line=$(head -n1 ${file})

	# Return nothing if no shebang was found.
	[ "${first_line:0:2}" = "#!" ] || return

	first_line="${first_line:2:${#first_line}}"

	# Choose the first argument and strip any parameters if available
	local interpreter
	for interpreter in ${first_line}; do
		# Skip interpreters that do that have an absolute path.
		[ "${interpreter:0:1}" = "/" ] || break

		echo "${interpreter}"
		return
	done
}

# Check if a file is statically linked.
#
function file_is_static() {
	local file=${1}

	file ${file} 2>/dev/null | grep -q "statically linked"
}

# Get NEEDED from a file.
#
function file_get_needed() {
	local file=${1}

	local out=$(readelf -d ${file} 2>/dev/null | grep NEEDED | \
		tr -d "[]" | awk '{ print $NF }')

	echo "${out}$(file_is_64bit ${file})"
}

# Get RPATH from a file.
#
function file_get_rpath() {
	local file=${1}

	readelf -d ${file} 2>/dev/null | grep RPATH | \
		tr -d "[]" | awk '{ print $NF }'
}

# Get SONAME from a file.
#
function file_get_soname() {
	local file=${1}

	objdump -p ${file} 2>/dev/null | awk '/SONAME/ { print $2 }'
}

# Check if a file is a shared object.
#
function file_is_shared_object() {
	local file=${1}

	file ${file} 2>/dev/null | grep -q "shared object"
}

# Check if a file has the canary.
#
function file_has_canary() {
	local file=${1}

	readelf -s ${file} 2>/dev/null | grep -q "__stack_chk_fail"
}

# Check if a file has an executeable stack.
#
function file_has_execstack() {
	local file=${1}

	readelf -h ${file} 2>/dev/null | grep -qE "Type:[[:space:]]*EXEC"
}

# Check if a file has NX.
#
function file_has_nx() {
	local file=${1}

	readelf -l ${file} 2>/dev/null | grep "GNU_STACK" | grep -q "RWE"
	[ $? != 0 ]
}

# Check if a file is partly RELRO.
#
function file_is_relro_partly() {
	local file=${1}

	readelf -l ${file} 2>/dev/null | grep -q "GNU_RELRO"
}

# Check if a file is fully RELRO.
#
function file_is_relro_full() {
	local file=${1}

	if file_is_relro_partly ${file}; then
		readelf -d ${file} 2>/dev/null | grep -q "BIND_NOW"
		return $?
	fi
	return 1
}

# Find all ELF files.
#
function find_elf_files() {
	local dir
	local dirs
	local prefix

	while [ $# -gt 0 ]; do
		case "${1}" in
			--prefix=*)
				prefix="${1#--prefix=}/"
				;;
			*)
				dirs="${dirs} ${1}"
				;;
		esac
		shift
	done

	local file
	local files

	for dir in ${dirs}; do
		dir="${prefix}${dir}"
		for file in $(find ${dir} -type f -not -name "*.ko" 2>/dev/null); do
			case "${file}" in
				# Skip kernel modules.
				*/lib/modules/*)
					continue
					;;
			esac

			if file_is_elf ${file} && ! file_is_static ${file}; then
				files="${files} ${file}"
			fi
		done
	done

	echo ${files}
}

function filter_startfiles() {
	local file=${1}

	grep -qE "crt[1in]\.o$" <<<${file}
}
