]>
Commit | Line | Data |
---|---|---|
1e390209 AW |
1 | #!/bin/bash |
2 | ||
3 | exec </dev/null | |
4 | ||
5 | object="$1" | |
6 | src="$2" | |
7 | bit16="$3" | |
8 | ||
9 | SECTION=".discard.retpoline_safe" | |
10 | ||
11 | # Form an associative lookup for the symbol numbers in the ELF symbol table. | |
12 | # Uses 8 character 0 expanded hexadecimal key for ease of consumption. | |
13 | __symbolmap_init() | |
14 | { | |
15 | readelf -W --syms "$1" | | |
16 | awk '($4 == "SECTION" && $1 ~ /^[0-9]*:/) { printf("%08x %08x\n", int($1), int($7)); }' | \ | |
17 | while read symbol_num section_num | |
18 | do | |
19 | echo "symbolmap_$symbol_num='$section_num'" | |
20 | done | |
21 | } | |
22 | symbolmap_init() | |
23 | { | |
24 | eval $(__symbolmap_init "$1") | |
25 | } | |
26 | symbolmap() | |
27 | { | |
28 | eval RET="\$symbolmap_$1" | |
29 | if [ "$RET" = '' ]; then | |
30 | echo "symbolmap: $1: invalid section" 1>&2 | |
31 | exit 1 | |
32 | fi | |
33 | } | |
34 | ||
35 | # Form an associative lookup for the section numbers in the ELF symbol table. | |
36 | # Uses 8 character 0 expanded hexadecimal key for ease of consumption. | |
37 | __sectionmap_init() | |
38 | { | |
39 | readelf -W --headers "$1" | \ | |
40 | awk ' | |
41 | { sub("\\[", ""); sub("\\]", ""); } | |
42 | ($1 ~ /^[0-9][0-9]*/) { printf("%08x %s %s %s\n", int($1), $2, $3, $4); } | |
43 | ' | \ | |
44 | { | |
45 | while read section_num section_name section_type section_vma | |
46 | do | |
47 | echo "sectionmap_$section_num='$section_name'" | |
48 | echo "sectionvma_$section_num='$section_vma'" | |
49 | case "$section_type" in | |
50 | REL|RELA) section_relocation="$section_type" ;; | |
51 | esac | |
52 | done | |
53 | echo "section_relocation='$section_relocation'" | |
54 | } | |
55 | } | |
56 | sectionmap_init() | |
57 | { | |
58 | eval $(__sectionmap_init "$1") | |
59 | } | |
60 | sectionmap() | |
61 | { | |
62 | eval RET="\$sectionmap_$1" | |
63 | if [ "$RET" = '' ]; then | |
64 | echo "sectionmap: $1: invalid section" 1>&2 | |
65 | exit 1 | |
66 | fi | |
67 | } | |
68 | sectionvma() | |
69 | { | |
70 | eval RET="\$sectionvma_$1" | |
71 | if [ "$RET" = '' ]; then | |
72 | echo "sectionvma: $1: invalid section" 1>&2 | |
73 | exit 1 | |
74 | fi | |
75 | } | |
76 | ||
77 | # Read and parse the hex-dump output. | |
78 | hex="[0-9a-f]" | |
79 | hex_8="$hex$hex$hex$hex$hex$hex$hex$hex" | |
80 | hexspc="[0-9a-f ]" | |
81 | hexspc_8="$hexspc$hexspc$hexspc$hexspc$hexspc$hexspc$hexspc$hexspc" | |
82 | ||
83 | raw32() | |
84 | { | |
85 | readelf --hex-dump "$2" "$1" 2>/dev/null | | |
86 | sed \ | |
87 | -e '/^Hex/d' -e '/^$/d' -e '/^ *NOTE/d' \ | |
88 | -e 's/ *[^ ][^ ]* *\('"$hex_8"'\) \('"$hexspc_8"'\) \('"$hexspc_8"'\) \('"$hexspc_8"'\) .*/\1 \2 \3 \4 /' \ | |
89 | -e 's/\('"$hex$hex"'\)\('"$hex$hex"'\)\('"$hex$hex"'\)\('"$hex$hex"'\) /\4\3\2\1 /g' \ | |
90 | -e 's/ $//g' -e 's/ /\n/g' | |
91 | } | |
92 | #-e 's/\([^ ][^ ][^ ][^ ][^ ][^ ][^ ][^ ]\) \([^ ][^ ][^ ][^ ][^ ][^ ][^ ][^ ]\) /\2\1 /g' \ | |
93 | ||
94 | rela() | |
95 | { | |
96 | #file="$(basename "$1")" | |
97 | file="$1" | |
98 | ||
99 | # Read relocation information for a 64bit binary. Each relocation entry | |
100 | # is 3 long longs so we collect 6 quads here. Note that the dump is in | |
101 | # listed in increasing byte order not withstanding the quad split. | |
102 | # | |
103 | # The record says to take the value of <symbol> add <symbol offset> and | |
104 | # shove that into <write offset> in the segment of the <symbol>. | |
105 | # | |
106 | # Format: | |
107 | # <write offset> 64 bits | |
108 | # <symbol number> 32 bits | |
109 | # <relocation type> 32 bits | |
110 | # <symbol offset> 64 bits | |
111 | raw32 "$1" ".rela$SECTION" | \ | |
112 | { | |
113 | a1=''; a2=''; a3=''; a4=''; a5='' | |
114 | while read a6 | |
115 | do | |
116 | [ "$a1" = '' ] && { a1="$a6"; continue; } | |
117 | [ "$a2" = '' ] && { a2="$a6"; continue; } | |
118 | [ "$a3" = '' ] && { a3="$a6"; continue; } | |
119 | [ "$a4" = '' ] && { a4="$a6"; continue; } | |
120 | [ "$a5" = '' ] && { a5="$a6"; continue; } | |
121 | ||
122 | #echo ">$a1< >$a2< >$a3< >$a4< >$a5< >$a6<" 1>&2 | |
123 | #echo "type<$a3> symbol<$a4> offset<$a2$a1> addr<$a6a5>" 1>&2 | |
124 | ||
125 | symbolmap "$a4"; section_num="$RET" | |
126 | #echo "section_num<$section_num>" 1>&2 | |
127 | ||
128 | sectionmap "$section_num"; section="$RET" | |
129 | sectionvma "$section_num"; vma="$RET" | |
130 | #echo "section<$section> vma<$vma>" 1>&2 | |
131 | ||
132 | # Adjust the segment addressing by the segment offset. | |
133 | printf -v addr "%u" "0x$a6$a5" | |
134 | printf -v vma "%u" "0x$vma" | |
135 | let offset="$addr + $vma" | |
136 | printf -v offset "%x" "$offset" | |
137 | ||
138 | echo "$file-$section-$offset" | |
139 | ||
140 | a1=''; a2=''; a3=''; a4=''; a5='' | |
141 | done | |
142 | } | sed -e 's/-00*\([0-9a-f]\)/-\1/' | |
143 | } | |
144 | ||
145 | # Form an associative lookup for the raw contents for an ELF section. | |
146 | # Uses 8 character 0 expanded hexadecimal key for ease of consumption. | |
147 | contentmap_init() | |
148 | { | |
7d21b076 | 149 | raw32 "$1" "$2" >"$tmp" |
1e390209 AW |
150 | let offset=0 |
151 | while read value | |
152 | do | |
153 | printf -v offset_hex "%08x" $offset | |
154 | eval contentmap_$offset_hex=\'$value\' | |
155 | ||
156 | let offset="$offset + 4" | |
7d21b076 KE |
157 | done <"$tmp" |
158 | rm -f "$tmp" | |
1e390209 AW |
159 | } |
160 | contentmap() | |
161 | { | |
162 | eval RET="\$contentmap_$1" | |
163 | if [ "$RET" = '' ]; then | |
164 | echo "contentmap: $1: invalid offset" 1>&2 | |
165 | exit 1 | |
166 | fi | |
167 | } | |
168 | ||
169 | rel() | |
170 | { | |
171 | # Load up the current contents of the $SECTION segment | |
172 | # as the offsets (see below) are recorded there and we will need | |
173 | # those to calculate the actuall address. | |
174 | contentmap_init "$1" "$SECTION" | |
175 | ||
176 | #file="$(basename "$1")" | |
177 | file="$1" | |
178 | ||
179 | # Read relocation information for a 32bit binary. Each relocation entry | |
180 | # is 3 longs so we collect 3 quads here. Note that the dump is in | |
181 | # listed in increasing byte order not withstanding the quad split. | |
182 | # | |
183 | # The record says to take the value of <symbol> and add that to the | |
184 | # existing contents of <write offset> in the segment of the <symbol>. | |
185 | # | |
186 | # Format: | |
187 | # <write offset> 32 bits | |
188 | # <symbol number> 24 bits | |
189 | # <relocation type> 8 bits | |
190 | raw32 "$1" ".rel$SECTION" | \ | |
191 | { | |
192 | a1='' | |
193 | while read a2 | |
194 | do | |
195 | [ "$a1" = '' ] && { a1="$a2"; continue; } | |
196 | ||
197 | #echo ">$a1< >$a2<" | |
198 | contentmap "$a1"; offset="$RET" | |
199 | symbolmap "00${a2%??}"; section_num="$RET" | |
200 | ||
201 | sectionmap "$section_num"; section="$RET" | |
202 | sectionvma "$section_num"; vma="$RET" | |
203 | #echo ">$a1< >$a2< >$offset< >$section<" | |
204 | ||
205 | echo "$file-$section-$offset" | |
206 | ||
207 | a1='' | |
208 | done | |
209 | } | sed -e 's/-00*\([0-9a-f]\)/-\1/' | |
210 | } | |
211 | ||
7d21b076 | 212 | tmp=$(mktemp --tmpdir "retpoline-extract-XXXXXX") |
1e390209 | 213 | |
88c426b4 AW |
214 | disassemble() |
215 | { | |
216 | local object="$1" | |
217 | local src="$2" | |
218 | local options="$3" | |
219 | local selector="$4" | |
220 | ||
221 | objdump $options --disassemble --no-show-raw-insn "$object" | \ | |
222 | awk -F' ' ' | |
223 | BEGIN { file="'"$object"'"; src="'"$src"'"; } | |
224 | /Disassembly of section/ { segment=$4; sub(":", "", segment); } | |
225 | /^[0-9a-f][0-9a-f]* <.*>:/ { tag=$0; sub(".*<", "", tag); sub(">.*", "", tag); } | |
e0602aaa AW |
226 | $0 ~ /(call|jmp)q? *\*0x[0-9a-f]*\(%rip\)/ { |
227 | next | |
228 | } | |
88c426b4 AW |
229 | $0 ~ /(call|jmp)q? *\*.*%/ { |
230 | sub(":", "", $1); | |
231 | if ('"$selector"') { | |
232 | offset=$1 | |
233 | $1=tag | |
234 | print(file "-" segment "-" offset " " src " " segment " " $0); | |
235 | } | |
236 | } | |
237 | ' | |
238 | } | |
239 | ||
1e390209 AW |
240 | # Accumulate potentially vunerable indirect call/jmp sequences. We do this |
241 | # by examining the raw disassembly for affected forms, recording the location | |
242 | # of each. | |
243 | case "$bit16" in | |
88c426b4 AW |
244 | '') disassemble "$object" "$src" '' 'segment != ".init.text"' ;; |
245 | *) disassemble "$object" "$src" '--disassembler-options=i8086' 'segment != ".init.text" && segment != ".text32" && segment != ".text64"' | |
246 | disassemble "$object" "$src" '--disassembler-options=i386' 'segment == ".text32"' | |
247 | disassemble "$object" "$src" '--disassembler-options=x86-64' 'segment == ".text64"' | |
248 | ;; | |
249 | esac | sort -k 1b,1 >"$object.ur-detected" | |
1e390209 AW |
250 | [ ! -s "$object.ur-detected" ] && rm -f "$object.ur-detected" |
251 | ||
252 | # Load up the symbol table and section mappings. | |
253 | symbolmap_init "$object" | |
254 | sectionmap_init "$object" | |
255 | ||
256 | # Accumulate annotated safe indirect call/jmp sequences. We do this by examining | |
257 | # the $SECTION sections (and their associated relocation information), | |
258 | # each entry represents the address of an instruction which has been marked | |
259 | # as ok. | |
260 | case "$section_relocation" in | |
261 | REL) rel "$object" ;; | |
262 | RELA) rela "$object" ;; | |
263 | esac | sort -k 1b,1 >"$object.ur-safe" | |
264 | [ ! -s "$object.ur-safe" ] && rm -f "$object.ur-safe" | |
265 | ||
266 | # We will perform the below join on the summarised and sorted fragments | |
267 | # formed above. This is performed in retpoline-check. | |
268 | #join -v 1 -j 1 "$tmp.extracted" "$tmp.safe" | sed -s 's/[^ ]* *//' | |
269 | ||
7d21b076 | 270 | rm -f "$tmp" |