در دستگاههایی که از Android 10 (سطح میانای برنامه کاربردی ۲۹) و بالاتر استفاده میکنند، لایهبندی OpenGL ES (GLES) دردسترس است. برنامه قابلاشکالزدایی میتواند لایههای GLES را از APK، از دایرکتوری پایه، یا از APK لایه انتخابشده بار کند.
استفاده از لایه GLES مشابه استفاده از لایه اعتبارسنجی Vulkan است.
الزامات
لایههای GLES فقط در GLES نسخههای ۲.۰ و بالاتر پشتیبانی میشود.
مقداردهی اولیه لایه
پساز پر کردن نقاط ورودی استاندارد، «بارکننده EGL» یک GLES
LayerLoader را نمونهسازی میکند. اگر لایههای اشکالزدایی فعال باشند، LayerLoader
دایرکتوریهای مشخصشده را برای لایهها اسکن میکند،
همانطور که بارکننده Vulkan انجام میدهد.
اگر لایهبندی فعال باشد، LayerLoader فهرست لایه مشخصشدهای را جستجو و شمارهگذاری میکند. فهرست لایه با نام فایلهای جداشده با دونقطه مشخص میشود.
LayerLoader لایهها را به ترتیبی که مشخص میکنید پیمایش میکند، بنابراین اولین
لایه مستقیماً زیر برنامه است. برای هر لایه، LayerLoader
نقاط ورودی AndroidGLESLayer_Initialize و
AndroidGLESLayer_GetProcAddress را ردیابی میکند. لایهها باید این میاناهای قابلبارگیری را ارائه دهند.
typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*); void* AndroidGLESLayer_Initialize(void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address))
AndroidGLESLayer_Initialize() شناسهای برای لایه مورداستفاده (layer_id) و نقطه ورودیای ارائه میدهد که میتوان آن را برای جستجوی توابع زیر لایه فراخوانی کرد. از نقطه ورودی میتوان بهصورت نشاندادهشده در نمونه کد زیر استفاده کرد:
const char* func = "eglFoo"; void* gpa = get_next_layer_proc_address(layer_id, func);
AndroidGLESLayer_GetProcAddress نشانی تماس بعدی را در زنجیرهای که لایه باید پساز اتمام تماس بگیرد میگیرد. اگر فقط یک لایه وجود داشته باشد،
next برای اکثر عملکردها مستقیماً به درایور اشاره میکند.
typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; void* AndroidGLESLayer_GetProcAddress(const char *funcName, EGLFuncPointer next)
برای هر لایهای که GLES LayerLoader پیدا میکند،
AndroidGLESLayer_Initialize را فراخوانی میکند، فهرستهای تابع libEGL را مرور میکند، و
AndroidGLESLayer_GetProcAddress را برای همه تابعهای شناختهشده فراخوانی میکند. اینکه چگونه نشانی بعدی ردیابی شود به لایه بستگی دارد. اگر لایه تابعی را رهگیری کند،
نشانی تابع را ردیابی میکند. اگر لایه تابعی را رهگیری نکند،
AndroidGLESLayer_GetProcAddress همان نشانی تابعی را که به آن ارسال شده است
برمیگرداند. سپس LayerLoader فهرست قلاب تابع را بهروز میکند تا به نقطه ورودی لایه اشاره کند.
لایهها ملزم نیستند با اطلاعاتی که AndroidGLESLayer_Initialize و get_next_layer_proc_address ارائه میدهند کاری انجام دهند، اما ارائه دادهها باعث میشود لایههای موجود، مانند Android GPU Inspector و RenderDoc، راحتتر از Android پشتیبانی کنند. با آن دادهها، لایه میتواند بهجای منتظر ماندن برای تماس با AndroidGLESLayer_GetProcAddress، بهطور مستقل عملکردها را جستجو کند. اگر لایهها تصمیم بگیرند که خودشان را قبلاز اینکه بارکننده همه نقاط ورودی را پرسمان کند مقداردهی اولیه کنند، باید از get_next_layer_proc_address استفاده کنند. eglGetProcAddress باید
ازطریق زنجیره به پلاتفرم منتقل شود.
لایههای مکان
GLES LayerLoader لایهها را در مکانهای زیر بهترتیب اولویت جستجو میکند:
۱. مکان سیستم برای ریشه
این کار به دسترسی ریشه نیاز دارد
adb root adb disable-verity adb reboot adb root adb shell setenforce 0 adb shell mkdir -p /data/local/debug/gles adb push <layer>.so /data/local/debug/gles/
۲. فهرست راهنمای پایه برنامه
برنامه هدف باید قابلاشکالزدایی باشد، یا باید دسترسی ریشه داشته باشید:
adb push libGLTrace.so /data/local/tmp adb shell run-as com.android.gl2jni cp /data/local/tmp/libGLTrace.so . adb shell run-as com.android.gl2jni ls | grep libGLTrace libGLTrace.so
۳. APK خارجی
رابط دودویی کاربردی (ABI) برنامه هدف را تعیین کنید، سپس APK حاوی لایههایی را که میخواهید بار شود نصب کنید:
adb install --abi armeabi-v7a layers.apk
۴. در APK برنامه هدف
مثال زیر نشان میدهد که چگونه لایهها را در APK برنامه قرار دهید:
$ jar tf GLES_layers.apk lib/arm64-v8a/libGLES_glesLayer1.so lib/arm64-v8a/libGLES_glesLayer2.so lib/arm64-v8a/libGLES_glesLayer3.so lib/armeabi-v7a/libGLES_glesLayer1.so lib/armeabi-v7a/libGLES_glesLayer2.so lib/armeabi-v7a/libGLES_glesLayer3.so resources.arsc AndroidManifest.xml META-INF/CERT.SF META-INF/CERT.RSA META-INF/MANIFEST.MF
فعال کردن لایهها
میتوانید لایههای GLES را برای هر برنامه یا بهصورت سراسری فعال کنید. تنظیمات هر برنامه در بازراهاندازیها حفظ میشود، درحالیکه خصوصیتهای سراسری در بازراهاندازی پاک میشود.
مدل و خطمشیهای امنیتی Android تفاوت قابلتوجهی با پلاتفرمهای دیگر دارد. برای بار کردن لایههای خارجی، یکی از موارد زیر باید درست باشد:
فایل مانیفست برنامه هدف شامل عنصر فراداده زیر است (فقط برای برنامههایی اعمال میشود که Android نسخه ۱۱ (سطح میانای برنامهسازی کاربردی ۳۰) یا بالاتر را هدف قرار میدهند):
<meta-data android:name="com.android.graphics.injectLayers.enable" android:value="true" />باید از این گزینه برای نمایهسازی برنامهتان استفاده کنید.
برنامه هدف قابلاشکالزدایی است. این گزینه اطلاعات اشکالزدایی بیشتری به شما میدهد، اما ممکن است بر عملکرد برنامهتان تأثیر منفی بگذارد.
برنامه هدف در ساخت userdebug سیستمعامل اجرا میشود که دسترسی ریشه را اعطا میکند.
برای فعال کردن لایهها برای هر برنامه:
# Enable layers adb shell settings put global enable_gpu_debug_layers 1 # Specify target application adb shell settings put global gpu_debug_app <package_name> # Specify layer list (from top to bottom) # Layers are identified by their filenames, such as "libGLLayer.so" adb shell settings put global gpu_debug_layers_gles <layer1:layer2:layerN> # Specify packages to search for layers adb shell settings put global gpu_debug_layer_app <package1:package2:packageN>
برای غیرفعال کردن لایهها برای هر برنامه:
# Delete the global setting that enables layers adb shell settings delete global enable_gpu_debug_layers # Delete the global setting that selects target application adb shell settings delete global gpu_debug_app # Delete the global setting that specifies layer list adb shell settings delete global gpu_debug_layers_gles # Delete the global setting that specifies layer packages adb shell settings delete global gpu_debug_layer_app
برای فعال کردن لایهها بهصورت سراسری:
# This attempts to load layers for all applications, including native # executables adb shell setprop debug.gles.layers <layer1:layer2:layerN>
ایجاد لایه
لایهها باید دو تابع زیر را که در مقداردهی اولیه EGL Loader شرح داده شده است نشان دهند:
AndroidGLESLayer_Initialize AndroidGLESLayer_GetProcAddress
لایههای غیرفعال
برای لایهای که فقط تعداد کمی از توابع را رهگیری میکند، لایه مقداردهیشده غیرفعال بهینه است. لایه مقداردهیشده غیرفعال منتظر میماند
تا GLES LayerLoader تابع موردنیازش را مقداردهی کند.
نمونه کد زیر نحوه ایجاد لایه غیرفعال را نشان میدهد.
namespace { std::unordered_map<std::string, EGLFuncPointer> funcMap; EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig ( EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config) { EGLFuncPointer entry = funcMap["eglChooseConfig"]; typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)( EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*); PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry); return next(dpy, attrib_list, configs, config_size, num_config); } EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) { #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \ return (EGLFuncPointer)glesLayer_##func; } GETPROCADDR(eglChooseConfig); // Don't return anything for unrecognized functions return nullptr; } EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer( void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { // This function is purposefully empty, since this layer does not proactively // look up any entrypoints } EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress( const char* funcName, EGLFuncPointer next) { EGLFuncPointer entry = eglGPA(funcName); if (entry != nullptr) { funcMap[std::string(funcName)] = next; return entry; } return next; } } // namespace extern "C" { __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize( void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address); } __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddress( const char *funcName, EGLFuncPointer next) { return (void*)glesLayer_GetLayerProcAddress(funcName, next); } }
لایههای فعال
برای لایههای رسمیتر که باید در ابتدا کاملاً مقداردهی اولیه شوند، یا لایههایی که باید افزونههای ناشناخته برای EGL Loader را جستجو کنند، مقداردهی اولیه لایه فعال لازم است. این لایه از
get_next_layer_proc_address که AndroidGLESLayer_Initialize ارائه میدهد برای
جستجوی تابع استفاده میکند. این لایه همچنان باید به درخواستهای AndroidGLESLayer_GetProcAddress از بارکننده پاسخ دهد تا پلاتفرم بداند تماسها را به کجا هدایت کند. نمونه کد زیر نحوه ایجاد لایه فعال را نشان میدهد.
namespace { std::unordered_map<std::string, EGLFuncPointer> funcMap; EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig ( EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config) { EGLFuncPointer entry = funcMap["eglChooseConfig"]; typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)( EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*); PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry); return next(dpy, attrib_list, configs, config_size, num_config); } EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) { #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \ return (EGLFuncPointer)glesLayer_##func; } GETPROCADDR(eglChooseConfig); // Don't return anything for unrecognized functions return nullptr; } EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer( void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { // Note: This is where the layer would populate its function map with all the // functions it cares about const char* func = “eglChooseConfig”; funcMap[func] = get_next_layer_proc_address(layer_id, func); } EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress( const char* funcName, EGLFuncPointer next) { EGLFuncPointer entry = eglGPA(funcName); if (entry != nullptr) { return entry; } return next; } } // namespace extern "C" { __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize( void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) { return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address); } __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddress( const char *funcName, EGLFuncPointer next) { return (void*)glesLayer_GetLayerProcAddress(funcName, next); } }