Próbuję zbudować (z Clangment) Moja aplikacja z opisanym tutaj opisanym TUTAJ (HTTPS: // Github.com/google/sanitizers/wiki/addresssanitizer, bardziej precyzyjnie tutaj: HTTPS: //Github.com/google/saniTizers/wiki/addresssanitizerONAndroid), ale mam problemy ze zrozumieniem całego procesu, zwłaszcza przy użyciu gradle.

Wygląda na to, że jest co najmniej 3 sposoby umożliwienia:

1 °) Po pierwszym linku T mówi, że wszystko, co musisz zrobić, to zrobić:

  • Dodawanie -fsanitize=address do CPPFLAGS + opcjonalne -fno-omit-frame-pointer

  • Dodawanie -fsanitize=address do flag liniowców (czy to konieczne?)

2 °) Po drugim łączu wydaje się, że musisz wykonać:

  • tak samo jak pierwszy
  • Urządzenie root Uruchom na nim asan_device_setup przez ADB
  • Dodaj gdzieś LD_PRELOAD=libclang_rt.asan-arm-android.so? Myślę, że należy umieścić w części "argumentów" Gradle EnterniseBuildBuild? Ale gdzie aplikacja może znaleźć tę bibliotekę? Czy muszę to połączyć? Czy jest już gdzieś na urządzeniu?

3 °) Znalazłem również "nowy" sposób robienia tego, co nie powinno wymagać dostępu do korzeni (cóż, ale jest to błąd, z którego należy poprawić):

https://virtualrealitypop.com/reo-ndk-secrets-7d075A9B084.

Ta metoda faktycznie robi to, co robi się w pierwszym i drugim punkcie, plus uruchamia aplikację, uruchamiając skrypt powłoki, który eksportuje pewne wartości dla ASAN do pracy.


Jeśli chodzi o moje dochodzenie, jestem trochę zdezorientowany na tym, jaka jest właściwa metoda posiadania w pełni odesetizowanej aplikacji (z bibliotekami statystycznie) pracującymi na moim zakorzenionym emulatorze.

Dalsze, które poszedłem, było rzeczywiście zbudować i uruchomić aplikację (przy użyciu 2 °), ale bez określania flagi LD_Preload), ale aplikacja ulega awarii z przepełnieniem kontenera w niektórych funkcjach eglmakeCurrent, która nie była nawet częścią mojego kodu, a Nie dostaję dla tego żadnego stosu:

02-19 16:26:21.553 28771-28789/com.mycompany.myapp I/zygote: Background concurrent copying GC freed 10159(1175KB) AllocSpace objects, 12(304KB) LOS objects, 50% free, 2MB/4MB, paused 144.861ms total 1.252s
[ 02-19 16:26:21.554 28771:28956 I/         ]
=================================================================
[ 02-19 16:26:21.554 28771:28956 I/         ]
[ 02-19 16:26:21.557 28771:28956 I/         ]
[ 02-19 16:26:21.563 28771:28956 I/         ]
==28771==ERROR: AddressSanitizer: container-overflow on address 0xa136e990 at pc 0xa49849e2 bp 0x82e60558 sp 0x82e60128
[ 02-19 16:26:21.563 28771:28956 I/         ]
[ 02-19 16:26:21.565 28771:28956 I/         ]
[ 02-19 16:26:21.566 28771:28956 I/         ]
WRITE of size 2 at 0xa136e990 thread T334 (GLThread 337)
[ 02-19 16:26:21.566 28771:28956 I/         ]

Nie jestem pewien, czy jest to prawdziwy przepełnienie, ponieważ nie jestem pewien, że moja aplikacja jest zbudowana z sanitizer (zbudowałem moją + całą moją statykę, ale jest wystarczająco dużo?), I android android-gradle-plugin clang address-sanitizer

2
downstroy 19 luty 2018, 19:28

3 odpowiedzi

Najlepsza odpowiedź

Więc po odrobinie walki użyłem metody opisanej https://virtualrealitypop.com/reo-ndk-secrets-7d075A9B084. Dodałem nowy cel sanitize_debug w mojej build.gradle z następującymi:

tasks.whenTaskAdded { task ->
    if (task.name == 'generateSanitize_debugBuildConfig') {
        task.dependsOn createWrapScriptAddDir
    }
}

task deleteASAN(type: Delete) {
    delete 'jni/sanitizer/'
}

static def writeWrapScriptToFullyCompileJavaApp(wrapFile, abi) {
    if(abi == "armeabi" || abi == "armeabi-v7a")
        abi = "arm"
    if(abi == "arm64-v8a")
        abi = "aarch64"
    if (abi == "x86")
        abi = "i686"
    wrapFile.withWriter { writer ->
        writer.write('#!/system/bin/sh\n')
        writer.write('HERE="$(cd "$(dirname "$0")" && pwd)"\n')
        writer.write('export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1\n')
        writer.write('export ASAN_ACTIVATION_OPTIONS=include_if_exists=/data/local/tmp/asan.options.b\n')
        writer.write("export LD_PRELOAD=\$HERE/libclang_rt.asan-${abi}-android.so\n")
        writer.write('\$@\n')
    }
}


task copyASANLibs {
    def libDirs = android.ndkDirectory.absolutePath + "/toolchains/llvm/prebuilt/"

    for (String abi : rootProject.ext.abiFilters) {
        def destDir = new File("wizards/src/sanitize_debug/jniLibs/" + abi)
        destDir.mkdirs()

        def renamedAbi = abi
        if(abi == "armeabi-v7a" || abi == "armeabi")
            renamedAbi = "arm"
        if(abi == "arm64-v8a")
            renamedAbi = "aarch64"
        if (abi == "x86")
            renamedAbi = "i686"


        FileTree tree = fileTree(dir: libDirs).include("**/*asan*${renamedAbi}*.so")
        tree.each { File file ->
            copy {
                from file
                into destDir.absolutePath
            }
        }
    }
}
task createWrapScriptAddDir(dependsOn: copyASANLibs) {
    for (String abi : rootProject.ext.abiFilters) {
        def dir = new File("wizards/src/sanitize_debug/resources/lib/" + abi)
        dir.mkdirs()
        def wrapFile = new File(dir, "wrap.sh")
        wrapFile.setExecutable(true, false)
        writeWrapScriptToFullyCompileJavaApp(wrapFile, abi)
    }
}

Rzeczy, które należy poprawić, są

1 °) Faza oczyszczania podczas przełączania na kompilację bez odkładzii musi być wywoływana ręcznie,

2 °) skrypt opakowania jest zbudowany i pakowany dla wszystkich architektur ramienia, który jest obejściem faktem, że nie można łatwo określić architektury docelowej do SCRIP.SH (patrz błąd https://issuetracker.google.com/issues/74058603 )

5
downstroy 23 kwiecień 2018, 14:09

Aby również debugować możesz dostosować odpowiedź z w dół i skrypt Tweak Wrap.sh, ponieważ prosta wersja opakowania. SH zapobiegnie debugowaniu (z https://developer.Android.com/ndk/guides/ skrypt wrap-):

        writer.write('#!/system/bin/sh\n')
        writer.write('HERE="$(cd "$(dirname "$0")" && pwd)"\n')
        writer.write('export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1\n')
        writer.write('export ASAN_ACTIVATION_OPTIONS=include_if_exists=/data/local/tmp/asan.options.b\n')
        writer.write("export LD_PRELOAD=\$HERE/libclang_rt.asan-${abi}-android.so\n")
        writer.write('cmd=$1\n')
        writer.write('shift\n')
        writer.write('os_version=$(getprop ro.build.version.sdk)\n')
        writer.write('if [ "$os_version" -eq "27" ]; then\n')
        writer.write('cmd="$cmd -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable $@"\n')
        writer.write('elif [ "$os_version" -eq "28" ]; then\n')
        writer.write('cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable $@"\n')
        writer.write('else\n')
        writer.write('cmd="$cmd -XjdwpProvider:adbconnection $@"\n')
        writer.write('fi\n')
        writer.write('exec $cmd\n')
2
JE42 12 czerwiec 2019, 11:08

Spędziłem kilka godzin dowiedzieć się, jak włączyć sanitizer tylko w budowie debugowania. Oto moje kompletne rozwiązanie (na podstawie w dół i JE42 - Dziękuję):

Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())

project.ext {
    ndkDir = properties.getProperty('ndk.dir')
}

task deleteASAN(type: Delete) {
    delete 'app\\src\\sanitizer'
}

clean.dependsOn(deleteASAN)

static def writeWrapScriptToFullyCompileJavaApp(wrapFile, arch) {
    wrapFile.withWriter { writer ->
        writer.write('#!/system/bin/sh\n')
        writer.write('HERE="$(cd "$(dirname "$0")" && pwd)"\n')
        writer.write('export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1\n')
        writer.write('export ASAN_ACTIVATION_OPTIONS=include_if_exists=/data/local/tmp/asan.options.b\n')
        writer.write("export LD_PRELOAD=\$HERE/libclang_rt.asan-${arch}-android.so\n")
        writer.write('cmd=$1\n')
        writer.write('shift\n')
        writer.write('os_version=$(getprop ro.build.version.sdk)\n')
        writer.write('if [ "$os_version" -eq "27" ]; then\n')
        writer.write('cmd="$cmd -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable $@"\n')
        writer.write('elif [ "$os_version" -eq "28" ]; then\n')
        writer.write('cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable $@"\n')
        writer.write('else\n')
        writer.write('cmd="$cmd -XjdwpProvider:adbconnection $@"\n')
        writer.write('fi\n')
        writer.write('exec $cmd\n')
    }
}

def copySanitizerLibAndWrapScript(ndkdir) {
    def sanitizerLibDir = new File(ndkdir).absolutePath + "\\toolchains\\llvm\\prebuilt\\"

    for (String abi in ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64']) {
        def arch = abi
        if (abi == "armeabi-v7a") {
            arch = "arm"
        } else if (abi == 'arm64-v8a') {
            arch = "aarch64"
        } else if (abi == "x86") {
            arch = "i686"
        } else if (abi == "x86_64") {
            arch = "x86_64"
        }

        def sanitizerLibAbiDir = new File("app\\src\\sanitizer\\jniLibs\\" + abi)
        sanitizerLibAbiDir.mkdirs()
        FileTree sanitizerLibAbiFileTree = fileTree(dir: sanitizerLibDir).include("**\\*asan*${arch}*.so")

        sanitizerLibAbiFileTree.each { File file ->
            copy {
                from file
                into sanitizerLibAbiDir.absolutePath
            }
        }

        def wrapScriptAbiDir = new File("app\\src\\sanitizer\\resources\\lib\\" + abi)
        wrapScriptAbiDir.mkdirs()
        def wrapFile = new File(wrapScriptAbiDir, "wrap.sh")
        wrapFile.setExecutable(true, false)
        writeWrapScriptToFullyCompileJavaApp(wrapFile, arch)
    }
}

gradle.taskGraph.whenReady { if (it.hasTask(generateDebugBuildConfig)) {
    copySanitizerLibAndWrapScript(rootProject.ext.ndkDir)
} }
1
wierzmar 15 kwiecień 2020, 14:46