]>
Commit | Line | Data |
---|---|---|
92a42be0 | 1 | #!/bin/bash |
1a4d82fc JJ |
2 | #===- lib/asan/scripts/asan_device_setup -----------------------------------===# |
3 | # | |
4 | # The LLVM Compiler Infrastructure | |
5 | # | |
6 | # This file is distributed under the University of Illinois Open Source | |
7 | # License. See LICENSE.TXT for details. | |
8 | # | |
9 | # Prepare Android device to run ASan applications. | |
10 | # | |
11 | #===------------------------------------------------------------------------===# | |
12 | ||
92a42be0 | 13 | set -e |
1a4d82fc JJ |
14 | |
15 | HERE="$(cd "$(dirname "$0")" && pwd)" | |
16 | ||
17 | revert=no | |
18 | extra_options= | |
19 | device= | |
20 | lib= | |
92a42be0 | 21 | use_su=0 |
1a4d82fc JJ |
22 | |
23 | function usage { | |
24 | echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]" | |
25 | echo " --revert: Uninstall ASan from the device." | |
26 | echo " --lib: Path to ASan runtime library." | |
27 | echo " --extra-options: Extra ASAN_OPTIONS." | |
28 | echo " --device: Install to the given device. Use 'adb devices' to find" | |
29 | echo " device-id." | |
92a42be0 SL |
30 | echo " --use-su: Use 'su -c' prefix for every adb command instead of using" |
31 | echo " 'adb root' once." | |
1a4d82fc JJ |
32 | echo |
33 | exit 1 | |
34 | } | |
35 | ||
92a42be0 SL |
36 | function adb_push { |
37 | if [ $use_su -eq 0 ]; then | |
38 | $ADB push "$1" "$2" | |
39 | else | |
40 | local FILENAME=$(basename $1) | |
41 | $ADB push "$1" "/data/local/tmp/$FILENAME" | |
42 | $ADB shell su -c "rm \\\"$2/$FILENAME\\\"" >&/dev/null | |
43 | $ADB shell su -c "cat \\\"/data/local/tmp/$FILENAME\\\" > \\\"$2/$FILENAME\\\"" | |
44 | $ADB shell su -c "rm \\\"/data/local/tmp/$FILENAME\\\"" | |
45 | fi | |
46 | } | |
47 | ||
48 | function adb_remount { | |
49 | if [ $use_su -eq 0 ]; then | |
50 | $ADB remount | |
51 | else | |
52 | local STORAGE=`$ADB shell mount | grep /system | cut -d ' ' -f1` | |
53 | if [ "$STORAGE" != "" ]; then | |
54 | echo Remounting $STORAGE at /system | |
55 | $ADB shell su -c "mount -o remount,rw $STORAGE /system" | |
56 | else | |
57 | echo Failed to get storage device name for "/system" mount point | |
58 | fi | |
59 | fi | |
60 | } | |
61 | ||
62 | function adb_shell { | |
63 | if [ $use_su -eq 0 ]; then | |
64 | $ADB shell $@ | |
65 | else | |
66 | $ADB shell su -c "$*" | |
67 | fi | |
68 | } | |
69 | ||
70 | function adb_root { | |
71 | if [ $use_su -eq 0 ]; then | |
72 | $ADB root | |
73 | fi | |
74 | } | |
75 | ||
76 | function adb_wait_for_device { | |
77 | $ADB wait-for-device | |
78 | } | |
79 | ||
80 | function adb_pull { | |
81 | if [ $use_su -eq 0 ]; then | |
82 | $ADB pull "$1" "$2" | |
83 | else | |
84 | local FILENAME=$(basename $1) | |
85 | $ADB shell rm "/data/local/tmp/$FILENAME" >&/dev/null | |
86 | $ADB shell su -c "[ -f \\\"$1\\\" ] && cat \\\"$1\\\" > \\\"/data/local/tmp/$FILENAME\\\" && chown root.shell \\\"/data/local/tmp/$FILENAME\\\" && chmod 755 \\\"/data/local/tmp/$FILENAME\\\"" && | |
87 | $ADB pull "/data/local/tmp/$FILENAME" "$2" >&/dev/null && $ADB shell "rm \"/data/local/tmp/$FILENAME\"" | |
88 | fi | |
89 | } | |
90 | ||
91 | function get_device_arch { # OUT OUT64 | |
92 | local _outvar=$1 | |
93 | local _outvar64=$2 | |
94 | local _ABI=$(adb_shell getprop ro.product.cpu.abi) | |
95 | local _ARCH= | |
96 | local _ARCH64= | |
97 | if [[ $_ABI == x86* ]]; then | |
98 | _ARCH=i686 | |
99 | elif [[ $_ABI == armeabi* ]]; then | |
100 | _ARCH=arm | |
101 | elif [[ $_ABI == arm64-v8a* ]]; then | |
102 | _ARCH=arm | |
103 | _ARCH64=aarch64 | |
104 | else | |
105 | echo "Unrecognized device ABI: $_ABI" | |
106 | exit 1 | |
107 | fi | |
108 | eval $_outvar=\$_ARCH | |
109 | eval $_outvar64=\$_ARCH64 | |
110 | } | |
111 | ||
1a4d82fc JJ |
112 | while [[ $# > 0 ]]; do |
113 | case $1 in | |
114 | --revert) | |
115 | revert=yes | |
116 | ;; | |
117 | --extra-options) | |
118 | shift | |
119 | if [[ $# == 0 ]]; then | |
120 | echo "--extra-options requires an argument." | |
121 | exit 1 | |
122 | fi | |
123 | extra_options="$1" | |
124 | ;; | |
125 | --lib) | |
126 | shift | |
127 | if [[ $# == 0 ]]; then | |
128 | echo "--lib requires an argument." | |
129 | exit 1 | |
130 | fi | |
131 | lib="$1" | |
132 | ;; | |
133 | --device) | |
134 | shift | |
135 | if [[ $# == 0 ]]; then | |
136 | echo "--device requires an argument." | |
137 | exit 1 | |
138 | fi | |
139 | device="$1" | |
140 | ;; | |
92a42be0 SL |
141 | --use-su) |
142 | use_su=1 | |
143 | ;; | |
1a4d82fc JJ |
144 | *) |
145 | usage | |
146 | ;; | |
147 | esac | |
148 | shift | |
149 | done | |
150 | ||
151 | ADB=${ADB:-adb} | |
152 | if [[ x$device != x ]]; then | |
153 | ADB="$ADB -s $device" | |
154 | fi | |
155 | ||
92a42be0 SL |
156 | if [ $use_su -eq 1 ]; then |
157 | # Test if 'su' is present on the device | |
158 | SU_TEST_OUT=`$ADB shell su -c "echo foo" 2>&1 | sed 's/\r$//'` | |
159 | if [ $? != 0 -o "$SU_TEST_OUT" != "foo" ]; then | |
160 | echo "ERROR: Cannot use 'su -c':" | |
161 | echo "$ adb shell su -c \"echo foo\"" | |
162 | echo $SU_TEST_OUT | |
163 | echo "Check that 'su' binary is correctly installed on the device or omit" | |
164 | echo " --use-su flag" | |
165 | exit 1 | |
166 | fi | |
167 | fi | |
168 | ||
169 | echo '>> Remounting /system rw' | |
170 | adb_wait_for_device | |
171 | adb_root | |
172 | adb_wait_for_device | |
173 | adb_remount | |
174 | adb_wait_for_device | |
175 | ||
176 | get_device_arch ARCH ARCH64 | |
177 | echo "Target architecture: $ARCH" | |
178 | ASAN_RT="libclang_rt.asan-$ARCH-android.so" | |
179 | if [[ -n $ARCH64 ]]; then | |
180 | echo "Target architecture: $ARCH64" | |
181 | ASAN_RT64="libclang_rt.asan-$ARCH64-android.so" | |
182 | fi | |
1a4d82fc JJ |
183 | |
184 | if [[ x$revert == xyes ]]; then | |
185 | echo '>> Uninstalling ASan' | |
92a42be0 SL |
186 | |
187 | if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then | |
188 | echo '>> Pre-L device detected.' | |
189 | adb_shell mv /system/bin/app_process.real /system/bin/app_process | |
190 | adb_shell rm /system/bin/asanwrapper | |
191 | elif ! adb_shell ls -l /system/bin/app_process64.real | grep -o 'No such file or directory' >&/dev/null; then | |
192 | # 64-bit installation. | |
193 | adb_shell mv /system/bin/app_process32.real /system/bin/app_process32 | |
194 | adb_shell mv /system/bin/app_process64.real /system/bin/app_process64 | |
195 | adb_shell rm /system/bin/asanwrapper | |
196 | adb_shell rm /system/bin/asanwrapper64 | |
197 | else | |
198 | # 32-bit installation. | |
199 | adb_shell rm /system/bin/app_process.wrap | |
200 | adb_shell rm /system/bin/asanwrapper | |
201 | adb_shell rm /system/bin/app_process | |
202 | adb_shell ln -s /system/bin/app_process32 /system/bin/app_process | |
203 | fi | |
1a4d82fc JJ |
204 | |
205 | echo '>> Restarting shell' | |
92a42be0 SL |
206 | adb_shell stop |
207 | adb_shell start | |
208 | ||
209 | # Remove the library on the last step to give a chance to the 'su' binary to | |
210 | # be executed without problem. | |
211 | adb_shell rm /system/lib/$ASAN_RT | |
1a4d82fc JJ |
212 | |
213 | echo '>> Done' | |
214 | exit 0 | |
215 | fi | |
216 | ||
217 | if [[ -d "$lib" ]]; then | |
218 | ASAN_RT_PATH="$lib" | |
219 | elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then | |
220 | ASAN_RT_PATH=$(dirname "$lib") | |
221 | elif [[ -f "$HERE/$ASAN_RT" ]]; then | |
222 | ASAN_RT_PATH="$HERE" | |
223 | elif [[ $(basename "$HERE") == "bin" ]]; then | |
224 | # We could be in the toolchain's base directory. | |
92a42be0 SL |
225 | # Consider ../lib, ../lib/asan, ../lib/linux and ../lib/clang/$VERSION/lib/linux. |
226 | P=$(ls "$HERE"/../lib/"$ASAN_RT" "$HERE"/../lib/asan/"$ASAN_RT" "$HERE"/../lib/linux/"$ASAN_RT" "$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1) | |
1a4d82fc JJ |
227 | if [[ -n "$P" ]]; then |
228 | ASAN_RT_PATH="$(dirname "$P")" | |
229 | fi | |
230 | fi | |
231 | ||
232 | if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then | |
92a42be0 | 233 | echo ">> ASan runtime library not found" |
1a4d82fc JJ |
234 | exit 1 |
235 | fi | |
236 | ||
92a42be0 SL |
237 | if [[ -n "$ASAN_RT64" ]]; then |
238 | if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT64" ]]; then | |
239 | echo ">> ASan runtime library not found" | |
240 | exit 1 | |
241 | fi | |
242 | fi | |
243 | ||
1a4d82fc JJ |
244 | TMPDIRBASE=$(mktemp -d) |
245 | TMPDIROLD="$TMPDIRBASE/old" | |
246 | TMPDIR="$TMPDIRBASE/new" | |
247 | mkdir "$TMPDIROLD" | |
248 | ||
92a42be0 SL |
249 | RELEASE=$(adb_shell getprop ro.build.version.release) |
250 | PRE_L=0 | |
251 | if echo "$RELEASE" | grep '^4\.' >&/dev/null; then | |
252 | PRE_L=1 | |
253 | fi | |
1a4d82fc | 254 | |
92a42be0 | 255 | if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then |
1a4d82fc | 256 | |
92a42be0 SL |
257 | if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then |
258 | echo '>> Old-style ASan installation detected. Reverting.' | |
259 | adb_shell mv /system/bin/app_process.real /system/bin/app_process | |
260 | fi | |
261 | ||
262 | echo '>> Pre-L device detected. Setting up app_process symlink.' | |
263 | adb_shell mv /system/bin/app_process /system/bin/app_process32 | |
264 | adb_shell ln -s /system/bin/app_process32 /system/bin/app_process | |
265 | fi | |
266 | ||
267 | echo '>> Copying files from the device' | |
268 | if [[ -n "$ASAN_RT64" ]]; then | |
269 | adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true | |
270 | adb_pull /system/lib64/"$ASAN_RT64" "$TMPDIROLD" || true | |
271 | adb_pull /system/bin/app_process32 "$TMPDIROLD" || true | |
272 | adb_pull /system/bin/app_process32.real "$TMPDIROLD" || true | |
273 | adb_pull /system/bin/app_process64 "$TMPDIROLD" || true | |
274 | adb_pull /system/bin/app_process64.real "$TMPDIROLD" || true | |
275 | adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true | |
276 | adb_pull /system/bin/asanwrapper64 "$TMPDIROLD" || true | |
277 | else | |
278 | adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true | |
279 | adb_pull /system/bin/app_process32 "$TMPDIROLD" || true | |
280 | adb_pull /system/bin/app_process.wrap "$TMPDIROLD" || true | |
281 | adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true | |
1a4d82fc | 282 | fi |
92a42be0 | 283 | cp -r "$TMPDIROLD" "$TMPDIR" |
1a4d82fc | 284 | |
92a42be0 SL |
285 | if [[ -f "$TMPDIR/app_process.wrap" || -f "$TMPDIR/app_process64.real" ]]; then |
286 | echo ">> Previous installation detected" | |
1a4d82fc | 287 | else |
92a42be0 | 288 | echo ">> New installation" |
1a4d82fc JJ |
289 | fi |
290 | ||
291 | echo '>> Generating wrappers' | |
292 | ||
293 | cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/" | |
92a42be0 SL |
294 | if [[ -n "$ASAN_RT64" ]]; then |
295 | cp "$ASAN_RT_PATH/$ASAN_RT64" "$TMPDIR/" | |
296 | fi | |
1a4d82fc JJ |
297 | |
298 | # FIXME: alloc_dealloc_mismatch=0 prevents a failure in libdvm startup, | |
299 | # which may or may not be a real bug (probably not). | |
92a42be0 SL |
300 | ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0,malloc_context_size=0 |
301 | ||
302 | function generate_zygote_wrapper { # from, to, asan_rt | |
303 | local _from=$1 | |
304 | local _to=$2 | |
305 | local _asan_rt=$3 | |
306 | cat <<EOF >"$TMPDIR/$_from" | |
307 | #!/system/bin/sh-from-zygote | |
308 | ASAN_OPTIONS=$ASAN_OPTIONS \\ | |
309 | ASAN_ACTIVATION_OPTIONS=include_if_exists=/data/local/tmp/asan.options.%b \\ | |
310 | LD_PRELOAD=\$LD_PRELOAD:$_asan_rt \\ | |
311 | exec $_to \$@ | |
312 | ||
313 | EOF | |
314 | } | |
315 | ||
316 | # On Android-L not allowing user segv handler breaks some applications. | |
317 | if [[ PRE_L -eq 0 ]]; then | |
318 | ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1" | |
319 | fi | |
320 | ||
1a4d82fc JJ |
321 | if [[ x$extra_options != x ]] ; then |
322 | ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options" | |
323 | fi | |
324 | ||
325 | # Zygote wrapper. | |
92a42be0 SL |
326 | if [[ -f "$TMPDIR/app_process64" ]]; then |
327 | # A 64-bit device. | |
328 | if [[ ! -f "$TMPDIR/app_process64.real" ]]; then | |
329 | # New installation. | |
330 | mv "$TMPDIR/app_process32" "$TMPDIR/app_process32.real" | |
331 | mv "$TMPDIR/app_process64" "$TMPDIR/app_process64.real" | |
332 | fi | |
333 | generate_zygote_wrapper "app_process32" "/system/bin/app_process32.real" "$ASAN_RT" | |
334 | generate_zygote_wrapper "app_process64" "/system/bin/app_process64.real" "$ASAN_RT64" | |
335 | else | |
336 | # A 32-bit device. | |
337 | generate_zygote_wrapper "app_process.wrap" "/system/bin/app_process32" "$ASAN_RT" | |
338 | fi | |
1a4d82fc JJ |
339 | |
340 | # General command-line tool wrapper (use for anything that's not started as | |
341 | # zygote). | |
342 | cat <<EOF >"$TMPDIR/asanwrapper" | |
343 | #!/system/bin/sh | |
92a42be0 SL |
344 | LD_PRELOAD=$ASAN_RT \\ |
345 | exec \$@ | |
346 | ||
347 | EOF | |
348 | ||
349 | if [[ -n "$ASAN_RT64" ]]; then | |
350 | cat <<EOF >"$TMPDIR/asanwrapper64" | |
351 | #!/system/bin/sh | |
352 | LD_PRELOAD=$ASAN_RT64 \\ | |
1a4d82fc JJ |
353 | exec \$@ |
354 | ||
355 | EOF | |
92a42be0 SL |
356 | fi |
357 | ||
358 | function install { # from, to, chmod, chcon | |
359 | local _from=$1 | |
360 | local _to=$2 | |
361 | local _mode=$3 | |
362 | local _context=$4 | |
363 | local _basename="$(basename "$_from")" | |
364 | echo "Installing $_to/$_basename $_mode $_context" | |
365 | adb_push "$_from" "$_to/$_basename" | |
366 | adb_shell chown root.shell "$_to/$_basename" | |
367 | if [[ -n "$_mode" ]]; then | |
368 | adb_shell chmod "$_mode" "$_to/$_basename" | |
369 | fi | |
370 | if [[ -n "$_context" ]]; then | |
371 | adb_shell chcon "$_context" "$_to/$_basename" | |
372 | fi | |
373 | } | |
1a4d82fc JJ |
374 | |
375 | if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then | |
92a42be0 SL |
376 | # Make SELinux happy by keeping app_process wrapper and the shell |
377 | # it runs on in zygote domain. | |
378 | ENFORCING=0 | |
379 | if adb_shell getenforce | grep Enforcing >/dev/null; then | |
380 | # Sometimes shell is not allowed to change file contexts. | |
381 | # Temporarily switch to permissive. | |
382 | ENFORCING=1 | |
383 | adb_shell setenforce 0 | |
384 | fi | |
385 | ||
386 | if [[ PRE_L -eq 1 ]]; then | |
387 | CTX=u:object_r:system_file:s0 | |
388 | else | |
389 | CTX=u:object_r:zygote_exec:s0 | |
390 | fi | |
391 | ||
1a4d82fc | 392 | echo '>> Pushing files to the device' |
92a42be0 SL |
393 | |
394 | if [[ -n "$ASAN_RT64" ]]; then | |
395 | install "$TMPDIR/$ASAN_RT" /system/lib 644 | |
396 | install "$TMPDIR/$ASAN_RT64" /system/lib64 644 | |
397 | install "$TMPDIR/app_process32" /system/bin 755 $CTX | |
398 | install "$TMPDIR/app_process32.real" /system/bin 755 $CTX | |
399 | install "$TMPDIR/app_process64" /system/bin 755 $CTX | |
400 | install "$TMPDIR/app_process64.real" /system/bin 755 $CTX | |
401 | install "$TMPDIR/asanwrapper" /system/bin 755 | |
402 | install "$TMPDIR/asanwrapper64" /system/bin 755 | |
403 | else | |
404 | install "$TMPDIR/$ASAN_RT" /system/lib 644 | |
405 | install "$TMPDIR/app_process32" /system/bin 755 $CTX | |
406 | install "$TMPDIR/app_process.wrap" /system/bin 755 $CTX | |
407 | install "$TMPDIR/asanwrapper" /system/bin 755 $CTX | |
408 | ||
409 | adb_shell rm /system/bin/app_process | |
410 | adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process | |
411 | fi | |
412 | ||
413 | adb_shell cp /system/bin/sh /system/bin/sh-from-zygote | |
414 | adb_shell chcon $CTX /system/bin/sh-from-zygote | |
415 | ||
416 | if [ $ENFORCING == 1 ]; then | |
417 | adb_shell setenforce 1 | |
418 | fi | |
1a4d82fc JJ |
419 | |
420 | echo '>> Restarting shell (asynchronous)' | |
92a42be0 SL |
421 | adb_shell stop |
422 | adb_shell start | |
1a4d82fc JJ |
423 | |
424 | echo '>> Please wait until the device restarts' | |
425 | else | |
426 | echo '>> Device is up to date' | |
427 | fi | |
428 | ||
429 | rm -r "$TMPDIRBASE" |