Tập lệnh bao bọc môi trường shell

Khi gỡ lỗi và phân tích tài nguyên cho ứng dụng bằng mã gốc, bạn nên dùng những công cụ gỡ lỗi cần được bật khi khởi động quy trình. Muốn vậy, bạn phải chạy ứng dụng theo một quy trình mới thay vì sao chép từ zygote. Ví dụ:

Sử dụng tập lệnh bao bọc môi trường shell

wrap.sh rất dễ sử dụng:

  1. Biên dịch một tệp APK tuỳ chỉnh có thể gỡ lỗi giúp đóng gói nội dung sau:
  2. Cài đặt APK có thể gỡ lỗi trên một thiết bị.
  3. Khởi chạy ứng dụng.

Tạo tập lệnh bao bọc môi trường shell

Khi bạn khởi chạy một APK có thể gỡ lỗi chứa wrap.sh, hệ thống sẽ thực thi tập lệnh và truyền lệnh khởi động ứng dụng dưới dạng đối số. Tập lệnh chịu trách nhiệm khởi động ứng dụng, nhưng có thể làm thay đổi bất kỳ môi trường hoặc đối số nào. Tập lệnh phải tuân theo cú pháp MirBSD Korn shell (mksh).

Đoạn mã sau đây cho biết cách viết một tệp wrap.sh đơn giản chỉ có trách nhiệm khởi động ứng dụng:

#!/system/bin/sh
exec "$@"

Gỡ lỗi Malloc

Để sử dụng công cụ gỡ lỗi malloc thông qua wrap.sh, bạn cần bao gồm dòng sau:

#!/system/bin/sh
LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper "$@"

ASan

Tài liệu về ASan có một ví dụ về cách thực hiện việc này cho ASan.

Đóng gói wrap.sh

Để tận dụng wrap.sh, APK của bạn phải là tệp có thể gỡ lỗi. Hãy đảm bảo rằng chế độ cài đặt android:debuggable="true" được định cấu hình trong phần tử <application> của tệp kê khai Android hoặc nếu bạn đang sử dụng Android Studio, thì đảm bảo bạn đã định cấu hình bản gỡ lỗi trong tệp build.gradle.

Bạn cũng cần đặt useLegacyPackaging thành true trong tệp build.gradle của ứng dụng. Trong hầu hết các trường hợp, tuỳ chọn này được đặt là false theo mặc định. Vì vậy, bạn nên đặt tuỳ chọn này là true một cách rõ ràng để tránh mọi tình huống bất ngờ.

Bạn phải đóng gói tập lệnh wrap.sh bằng các thư viện gốc của ứng dụng. Nếu ứng dụng của bạn không chứa thư viện gốc, hãy thêm thư mục lib vào thư mục dự án theo cách thủ công. Đối với mỗi cấu trúc mà ứng dụng của bạn hỗ trợ, bạn phải cung cấp một bản sao của tập lệnh bao bọc môi trường shell trong thư mục thư viện gốc đó.

Ví dụ sau đây cho thấy bố cục tệp để hỗ trợ cả cấu trúc ARMv8 và x86-64:

# App Directory
|- AndroidManifest.xml
|- …
|- lib
   |- arm64-v8a
      |- ...
      |- wrap.sh
   |- x86_64
      |- ...
      |- wrap.sh

Android Studio chỉ đóng gói các tệp .so từ thư mục lib/ nên nếu là người dùng Android Studio, bạn cần đặt tệp wrap.sh trong thư mục src/main/resources/lib/* để chúng được đóng gói đúng cách.

Lưu ý: resources/lib/x86 sẽ hiển thị trong giao diện người dùng dưới dạng lib.x86 nhưng đây thực ra là một thư mục con:

Ví dụ về cách đóng gói wrap.sh trong Android Studio

Gỡ lỗi khi sử dụng wrap.sh

Nếu muốn đính kèm một trình gỡ lỗi khi sử dụng wrap.sh, tập lệnh môi trường shell của bạn sẽ cần bật tính năng gỡ lỗi theo cách thủ công. Cách thực hiện việc này sẽ khác nhau giữa các bản phát hành, vì vậy, ví dụ này cho biết cách thêm tuỳ chọn thích hợp cho tất cả các bản phát hành hỗ trợ wrap.sh:

#!/system/bin/sh

cmd=$1
shift

os_version=$(getprop ro.build.version.sdk)

if [ "$os_version" -eq "27" ]; then
  cmd="$cmd -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable $@"
elif [ "$os_version" -eq "28" ]; then
  cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable $@"
else
  cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y $@"
fi

exec $cmd