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=
21
22function usage {
23    echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]"
24    echo "  --revert: Uninstall ASan from the device."
25    echo "  --lib: Path to ASan runtime library."
26    echo "  --extra-options: Extra ASAN_OPTIONS."
27    echo "  --device: Install to the given device. Use 'adb devices' to find"
28    echo "            device-id."
29    echo
30    exit 1
31}
32
33function get_device_arch { # OUTVAR
34    local _outvar=$1
35    local _ABI=$($ADB shell getprop ro.product.cpu.abi)
36    local _ARCH=
37    if [[ $_ABI == x86* ]]; then
38        _ARCH=i686
39    elif [[ $_ABI == armeabi* ]]; then
40        _ARCH=arm
41    else
42        echo "Unrecognized device ABI: $_ABI"
43        exit 1
44    fi
45    eval $_outvar=\$_ARCH
46}
47
48while [[ $# > 0 ]]; do
49  case $1 in
50    --revert)
51      revert=yes
52      ;;
53    --extra-options)
54      shift
55      if [[ $# == 0 ]]; then
56        echo "--extra-options requires an argument."
57        exit 1
58      fi
59      extra_options="$1"
60      ;;
61    --lib)
62      shift
63      if [[ $# == 0 ]]; then
64        echo "--lib requires an argument."
65        exit 1
66      fi
67      lib="$1"
68      ;;
69    --device)
70      shift
71      if [[ $# == 0 ]]; then
72        echo "--device requires an argument."
73        exit 1
74      fi
75      device="$1"
76      ;;
77    *)
78      usage
79      ;;
80  esac
81  shift
82done
83
84ADB=${ADB:-adb}
85if [[ x$device != x ]]; then
86    ADB="$ADB -s $device"
87fi
88
89echo '>> Remounting /system rw'
90$ADB root
91$ADB wait-for-device
92$ADB remount
93$ADB wait-for-device
94
95get_device_arch ARCH
96echo "Target architecture: $ARCH"
97ASAN_RT="libclang_rt.asan-$ARCH-android.so"
98
99if [[ x$revert == xyes ]]; then
100    echo '>> Uninstalling ASan'
101
102    if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then
103        echo '>> Pre-L device detected.'
104        $ADB shell mv /system/bin/app_process.real /system/bin/app_process
105        $ADB shell rm /system/bin/asanwrapper
106        $ADB shell rm /system/lib/$ASAN_RT
107    else
108        $ADB shell rm /system/bin/app_process.wrap
109        $ADB shell rm /system/bin/asanwrapper
110        $ADB shell rm /system/lib/$ASAN_RT
111        $ADB shell rm /system/bin/app_process
112        $ADB shell ln -s /system/bin/app_process32 /system/bin/app_process
113    fi
114
115    echo '>> Restarting shell'
116    $ADB shell stop
117    $ADB shell start
118
119    echo '>> Done'
120    exit 0
121fi
122
123if [[ -d "$lib" ]]; then
124    ASAN_RT_PATH="$lib"
125elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then
126    ASAN_RT_PATH=$(dirname "$lib")
127elif [[ -f "$HERE/$ASAN_RT" ]]; then
128    ASAN_RT_PATH="$HERE"
129elif [[ $(basename "$HERE") == "bin" ]]; then
130    # We could be in the toolchain's base directory.
131    # Consider ../lib, ../lib/asan, ../lib/linux and ../lib/clang/$VERSION/lib/linux.
132    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)
133    if [[ -n "$P" ]]; then
134        ASAN_RT_PATH="$(dirname "$P")"
135    fi
136fi
137
138if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then
139    echo ">> ASan runtime library not found"
140    exit 1
141fi
142
143TMPDIRBASE=$(mktemp -d)
144TMPDIROLD="$TMPDIRBASE/old"
145TMPDIR="$TMPDIRBASE/new"
146mkdir "$TMPDIROLD"
147
148RELEASE=$($ADB shell getprop ro.build.version.release)
149PRE_L=0
150if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
151    PRE_L=1
152fi
153
154if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then
155
156    if $ADB pull /system/bin/app_process.real /dev/null >&/dev/null; then
157        echo '>> Old-style ASan installation detected. Reverting.'
158        $ADB shell mv /system/bin/app_process.real /system/bin/app_process
159    fi
160
161    echo '>> Pre-L device detected. Setting up app_process symlink.'
162    $ADB shell mv /system/bin/app_process /system/bin/app_process32
163    $ADB shell ln -s /system/bin/app_process32 /system/bin/app_process
164fi
165
166echo '>> Copying files from the device'
167$ADB pull /system/bin/app_process.wrap "$TMPDIROLD" || true
168$ADB pull /system/bin/asanwrapper "$TMPDIROLD" || true
169$ADB pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
170cp -r "$TMPDIROLD" "$TMPDIR"
171
172if [[ -f "$TMPDIR/app_process.wrap" ]]; then
173    echo ">> Previous installation detected"
174else
175    echo ">> New installation"
176fi
177
178echo '>> Generating wrappers'
179
180cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/"
181
182# FIXME: alloc_dealloc_mismatch=0 prevents a failure in libdvm startup,
183# which may or may not be a real bug (probably not).
184ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0
185
186# On Android-L not allowing user segv handler breaks some applications.
187if $ADB shell 'echo $LD_PRELOAD' | grep libsigchain.so >&/dev/null; then
188    ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1"
189fi
190
191if [[ x$extra_options != x ]] ; then
192    ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options"
193fi
194
195# Zygote wrapper.
196cat <<EOF >"$TMPDIR/app_process.wrap"
197#!/system/bin/sh-from-zygote
198ASAN_OPTIONS=$ASAN_OPTIONS \\
199LD_PRELOAD=\$LD_PRELOAD:$ASAN_RT \\
200exec /system/bin/app_process32 \$@
201
202EOF
203
204# General command-line tool wrapper (use for anything that's not started as
205# zygote).
206cat <<EOF >"$TMPDIR/asanwrapper"
207#!/system/bin/sh
208LD_PRELOAD=$ASAN_RT \\
209exec \$@
210
211EOF
212
213if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
214    echo '>> Pushing files to the device'
215    $ADB push "$TMPDIR/$ASAN_RT" /system/lib/
216    $ADB push "$TMPDIR/app_process.wrap" /system/bin/app_process.wrap
217    $ADB push "$TMPDIR/asanwrapper" /system/bin/asanwrapper
218
219    $ADB shell rm /system/bin/app_process
220    $ADB shell ln -s /system/bin/app_process.wrap /system/bin/app_process
221
222    $ADB shell chown root.shell \
223        /system/lib/"$ASAN_RT" \
224        /system/bin/app_process.wrap \
225        /system/bin/asanwrapper
226    $ADB shell chmod 644 \
227        /system/lib/"$ASAN_RT"
228    $ADB shell chmod 755 \
229        /system/bin/app_process.wrap \
230        /system/bin/asanwrapper
231
232    # Make SELinux happy by keeping app_process wrapper and the shell
233    # it runs on in zygote domain.
234    ENFORCING=0
235    if $ADB shell getenforce | grep Enforcing >/dev/null; then
236        # Sometimes shell is not allowed to change file contexts.
237        # Temporarily switch to permissive.
238        ENFORCING=1
239        $ADB shell setenforce 0
240    fi
241
242    $ADB shell cp /system/bin/sh /system/bin/sh-from-zygote
243
244    if [[ PRE_L -eq 1 ]]; then
245        CTX=u:object_r:system_file:s0
246    else
247        CTX=u:object_r:zygote_exec:s0
248    fi
249    $ADB shell chcon $CTX \
250        /system/bin/sh-from-zygote \
251        /system/bin/app_process.wrap \
252        /system/bin/app_process32
253
254    if [ $ENFORCING == 1 ]; then
255        $ADB shell setenforce 1
256    fi
257
258    echo '>> Restarting shell (asynchronous)'
259    $ADB shell stop
260    $ADB shell start
261
262    echo '>> Please wait until the device restarts'
263else
264    echo '>> Device is up to date'
265fi
266
267rm -r "$TMPDIRBASE"
268