1#!/bin/bash
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
13set -e
14
15HERE="$(cd "$(dirname "$0")" && pwd)"
16
17revert=no
18extra_options=
19device=
20lib=
21use_su=0
22
23function 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."
30    echo "  --use-su: Use 'su -c' prefix for every adb command instead of using"
31    echo "            'adb root' once."
32    echo
33    exit 1
34}
35
36function 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
48function 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
62function adb_shell {
63  if [ $use_su -eq 0 ]; then
64    $ADB shell $@
65  else
66    $ADB shell su -c "$*"
67  fi
68}
69
70function adb_root {
71  if [ $use_su -eq 0 ]; then
72    $ADB root
73  fi
74}
75
76function adb_wait_for_device {
77  $ADB wait-for-device
78}
79
80function 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
91function get_device_arch { # OUTVAR
92    local _outvar=$1
93    local _ABI=$(adb_shell getprop ro.product.cpu.abi)
94    local _ARCH=
95    if [[ $_ABI == x86* ]]; then
96        _ARCH=i686
97    elif [[ $_ABI == armeabi* ]]; then
98        _ARCH=arm
99    else
100        echo "Unrecognized device ABI: $_ABI"
101        exit 1
102    fi
103    eval $_outvar=\$_ARCH
104}
105
106while [[ $# > 0 ]]; do
107  case $1 in
108    --revert)
109      revert=yes
110      ;;
111    --extra-options)
112      shift
113      if [[ $# == 0 ]]; then
114        echo "--extra-options requires an argument."
115        exit 1
116      fi
117      extra_options="$1"
118      ;;
119    --lib)
120      shift
121      if [[ $# == 0 ]]; then
122        echo "--lib requires an argument."
123        exit 1
124      fi
125      lib="$1"
126      ;;
127    --device)
128      shift
129      if [[ $# == 0 ]]; then
130        echo "--device requires an argument."
131        exit 1
132      fi
133      device="$1"
134      ;;
135    --use-su)
136      use_su=1
137      ;;
138    *)
139      usage
140      ;;
141  esac
142  shift
143done
144
145ADB=${ADB:-adb}
146if [[ x$device != x ]]; then
147    ADB="$ADB -s $device"
148fi
149
150if [ $use_su -eq 1 ]; then
151  # Test if 'su' is present on the device
152  SU_TEST_OUT=`$ADB shell su -c "echo foo" 2>&1 | sed 's/\r$//'`
153  if [ $? != 0 -o "$SU_TEST_OUT" != "foo" ]; then
154    echo "ERROR: Cannot use 'su -c':"
155    echo "$ adb shell su -c \"echo foo\""
156    echo $SU_TEST_OUT
157    echo "Check that 'su' binary is correctly installed on the device or omit"
158    echo "            --use-su flag"
159    exit 1
160  fi
161fi
162
163echo '>> Remounting /system rw'
164adb_wait_for_device
165adb_root
166adb_wait_for_device
167adb_remount
168adb_wait_for_device
169
170get_device_arch ARCH
171echo "Target architecture: $ARCH"
172ASAN_RT="libclang_rt.asan-$ARCH-android.so"
173
174if [[ x$revert == xyes ]]; then
175    echo '>> Uninstalling ASan'
176
177    if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
178        echo '>> Pre-L device detected.'
179        adb_shell mv /system/bin/app_process.real /system/bin/app_process
180        adb_shell rm /system/bin/asanwrapper
181    else
182        adb_shell rm /system/bin/app_process.wrap
183        adb_shell rm /system/bin/asanwrapper
184        adb_shell rm /system/bin/app_process
185        adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
186    fi
187
188    echo '>> Restarting shell'
189    adb_shell stop
190    adb_shell start
191
192    # Remove the library on the last step to give a chance to the 'su' binary to
193    # be executed without problem.
194    adb_shell rm /system/lib/$ASAN_RT
195
196    echo '>> Done'
197    exit 0
198fi
199
200if [[ -d "$lib" ]]; then
201    ASAN_RT_PATH="$lib"
202elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then
203    ASAN_RT_PATH=$(dirname "$lib")
204elif [[ -f "$HERE/$ASAN_RT" ]]; then
205    ASAN_RT_PATH="$HERE"
206elif [[ $(basename "$HERE") == "bin" ]]; then
207    # We could be in the toolchain's base directory.
208    # Consider ../lib, ../lib/asan, ../lib/linux and ../lib/clang/$VERSION/lib/linux.
209    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)
210    if [[ -n "$P" ]]; then
211        ASAN_RT_PATH="$(dirname "$P")"
212    fi
213fi
214
215if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then
216    echo ">> ASan runtime library not found"
217    exit 1
218fi
219
220TMPDIRBASE=$(mktemp -d)
221TMPDIROLD="$TMPDIRBASE/old"
222TMPDIR="$TMPDIRBASE/new"
223mkdir "$TMPDIROLD"
224
225RELEASE=$(adb_shell getprop ro.build.version.release)
226PRE_L=0
227if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
228    PRE_L=1
229fi
230
231if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
232
233    if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then
234        echo '>> Old-style ASan installation detected. Reverting.'
235        adb_shell mv /system/bin/app_process.real /system/bin/app_process
236    fi
237
238    echo '>> Pre-L device detected. Setting up app_process symlink.'
239    adb_shell mv /system/bin/app_process /system/bin/app_process32
240    adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
241fi
242
243echo '>> Copying files from the device'
244adb_pull /system/bin/app_process.wrap "$TMPDIROLD" || true
245adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true
246adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
247cp -r "$TMPDIROLD" "$TMPDIR"
248
249if [[ -f "$TMPDIR/app_process.wrap" ]]; then
250    echo ">> Previous installation detected"
251else
252    echo ">> New installation"
253fi
254
255echo '>> Generating wrappers'
256
257cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/"
258
259# FIXME: alloc_dealloc_mismatch=0 prevents a failure in libdvm startup,
260# which may or may not be a real bug (probably not).
261ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0
262
263# On Android-L not allowing user segv handler breaks some applications.
264if [[ PRE_L -eq 0 ]]; then
265    ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1"
266fi
267
268if [[ x$extra_options != x ]] ; then
269    ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options"
270fi
271
272# Zygote wrapper.
273cat <<EOF >"$TMPDIR/app_process.wrap"
274#!/system/bin/sh-from-zygote
275ASAN_OPTIONS=$ASAN_OPTIONS \\
276LD_PRELOAD=\$LD_PRELOAD:$ASAN_RT \\
277exec /system/bin/app_process32 \$@
278
279EOF
280
281# General command-line tool wrapper (use for anything that's not started as
282# zygote).
283cat <<EOF >"$TMPDIR/asanwrapper"
284#!/system/bin/sh
285LD_PRELOAD=$ASAN_RT \\
286exec \$@
287
288EOF
289
290if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
291    echo '>> Pushing files to the device'
292    adb_push "$TMPDIR/$ASAN_RT" /system/lib/
293    adb_push "$TMPDIR/app_process.wrap" /system/bin
294    adb_push "$TMPDIR/asanwrapper" /system/bin
295
296    adb_shell rm /system/bin/app_process
297    adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process
298
299    adb_shell chown root.shell \
300        /system/lib/"$ASAN_RT" \
301        /system/bin/app_process.wrap \
302        /system/bin/asanwrapper
303    adb_shell chmod 644 \
304        /system/lib/"$ASAN_RT"
305    adb_shell chmod 755 \
306        /system/bin/app_process.wrap \
307        /system/bin/asanwrapper
308
309    # Make SELinux happy by keeping app_process wrapper and the shell
310    # it runs on in zygote domain.
311    ENFORCING=0
312    if adb_shell getenforce | grep Enforcing >/dev/null; then
313        # Sometimes shell is not allowed to change file contexts.
314        # Temporarily switch to permissive.
315        ENFORCING=1
316        adb_shell setenforce 0
317    fi
318
319    adb_shell cp /system/bin/sh /system/bin/sh-from-zygote
320
321    if [[ PRE_L -eq 1 ]]; then
322        CTX=u:object_r:system_file:s0
323    else
324        CTX=u:object_r:zygote_exec:s0
325    fi
326    adb_shell chcon $CTX \
327        /system/bin/sh-from-zygote \
328        /system/bin/app_process.wrap \
329        /system/bin/app_process32
330
331    if [ $ENFORCING == 1 ]; then
332        adb_shell setenforce 1
333    fi
334
335    echo '>> Restarting shell (asynchronous)'
336    adb_shell stop
337    adb_shell start
338
339    echo '>> Please wait until the device restarts'
340else
341    echo '>> Device is up to date'
342fi
343
344rm -r "$TMPDIRBASE"
345