#!/bin/sh
#
# This file is part of libunwind.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
verbose=false
if [ "$1" = "-v" ]; then
    verbose=true
    shift
fi

build_plat=x86_64
plat=x86_64
os=linux-musl
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).
    case "$plat}" in
        arm*|aarch64*)
            ignore __bss_start__
            ignore __bss_end__
            ignore __end__
            ignore _bss_end__
            ;;
        mips*)
            ignore _fbss
            ignore _fdata
            ignore _ftext
            ignore _gp
            ;;
        loongarch64)
            ignore _fbss
            ignore _fdata
            ignore _ftext
            ignore _gp
            ;;
    esac

    case "${os}" in
        solaris2.11)
            ignore _PROCEDURE_LINKAGE_TABLE_
            ignore _etext
            ;;
        nto*)
            ignore _btext
            ;;
    esac
}

check_local_unw_abi () {
    match _UL${plat}_apply_reg_state
    match _UL${plat}_reg_states_iterate
    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_info_in_range
    match _UL${plat}_get_proc_name
    match _UL${plat}_get_proc_name_by_ip
    match _UL${plat}_get_elf_filename
    match _UL${plat}_get_elf_filename_by_ip
    match _UL${plat}_get_reg
    match _UL${plat}_get_save_loc
    match _UL${plat}_init_local
    match _UL${plat}_init_local2
    match _UL${plat}_init_remote
    match _UL${plat}_is_plt_entry
    match _UL${plat}_is_signal_frame
    match _UL${plat}_local_addr_space
    match _UL${plat}_resume
    match _UL${plat}_set_iterate_phdr_function
    match _UL${plat}_set_caching_policy
    match _UL${plat}_set_cache_size
    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}_get_elf_image
    match _U${plat}_get_exe_image_path
    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
    match unw_backtrace2

    case ${plat} in
	arm)
	    match _U${plat}_getcontext
	    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 _U${plat}_getcontext
	    match _UL${plat}_dwarf_search_unwind_table
	    match _UL${plat}_dwarf_find_unwind_table
	    match _U${plat}_setcontext
	    ;;
	ia64)
	    match _U${plat}_getcontext
	    match _UL${plat}_search_unwind_table
	    ;;
	x86)
	    match _U${plat}_getcontext
	    match _U${plat}_is_fpreg
	    match _UL${plat}_dwarf_search_unwind_table
	    match _UL${plat}_dwarf_find_unwind_table
	    ;;
	x86_64)
	    match _U${plat}_getcontext
	    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}_getcontext
	    match _U${plat}_get_func_addr
	    match _U${plat}_is_fpreg
	    match _UL${plat}_dwarf_search_unwind_table
	    match _UL${plat}_dwarf_find_unwind_table
	    ;;
	s390x)
	    match _U${plat}_getcontext
	    match _U${plat}_is_fpreg
	    match _UL${plat}_dwarf_search_unwind_table
	    match _UL${plat}_dwarf_find_unwind_table
	    match _U${plat}_setcontext
	    ;;
	riscv)
	    match _U${plat}_getcontext
	    match _U${plat}_is_fpreg
	    match _UL${plat}_dwarf_search_unwind_table
	    match _UL${plat}_dwarf_find_unwind_table
	    match _U${plat}_setcontext
	    ;;
	loongarch64)
	    match _U${plat}_getcontext
	    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 [ xno = xyes ]; then
	match _UL${plat}_dwarf_find_debug_frame
    fi

}

check_generic_unw_abi () {
    match _U${plat}_apply_reg_state
    match _U${plat}_reg_states_iterate
    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_info_in_range
    match _U${plat}_get_proc_name
    match _U${plat}_get_proc_name_by_ip
    match _U${plat}_get_elf_filename
    match _U${plat}_get_elf_filename_by_ip
    match _U${plat}_get_reg
    match _U${plat}_get_save_loc
    match _U${plat}_init_local
    match _U${plat}_init_local2
    match _U${plat}_init_remote
    match _U${plat}_is_plt_entry
    match _U${plat}_is_signal_frame
    match _U${plat}_local_addr_space
    match _U${plat}_regname
    match _U${plat}_resume
    match _U${plat}_set_iterate_phdr_function
    match _U${plat}_set_caching_policy
    match _U${plat}_set_cache_size
    match _U${plat}_set_fpreg
    match _U${plat}_set_reg
    match _U${plat}_step
    match _U${plat}_strerror

    case ${plat} in
	aarch64)
	    match _U${plat}_is_fpreg
	    match _U${plat}_get_elf_image
	    match _U${plat}_get_exe_image_path
	    match _U${plat}_dwarf_search_unwind_table
	    match _U${plat}_dwarf_find_unwind_table
	    ;;
	arm)
	    match _U${plat}_is_fpreg
	    match _U${plat}_get_elf_image
	    match _U${plat}_get_exe_image_path
	    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
	    match _U${plat}_get_exe_image_path
	    ;;
	ia64)
	    match _U${plat}_search_unwind_table
	    match _U${plat}_find_dyn_list
	    if [ $plat = $build_plat ]; then
		match _U${plat}_get_elf_image
		match _U${plat}_get_exe_image_path
		case $os in
		    linux*)
			match _U${plat}_get_kernel_table
			;;
		esac
	    fi
	    ;;
	x86)
	    match _U${plat}_get_elf_image
	    match _U${plat}_get_exe_image_path
	    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}_get_exe_image_path
	    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_exe_image_path
	    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
	    ;;
	s390x)
	    match _U${plat}_is_fpreg
	    match _U${plat}_get_elf_image
	    match _U${plat}_get_exe_image_path
	    match _U${plat}_dwarf_search_unwind_table
	    match _U${plat}_dwarf_find_unwind_table
	    ;;
	riscv)
	    match _U${plat}_get_elf_image
	    match _U${plat}_get_exe_image_path
	    match _U${plat}_is_fpreg
	    match _U${plat}_dwarf_search_unwind_table
	    match _U${plat}_dwarf_find_unwind_table
	    ;;
	loongarch64)
	    match _U${plat}_get_elf_image
	    match _U${plat}_get_exe_image_path
	    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
	    match _U${plat}_get_elf_image
	    match _U${plat}_get_exe_image_path
	    ;;
    esac

    if [ xno = 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 [ xno = 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
