1#!/usr/bin/env bash 2 3# Copyright 2016 The Fuchsia Authors 4# 5# Use of this source code is governed by a MIT-style 6# license that can be found in the LICENSE file or at 7# https://opensource.org/licenses/MIT 8 9# This script reads symbols with nm and writes a C header file that 10# defines macros <NAME>_CODE_*, <NAME>_DATA_* and <NAME>_ENTRY, with 11# the address constants found in the symbol table for the symbols 12# CODE_*, DATA_* and _start, respectively. 13# 14# When there is a dynamic symbol table, then it also emits macros 15# <NAME>_DYNSYM_* giving the dynamic symbol table index of each 16# exported symbol, and <NAME>_DYNSYM_COUNT giving the total number 17# of entries in the table. 18 19usage() { 20 echo >&2 "Usage: $0 NM READELF OUTFILE {NAME DSO}..." 21 exit 2 22} 23 24if [ $# -lt 3 ]; then 25 usage 26fi 27 28NM="$1" 29shift 30READELF="$1" 31shift 32OUTFILE="$1" 33shift 34 35set -e 36if [ -n "$BASH_VERSION" ]; then 37 set -o pipefail 38fi 39 40grok_code_symbols() { 41 local symbol type addr size rest 42 while read symbol type addr size rest; do 43 case "$symbol" in 44 CODE_*|DATA_*|SYSCALL_*|_start) 45 if [ "$symbol" = _start ]; then 46 symbol=ENTRY 47 fi 48 echo "#define ${1}_${symbol} 0x${addr}" >> $OUTFILE 49 case "$size" in 50 ''|0|0x0) ;; 51 *) echo "#define ${1}_${symbol}_SIZE 0x${size}" >> $OUTFILE 52 esac 53 status=0 54 ;; 55 esac 56 done 57 return $status 58} 59 60find_code_symbols() { 61 "$NM" -P -S -n "$2" | grok_code_symbols "$1" 62} 63 64grok_dynsym_slots() { 65 local symno=0 66 local symbol rest 67 while read symbol rest; do 68 symno=$((symno+1)) 69 echo "#define ${1}_DYNSYM_${symbol} ${symno}" >> $OUTFILE 70 done 71 if [ $symno -gt 0 ]; then 72 symno=$((symno+1)) 73 echo "#define ${1}_DYNSYM_COUNT ${symno}" >> $OUTFILE 74 fi 75} 76 77find_dynsym_slots() { 78 "$NM" -P -D -p "$2" | grok_dynsym_slots "$1" 79} 80 81SEGMENTS_HAVE_DYNSYM=22 82SEGMENTS_NO_DYNSYM=33 83 84grok_segments() { 85 local line 86 local status=$SEGMENTS_NO_DYNSYM 87 while read line; do 88 case "$line" in 89 # Program header for the code segment, e.g.: 90 # LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x00f50f 0x00f50f R E 0x1000 91 # ^^^^^ vaddr ^^^^^^ ^filesz^ 92 *LOAD*\ R\ E\ *) 93 local words=($line) 94 local vaddr=$(printf '%#x' ${words[2]}) 95 local filesz=$(printf '%#x' ${words[4]}) 96 echo "#define ${1}_CODE_START $vaddr" >> $OUTFILE 97 echo "#define ${1}_CODE_END (((${1}_CODE_START + $filesz + (1 << PAGE_SIZE_SHIFT) - 1) >> PAGE_SIZE_SHIFT) << PAGE_SIZE_SHIFT)" >> $OUTFILE 98 ;; 99 # Make sure there's no writable segment. 100 *LOAD*W*) 101 echo >&2 "$0: writable segment: $line" 102 exit 1 103 ;; 104 # Section header for .dynsym, e.g.: 105 # [ 3] .dynsym DYNSYM 0000000000001268 001268 000018 18 A 6 1 8 106 # ^^^^^ addr ^^^^^ ^size^ ^entsize^ 107 *DYNSYM*) 108 line="${line#*DYNSYM}" 109 local words=($line) 110 local addr=$(printf '%#x' 0x${words[0]}) 111 local size=$(printf '%#x' 0x${words[2]}) 112 local entsize=$(printf '%#x' 0x${words[3]}) 113 # A dummy .dynsym has a single entry. Don't count that case. 114 if [ $size != $entsize ]; then 115 status=$SEGMENTS_HAVE_DYNSYM 116 echo "#define ${1}_DATA_START_dynsym $addr" >> $OUTFILE 117 echo "#define ${1}_DATA_END_dynsym (${1}_DATA_START_dynsym + $size)" >> $OUTFILE 118 fi 119 ;; 120 esac 121 done 122 return $status 123} 124 125find_segments() { 126 # This if silliness disarms -e so we can observe grok_segments's return code. 127 if { 128 "$READELF" -W -S -l "$2" | grok_segments "$1" 129 case $? in 130 $SEGMENTS_NO_DYNSYM) have_dynsym=no ;; 131 $SEGMENTS_HAVE_DYNSYM) have_dynsym=yes ;; 132 *) exit $? ;; 133 esac 134 return 0 135 }; then : ; fi 136} 137 138while [ $# -gt 0 ]; do 139 if [ $# -lt 2 ]; then 140 usage 141 fi 142 echo "#define ${1}_FILENAME \"${2}\"" > $OUTFILE 143 find_segments "$1" "$2" 144 find_code_symbols "$1" "$2" 145 if [ $have_dynsym = yes ]; then 146 find_dynsym_slots "$1" "$2" 147 fi 148 shift 2 149done 150