Home | History | Annotate | Download | only in scripts
      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 
     13 set -e
     14 
     15 HERE="$(cd "$(dirname "$0")" && pwd)"
     16 
     17 revert=no
     18 extra_options=
     19 device=
     20 lib=
     21 use_su=0
     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."
     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 
     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 { # 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 
    106 while [[ $# > 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
    143 done
    144 
    145 ADB=${ADB:-adb}
    146 if [[ x$device != x ]]; then
    147     ADB="$ADB -s $device"
    148 fi
    149 
    150 if [ $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
    161 fi
    162 
    163 echo '>> Remounting /system rw'
    164 adb_wait_for_device
    165 adb_root
    166 adb_wait_for_device
    167 adb_remount
    168 adb_wait_for_device
    169 
    170 get_device_arch ARCH
    171 echo "Target architecture: $ARCH"
    172 ASAN_RT="libclang_rt.asan-$ARCH-android.so"
    173 
    174 if [[ 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
    198 fi
    199 
    200 if [[ -d "$lib" ]]; then
    201     ASAN_RT_PATH="$lib"
    202 elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then
    203     ASAN_RT_PATH=$(dirname "$lib")
    204 elif [[ -f "$HERE/$ASAN_RT" ]]; then
    205     ASAN_RT_PATH="$HERE"
    206 elif [[ $(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
    213 fi
    214 
    215 if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then
    216     echo ">> ASan runtime library not found"
    217     exit 1
    218 fi
    219 
    220 TMPDIRBASE=$(mktemp -d)
    221 TMPDIROLD="$TMPDIRBASE/old"
    222 TMPDIR="$TMPDIRBASE/new"
    223 mkdir "$TMPDIROLD"
    224 
    225 RELEASE=$(adb_shell getprop ro.build.version.release)
    226 PRE_L=0
    227 if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
    228     PRE_L=1
    229 fi
    230 
    231 if ! 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
    241 fi
    242 
    243 echo '>> Copying files from the device'
    244 adb_pull /system/bin/app_process.wrap "$TMPDIROLD" || true
    245 adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true
    246 adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
    247 cp -r "$TMPDIROLD" "$TMPDIR"
    248 
    249 if [[ -f "$TMPDIR/app_process.wrap" ]]; then
    250     echo ">> Previous installation detected"
    251 else
    252     echo ">> New installation"
    253 fi
    254 
    255 echo '>> Generating wrappers'
    256 
    257 cp "$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).
    261 ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0
    262 
    263 # On Android-L not allowing user segv handler breaks some applications.
    264 if [[ PRE_L -eq 0 ]]; then
    265     ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1"
    266 fi
    267 
    268 if [[ x$extra_options != x ]] ; then
    269     ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options"
    270 fi
    271 
    272 # Zygote wrapper.
    273 cat <<EOF >"$TMPDIR/app_process.wrap"
    274 #!/system/bin/sh-from-zygote
    275 ASAN_OPTIONS=$ASAN_OPTIONS \\
    276 LD_PRELOAD=\$LD_PRELOAD:$ASAN_RT \\
    277 exec /system/bin/app_process32 \$@
    278 
    279 EOF
    280 
    281 # General command-line tool wrapper (use for anything that's not started as
    282 # zygote).
    283 cat <<EOF >"$TMPDIR/asanwrapper"
    284 #!/system/bin/sh
    285 LD_PRELOAD=$ASAN_RT \\
    286 exec \$@
    287 
    288 EOF
    289 
    290 if ! ( 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'
    340 else
    341     echo '>> Device is up to date'
    342 fi
    343 
    344 rm -r "$TMPDIRBASE"
    345