#!/bin/bash

# shlib-collision-watchdog -- break the build if two versions of a
# library accidentally get linked into the same address space

# Note: ld already warns about this, but not in a way that can be used
# to break the build thus rendering it rather useless:
# /usr/bin/ld: warning: libbctoolbox.so.1, needed by /lib/x86_64-linux-gnu/libbcmatroska2.so.5, may conflict with libbctoolbox.so.2

set -e

executable="$1"

lddoutput="$(mktemp)"
trap "rm -f $lddoutput" EXIT INT QUIT TERM

# $libs: will hold sorted lines like
#libFLAC.so.12
libs="$(mktemp)"
trap "rm -f $lddoutput $libs" EXIT INT QUIT TERM

# $withdupes: will hold sorted (and possibly duplicate) lines like
#libFLAC
withdupes="$(mktemp)"
trap "rm -f $lddoutput $libs $withdupes" EXIT INT QUIT TERM

# $dupefree: will hold sorted and duplicate-free lines like
#libFLAC
dupefree="$(mktemp)"
trap "rm -f $lddoutput $libs $withdupes $dupefree" EXIT INT QUIT TERM

ldd "$executable" | grep ' => ' > "$lddoutput"
sed 's@ => .*$@@' "$lddoutput" |tr -d '\t '|sort > "$libs"
sed 's@\.so\..*$@@' "$libs" > "$withdupes"
uniq < "$withdupes" > "$dupefree"

rc=0
if ! cmp --quiet "$withdupes" "$dupefree" ; then
    echo "Error: more than one version of a library is being loaded into ${executable}:"

    declare -A soname2dtneeders
    declare -A soname2path
    linecount=0
    while read soname path ; do
        let linecount=$((linecount+1))
        if [ -z "$soname" -o -z "$path" ]; then
            break;
        elif [ "$path" = "not found" ]; then
            # FIXME: handle unresolvable sonames more gracefully
            continue;
        fi
        soname2path["$soname"]="$path"
        dtneededs="$(readelf -W -d "$path" |grep '(NEEDED)' | cut -d '[' -f2|tr -d ']' | tr '\n' ' ')"
        dtneededs="${dtneededs% }"
        for dtneeded in $dtneededs; do
            if [ "${#soname2dtneeders[$dtneeded]}" -eq 0 ]; then
                soname2dtneeders["$dtneeded"]="$soname"
            else
                soname2dtneeders["$dtneeded"]+=" $soname"
            fi
        done
    done < <(sed 's@^[[:space:]]*@@;s@ => @ @;s@ (.\+)$@@' "$lddoutput")

    for f in $(diff -U "$linecount" "$withdupes" "$dupefree" | sed -n '3,$p'| sed -n '/^-/s@@@p' ); do
        collisions="$(grep -e "$f" "$libs")"
        for soname in $collisions; do
            echo -n "$soname (via"
            for s in ${soname2dtneeders[$soname]}; do
                echo -n " ${soname2path[$s]}"
            done
            echo ")"
        done
    done
    rc=1
fi

exit $rc
