| 1 | #! /bin/sh |
| 2 | # Embed an SPU ELF executable into a PowerPC object file. |
| 3 | # |
| 4 | # Copyright 2006, 2007, 2008 Free Software Foundation, Inc. |
| 5 | # |
| 6 | # This file is part of GNU Binutils. |
| 7 | # |
| 8 | # This program is free software; you can redistribute it and/or modify |
| 9 | # it under the terms of the GNU General Public License as published by |
| 10 | # the Free Software Foundation; either version 3 of the License, or |
| 11 | # (at your option) any later version. |
| 12 | # |
| 13 | # This program is distributed in the hope that it will be useful, |
| 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | # GNU General Public License for more details. |
| 17 | # |
| 18 | # You should have received a copy of the GNU General Public License |
| 19 | # along with this program; if not, write to the Free Software |
| 20 | # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA |
| 21 | # 02110-1301, USA. |
| 22 | |
| 23 | usage () |
| 24 | { |
| 25 | echo "Usage: embedspu [flags] symbol_name input_filename output_filename" |
| 26 | echo |
| 27 | echo " input_filename: SPU ELF executable to be embedded" |
| 28 | echo " output_filename: Resulting PowerPC object file" |
| 29 | echo " symbol_name: Name of program handle struct to be defined" |
| 30 | echo " flags: GCC flags defining PowerPC object file format" |
| 31 | echo " (e.g. -m32 or -m64)" |
| 32 | exit 1 |
| 33 | } |
| 34 | |
| 35 | program_transform_name= |
| 36 | mydir=`dirname "$0"` |
| 37 | |
| 38 | find_prog () |
| 39 | { |
| 40 | prog=`echo $1 | sed "$program_transform_name"` |
| 41 | prog="$mydir/$prog" |
| 42 | test -x "$prog" && return 0 |
| 43 | prog="$mydir/$1" |
| 44 | test -x "$prog" && return 0 |
| 45 | prog=`echo $1 | sed "$program_transform_name"` |
| 46 | which $prog > /dev/null 2> /dev/null && return 0 |
| 47 | return 1 |
| 48 | } |
| 49 | |
| 50 | SYMBOL= |
| 51 | INFILE= |
| 52 | OUTFILE= |
| 53 | FLAGS= |
| 54 | |
| 55 | parse_args () |
| 56 | { |
| 57 | while test -n "$1"; do |
| 58 | case "$1" in |
| 59 | -*) FLAGS="${FLAGS} $1" ;; |
| 60 | *) if test -z "$SYMBOL"; then |
| 61 | SYMBOL="$1" |
| 62 | elif test -z "$INFILE"; then |
| 63 | INFILE="$1" |
| 64 | elif test -z "$OUTFILE"; then |
| 65 | OUTFILE="$1" |
| 66 | else |
| 67 | echo "Too many arguments!" |
| 68 | usage |
| 69 | fi ;; |
| 70 | esac |
| 71 | shift |
| 72 | done |
| 73 | if test -z "$OUTFILE"; then |
| 74 | usage |
| 75 | fi |
| 76 | if test ! -r "$INFILE"; then |
| 77 | echo "${INFILE}: File not found" |
| 78 | usage |
| 79 | fi |
| 80 | } |
| 81 | |
| 82 | main () |
| 83 | { |
| 84 | parse_args "$@" |
| 85 | |
| 86 | # Find a powerpc gcc. Support running from a combined tree build. |
| 87 | if test -x "$mydir/../gcc/xgcc"; then |
| 88 | CC="$mydir/../gcc/xgcc -B$mydir/../gcc/" |
| 89 | else |
| 90 | find_prog gcc |
| 91 | if test $? -ne 0; then |
| 92 | echo "Cannot find $prog" |
| 93 | exit 1 |
| 94 | fi |
| 95 | CC="$prog" |
| 96 | fi |
| 97 | |
| 98 | # Find readelf. Any old readelf should do. |
| 99 | find_prog readelf |
| 100 | if test $? -ne 0; then |
| 101 | if which readelf > /dev/null 2> /dev/null; then |
| 102 | prog=readelf |
| 103 | else |
| 104 | echo "Cannot find $prog" |
| 105 | exit 1 |
| 106 | fi |
| 107 | fi |
| 108 | READELF="$prog" |
| 109 | |
| 110 | # Sanity check the input file |
| 111 | if ! ${READELF} -h ${INFILE} | grep 'Class:.*ELF32' >/dev/null 2>/dev/null \ |
| 112 | || ! ${READELF} -h ${INFILE} | grep 'Type:.*EXEC' >/dev/null 2>/dev/null \ |
| 113 | || ! ${READELF} -h ${INFILE} | egrep 'Machine:.*(SPU|17)' >/dev/null 2>/dev/null |
| 114 | then |
| 115 | echo "${INFILE}: Does not appear to be an SPU executable" |
| 116 | exit 1 |
| 117 | fi |
| 118 | |
| 119 | toe=`${READELF} -S ${INFILE} | sed -n -e 's, *\[ *\([0-9]*\)\] *\.toe *[PROGN]*BITS *\([0-9a-f]*\).*,\1 \2,p'` |
| 120 | toe_addr=`echo $toe | sed -n -e 's,.* ,,p'` |
| 121 | toe=`echo $toe | sed -n -e 's, .*,,p'` |
| 122 | has_ea=`${READELF} -S ${INFILE} | sed -n -e 's, *\[ *\([0-9]*\)\] *\._ea *PROGBITS.*,\1,p'` |
| 123 | # For loaded sections, pick off section number, address, and file offset |
| 124 | sections=`${READELF} -S ${INFILE} | sed -n -e 's, *\[ *\([0-9]*\)\] *[^ ]* *PROGBITS *\([0-9a-f]*\) *\([0-9a-f]*\).*,\1 \2 \3,p'` |
| 125 | sections=`echo ${sections}` |
| 126 | # For relocation sections, pick off file offset and info (points to |
| 127 | # section where relocs apply) |
| 128 | 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'` |
| 129 | relas=`echo ${relas}` |
| 130 | |
| 131 | # Build embedded SPU image. |
| 132 | # 1. The whole SPU ELF file is written to .rodata.speelf |
| 133 | # 2. Symbols starting with the string "_EAR_" in the SPU ELF image are |
| 134 | # special. They allow an SPU program to access corresponding symbols |
| 135 | # (ie. minus the _EAR_ prefix), in the PowerPC program. _EAR_ without |
| 136 | # a suffix is used to refer to the addrress of the SPU image in |
| 137 | # PowerPC address space. _EAR_* symbols must all be defined in .toe |
| 138 | # at 16 byte intervals, or they must be defined in other non-bss |
| 139 | # sections. |
| 140 | # Find all _EAR_ symbols in .toe using readelf, sort by address, and |
| 141 | # write the address of the corresponding PowerPC symbol in a table |
| 142 | # built in .data.spetoe. For _EAR_ symbols not in .toe, create |
| 143 | # .reloc commands to relocate their location directly. |
| 144 | # 3. Look for R_SPU_PPU32 and R_SPU_PPU64 relocations in the SPU ELF image |
| 145 | # and create .reloc commands for them. |
| 146 | # 4. Write a struct spe_program_handle to .data. |
| 147 | # 5. Write a table of _SPUEAR_ symbols. |
| 148 | ${CC} ${FLAGS} -x assembler-with-cpp -nostartfiles -nostdlib \ |
| 149 | -Wa,-mbig -Wa,-noexecstack -Wl,-r -Wl,-x -o ${OUTFILE} - <<EOF |
| 150 | .section .data.spetoe,"aw",@progbits |
| 151 | .p2align 7 |
| 152 | __spetoe__: |
| 153 | `${READELF} -s -W ${INFILE} | grep ' _EAR_' | sort -k 2 | awk \ |
| 154 | 'BEGIN { \ |
| 155 | addr = strtonum ("0x" "'${toe_addr:-0}'"); \ |
| 156 | split ("'"${sections}"'", s, " "); \ |
| 157 | for (i = 1; i in s; i += 3) { \ |
| 158 | sec_off[s[i]] = strtonum ("0x" s[i+2]) - strtonum ("0x" s[i+1]); \ |
| 159 | } \ |
| 160 | } \ |
| 161 | $7 == "'${toe}'" && strtonum ("0x" $2) != addr { \ |
| 162 | print "#error Symbol " $8 " not in 16 byte element toe array!"; \ |
| 163 | } \ |
| 164 | $7 == "'${toe}'" { \ |
| 165 | addr = addr + 16; \ |
| 166 | } \ |
| 167 | $7 == "'${toe}'" { \ |
| 168 | print "#ifdef _LP64"; \ |
| 169 | print " .quad " ($8 == "_EAR_" ? "__speelf__" : substr($8, 6)) ", 0"; \ |
| 170 | print "#else"; \ |
| 171 | print " .int 0, " ($8 == "_EAR_" ? "__speelf__" : substr($8, 6)) ", 0, 0"; \ |
| 172 | print "#endif"; \ |
| 173 | } \ |
| 174 | $7 != "'${toe}'" && $7 in sec_off { \ |
| 175 | print "#ifdef _LP64"; \ |
| 176 | print " .reloc __speelf__+" strtonum ("0x" $2) + sec_off[$7] ", R_PPC64_ADDR64, " ($8 == "_EAR_" ? "__speelf__" : substr($8, 6)); \ |
| 177 | print "#else"; \ |
| 178 | print " .reloc __speelf__+" strtonum ("0x" $2) + sec_off[$7] + 4 ", R_PPC_ADDR32, " ($8 == "_EAR_" ? "__speelf__" : substr($8, 6)); \ |
| 179 | print "#endif"; \ |
| 180 | if (!donedef) { print "#define HAS_RELOCS 1"; donedef = 1; }; \ |
| 181 | } \ |
| 182 | $7 != "'${toe}'" && ! $7 in sec_off { \ |
| 183 | print "#error Section not found for " $8; \ |
| 184 | } \ |
| 185 | '` |
| 186 | `test -z "${relas}" || ${READELF} -r -W ${INFILE} | awk \ |
| 187 | 'BEGIN { \ |
| 188 | split ("'"${sections}"'", s, " "); \ |
| 189 | for (i = 1; i in s; i += 3) { \ |
| 190 | sec_off[s[i]] = strtonum ("0x" s[i+2]) - strtonum ("0x" s[i+1]); \ |
| 191 | } \ |
| 192 | split ("'"${relas}"'", s, " "); \ |
| 193 | for (i = 1; i in s; i += 2) { \ |
| 194 | rela[s[i]] = strtonum (s[i+1]); \ |
| 195 | } \ |
| 196 | } \ |
| 197 | /^Relocation section/ { \ |
| 198 | sec = substr($6, 3); \ |
| 199 | } \ |
| 200 | $3 ~ /R_SPU_PPU/ { \ |
| 201 | print "#ifdef _LP64"; \ |
| 202 | print " .reloc __speelf__+" strtonum ("0x" $1) + sec_off[rela[sec]] ", R_PPC64_ADDR" substr($3, 10) ", " ($5 != "" ? $5 "+0x" $7 : "__speelf__ + 0x" $4); \ |
| 203 | print "#else"; \ |
| 204 | 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); \ |
| 205 | print "#endif"; \ |
| 206 | if (!donedef) { print "#define HAS_RELOCS 1"; donedef = 1; }; \ |
| 207 | } \ |
| 208 | $3 ~ /unrecognized:/ { \ |
| 209 | print "#ifdef _LP64"; \ |
| 210 | print " .reloc __speelf__+" strtonum ("0x" $1) + sec_off[rela[sec]] ", R_PPC64_ADDR" ($4 == "f" ? "64" : "32") ", " ($6 != "" ? $6 "+0x" $8 : "__speelf__ + 0x" $5); \ |
| 211 | print "#else"; \ |
| 212 | print " .reloc __speelf__+" strtonum ("0x" $1) + sec_off[rela[sec]] + ($4 == "f" ? 4 : 0)", R_PPC_ADDR32, " ($6 != "" ? $6 "+0x" $8 : "__speelf__ + 0x" $5); \ |
| 213 | print "#endif"; \ |
| 214 | if (!donedef) { print "#define HAS_RELOCS 1"; donedef = 1; }; \ |
| 215 | } \ |
| 216 | '` |
| 217 | #if ${has_ea:-0} |
| 218 | .section .data.speelf,"aw",@progbits |
| 219 | #elif defined (HAS_RELOCS) && (defined (__PIC__) || defined (__PIE__)) |
| 220 | .section .data.rel.ro.speelf,"a",@progbits |
| 221 | #else |
| 222 | .section .rodata.speelf,"a",@progbits |
| 223 | #endif |
| 224 | .p2align 7 |
| 225 | __speelf__: |
| 226 | .incbin "${INFILE}" |
| 227 | |
| 228 | .section .data.spehandle,"aw",@progbits |
| 229 | .globl ${SYMBOL} |
| 230 | .type ${SYMBOL}, @object |
| 231 | # fill in a struct spe_program_handle |
| 232 | #ifdef _LP64 |
| 233 | .p2align 3 |
| 234 | ${SYMBOL}: |
| 235 | .int 24 |
| 236 | .int 0 |
| 237 | .quad __speelf__ |
| 238 | .quad __spetoe__ |
| 239 | #else |
| 240 | .p2align 2 |
| 241 | ${SYMBOL}: |
| 242 | .int 12 |
| 243 | .int __speelf__ |
| 244 | .int __spetoe__ |
| 245 | #endif |
| 246 | .size ${SYMBOL}, . - ${SYMBOL} |
| 247 | |
| 248 | `${READELF} -s -W ${INFILE} | grep ' _SPUEAR_' | sort -k 2 | awk \ |
| 249 | '{ \ |
| 250 | print " .globl '${SYMBOL}'_" substr($8, 9); \ |
| 251 | print " .type '${SYMBOL}'_" substr($8, 9) ", @object"; \ |
| 252 | print " .size '${SYMBOL}'_" substr($8, 9) ", 4"; \ |
| 253 | print "'${SYMBOL}'_" substr($8, 9) ":"; \ |
| 254 | print " .int 0x" $2; \ |
| 255 | } \ |
| 256 | '` |
| 257 | EOF |
| 258 | } |
| 259 | |
| 260 | main "$@" |