1214571Sdim#! /bin/sh 2214571Sdim# Embed an SPU ELF executable into a PowerPC object file. 3214571Sdim# 4214571Sdim# Copyright 2006, 2007 Free Software Foundation, Inc. 5214571Sdim# 6214571Sdim# This file is part of GNU Binutils. 7214571Sdim# 8214571Sdim# This program is free software; you can redistribute it and/or modify 9214571Sdim# it under the terms of the GNU General Public License as published by 10214571Sdim# the Free Software Foundation; either version 2 of the License, or 11214571Sdim# (at your option) any later version. 12214571Sdim# 13214571Sdim# This program is distributed in the hope that it will be useful, 14214571Sdim# but WITHOUT ANY WARRANTY; without even the implied warranty of 15214571Sdim# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16214571Sdim# GNU General Public License for more details. 17214571Sdim# 18214571Sdim# You should have received a copy of the GNU General Public License 19214571Sdim# along with this program; if not, write to the Free Software 20214571Sdim# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 21214571Sdim# 02110-1301, USA. 22214571Sdim 23214571Sdimusage () 24214571Sdim{ 25214571Sdim echo "Usage: embedspu [flags] symbol_name input_filename output_filename" 26214571Sdim echo 27214571Sdim echo " input_filename: SPU ELF executable to be embedded" 28214571Sdim echo " output_filename: Resulting PowerPC object file" 29214571Sdim echo " symbol_name: Name of program handle struct to be defined" 30214571Sdim echo " flags: GCC flags defining PowerPC object file format" 31214571Sdim echo " (e.g. -m32 or -m64)" 32214571Sdim exit 1 33214571Sdim} 34214571Sdim 35214571Sdimprogram_transform_name= 36214571Sdimmydir=`dirname "$0"` 37214571Sdim 38214571Sdimfind_prog () 39214571Sdim{ 40214571Sdim prog=`echo $1 | sed "$program_transform_name"` 41214571Sdim prog="$mydir/$prog" 42214571Sdim test -x "$prog" && return 0 43214571Sdim prog="$mydir/$1" 44214571Sdim test -x "$prog" && return 0 45214571Sdim prog=`echo $1 | sed "$program_transform_name"` 46214571Sdim which $prog > /dev/null 2> /dev/null && return 0 47214571Sdim return 1 48214571Sdim} 49214571Sdim 50214571SdimSYMBOL= 51214571SdimINFILE= 52214571SdimOUTFILE= 53214571SdimFLAGS= 54214571Sdim 55214571Sdimparse_args () 56214571Sdim{ 57214571Sdim while test -n "$1"; do 58214571Sdim case "$1" in 59214571Sdim -*) FLAGS="${FLAGS} $1" ;; 60214571Sdim *) if test -z "$SYMBOL"; then 61214571Sdim SYMBOL="$1" 62214571Sdim elif test -z "$INFILE"; then 63214571Sdim INFILE="$1" 64214571Sdim elif test -z "$OUTFILE"; then 65214571Sdim OUTFILE="$1" 66214571Sdim else 67214571Sdim echo "Too many arguments!" 68214571Sdim usage 69214571Sdim fi ;; 70214571Sdim esac 71214571Sdim shift 72214571Sdim done 73214571Sdim if test -z "$OUTFILE"; then 74214571Sdim usage 75214571Sdim fi 76214571Sdim if test ! -r "$INFILE"; then 77214571Sdim echo "${INFILE}: File not found" 78214571Sdim usage 79214571Sdim fi 80214571Sdim} 81214571Sdim 82214571Sdimmain () 83214571Sdim{ 84214571Sdim parse_args "$@" 85214571Sdim 86214571Sdim # Find a powerpc gcc. Support running from a combined tree build. 87214571Sdim if test -x "$mydir/../gcc/xgcc"; then 88214571Sdim CC="$mydir/../gcc/xgcc -B$mydir/../gcc/" 89214571Sdim else 90214571Sdim find_prog gcc 91214571Sdim if test $? -ne 0; then 92214571Sdim echo "Cannot find $prog" 93214571Sdim exit 1 94214571Sdim fi 95214571Sdim CC="$prog" 96214571Sdim fi 97214571Sdim 98214571Sdim # Find readelf. Any old readelf should do. 99214571Sdim find_prog readelf 100214571Sdim if test $? -ne 0; then 101214571Sdim if which readelf > /dev/null 2> /dev/null; then 102214571Sdim prog=readelf 103214571Sdim else 104214571Sdim echo "Cannot find $prog" 105214571Sdim exit 1 106214571Sdim fi 107214571Sdim fi 108214571Sdim READELF="$prog" 109214571Sdim 110214571Sdim # Sanity check the input file 111214571Sdim if ! ${READELF} -h ${INFILE} | grep 'Class:.*ELF32' >/dev/null 2>/dev/null \ 112214571Sdim || ! ${READELF} -h ${INFILE} | grep 'Type:.*EXEC' >/dev/null 2>/dev/null \ 113214571Sdim || ! ${READELF} -h ${INFILE} | egrep 'Machine:.*(SPU|17)' >/dev/null 2>/dev/null 114214571Sdim then 115214571Sdim echo "${INFILE}: Does not appear to be an SPU executable" 116214571Sdim exit 1 117214571Sdim fi 118214571Sdim 119214571Sdim toe=`${READELF} -S ${INFILE} | sed -n -e 's, *\[ *\([0-9]*\)\] *\.toe *[PROGN]*BITS *\([0-9a-f]*\).*,\1 \2,p'` 120214571Sdim toe_addr=`echo $toe | sed -n -e 's,.* ,,p'` 121214571Sdim toe=`echo $toe | sed -n -e 's, .*,,p'` 122214571Sdim # For loaded sections, pick off section number, address, and file offset 123214571Sdim sections=`${READELF} -S ${INFILE} | sed -n -e 's, *\[ *\([0-9]*\)\] *[^ ]* *PROGBITS *\([0-9a-f]*\) *\([0-9a-f]*\).*,\1 \2 \3,p'` 124214571Sdim sections=`echo ${sections}` 125214571Sdim # For relocation sections, pick off file offset and info (points to 126214571Sdim # section where relocs apply) 127214571Sdim relas=`${READELF} -S ${INFILE} | sed -n -e 's, *\[ *[0-9]*\] *[^ ]* *RELA *[0-9a-f]* *0*\([0-9a-f][0-9a-f]*\).* \([0-9a-f][0-9a-f]*\) *[0-9a-f][0-9a-f]*$,\1 \2,p'` 128214571Sdim relas=`echo ${relas}` 129214571Sdim 130214571Sdim # Build embedded SPU image. 131214571Sdim # 1. The whole SPU ELF file is written to .rodata.speelf 132214571Sdim # 2. Symbols starting with the string "_EAR_" in the SPU ELF image are 133214571Sdim # special. They allow an SPU program to access corresponding symbols 134214571Sdim # (ie. minus the _EAR_ prefix), in the PowerPC program. _EAR_ without 135214571Sdim # a suffix is used to refer to the addrress of the SPU image in 136214571Sdim # PowerPC address space. _EAR_* symbols must all be defined in .toe 137214571Sdim # at 16 byte intervals, or they must be defined in other non-bss 138214571Sdim # sections. 139214571Sdim # Find all _EAR_ symbols in .toe using readelf, sort by address, and 140214571Sdim # write the address of the corresponding PowerPC symbol in a table 141214571Sdim # built in .data.spetoe. For _EAE_ symbols not in .toe, create 142214571Sdim # .reloc commands to relocate their location directly. 143214571Sdim # 3. Look for R_SPU_PPU32 and R_SPU_PPU64 relocations in the SPU ELF image 144214571Sdim # and create .reloc commands for them. 145214571Sdim # 4. Write a struct spe_program_handle to .data. 146214571Sdim # 5. Write a table of _SPUEAR_ symbols. 147214571Sdim ${CC} ${FLAGS} -x assembler-with-cpp -nostartfiles -nostdlib \ 148214571Sdim -Wa,-mbig -Wl,-r -Wl,-x -o ${OUTFILE} - <<EOF 149214571Sdim .section .data.spetoe,"aw",@progbits 150214571Sdim .p2align 7 151214571Sdim__spetoe__: 152214571Sdim`${READELF} -s -W ${INFILE} | grep ' _EAR_' | sort -k 2 | awk \ 153214571Sdim'BEGIN { \ 154214571Sdim addr = strtonum ("0x" "'${toe_addr-0}'"); \ 155214571Sdim split ("'"${sections}"'", s, " "); \ 156214571Sdim for (i = 1; i in s; i += 3) { \ 157214571Sdim sec_off[s[i]] = strtonum ("0x" s[i+2]) - strtonum ("0x" s[i+1]); \ 158214571Sdim } \ 159214571Sdim} \ 160214571Sdim$7 == "'${toe}'" && strtonum ("0x" $2) != addr { \ 161214571Sdim print "#error Symbol " $8 " not in 16 byte element toe array!"; \ 162214571Sdim} \ 163214571Sdim$7 == "'${toe}'" { \ 164214571Sdim addr = addr + 16; \ 165214571Sdim} \ 166214571Sdim$7 == "'${toe}'" { \ 167214571Sdim print "#ifdef _LP64"; \ 168214571Sdim print " .quad " ($8 == "_EAR_" ? "__speelf__" : substr($8, 6)) ", 0"; \ 169214571Sdim print "#else"; \ 170214571Sdim print " .int 0, " ($8 == "_EAR_" ? "__speelf__" : substr($8, 6)) ", 0, 0"; \ 171214571Sdim print "#endif"; \ 172214571Sdim} \ 173214571Sdim$7 != "'${toe}'" && $7 in sec_off { \ 174214571Sdim print "#ifdef _LP64"; \ 175214571Sdim print " .reloc __speelf__+" strtonum ("0x" $2) + sec_off[$7] ", R_PPC64_ADDR64, " ($8 == "_EAR_" ? "__speelf__" : substr($8, 6)); \ 176214571Sdim print "#else"; \ 177214571Sdim print " .reloc __speelf__+" strtonum ("0x" $2) + sec_off[$7] + 4 ", R_PPC_ADDR32, " ($8 == "_EAR_" ? "__speelf__" : substr($8, 6)); \ 178214571Sdim print "#endif"; \ 179214571Sdim if (!donedef) { print "#define HAS_RELOCS 1"; donedef = 1; }; \ 180214571Sdim} \ 181214571Sdim$7 != "'${toe}'" && ! $7 in sec_off { \ 182214571Sdim print "#error Section not found for " $8; \ 183214571Sdim} \ 184214571Sdim'` 185214571Sdim`test -z "${relas}" || ${READELF} -r -W ${INFILE} | awk \ 186214571Sdim'BEGIN { \ 187214571Sdim split ("'"${sections}"'", s, " "); \ 188214571Sdim for (i = 1; i in s; i += 3) { \ 189214571Sdim sec_off[s[i]] = strtonum ("0x" s[i+2]) - strtonum ("0x" s[i+1]); \ 190214571Sdim } \ 191214571Sdim split ("'"${relas}"'", s, " "); \ 192214571Sdim for (i = 1; i in s; i += 2) { \ 193214571Sdim rela[s[i]] = strtonum (s[i+1]); \ 194214571Sdim } \ 195214571Sdim} \ 196214571Sdim/^Relocation section/ { \ 197214571Sdim sec = substr($6, 3); \ 198214571Sdim} \ 199214571Sdim$3 ~ /R_SPU_PPU/ { \ 200214571Sdim print "#ifdef _LP64"; \ 201214571Sdim print " .reloc __speelf__+" strtonum ("0x" $1) + sec_off[rela[sec]] ", R_PPC64_ADDR" substr($3, 10) ", " ($5 != "" ? $5 "+0x" $7 : "__speelf__ + 0x" $4); \ 202214571Sdim print "#else"; \ 203214571Sdim print " .reloc __speelf__+" strtonum ("0x" $1) + sec_off[rela[sec]] + (substr($3, 10) == "64" ? 4 : 0)", R_PPC_ADDR32, " ($5 != "" ? $5 "+0x" $7 : "__speelf__ + 0x" $4); \ 204214571Sdim print "#endif"; \ 205214571Sdim if (!donedef) { print "#define HAS_RELOCS 1"; donedef = 1; }; \ 206214571Sdim} \ 207214571Sdim$3 ~ /unrecognized:/ { \ 208214571Sdim print "#ifdef _LP64"; \ 209214571Sdim print " .reloc __speelf__+" strtonum ("0x" $1) + sec_off[rela[sec]] ", R_PPC64_ADDR" ($4 == "f" ? "64" : "32") ", " ($6 != "" ? $6 "+0x" $8 : "__speelf__ + 0x" $5); \ 210214571Sdim print "#else"; \ 211214571Sdim print " .reloc __speelf__+" strtonum ("0x" $1) + sec_off[rela[sec]] + ($4 == "f" ? 4 : 0)", R_PPC_ADDR32, " ($6 != "" ? $6 "+0x" $8 : "__speelf__ + 0x" $5); \ 212214571Sdim print "#endif"; \ 213214571Sdim if (!donedef) { print "#define HAS_RELOCS 1"; donedef = 1; }; \ 214214571Sdim} \ 215214571Sdim'` 216214571Sdim#if defined (HAS_RELOCS) && (defined (__PIC__) || defined (__PIE__)) 217214571Sdim .section .data.rel.ro.speelf,"a",@progbits 218214571Sdim#else 219214571Sdim .section .rodata.speelf,"a",@progbits 220214571Sdim#endif 221214571Sdim .p2align 7 222214571Sdim__speelf__: 223214571Sdim .incbin "${INFILE}" 224214571Sdim 225214571Sdim .section .data,"aw",@progbits 226214571Sdim .globl ${SYMBOL} 227214571Sdim .type ${SYMBOL}, @object 228214571Sdim# fill in a struct spe_program_handle 229214571Sdim#ifdef _LP64 230214571Sdim .p2align 3 231214571Sdim${SYMBOL}: 232214571Sdim .int 24 233214571Sdim .int 0 234214571Sdim .quad __speelf__ 235214571Sdim .quad __spetoe__ 236214571Sdim#else 237214571Sdim .p2align 2 238214571Sdim${SYMBOL}: 239214571Sdim .int 12 240214571Sdim .int __speelf__ 241214571Sdim .int __spetoe__ 242214571Sdim#endif 243214571Sdim .size ${SYMBOL}, . - ${SYMBOL} 244214571Sdim 245214571Sdim`${READELF} -s -W ${INFILE} | grep ' _SPUEAR_' | sort -k 2 | awk \ 246214571Sdim'{ \ 247214571Sdim print " .globl '${SYMBOL}'_" substr($8, 9); \ 248214571Sdim print " .type '${SYMBOL}'_" substr($8, 9) ", @object"; \ 249214571Sdim print " .size '${SYMBOL}'_" substr($8, 9) ", 4"; \ 250214571Sdim print "'${SYMBOL}'_" substr($8, 9) ":"; \ 251214571Sdim print " .int 0x" $2; \ 252214571Sdim} \ 253214571Sdim'` 254214571SdimEOF 255214571Sdim} 256214571Sdim 257214571Sdimmain "$@" 258