#!/bin/sh
verbose=false
if [ "$1" = "-v" ]; then
    verbose=true
    shift
fi

build_plat=@build_arch@
plat=@arch@
os=@target_os@
num_errors=0

LIBUNWIND=../src/.libs/libunwind.so
LIBUNWIND_GENERIC=../src/.libs/libunwind-${plat}.so

fetch_symtab () {
    filename=$1

    if [ ! -r $filename ]; then
	return
    fi

    if $verbose; then
	echo "Checking $filename..."
    fi

    #
    # Unfortunately, "nm --defined" is a GNU-extension.  For portability,
    # build the list of defined symbols by hand.
    #
    symtab=`nm -g $filename`
    saved_IFS="$IFS"
    IFS=""
    undef=`nm -g -u $filename`
    for line in $undef; do
	symtab=`echo "$symtab" | grep -v "^${line}"\$`
    done;
    IFS="$saved_IFS"
}

ignore () {
    sym=$1
    symtab=`echo "$symtab" | grep -v " ${sym}\$"`
}

match () {
    sym=$1
    if `echo "$symtab" | grep -q " ${sym}\$"`; then
	symtab=`echo "$symtab" | grep -v " ${sym}\$"`
    else
	echo "  ERROR: Symbol \"$sym\" missing."
	num_errors=`expr $num_errors + 1`
    fi
}

#
# Filter out miscellaneous symbols that get defined by the
# linker for each shared object.
#
filter_misc () {
    ignore _DYNAMIC
    ignore _GLOBAL_OFFSET_TABLE_
    ignore __bss_start
    ignore _edata
    ignore _end
    ignore _Uelf32_get_proc_name
    ignore _Uelf32_valid_object
    ignore _Uelf64_get_proc_name
    ignore _Uelf64_valid_object
    ignore _U.*debug_level
    ignore ICRT.INTERNAL	# ICC 8.x defines this

    # Ignore symbols generated by the ARM Linux default linker script.
    # For details see the binutils sources (src/ld/emulparams/armelf_linux.sh).
    if [ ${plat} = "arm" ]; then
	ignore __bss_start__
	ignore __bss_end__
	ignore __end__
	ignore _bss_end__
    fi

    if [ ${plat} = "mips" ]; then
	ignore _fbss
	ignore _fdata
	ignore _ftext
	ignore _gp
    fi
}

check_local_unw_abi () {
    match _UL${plat}_create_addr_space
    match _UL${plat}_destroy_addr_space
    match _UL${plat}_get_fpreg
    match _UL${plat}_get_proc_info
    match _UL${plat}_get_proc_info_by_ip
    match _UL${plat}_get_proc_name
    match _UL${plat}_get_reg
    match _UL${plat}_get_save_loc
    match _UL${plat}_init_local
    match _UL${plat}_init_remote
    match _UL${plat}_is_signal_frame
    match _UL${plat}_handle_signal_frame
    match _UL${plat}_local_addr_space
    match _UL${plat}_resume
    match _UL${plat}_set_caching_policy
    match _UL${plat}_set_reg
    match _UL${plat}_set_fpreg
    match _UL${plat}_step

    match _U${plat}_flush_cache
    match _U${plat}_get_accessors
    match _U${plat}_getcontext
    match _U${plat}_regname
    match _U${plat}_strerror

    match _U_dyn_cancel
    match _U_dyn_info_list_addr
    match _U_dyn_register

    match unw_backtrace
    match backtrace

    case ${plat} in
	arm)
	    match _U${plat}_get_elf_image
	    match _U${plat}_is_fpreg
	    match _UL${plat}_search_unwind_table
	    match _UL${plat}_dwarf_search_unwind_table
	    match _UL${plat}_dwarf_find_unwind_table
	    ;;
	hppa)
	    match _UL${plat}_dwarf_search_unwind_table
	    match _UL${plat}_dwarf_find_unwind_table
	    match _U${plat}_get_elf_image
	    match _U${plat}_setcontext
	    ;;
	ia64)
	    match _UL${plat}_search_unwind_table
	    match _U${plat}_get_elf_image
	    ;;
	x86)
	    match _U${plat}_get_elf_image
	    match _U${plat}_is_fpreg
	    match _UL${plat}_dwarf_search_unwind_table
	    match _UL${plat}_dwarf_find_unwind_table
	    ;;
	x86_64)
	    match _U${plat}_get_elf_image
	    match _U${plat}_is_fpreg
	    match _UL${plat}_dwarf_search_unwind_table
	    match _UL${plat}_dwarf_find_unwind_table
	    match _U${plat}_setcontext
	    ;;
	ppc*)
	    match _U${plat}_get_func_addr
	    match _U${plat}_get_elf_image
	    match _U${plat}_is_fpreg
	    match _UL${plat}_dwarf_search_unwind_table
	    match _UL${plat}_dwarf_find_unwind_table
	    ;;
	*)
	    match _U${plat}_is_fpreg
	    match _UL${plat}_dwarf_search_unwind_table
	    match _UL${plat}_dwarf_find_unwind_table
	    ;;
    esac

    if [ x@enable_debug_frame@ = xyes ]; then
	match _UL${plat}_dwarf_find_debug_frame
    fi

}

check_generic_unw_abi () {
    match _U${plat}_create_addr_space
    match _U${plat}_destroy_addr_space
    match _U${plat}_flush_cache
    match _U${plat}_get_accessors
    match _U${plat}_get_fpreg
    match _U${plat}_get_proc_info
    match _U${plat}_get_proc_info_by_ip
    match _U${plat}_get_proc_name
    match _U${plat}_get_reg
    match _U${plat}_get_save_loc
    match _U${plat}_init_local
    match _U${plat}_init_remote
    match _U${plat}_is_signal_frame
    match _U${plat}_handle_signal_frame
    match _U${plat}_local_addr_space
    match _U${plat}_regname
    match _U${plat}_resume
    match _U${plat}_set_caching_policy
    match _U${plat}_set_fpreg
    match _U${plat}_set_reg
    match _U${plat}_step
    match _U${plat}_strerror

    case ${plat} in
	arm)
	    match _U${plat}_is_fpreg
	    match _U${plat}_get_elf_image
	    match _U${plat}_search_unwind_table
	    match _U${plat}_dwarf_search_unwind_table
	    match _U${plat}_dwarf_find_unwind_table
	    ;;
	hppa)
	    match _U${plat}_dwarf_search_unwind_table
	    match _U${plat}_dwarf_find_unwind_table
	    match _U${plat}_get_elf_image
	    ;;
	ia64)
	    match _U${plat}_search_unwind_table
	    match _U${plat}_find_dyn_list
	    if [ $plat = $build_plat ]; then
		match _U${plat}_get_elf_image
		case $os in
		    linux*)
			match _U${plat}_get_kernel_table
			;;
		esac
	    fi
	    ;;
	x86)
	    match _U${plat}_get_elf_image
	    match _U${plat}_is_fpreg
	    match _U${plat}_dwarf_search_unwind_table
	    match _U${plat}_dwarf_find_unwind_table
	    ;;
	x86_64)
	    match _U${plat}_get_elf_image
	    match _U${plat}_is_fpreg
	    match _U${plat}_dwarf_search_unwind_table
	    match _U${plat}_dwarf_find_unwind_table
	    ;;
	ppc*)
	    match _U${plat}_get_elf_image
	    match _U${plat}_get_func_addr
	    match _U${plat}_is_fpreg
	    match _U${plat}_dwarf_search_unwind_table
	    match _U${plat}_dwarf_find_unwind_table
	    ;;
	*)
	    match _U${plat}_is_fpreg
	    match _U${plat}_dwarf_search_unwind_table
	    match _U${plat}_dwarf_find_unwind_table
	    ;;
    esac

    if [ x@enable_debug_frame@ = xyes ]; then
	match _U${plat}_dwarf_find_debug_frame
    fi
}

check_cxx_abi () {
    match _Unwind_Backtrace
    match _Unwind_DeleteException
    match _Unwind_FindEnclosingFunction
    match _Unwind_ForcedUnwind
    match _Unwind_GetBSP
    match _Unwind_GetCFA
    match _Unwind_GetDataRelBase
    match _Unwind_GetGR
    match _Unwind_GetIP
    match _Unwind_GetIPInfo
    match _Unwind_GetLanguageSpecificData
    match _Unwind_GetRegionStart
    match _Unwind_GetTextRelBase
    match _Unwind_RaiseException
    match _Unwind_Resume
    match _Unwind_Resume_or_Rethrow
    match _Unwind_SetGR
    match _Unwind_SetIP
    match __libunwind_Unwind_Backtrace
    match __libunwind_Unwind_DeleteException
    match __libunwind_Unwind_FindEnclosingFunction
    match __libunwind_Unwind_ForcedUnwind
    match __libunwind_Unwind_GetBSP
    match __libunwind_Unwind_GetCFA
    match __libunwind_Unwind_GetDataRelBase
    match __libunwind_Unwind_GetGR
    match __libunwind_Unwind_GetIP
    match __libunwind_Unwind_GetIPInfo
    match __libunwind_Unwind_GetLanguageSpecificData
    match __libunwind_Unwind_GetRegionStart
    match __libunwind_Unwind_GetTextRelBase
    match __libunwind_Unwind_RaiseException
    match __libunwind_Unwind_Resume
    match __libunwind_Unwind_Resume_or_Rethrow
    match __libunwind_Unwind_SetGR
    match __libunwind_Unwind_SetIP
    case $os in
	linux*)
	    # needed only for Intel 8.0 bug-compatibility
	    match _ReadSLEB
	    match _ReadULEB
	    ;;
    esac
}

check_empty () {
    if [ -n "$symtab" ]; then
	printf "  ERROR: Extraneous symbols:\n$symtab\n"
	num_errors=`expr $num_errors + 1`
    fi
}

if [ $plat = $build_plat ]; then
    fetch_symtab $LIBUNWIND
    filter_misc
    check_local_unw_abi
    if [ x@enable_cxx_exceptions@ = xyes ]; then
      check_cxx_abi
    fi
    check_empty
fi

fetch_symtab $LIBUNWIND_GENERIC
filter_misc
check_generic_unw_abi
check_empty

if [ $num_errors -gt 0 ]; then
    echo "FAILURE: Detected $num_errors errors"
    exit 1
fi

if $verbose; then
    echo "  SUCCESS: all checks passed"
fi
exit 0
