#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# bos720 src/bos/usr/ccs/bin/rtl_enable/POWER/rtl_enable.sh 1.10 
#  
# Licensed Materials - Property of IBM 
#  
# Restricted Materials of IBM 
#  
# COPYRIGHT International Business Machines Corp. 1996,2007 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)93        1.10  src/bos/usr/ccs/bin/rtl_enable/POWER/rtl_enable.sh, cmdld, bos720 3/29/07 04:11:32
#
#   COMPONENT_NAME: cmdld
#
#   FUNCTIONS: none
#
#   ORIGINS: 27
#

#
# Shell script to relink executables or shared objects with runtime linking
# enabled.
#

command_name=$0
LD=${LD:-/bin/ld}
TMPDIR=${TMPDIR:-/tmp}
WRKDIR=

AR=/usr/bin/ar
AWK=/usr/bin/awk
BASENAME=/usr/bin/basename
CHMOD=/usr/bin/chmod
CP=/usr/bin/cp
DIRNAME=/usr/bin/dirname
DUMP=/usr/sbin/dump
FILE=/usr/bin/file
GREP=/usr/bin/grep
HEAD=/usr/bin/head
MKDIR=/usr/bin/mkdir
MV=/usr/bin/mv
OD=/usr/bin/od
PRINTF=/usr/bin/printf
RM=/usr/bin/rm
TOUCH=/usr/bin/touch

NLSSET=2
rc=0
OBJ_FLAG=
lflag=
oflag=
sflag=
Rflag=
curdir=$(/bin/pwd)
alias showmsg='/usr/bin/dspmsg -s $NLSSET ld.cat'

#  Usage:
usage()
{
	showmsg 1 \
'Usage: %s [-X{32|64|32_64}] [-R|-o ofile] [-ls] ifile \
\t[ ld-opts ... ] [ -F objs-libs ... ]\n' \
	$command_name
	exit 1
}

check_write()
{
	if $TOUCH $1 2>/dev/null
	then	return 1
	else	showmsg 10 '%s: Cannot write to file %s\n' $command_name $1
		return 0
	fi
}

# Process options
while getopts ':lo:sRX:' opt
do	case $opt in
	l)	lflag=1
		;;
	o)	oflag=1
		ofile="$OPTARG"
		if [ "$Rflag" -eq 1 ]
		then	usage $command_name
		fi
		;;
	s)	sflag=1; lflag=1;	# -s implies -l
		;;
	R)	Rflag=1
		if [ "$oflag" -eq 1 ]
		then	usage $command_name
		fi
		;;
	X)
		if [ "$OBJ_FLAG" = "32" -o "$OBJ_FLAG" = "32_64" -a "$OPTARG" = "64" ]
		then
			OBJ_FLAG=32_64
		elif [ "$OBJ_FLAG" = "64" -o "$OBJ_FLAG" = "32_64" \
			 -a "$OPTARG" = "32" ]
		     then
			OBJ_FLAG=32_64
		else
		     OBJ_FLAG=$OPTARG
		fi
		;;
	':')	showmsg 2 '%s: Missing parameter for option %s\n' \
			$command_name $1 $OPTARG
		usage $command_name
		;;
	'?')	showmsg 3 '%s: Unknown option %s\n' $command_name '-'$OPTARG
		usage $command_name
		;;
	esac
done
shift $(($OPTIND - 1))


if [ $# = 0 ]
then	showmsg 4 '%s: Missing input file\n' $command_name
	usage $command_name
fi

#Verify the specified OBJECT mode
if [ "$OBJ_FLAG" != "" -a "$OBJ_FLAG" != "32"  \
     -a "$OBJ_FLAG" != "64" -a "$OBJ_FLAG" != "32_64" ]
then
    showmsg 13 "%s: The specified object mode is not valid.\n\
\tSpecify -X32, -X64, or -X32_64.\n" $command_name
    exit 1
fi

#If no -X given, look for OBJECT_MODE from environment
if [ "$OBJ_FLAG" = "" ]
then
    OBJ_FLAG=$OBJECT_MODE
    if [ "$OBJ_FLAG" != "" -a "$OBJ_FLAG" != "32"  \
         -a "$OBJ_FLAG" != "64" -a "$OBJ_FLAG" != "32_64" ]
    then
            showmsg 14 "%s: The OBJECT_MODE environment variable has an \
invalid setting.\n\tOBJECT_MODE must be 32, 64, or 32_64.\n" $command_name
        exit 1
    else if [ "$OBJ_FLAG" = "" ]
         then
		OBJ_FLAG=32	#default to 32-bit
         fi
    fi
fi

PLAIN_IFILE=$1
shift

if ! $HEAD -c 1 $PLAIN_IFILE  > /dev/null 2>/dev/null
then	showmsg 5 '%s: Cannot open or find file: %s\n' \
		$command_name $PLAIN_IFILE
	exit 1
fi

# Collect other arguments.  User-specified ld options are taken from
# command line up to -F flag.  Arguments after -F flag are additional
# object files and libraries.
user_ldopts=
other_args=
Fflag=
while [ $# -gt 0 ]
do	if [ $1 = -F ]
	then	Fflag=1
		shift
		other_args="$*"
		break
	else	user_ldopts="$user_ldopts $1"
	fi
	shift
done

ifile_dir=$(cd $($DIRNAME $PLAIN_IFILE) > /dev/null; /bin/pwd)
Ifile=$($BASENAME $PLAIN_IFILE)
ibase=${Ifile%.*}		# Strip off suffix, if any
IFILE=$ifile_dir/$Ifile

# Use current directory if we'll be saving files there, either because
#  -l (or -s) is used or because -R and -o are not used.
if [ ! -z "$lflag" -o -z "$Rflag" -a -z "$oflag" ]; then :
else	WRKDIR=$TMPDIR/rtl$$
	trap "cd $curdir; $RM -rf $WRKDIR" 0 1 2 13 15
	$MKDIR $WRKDIR
fi

if [ ! -z "$oflag" ]
then	if [ -z "$WRKDIR" ]
	then	OFILE=$ofile	# Write directly to output file.
	else	if ! cd $($DIRNAME $ofile) > /dev/null 2> /dev/null
		then	# if we can't cd there, we can't write there.
			showmsg 2 '%s: Cannot write to file %s\n' \
				$command_name $ofile
			exit 1
		fi
		# Get full pathname of output file
		OFILE=$(/bin/pwd)/$($BASENAME $ofile)
		cd - > /dev/null
	fi
elif [ ! -z "$Rflag" ]
then	OFILE=$IFILE
else	OFILE=$curdir/$Ifile.new	# Output file goes in current
					# directory, even if input file is
					# not in the current directory.
fi

if [ ! -z "$WRKDIR" ]; then cd $WRKDIR; fi

# Get file name for writing shell script. Make sure we can write to the file.
if [ -z "$sflag" ]
then	SFILE=/dev/null
else	SFILE=$curdir/$ibase.sh
	check_write $SFILE
	print '#!/bin/ksh' > $SFILE
	print "LD='$LD'" >> $SFILE
	$CHMOD +x $SFILE
fi

awkcmd='
BEGIN		{previmpid = -1;}
$1 == "0"	{printf("-blibpath:%s", $NF);
		 in_libpath = 1;
		 next;
		}
/^\[/		{in_libpath = 0;
		 f = tolower(substr($4, length($4)));
		 impid_cmd = "printf %d " $7
		 impid_cmd | getline impid
		 close(impid_cmd)
		 if (match(f, "[89abcdef]")) {
			if (previmpid != impid) {
				previmpid = impid;
				printf "#!" > IMPFILE;
				if (path[impid] == "/")
					printf "%s", path[impid] > IMPFILE;
				else if (path[impid])
					printf "%s/", path[impid] > IMPFILE;
				printf "%s", base[impid] > IMPFILE;
				if (member[impid])
					printf "(%s)", member[impid] > IMPFILE;
				print "" > IMPFILE;
			}
			if ($5 == "0x07")	print $NF, $2 > IMPFILE;
			else			print $NF > IMPFILE;
		 }
		 if (match(f, "[2367abef]"))	print $NF > EXPFILE;
		 if (match(f, "[4567cdef]"))	{print " -e"$NF; eseen = 1;}
		 if ((match(f, "[01]"))&&( ! match($NF, "__rtinit")))		
			print $NF " list" > EXPFILE
		}

in_libpath	{if (NF == 2) base[$1] = $2;
		 else if (NF == 4) {
			path[$1] = substr($2, 1);
			base[$1] = $3;
			member[$1] = $4;
		 }
		 else if (substr($2,1,1) == "/") {
			path[$1] = substr($2, 1);
			base[$1] = $3;
		 }
		 else {
			base[$1] = $2;
			member[$1] = $3;
		 }
		}
END		{if (eseen != 1) print " -bnoentry"}'

# Function process_rtinit.  Retrieves __rtinit structure information
# from file to allow files linked with -binitfini to be relinked
# Arguements:   None
#
# Returns:      string of -binitfini commands
function process_rtinit
{
        rtinitawk='/^\[0\].*__rtinit$/{print (substr($2, 3));}'

        datasectawk='
        BEGIN   {while (! match($4, ".data")) getline;}
                {print (substr($1,3)); print (substr ($4, 3));}'



        #get address of __rtinit structure
        rtinit_addr=$(LANG=C $DUMP -HTp -X"$OBJ_FLAG" $ifile | $AWK "$rtinitawk")
        
	#continue only if __rtinit is used
        if [[ -n $rtinit_addr ]]; then

           #get the phy_addr and RAWptr of .data section
           datasect=$(LANG=C $DUMP -hp -X"$OBJ_FLAG" $ifile | $AWK "$datasectawk")
           set $datasect
           typeset -i16 phys_addr RAW_ptr
           phys_addr=16#$1
           RAW_ptr=16#$2
           
	   #adjust rtinit_addr
           let rtinit_addr=16#$rtinit_addr-$phys_addr+$RAW_ptr
	   
	   #retrieve init/fini descriptors info
           offsets=$(LANG=C $OD -An -tx -j"$rtinit_addr" -N20 $ifile)
           set $offsets
	   typeset -i init_offset fini_offset descriptor_size
	   if [[ $OBJ_FLAG = "32" ]]; then
              init_offset=16#$2+$rtinit_addr
              fini_offset=16#$3+$rtinit_addr
              descriptor_size=16#$4
	   else	 #64-bit mode
	      init_offset=16#$3+$rtinit_addr
              fini_offset=16#$4+$rtinit_addr
              descriptor_size=16#$5
	   fi
           typeset -i index noinits nofinis
           let index=0
           let noinits=0
           let nofinis=0

           #retrieve init func names
           if [[ $init_offset > $rtinit_addr ]]; then
              name_offset=$(LANG=C $OD -An -tx -j"$init_offset" -N12 $ifile)
              set $name_offset
              typeset -i funcname
	      if [[ $OBJ_FLAG = "32" ]]; then
                 funcname=16#$2+$rtinit_addr
	      else  #64-bit
		 funcname=16#$3+$rtinit_addr
	      fi
              while [[ $funcname -ne $rtinit_addr ]]; do
                 cmdarr[$index]=$(LANG=C $OD -An -S1 -j"$funcname" $ifile | $HEAD -1)
	         noinits=$noinits+1
                 index=$index+1
                 init_offset=$init_offset+$descriptor_size
                 name_offset=$(LANG=C $OD -An -tx -j"$init_offset" -N12 $ifile)
                 set $name_offset
		 if [[ $OBJ_FLAG = "32" ]]; then
		    funcname=16#$2+$rtinit_addr
		 else  #64-bit
	            funcname=16#$3+$rtinit_addr
	         fi
              done
	   fi

           #retrieve fini func names
           if [[ $fini_offset > $rtinit_addr ]]; then
              name_offset=$(LANG=C $OD -An -tx -j"$fini_offset" -N12 $ifile)
              set $name_offset
	      if [[ $OBJ_FLAG = "32" ]]; then
                 funcname=16#$2+$rtinit_addr
              else  #64-bit
                 funcname=16#$3+$rtinit_addr
	      fi
              while [[ $funcname -ne $rtinit_addr ]]; do
                 cmdarr[$index]=$(LANG=C $OD -An -S1 -j"$funcname" $ifile | $HEAD -1)
                 nofinis=$nofinis+1
                 index=$index+1
                 fini_offset=$fini_offset+$descriptor_size
                 name_offset=$(LANG=C $OD -An -tx -j"$fini_offset" -N12 $ifile)
                 set $name_offset
		 if [[ $OBJECT_FLAG = "32" ]]; then
                    funcname=16#$2+$rtinit_addr
		 else  #64-bit
		    funcname=16#$3+$rtinit_addr
	 	 fi
              done
	   fi

           #make command
           typeset -i ctr priority
           let ctr=0
           let priority=100
           while [[ $ctr < $noinits ]]; do
              $PRINTF "%s%s%s%d%s" "-binitfini:" ${cmdarr[$ctr]} "::" $priority " "
              ctr=$ctr+1
              priority=$priority-10
           done
           nofinis=$noinits+$nofinis
           while [[ $ctr < $nofinis ]]; do
              priority=$priority+10
              $PRINTF "%s%s%s%d%s" "-binitfini::" ${cmdarr[$ctr]} ":" $priority " "
              ctr=$ctr+1
           done

        fi
}


# Function process_file.  Relink an object file.
# Arguments:	$1:	Input file
#		$2:	Basename for import and export files
#		$3:	Output file name
#		$4:	Command to print to script file for good archive member
#
# Returns:	0	Success
#		1	Input file is not an XCOFF executable
#		2	Some other error occurred
#		3	The link command failed.
function process_file
{
	ifile=$1
	imp=$2.imp
	exp=$2.exp
	ofile=$3

	# Check for an XCOFF executable.  Return 1 for non-executables.
	ftype=$(LANG=C $DUMP -X32_64 -ov $ifile 2>/dev/null | $GREP '^Flags' )
	if [[ ! $ftype = *EXEC* ]]; then	return 1; fi
	if [[ $ftype = *LOADONLY* ]]; then isLoadOnly=1; else isLoadOnly=0; fi

	#It's an executable, but is it valid in our object mode?
	mtype=$(LANG=C $DUMP -X32_64 -ov $ifile 2>/dev/null | $GREP '^Magic' )
	[[ $mtype = *32* ]] && mtype=32 || mtype=64

	if [ "$OBJ_FLAG" = "32_64" ]; then
		OBJ_FLAG=$mtype
	elif [ "$OBJ_FLAG" != "$mtype" ]; then
		return 4	# not valid in this object mode
	fi

	# Is it a regular executable or a shared object file?
	if [[ $ftype = *SHROBJ* ]]
	then	ldflags='-bM:SRE'
		imode=-bdynamic
	else	ldflags=
		imode=
	fi

	# See if we can create import/export files.
	if check_write $imp; then return 2; fi
	if check_write $exp
	then	$RM -f $imp
		return 2
	fi

	# Build the IMPORTS and EXPORTS lists and find the entry point
	# if any.  In addition, assign a value to LIBPATH_EP
	# containing the libpath information and the entrypoint name (or
	# -bnoentry if none).
	#
	# This information is extracted from the XCOFF loader section.

	LIBPATH_EP=$(LANG=C $DUMP -X$OBJ_FLAG -HTp $ifile \
		| $AWK "$awkcmd" IMPFILE=$imp EXPFILE=$exp)
	if [ $? -ne 0 ]
	then	showmsg 7 '%s: Error processing input object: %s\n' \
			$command_name "$1"
		return 2
	fi

	#retrieve any initfini commands
	initfini=$(process_rtinit)

	if [ "$sflag" -eq 1 ]
	then	if [ ! -z "$4" ]
		then print -- "$4" >> $SFILE
		fi
		print -- '$'LD \
			$other_args \
			-b$OBJ_FLAG \
			-brtl -bnortllib -bnosymbolic -bnoautoexp \
			-bstatic $1 $imode -o $ofile.'$$' \
			-bI:$imp -bE:$exp \
			$ldflags $initfini \
			$LIBPATH_EP \
			-bdynamic $user_ldopts >> $SFILE
		if [ $isLoadOnly -eq 1 ]
		then print -- strip -e $ofile.'$$' >> $SFILE
		fi
		ldrc=0
	else	if check_write $ofile.$$
		then	$RM -rf $imp $exp
			return 2
		fi
		$LD	 $other_args \
			-b$OBJ_FLAG \
			-brtl -bnortllib -bnosymbolic -bnoautoexp \
			-bstatic $1 $imode -o $ofile.$$ \
			-bI:$imp -bE:$exp \
			$ldflags $initfini \
			$LIBPATH_EP \
			-bdynamic $user_ldopts
		ldrc=$?
		if [ $ldrc -ne 0 ]; then ldrc=3; fi
		if [ $isLoadOnly -eq 1 ]; then strip -e $ofile.$$; fi

		# Clean up import and export file.
		if [ -z "$lflag" ]; then $RM -f $imp $exp; fi
	fi
	return $ldrc
}

# Function process_archive.  Extract each member from the archive and pass
# 	it to process_file for processing.  Replace member in new archive
#	if it was enabled.
#
function process_archive
{
	integer bad=0
	integer good=0
	for f in $($AR -X$OBJ_FLAG -t $IFILE)
	do	$AR -X$OBJ_FLAG -x $IFILE $f
		$CHMOD +w $f
		fbase=${f%.*}		# Strip off suffix, if any
		process_file $f $fbase $f "$AR -X$OBJ_FLAG -x $IFILE $f"
		ldrc=$?
		case $ldrc in
		0)	((good += 1))
			if [ -z "$sflag" ]
			then	$MV -f $f.$$ $f
				$AR -X$OBJ_FLAG -ro $OFILE.$$ $f
			else	print -- $MV -f $f.'$$' $f >> $SFILE
				print -- $AR -X$OBJ_FLAG -ro $OFILE.'$$' $f >> $SFILE
			fi
			print -- $RM -f $f >> $SFILE
			;;
		2)	((bad += 1))
			;;
		3)	((bad += 1))
			showmsg 8 \
			   '%s: Link command failed for archive member: %s\n' \
				$command_name $f
			;;
		esac
		$RM -f $f $f.$$
	done
	if [ $good -eq 0 -o $bad -gt 0 ]
	then	if [ $bad -eq 0 ]
		then	showmsg 11 "%s: Warning: Archive file %s\n\
\tNo archive members are executables or shared objects.\n" \
				$command_name $PLAIN_IFILE
		fi
		rc=1
	fi
}


# Check if the specified file is an archive.
ftype=$(LANG=C $FILE $(/bin/ls -l $IFILE|$AWK '{print $NF}') | $AWK '{print $2}')

if [[ "$ftype" = archive ]]
then
	if [ -z "$sflag" ]
	then	$CP $IFILE $OFILE.$$
		$CHMOD +w $OFILE.$$	# Make sure we can write to the file.
	else	print -- $CP $IFILE $OFILE.'$$' >> $SFILE
		print -- $CHMOD +w $OFILE.'$$' >> $SFILE
	fi
	if [ "$OBJ_FLAG" != "32_64" ]; then
		process_archive
	else
		OBJ_FLAG=32 process_archive
		OBJ_FLAG=64 process_archive
	fi
else
	process_file $IFILE $ibase $OFILE
	ldrc=$?
	case $ldrc in
	1)	showmsg 6 \
		 '%s: Input file is not an executable or shared object: %s\n' \
			$command_name "$PLAIN_IFILE"
		rc=1
		;;
	2)	rc=1		# Error already printed
		;;
	3)	showmsg 9 '%s: Link command failed for file: %s\n' \
			$command_name $PLAIN_IFILE
		rc=1
		;;
	4)	showmsg 15 \
		 '%s: Input file is not valid in the current object file mode: %s\n' \
			$command_name "$PLAIN_IFILE"
		rc=1
		;;
	esac
fi
if [ $rc -eq 0 ]
then	if [ -z "$sflag" ]
	then	$MV -f $OFILE.$$ $OFILE
	else	print -- $MV -f $OFILE.'$$' $OFILE >> $SFILE
	fi
else	# Failure
	$RM -f $OFILE.$$
	if [ ! -z "$sflag" ]; then $RM -f $SFILE; fi
	if [ ! -z "$Rflag" ]
	then	showmsg 12 '%s: Input file %s was not modified.\n' \
			$command_name $PLAIN_IFILE
	fi
fi

exit $rc