#!/bin/sh
#
# Script to "portably" generate /proc/PID/maps-like data for region_discover().
# region_discover() just needs "StartPage-EndPage ...x... absolute path"
# so this is all we really need to imitate.
#
# NOTE: This script assumes that 'nm' on dynamic objects reports zero-offset
#       addresses.  This seems to be the case everywhere that I've checked.
#
# gen-maps may be invoked in an infinite recursion if PCT and LD_PRELOAD are
# inherited.  Not sure if execlp () will elide passing the environment though.
# We really do need the LD_PRELOAD (without PCT being set) to be in effect for
# accurate 'ldd' data.

exec 2>/dev/null

if [ $# -lt 1 ]; then
    echo "$0 <executable_file> produces a Linux /proc/PID/maps like report"
    exit 1
fi

if [ ${2:-$1} -nt $1 ]; then
    exit 0
fi

absolute () {
    dir=`dirname $1`
    echo `(cd $dir; pwd)`/`basename $1`
}

exe=`absolute $1`

# This ldd script may need tweaking.  Basically we want a list of pairs:
#   "hex address where text segment gets mapped to" "absolute path to .so"
#
# ldd -f '%x %p\n' $exe > /tmp/gen-maps.$$ # Rats.  %x gives 0x prefix.
ldd $exe |
  sed -e '/:$/d' -e 's/.* => //' \
      -e 's/\([^ ]*\) *(0[xX]\([0-9a-fA-F]*\))/\2 \1/' |
  sort > /tmp/gen-maps.$$ ||
        rm -f /tmp/gen-maps.$$

if [ -f /tmp/gen-maps.$$ ]; then
    exec < /tmp/gen-maps.$$
else
    exec < /dev/null
fi

if [ $# -gt 1 ]; then
    mkdir -p `dirname $2`
    exec > $2
fi

# Helper shell function that computes the ceiling page of the text segment
# for executable/shared object $1 when it is mapped at base address $2
#
end_page () {
    (echo 16o
     size $1 | tail -1 | awk '{print $1}'       # decimal text segment size
     echo 16i
     echo FFF                                   # round size up to nearest
     echo +p
     echo 1000                                  # page boundary...
     echo /p
     echo 1000                                  # NOTE: systems with bigger
     echo '*p'                                  # page sizes adjust "1000"
     echo $2 | tr 'a-f' 'A-F'                   # add size to base address
     echo +p) |
           dc |
           tail -1                              # extract answer
}

# Map for exe file itself from 'size'...  The second argument to end_page() is
# system specific, though you can generally infer it from 'nm -n' on unstripped
# executable files.  Grumble.   dc is not very output-tweaking friendly.  This
# does not matter at all for str2num () in libitimer, but it looks ugly. :-(
#
case `uname` in
    OpenBSD)
        end=`end_page $exe 00001000`              # OpenBSD: map at 1000
        echo "00000000-0000${end}" " r-xp " $exe  # OpenBSD: but count from 0
        ;;
    FreeBSD)
#       end=`end_page $exe 08048000`
#       echo "08048000-0${end}" " r-xp " $exe
        end=`end_page $exe 0400000`
        echo "0400000-0${end}" " r-xp " $exe
        ;;
    Linux)
        echo 2>&1 Error --- gen-maps not needed on Linux
        ;;
        *)
    echo 2>&1 Error --- unknown executable type
esac
# Re-read our temp file
while read addr obj
do
        obj=`absolute $obj`             # Is this really necessary?
        end=`end_page $obj $addr`
        echo "${addr}-${end}" " r-xp " $obj
done

rm -f /tmp/gen-maps.$$
