renderScript

RenderScript הוא framework להרצת משימות אינטנסיביות עם ביצועים גבוהים ב-Android. הוא מיועד לשימוש עם חישוב מקבילי לנתונים, אבל גם עומסי עבודה סידוריים יכולים להפיק ממנו תועלת. סביבת זמן הריצה של RenderScript מבצעת עבודה במקביל במעבדים שזמינים במכשיר, כמו מעבדי CPU ו-GPU עם כמה ליבות, ומאפשרת למפתחים להתמקד בביטוי של אלגוריתמים במקום בתזמון העבודה. ‏RenderScript שימושי במיוחד לאפליקציות שמבצעות עיבוד תמונות, צילום חישובי או ראיית מכונה.

במכשירים עם Android מגרסה 8.0 ואילך נעשה שימוש במסגרת RenderScript וב-HAL של הספקים הבאים:

איור 1. קוד של ספק שמקשר לספריות פנימיות.

ההבדלים ביחס ל-RenderScript ב-Android 7.x ואילך כוללים:

  • שתי מכונות של ספריות פנימיות של RenderScript בתהליך. קבוצה אחת היא לנתיב החלופי של המעבד, והיא מתחילה ישירות ב-/system/lib. הקבוצה השנייה היא לנתיב של המעבד הגרפי, והיא מתחילה ב-/system/lib/vndk-sp.
  • ספריות ה-RS הפנימיות ב-/system/lib נוצרות כחלק מהפלטפורמה ומתעדכנות כשsystem.img משודרג. עם זאת, הספריות ב-/system/lib/vndk-sp נוצרות עבור הספק ולא מתעדכנות כשמבצעים שדרוג ל-system.img (אפשר לעדכן אותן כדי לתקן בעיות אבטחה, אבל ה-ABI שלהן נשאר זהה).
  • קוד הספק (RS HAL,‏ RS driver ו-bcc plugin) מקושר לספריות הפנימיות של RenderScript שנמצאות ב-/system/lib/vndk-sp. הם לא יכולים לקשר ל-libs ב-/system/lib כי libs בספרייה הזו מיועדים לפלטפורמה, ולכן יכול להיות שהם לא תואמים לקוד הספק (כלומר, ייתכן שנסיר סמלים). אם תעשו זאת, לא תוכלו לבצע עדכון OTA רק של המסגרת.

עיצוב

הקטעים הבאים מפרטים את העיצוב של RenderScript ב-Android 8.0 ואילך.

ספריות RenderScript שזמינות לספקים

בקטע הזה מפורטות ספריות RenderScript (שנקראות Vendor NDK for Same-Process HALs או VNDK-SP) שזמינות לקוד של הספק ואפשר ליצור אליהן קישור. מפורטות בו גם ספריות נוספות שלא קשורות ל-RenderScript אבל מסופקות גם לקוד הספק.

רשימת הספריות הבאה עשויה להשתנות בין גרסאות Android, אבל היא לא משתנה בגרסה ספציפית של Android. רשימה עדכנית של הספריות הזמינות מופיעה במאמר /system/etc/ld.config.txt.

ספריות RenderScript Libs שאינם RenderScript
  • android.hardware.graphics.renderscript@1.0.so
  • libRS_internal.so
  • libRSCpuRef.so
  • libblas.so
  • libbcinfo.so
  • libcompiler_rt.so
  • libRSDriver.so
  • libc.so
  • libm.so
  • libdl.so
  • libstdc++.so
  • liblog.so
  • libnativewindow.so
  • libsync.so
  • libvndksupport.so
  • libbase.so
  • libc++.so
  • libcutils.so
  • libutils.so
  • libhardware.so
  • libhidlbase.so
  • libhidltransport.so
  • libhwbinder.so
  • liblzma.so
  • libz.so
  • libEGL.so
  • libGLESv1_CM.so
  • libGLESv2.so

הגדרת מרחב השמות של Linker

הגבלת הקישור שחוסמת את השימוש בספריות שלא נמצאות ב-VNDK-SP על ידי קוד של ספק, נאכפת בסביבת זמן הריצה באמצעות מרחב השמות של ה-linker. (לפרטים, אפשר לעיין במצגת VNDK Design).

במכשיר עם Android מגרסה 8.0 ואילך, כל ממשקי ה-HAL באותו תהליך (SP-HAL) למעט RenderScript נטענים במרחב השמות של ה-linker‏ sphal. ‏RenderScript נטען למרחב השמות הספציפי של RenderScript‏ rs, מיקום שמאפשר אכיפה מעט פחות מחמירה של ספריות RenderScript. מכיוון שההטמעה של RS צריכה לטעון את הקוד הבינארי המהדר, הערך /data/*/*.so מתווסף לנתיב של מרחב השמות rs (לא מורשים SP-HAL אחרים לטעון ספריות מהמחיצה של הנתונים).

בנוסף, מרחב השמות rs מאפשר יותר ספריות מאשר מרחבי שמות אחרים. libmediandk.so ו-libft2.so חשופים למרחב השמות rs כי ל-libRS_internal.so יש תלות פנימית בספריות האלה.

איור 2. הגדרת מרחב שמות לקישור.

טעינה של מנהלי התקנים

נתיב חלופי למעבד (CPU)

בהתאם לנוכחות הבייט RS_CONTEXT_LOW_LATENCY כשיוצרים הקשר RS, המערכת בוחרת את הנתיב ל-CPU או ל-GPU. כשבוחרים את הנתיב של המעבד, libRS_internal.so (ההטמעה הראשית של מסגרת RS) מופיע ישירות ב-dlopen של מרחב השמות שמוגדר כברירת מחדל ב-linker, שבו מוצגת גרסת הפלטפורמה של ספריות RS.

כשהמערכת עוברת לנתיב החלופי של המעבד, לא נעשה שימוש כלל בהטמעת RS HAL מהספק, ונוצר אובייקט RsContext עם mVendorDriverName null. libRSDriver.so מdlopen (כברירת מחדל) וספריית הנהג נטענת ממרחב השמות default כי מבצע הקריאה החוזרת (libRS_internal.so) נטען גם הוא במרחב השמות default.

איור 3. נתיב חלופי ל-CPU.

נתיב ה-GPU

לנתיב ה-GPU, השדה libRS_internal.so נטען באופן שונה. קודם כול, libRS.so משתמש ב-android.hardware.renderscript@1.0.so (ובבסיסו libhidltransport.so) כדי לטעון את android.hardware.renderscript@1.0-impl.so (הטמעה של ספק של RS HAL) למרחב שמות אחר לקישור בשם sphal. לאחר מכן, ה-HAL של RS dlopens libRS_internal.so במרחב שמות אחר של קישור שנקרא rs.

ספקים יכולים לספק מנהל התקן RS משלהם על ידי הגדרת הדגל OVERRIDE_RS_DRIVER בזמן ה-build, שמוטמע בהטמעת ה-HAL של RS‏ (hardware/interfaces/renderscript/1.0/default/Context.cpp). לאחר מכן, שם מנהל ההתקן הזה עובר dlopen לצורך הקשר של RS בנתיב ה-GPU.

היצירה של האובייקט RsContext מועברת להטמעה של RS HAL. ה-HAL מבצע קריאה חוזרת למסגרת RS באמצעות הפונקציה rsContextCreateVendor(), עם שם הנהג שישמש כארגומנט. לאחר מכן, מסגרת RS טוענת את מנהל ההתקן שצוין כשהשדה RsContext מופעל. במקרה הזה, ספריית הנהגים נטענת למרחב השמות rs כי האובייקט RsContext נוצר בתוך מרחב השמות rs ו-/vendor/lib נמצא בנתיב החיפוש של מרחב השמות.

איור 4. נתיב חלופי ל-GPU.

במעבר ממרחב השמות default למרחב השמות sphal, libhidltransport.so משתמשת בפונקציה android_load_sphal_library() כדי להורות במפורש למקשר הדינמי לטעון את הספרייה -impl.so ממרחב השמות sphal.

במעבר ממרחב השמות sphal למרחב השמות rs, הטעינה מתבצעת באופן עקיף באמצעות השורה הבאה ב-/system/etc/ld.config.txt:

namespace.sphal.link.rs.shared_libs = libRS_internal.so

השורה הזו קובעת שהמקשר הדינמי צריך לטעון את libRS_internal.so ממרחב השמות rs כשלא ניתן למצוא או לטעון את הספרייה ממרחב השמות sphal (זה תמיד המצב כי מרחב השמות sphal לא מחפש את /system/lib/vndk-sp שבו נמצאת libRS_internal.so). עם ההגדרה הזו, קריאה פשוטה של dlopen() ל-libRS_internal.so מספיקה כדי לבצע את המעבר במרחב השמות.

טעינת הפלאגין של 'עותק מוסתר'

bcc plugin היא ספרייה שסופקה על ידי ספק, שנטענה במהדר (compiler) bcc. מכיוון ש-bcc הוא תהליך מערכת בספרייה /system/bin, אפשר להתייחס לספרייה bcc plugin כ-SP-HAL (כלומר HAL של ספק שאפשר לטעון ישירות לתהליך המערכת בלי להשתמש ב-binder). כספריית SP-HAL, הספרייה bcc-plugin:

  • אי אפשר לקשר לספריות של framework בלבד כמו libLLVM.so.
  • אפשר לקשר רק לספריות VNDK-SP שזמינות לספק.

כדי לאכוף את ההגבלה הזו, מעמיסים את bcc plugin למרחב השמות sphal באמצעות הפונקציה android_sphal_load_library(). בגרסאות קודמות של Android, שם הפלאגין צוין באמצעות האפשרות -load והספרייה נטענה באמצעות dlopen() הפשוט על ידי libLLVM.so. ב-Android מגרסה 8.0 ואילך, האפשרות הזו מצוינה באפשרות -plugin והספרייה נטענת ישירות על ידי bcc עצמו. האפשרות הזו מאפשרת להשתמש בנתיב לא ספציפי ל-Android לפרויקט LLVM בקוד פתוח.

איור 5. טעינת הפלאגין של bcc, Android 7.x וגרסאות קודמות.



איור 6. טעינת הפלאגין 'הוספת נמען ללא הודעה', Android מגרסה 8.0 ואילך.

נתיבי חיפוש של ld.mc

כשמריצים את ld.mc, חלק מהlibs של זמן הריצה של RS ניתנים כקלט למקשר. הביטקוד של ה-RS מהאפליקציה מקושר אל ה-libs של סביבת זמן הריצה, וכשהביטקוד שהומר נטען לתהליך של אפליקציה, ה-libs של סביבת זמן הריצה מקושרים שוב באופן דינמי מהביטקוד שהומר.

ספריות זמן הריצה כוללות:

  • libcompiler_rt.so
  • libm.so
  • libc.so
  • דרייבר RS (libRSDriver.so או OVERRIDE_RS_DRIVER)

כשאתם מעמיסים את הקוד המקודד ל-bitcode בתהליך האפליקציה, עליכם לספק את אותה ספרייה בדיוק שבה השתמש ld.mc. אחרת, יכול להיות שהקוד המקודד לא ימצא סמל שהיה זמין כשהוא קושר.

כדי לעשות זאת, מסגרת RS משתמשת בנתיבי חיפוש שונים לספריות בסביבת זמן הריצה בזמן ביצוע ld.mc, בהתאם לכך שמסגרת RS עצמה נטענת מ-/system/lib או מ-/system/lib/vndk-sp. כדי לקבוע זאת, אפשר לקרוא את הכתובת של סמל שרירותי של מסגרת RSA, ולהשתמש ב-dladdr() כדי למפות את נתיב הקובץ לכתובת.

מדיניות SELinux

כתוצאה משינויי המדיניות של SELinux ב-Android 8.0 ואילך, עליכם לפעול לפי כללים ספציפיים (שמופעלים באמצעות neverallows) כשאתם מסמנים קבצים נוספים במחיצה vendor:

  • vendor_file חייבת להיות תווית ברירת המחדל לכל הקבצים במחיצה vendor. מדיניות הפלטפורמה מחייבת גישה כזו כדי לגשת להטמעות של פרוטוקול העברת נתונים (HAL).
  • כל פריט exec_types חדש שנוסף במחיצה vendor באמצעות SEPolicy של הספק, חייב לכלול מאפיין vendor_file_type. האכיפה מתבצעת באמצעות neverallows.
  • כדי למנוע התנגשויות עם עדכונים עתידיים של הפלטפורמה או של framework, מומלץ להימנע מתיוג של קבצים שאינם exec_types במחיצה של vendor.
  • יש לתייג את כל יחסי התלות של ספריות ל-HAL של תהליך שמזוהה על ידי AOSP כ-same_process_hal_file.

אפשר לקרוא פרטים נוספים על המדיניות של SELinux במאמר Linux עם שיפור אבטחה ב-Android.

תאימות ABI לביטקוד

אם לא יתווספו ממשקי API חדשים, כלומר לא תהיה עלייה בגרסה של HAL, מסגרות ה-RS ימשיכו להשתמש במנהל ההתקן הקיים של GPU‏ (HAL 1.0).

בשינויים קטנים ב-HAL (HAL 1.1) שלא משפיעים על קוד ה-bit, המסגרות צריכות לעבור ל-CPU עבור ממשקי ה-API החדשים שנוספו ולהמשיך להשתמש ב-GPU (HAL 1.0) במקום אחר.

כשיש שינויים משמעותיים ב-HAL (HAL 2.0) שמשפיעים על הידור או קישור של קוד ביט, צריך להחליט לא לטעון את מנהלי ה-GPU שסופקו על ידי הספק, אלא להשתמש בנתיב של המעבד או של Vulkan לצורך האצה.

השימוש בקוד הבייט של RenderScript מתבצע בשלושה שלבים:

שלב פרטים
הדרכה ל-Compile
  • קוד הבייט (bc.) של הקלט ל-bcc חייב להיות בפורמט קוד בייט של LLVM 3.2, ו-bcc חייב להיות תואם לאחור לאפליקציות קיימות (קודמות).
  • עם זאת, המטא-נתונים ב- .bc עשויים להשתנות (יכולות להיות פונקציות חדשות של סביבת זמן הריצה, למשל פונקציות להקצאה, פונקציות מתמטיות וכו'). חלק מהפונקציות בסביבת זמן הריצה נמצאות ב-libclcore.bc, וחלק מהן נמצאות ב-LibRSDriver או ברכיב מקביל של הספק.
  • כדי להוסיף פונקציות חדשות בסביבת זמן הריצה או לשנות מטא-נתונים שגורמים לשגיאות, צריך להגדיל את רמת ה-API של קוד ה-bitcode. מכיוון שמנהלי ההתקנים של הספקים לא יוכלו לצרוך אותה, צריך גם להגדיל את גרסת ה-HAL.
  • יכול להיות שלספקים יהיו קומפילרים משלהם, אבל המסקנות או הדרישות של bcc חלות גם על הקומפילרים האלה.
קישור
  • קובץ ה-.o המהדר יהיה מקושר ל-driver של הספק, למשל: libRSDriver_foo.so וגם libcompiler_rt.so. הנתיב של המעבד (CPU) יקושר אל libRSDriver.so.
  • אם קובץ ה-‎ .o דורש ממשק API חדש בסביבת זמן ריצה מ-libRSDriver_foo, צריך לעדכן את מנהל ההתקן של הספק כדי שיתמוך בו.
  • לספקים מסוימים עשויים להיות קישורים משלהם, אבל הארגומנט של ld.mc חל גם עליהם.
טעינה
  • libRSCpuRef טוען את האובייקט המשותף. אם יש שינויים בממשק הזה, צריך לשדרג את גרסת ה-HAL.
  • ספקים יכולים להסתמך על libRSCpuRef כדי לטעון את האובייקט המשותף, או להטמיע אובייקט משלהם.

בנוסף ל-HAL, ממשקי ה-API בסביבת זמן הריצה והסמלים המיוצאים הם גם ממשקים. ממשק המשתמש לא השתנה מאז Android 7.0‏ (API 24), ואין תוכניות מיידיות לשינוי שלו ב-Android 8.0 ואילך. עם זאת, אם הממשק ישתנה, גם גרסת ה-HAL תתווסף.

הטמעות של ספקים

עבור Android מגרסה 8.0 ואילך, נדרשים שינויים מסוימים במנהל ההתקן של ה-GPU כדי שמנהל ההתקן של ה-GPU יפעל כראוי.

מודולים של מנהלי התקנים

  • המודולים של מנהלי ההתקנים לא יכולים להיות תלויים בספריות מערכת שלא מופיעות ברשימה.
  • הנהג חייב לספק android.hardware.renderscript@1.0-impl_{NAME} משלו, או להצהיר על ההטמעה android.hardware.renderscript@1.0-impl שמוגדרת כברירת מחדל כיחס התלות שלו.
  • ההטמעה של המעבד libRSDriver.so היא דוגמה טובה להסרת יחסי תלות שאינם של VNDK-SP.

מהדר ביטקוד

יש שתי דרכים לקמפל קוד בייט של RenderScript עבור מנהל ההתקן של הספק:

  1. הפעלת מהדר (compiler) ספציפי לספק ב-/vendor/bin/ (השיטה המועדפת להדרת GPU). בדומה למודולרים אחרים של הנהג, קובץ הבינארי של המהדר של הספק לא יכול להסתמך על ספריית מערכת שלא נכללת ברשימת ספריות RenderScript שזמינות לספקים.
  2. הפעלת 'עותק מוסתר של המערכת': /system/bin/bcc עם bcc plugin שסופק על ידי הספק. הפלאגין הזה לא יכול להסתמך על ספריית מערכת שלא מופיעה ברשימה של libs של RenderScript שזמינים לספקים.

אם הספק bcc plugin צריך להפריע ל-compilation של המעבד, ואי אפשר להסיר בקלות את התלות שלו ב-libLLVM.so, הספק צריך להעתיק את bcc (ואת כל יחסי התלות שאינם LL-NDK, כולל libLLVM.so,‏ libbcc.so) למחיצה /vendor.

בנוסף, הספקים צריכים לבצע את השינויים הבאים:

איור 7. שינויים ב-driver של הספק.

  1. מעתיקים את libclcore.bc למחיצה /vendor. כך אפשר לוודא שהסנכרון של libclcore.bc, של libLLVM.so ושל libbcc.so.
  2. משנים את הנתיב לקובץ ההפעלה bcc על ידי הגדרה של הערך RsdCpuScriptImpl::BCC_EXE_PATH מההטמעה של RS HAL.

מדיניות SELinux

מדיניות SELinux משפיעה גם על הדרייבר וגם על קובצי ההפעלה של המהדר. כל המודולים של מנהלי ההתקנים חייבים להיות מסומנים בתווית same_process_hal_file בקובץ file_contexts של המכשיר. לדוגמה:

/vendor/lib(64)?/libRSDriver_EXAMPLE\.so     u:object_r:same_process_hal_file:s0

תהליך האפליקציה צריך להיות מסוגל להפעיל את קובץ ההפעלה של המהדר, כמו גם את העותק של bcc (/vendor/bin/bcc) מהספק. לדוגמה:

device/vendor_foo/device_bar/sepolicy/file_contexts:
/vendor/bin/bcc                    u:object_r:same_process_hal_file:s0

מכשירים מדור קודם

מכשירים מדור קודם הם מכשירים שעומדים בתנאים הבאים:

  1. הערך של PRODUCT_SHIPPING_API_LEVEL נמוך מ-26.
  2. PRODUCT_FULL_TREBLE_OVERRIDE לא מוגדר.

במכשירים מדור קודם, ההגבלות לא נאכפות כשמשדרגים ל-Android 8.0 ואילך. המשמעות היא שמנהלי ההתקנים יוכלו להמשיך לקשר לספריות ב-/system/lib[64]. עם זאת, בגלל השינוי בארכיטקטורה שקשור ל-OVERRIDE_RS_DRIVER, צריך להתקין את android.hardware.renderscript@1.0-impl במחיצה /vendor. אם לא עושים זאת, זמן הריצה של RenderScript יאלץ לעבור לנתיב של המעבד.

למידע על המניעים להוצאה משימוש של Renderscript, כדאי לעיין בבלוג למפתחי Android: Android GPU Compute Going Forward. מידע על המשאבים שיופסקו כולל את הפרטים הבאים: