[asan] Support 'su' rooted devices in ASan setup script.
authorEvgeniy Stepanov <eugeni.stepanov@gmail.com>
Mon, 16 Feb 2015 10:22:12 +0000 (10:22 +0000)
committerEvgeniy Stepanov <eugeni.stepanov@gmail.com>
Mon, 16 Feb 2015 10:22:12 +0000 (10:22 +0000)
Android devices may not support 'adb root', but be rooted with 'su'
binary. This patch makes it possible to install ASAN to such
devices. When --use-su flag is specified, most 'adb ...' commangs are
changed to 'adb su -c "..."'.

Some other notes:
 * 'readlink' changed to 'ls -l', since not all devices have readlink
   in their firmware.
 * removing ASan library step moved to very end, because 'su' may not
   run properly without this library until shell will be restarted.

Patch by Dmitry <ripp at yandex-team dot ru>.

llvm-svn: 229368

compiler-rt/lib/asan/scripts/asan_device_setup

index c143b9f..104e07b 100755 (executable)
@@ -18,6 +18,7 @@ revert=no
 extra_options=
 device=
 lib=
+use_su=0
 
 function usage {
     echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]"
@@ -26,13 +27,70 @@ function usage {
     echo "  --extra-options: Extra ASAN_OPTIONS."
     echo "  --device: Install to the given device. Use 'adb devices' to find"
     echo "            device-id."
+    echo "  --use-su: Use 'su -c' prefix for every adb command instead of using"
+    echo "            'adb root' once."
     echo
     exit 1
 }
 
+function adb_push {
+  if [ $use_su -eq 0 ]; then
+    $ADB push "$1" "$2"
+  else
+    local FILENAME=$(basename $1)
+    $ADB push "$1" "/data/local/tmp/$FILENAME"
+    $ADB shell su -c "rm \\\"$2/$FILENAME\\\"" >&/dev/null
+    $ADB shell su -c "cat \\\"/data/local/tmp/$FILENAME\\\" > \\\"$2/$FILENAME\\\""
+    $ADB shell su -c "rm \\\"/data/local/tmp/$FILENAME\\\""
+  fi
+}
+
+function adb_remount {
+  if [ $use_su -eq 0 ]; then
+    $ADB remount
+  else
+    local STORAGE=`$ADB shell mount | grep /system | cut -d ' ' -f1`
+    if [ "$STORAGE" != "" ]; then
+      echo Remounting $STORAGE at /system
+      $ADB shell su -c "mount -o remount,rw $STORAGE /system"
+    else
+      echo Failed to get storage device name for "/system" mount point
+    fi
+  fi
+}
+
+function adb_shell {
+  if [ $use_su -eq 0 ]; then
+    $ADB shell $@
+  else
+    $ADB shell su -c "$*"
+  fi
+}
+
+function adb_root {
+  if [ $use_su -eq 0 ]; then
+    $ADB root
+  fi
+}
+
+function adb_wait_for_device {
+  $ADB wait-for-device
+}
+
+function adb_pull {
+  if [ $use_su -eq 0 ]; then
+    $ADB pull "$1" "$2"
+  else
+    local FILENAME=$(basename $1)
+    $ADB shell rm "/data/local/tmp/$FILENAME" >&/dev/null
+    $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\\\"" &&
+    $ADB pull "/data/local/tmp/$FILENAME" "$2" >&/dev/null && $ADB shell "rm \"/data/local/tmp/$FILENAME\""
+  fi
+}
+
 function get_device_arch { # OUTVAR
     local _outvar=$1
-    local _ABI=$($ADB shell getprop ro.product.cpu.abi)
+    local _ABI=$(adb_shell getprop ro.product.cpu.abi)
     local _ARCH=
     if [[ $_ABI == x86* ]]; then
         _ARCH=i686
@@ -74,6 +132,9 @@ while [[ $# > 0 ]]; do
       fi
       device="$1"
       ;;
+    --use-su)
+      use_su=1
+      ;;
     *)
       usage
       ;;
@@ -86,12 +147,25 @@ if [[ x$device != x ]]; then
     ADB="$ADB -s $device"
 fi
 
+if [ $use_su -eq 1 ]; then
+  # Test if 'su' is present on the device
+  SU_TEST_OUT=`$ADB shell su -c "echo foo" 2>&1 | sed 's/\r$//'`
+  if [ $? != 0 -o "$SU_TEST_OUT" != "foo" ]; then
+    echo "ERROR: Cannot use 'su -c':"
+    echo "$ adb shell su -c \"echo foo\""
+    echo $SU_TEST_OUT
+    echo "Check that 'su' binary is correctly installed on the device or omit"
+    echo "            --use-su flag"
+    exit 1
+  fi
+fi
+
 echo '>> Remounting /system rw'
-$ADB wait-for-device
-$ADB root
-$ADB wait-for-device
-$ADB remount
-$ADB wait-for-device
+adb_wait_for_device
+adb_root
+adb_wait_for_device
+adb_remount
+adb_wait_for_device
 
 get_device_arch ARCH
 echo "Target architecture: $ARCH"
@@ -100,22 +174,24 @@ ASAN_RT="libclang_rt.asan-$ARCH-android.so"
 if [[ x$revert == xyes ]]; then
     echo '>> Uninstalling ASan'
 
-    if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then
+    if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
         echo '>> Pre-L device detected.'
-        $ADB shell mv /system/bin/app_process.real /system/bin/app_process
-        $ADB shell rm /system/bin/asanwrapper
-        $ADB shell rm /system/lib/$ASAN_RT
+        adb_shell mv /system/bin/app_process.real /system/bin/app_process
+        adb_shell rm /system/bin/asanwrapper
     else
-        $ADB shell rm /system/bin/app_process.wrap
-        $ADB shell rm /system/bin/asanwrapper
-        $ADB shell rm /system/lib/$ASAN_RT
-        $ADB shell rm /system/bin/app_process
-        $ADB shell ln -s /system/bin/app_process32 /system/bin/app_process
+        adb_shell rm /system/bin/app_process.wrap
+        adb_shell rm /system/bin/asanwrapper
+        adb_shell rm /system/bin/app_process
+        adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
     fi
 
     echo '>> Restarting shell'
-    $ADB shell stop
-    $ADB shell start
+    adb_shell stop
+    adb_shell start
+
+    # Remove the library on the last step to give a chance to the 'su' binary to
+    # be executed without problem.
+    adb_shell rm /system/lib/$ASAN_RT
 
     echo '>> Done'
     exit 0
@@ -146,28 +222,28 @@ TMPDIROLD="$TMPDIRBASE/old"
 TMPDIR="$TMPDIRBASE/new"
 mkdir "$TMPDIROLD"
 
-RELEASE=$($ADB shell getprop ro.build.version.release)
+RELEASE=$(adb_shell getprop ro.build.version.release)
 PRE_L=0
 if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
     PRE_L=1
 fi
 
-if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then
+if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
 
-    if $ADB pull /system/bin/app_process.real /dev/null >&/dev/null; then
+    if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then
         echo '>> Old-style ASan installation detected. Reverting.'
-        $ADB shell mv /system/bin/app_process.real /system/bin/app_process
+        adb_shell mv /system/bin/app_process.real /system/bin/app_process
     fi
 
     echo '>> Pre-L device detected. Setting up app_process symlink.'
-    $ADB shell mv /system/bin/app_process /system/bin/app_process32
-    $ADB shell ln -s /system/bin/app_process32 /system/bin/app_process
+    adb_shell mv /system/bin/app_process /system/bin/app_process32
+    adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
 fi
 
 echo '>> Copying files from the device'
-$ADB pull /system/bin/app_process.wrap "$TMPDIROLD" || true
-$ADB pull /system/bin/asanwrapper "$TMPDIROLD" || true
-$ADB pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
+adb_pull /system/bin/app_process.wrap "$TMPDIROLD" || true
+adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true
+adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
 cp -r "$TMPDIROLD" "$TMPDIR"
 
 if [[ -f "$TMPDIR/app_process.wrap" ]]; then
@@ -213,52 +289,52 @@ EOF
 
 if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
     echo '>> Pushing files to the device'
-    $ADB push "$TMPDIR/$ASAN_RT" /system/lib/
-    $ADB push "$TMPDIR/app_process.wrap" /system/bin/app_process.wrap
-    $ADB push "$TMPDIR/asanwrapper" /system/bin/asanwrapper
+    adb_push "$TMPDIR/$ASAN_RT" /system/lib/
+    adb_push "$TMPDIR/app_process.wrap" /system/bin
+    adb_push "$TMPDIR/asanwrapper" /system/bin
 
-    $ADB shell rm /system/bin/app_process
-    $ADB shell ln -s /system/bin/app_process.wrap /system/bin/app_process
+    adb_shell rm /system/bin/app_process
+    adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process
 
-    $ADB shell chown root.shell \
+    adb_shell chown root.shell \
         /system/lib/"$ASAN_RT" \
         /system/bin/app_process.wrap \
         /system/bin/asanwrapper
-    $ADB shell chmod 644 \
+    adb_shell chmod 644 \
         /system/lib/"$ASAN_RT"
-    $ADB shell chmod 755 \
+    adb_shell chmod 755 \
         /system/bin/app_process.wrap \
         /system/bin/asanwrapper
 
     # Make SELinux happy by keeping app_process wrapper and the shell
     # it runs on in zygote domain.
     ENFORCING=0
-    if $ADB shell getenforce | grep Enforcing >/dev/null; then
+    if adb_shell getenforce | grep Enforcing >/dev/null; then
         # Sometimes shell is not allowed to change file contexts.
         # Temporarily switch to permissive.
         ENFORCING=1
-        $ADB shell setenforce 0
+        adb_shell setenforce 0
     fi
 
-    $ADB shell cp /system/bin/sh /system/bin/sh-from-zygote
+    adb_shell cp /system/bin/sh /system/bin/sh-from-zygote
 
     if [[ PRE_L -eq 1 ]]; then
         CTX=u:object_r:system_file:s0
     else
         CTX=u:object_r:zygote_exec:s0
     fi
-    $ADB shell chcon $CTX \
+    adb_shell chcon $CTX \
         /system/bin/sh-from-zygote \
         /system/bin/app_process.wrap \
         /system/bin/app_process32
 
     if [ $ENFORCING == 1 ]; then
-        $ADB shell setenforce 1
+        adb_shell setenforce 1
     fi
 
     echo '>> Restarting shell (asynchronous)'
-    $ADB shell stop
-    $ADB shell start
+    adb_shell stop
+    adb_shell start
 
     echo '>> Please wait until the device restarts'
 else