في 2008، عرّف iPhone 3G وأول App Store كثيراً من الناس إلى تطبيقات الهاتف المحمول. أنشأنا AirBuild3G لنمنح ذلك الهاتف مهمة جديدة. على iPhone 3G أو 3GS، يمكنك وصف تطبيق جديد والعمل على شفرته المصدرية باستخدام نموذج برمجة، ثم تجميعه وتشغيله على الهاتف نفسه. يعمل النموذج على خدمة تختارها. وتبقى ملفات مشروعك وأدوات التطوير على الهاتف.
بُني AirBuild3G نفسه باستخدام Xcode 27 على جهاز Mac. أما التطبيقات التي تنشئها به فتستخدم مترجماً برمجياً منفصلاً مثبتاً على الهاتف. تطلّب تشغيل الاثنين تغييرات في أربعة مجالات: إعداد الجهاز والربط وHTTPS وسلسلة الأدوات الخاصة بالهاتف. تتتبع الأقسام أدناه هذا العمل حتى تصل إلى تطبيقك الأول.
إعداد الجهاز الأصلي
تجعل الشاشة الصغيرة وزر الشاشة الرئيسية الفعلي iPhone 3G معروفاً من النظرة الأولى. والعودة إلى التطوير له تعني احترام أبعاده وحدوده الأصلية، حتى عناصر التحكم التي جعلت تطبيقاته مألوفة. يستخدم AirBuild3G نفسه عناصر تحكم UIKit المتاحة على iOS 4، بما فيها جداول الإعدادات المجمّعة وإجراءات شريط التنقل.
يعمل AirBuild3G على iPhone 3G و3GS. يتطلب 3G ملفات armv6 الثنائية، بينما يستخدم 3GS armv7. يحدد البرنامج الثابت حزمة البيئة التي يمكن لكل هاتف تثبيتها. جهاز armv6 المرجعي لدينا هو iPhone 3G يعمل بنظام iOS 4.2.1 مع برنامج ثابت مخصص.
- الإعدادالاستعادة / التنشيط
- الاتصالSSH + الشبكة
- التثبيتالتطبيق + حزمة البيئة
- البيئةالإعدادات ← البيئة
ابدأ بهاتف يقلع إلى شاشته الرئيسية ويقبل اتصالات SSH من جهاز Mac. يحتاج إلى اتصال شبكة يعمل ومدير حزم كي يستقبل حزم Debian ويثبتها. يحدد طراز الجهاز وإصدار البرنامج الثابت الحزم المطلوب إرسالها، ويحدد عنوان الشبكة وجهتها. تحتاج أيضاً إلى كابل USB موثوق ومصدر طاقة.
تحتاج بعض الهواتف الأقدم إلى خطوات إضافية للتنشيط والوصول إلى النظام. للأجهزة المدعومة، استخدم Legacy iOS Kit واتبع إرشاداته الخاصة بطراز هاتفك وبرنامجه الثابت. أنشئ نسخة احتياطية من الهاتف قبل البدء. لا ينشّط AirBuild3G الهاتف بنفسه.
يحتاج AirBuild3G أيضاً إلى Substrate لدعم TLS. على 3G، يجب أن يتضمن البناء armv6، فلا يمكن لحزمة تدعم armv7 وحده أن تعمل عليه مهما كان إصدارها. قد يتضمن هاتف أُعد باستخدام Legacy iOS Kit بناءً متوافقاً بالفعل. إذا كان Substrate يعمل، فاتركه كما هو.
تتبع الإعدادات ← البيئة القاعدة نفسها. فهي تضيف أدوات التطوير وتترك مدير الحزم العامل وخدمة SSH والمكونات الأساسية الأخرى كما هي. قد تؤدي ترقية هذه المكونات كلها إلى تثبيت ملفات ثنائية لا يستطيع النظام القديم تشغيلها.
البناء باستخدام Xcode 27
ما زال مترجم Xcode 27 ينتج كود armv6 وarmv7 ويقبل iOS 4.0 هدفاً للنشر. يأتي Xcode نفسه مع iOS 27 SDK فقط، لذلك نُجري التجميع بالاعتماد على ملفات الترويسة من SDK قديم. مرّر مسار SDK باسم LEGACY_SDK في سطر أوامر make، أو صدّره. تقرأه أيضاً البرامج النصية التي تبني حزم البيئة، ولا تصل إليها قيمة مضبوطة داخل Makefile وحده.
أداة الربط أكثر تشدداً. فهي ترفض armv6 هدفاً للربط، وترفض أي هدف نشر أقدم من iOS 4.3، وتفيد بأن مكتبات SDK القديم مبنية لمنصة غير معروفة. لذلك نربط هذه المكتبات بوحدات بديلة مولّدة ونحتفظ بـ SDK القديم لملفات الترويسة.
- مجمّعclang
- البنية
- armv6
- الحد الأدنى لنظام iOS
- 4.0
- الكود
- e92d40f0 …
- مربوطإعادة التسمية، ثم ld
- البنية
armv6armv7- الحد الأدنى لنظام iOS
4.05.0- الكود
- e92d40f0 …
- جاهز للتوزيعإعادة التسمية إلى الأصل، ثم التصحيح
- البنية
armv7armv6- الحد الأدنى لنظام iOS
5.04.0- الكود
- e92d40f0 …
يجمّع البناء كلا الجزأين لنظام iOS 4.0، ويربطهما لنظام iOS 5.0، ثم يصحح الحد الأدنى لإصدارهما ويعيده إلى 4.0. وبالنسبة إلى armv6، يعيد أيضاً تسمية كل ملف كائن بأنه armv7 كي تقبله أداة الربط، ثم يعيد تسمية الجزء المربوط إلى armv6. تغيّر إعادة التسمية البيانات الوصفية، لا التعليمات. يجب أن يكون الكود المجمّع ووحدات الرموز البديلة التي تضيفها أداة الربط قادرين على العمل على armv6 من الأصل.
MH_MAGIC = 0xFEEDFACE
SUBTYPES = {"armv6": 6, "armv7": 9}
def main():
…
path, want = sys.argv[1], SUBTYPES[sys.argv[2]]
with open(path, "rb") as f:
buf = bytearray(f.read())
if struct.unpack_from("<I", buf, 0)[0] != MH_MAGIC:
…
struct.pack_into("<i", buf, 8, want)
with open(path, "wb") as f:
f.write(buf)- قيمة cpusubtype في Mach-O لكل بنية: 6 يعني armv6 و9 يعني armv7.
- إعادة التسمية كلها: أربعة بايتات عند الإزاحة 8، في حقل cpusubtype بالترويسة. تمر التعليمات دون تغيير.
يحتفظ البناء بالخيار -no_pie لأن iOS لم يكن يدعم ASLR قبل 4.3. كما يعطّل دمج فئات Objective-C، الذي كان سيزيل بت Thumb من الأساليب المدمجة ويتسبب في تعطل AirBuild3G على 3GS. يتحقق make verify من البنى وأوامر التحميل وتعليمات armv6 وبت Thumb في أساليب armv7. نجاح الربط لا يثبت أياً من ذلك.
شغّل make لبناء AirBuild3G وأداته المساعدة ذات الصلاحيات. ينزّل make payload كل حزمة مثبتة الإصدار ويتحقق منها في ذاكرة تخزين مؤقت محلية، ويبني make package منها حزمة AirBuild3G وحزمتي البيئة دون اتصال. تحتاج عملية التحزيم إلى LEGACY_SDK أيضاً، لأن كل حزمة بيئة تحمل أرشيف SDK للهاتف.
يثبّت make install-device DEVICE=root@<ip> تطبيق AirBuild3G وحزمة بيئة iOS 4، ويحدّث الشاشة الرئيسية، ثم يعيد تشغيل التطبيق. بالنسبة إلى 3GS يعمل بنظام iOS 5 أو أحدث، مرّر ios6 كوسيط ثالث إلى tools/deploy_device.sh بدلاً من ذلك. يعمل التطبيق باسم المستخدم mobile، وتتولى أداة مساعدة صغيرة بجانبه كل ما يحتاج إلى root. أصبحت حزمة البيئة الآن على الهاتف، وتثبّت الإعدادات ← البيئة أدوات التطوير التي تحتويها.
الاتصال بخدمة نماذج
في الإعدادات، تُدخل ثلاث قيم ضمن نقطة النهاية: عنوان URL الأساسي ومفتاح API والنموذج. تنتقل الطلبات مباشرة من الهاتف إلى نقطة النهاية تلك. لا نشغّل خادماً وسيطاً، ولا يوجد تسجيل لحساب أو جهاز. تشغّل خدمة النماذج النموذج وتحاسبك على الاستخدام وتخبرك بما يجب إدخاله. إذا كان هاتفك هدية منا، فسيتصل بـ air.build بدلاً من ذلك.
لا يدعم iOS 4 الأصلي عبر SecureTransport سوى TLS 1.0، وقد تتطلب نقطة نهاية حديثة TLS 1.2. نضيف TLS 1.2 باستخدام TLSFix، وهو تعديل Substrate لـ CFNetwork.
يظل على الهاتف التحقق من هوية الخادم. لا يستطيع مخزن الثقة القديم دائماً بناء سلسلة الشهادات التي تستخدمها نقطة نهاية حديثة، وإضافة TLS 1.2 لا تحدّث ذلك المخزن. عندما يفشل الاتصال، تعامل مع التشفير والثقة بالشهادة كمشكلتين منفصلتين.
يتحقق AirBuild3G من الشهادات افتراضياً. إذا فشل طلب، فتحقق أولاً من عنوان URL الأساسي ومفتاح API والنموذج وسلسلة الشهادات. إعداد الوثوق بأي شهادة استثناء صريح. يؤدي تشغيله إلى إزالة التحقق من هوية الخادم، وقد يكشف مفتاح API الخاص بك لأي شخص يستطيع انتحال نقطة النهاية. اتركه متوقفاً إلا إذا كانت نقطة النهاية على شبكتك الخاصة. يقرأ AirBuild3G الإعداد عند كل تقييم للثقة، لذا يسري إيقافه على الطلب التالي.
static OSStatus ABLSecTrustEvaluate(SecTrustRef trust, SecTrustResultType *result) {
…
OSStatus status = ABLOriginalSecTrustEvaluate(trust, result);
if (!ABLTrustAnyCertificate()) {
return status;
}
if (result != NULL) {
*result = kSecTrustResultUnspecified;
}
return errSecSuccess;
}- يُجرى تقييم الهاتف نفسه أولاً.
- يكون خيار الوثوق بأي شهادة متوقفاً افتراضياً، لذا تُعاد تلك النتيجة دون تغيير. يُقرأ الإعداد من
NSUserDefaultsفي كل استدعاء.
يجب أن يناسب بث الرد إمكانات الهاتف أيضاً. يستقبل AirBuild3G البث ويفك ترميزه في خيط خلفي، ولا يمرر إلى الواجهة سوى النص المكتمل. ويُحدّث سجل المحادثة على فترات بدلاً من إعادة قياسه وتمريره مع كل رمز، ما يقلل عمل التخطيط أثناء وصول الرد. يدل ظهور رد على أن الاتصال يعمل. أما تجميع ما يكتبه النموذج فما زال يتطلب وجود البيئة على الهاتف.
تثبيت مترجم يستطيع الهاتف تشغيله
تثبّت الإعدادات ← البيئة المترجم وملفات ترويسة SDK ومكتبات الربط وأدوات التوقيع من حزمة البيئة. تحمل حزمة iOS 4 مترجم GCC لـ armv6، وتحمل حزمة iOS 5 والإصدارات الأحدث مترجم clang لـ armv7. يثبّت كل هاتف الحزمة المطابقة لبرنامجه الثابت فقط.
يُثبّت إصدار كل حزمة بحسب عنوان التنزيل والمجموع الاختباري، ويستخدم التثبيت تلك الملفات نفسها بدلاً من طلب أحدث الإصدارات من مستودع. يتم تخطي الحزم المثبتة بالفعل بالإصدار المحدد، لذلك ينتهي تشغيل البيئة للمرة الثانية فوراً. ثم تُعلّق تحديثات الحزم المثبتة كي يتوقف Cydia عن عرض ترقيات تعطل سلسلة الأدوات.
بالنسبة إلى iPhone 3G، تستخدم البيئة بناء 2008 من iphone-gcc 4.2. يحجز بناء 2009 غيغابايت واحداً لملفات الترويسة المجمّعة مسبقاً، ما يدفع جزءاً منه إلى ما بعد مساحة عناوين هاتف armv6. تتعطل الواجهات الخلفية للمترجم قبل أن تجمع أي شيء. يعرّف كلا البناءين نفسه بأنه Apple build 5555، لكن بناء 2008 وحده يلائم مساحة العناوين تلك.
0x300000002008 iphone-gcc · كل مقطع أقل من 6 MBيعمل
مصفوفة 512 MB · ملف ثنائي للاختباريعمل
مصفوفة 768 MB · ملف ثنائي للاختبارSIGBUS
2009 iphone-gcc · مساحة 1 GB لملفات الترويسة المجمّعة مسبقاًSIGBUS
تحتاج ملفات ترويسة SDK إلى تغييرات أيضاً، إذ لا يستطيع GCC هذا تحليل تعريفات الكتل فيها. قبل تحزيم ترويسات iOS 4، نزيل تلك التعريفات وكل تعريف يعتمد على نوع أُزيل. تحتفظ حزمة clang بترويسات Apple دون تغيير لأن clang يدعم الكتل. وتأتي كل حزمة أيضاً مع أداة توقيع يمكنها توقيع ما ينتجه مترجمها.
يحتفظ الهاتف بأطر النظام في ذاكرة dyld المشتركة، لذا لا تتضمن أدلة الأطر ملفات مكتبات تستطيع أداة الربط قراءتها. نوفر وحدات بديلة مولّدة تحمل الرموز المصدّرة وأسماء المكتبات الحقيقية. تحل أداة الربط كل مرجع بالاعتماد على وحدة بديلة وتسجّل المكتبة التي جاء منها، وعند التشغيل يربطه dyld بالتنفيذ الحقيقي في الذاكرة المشتركة. ولأن الوحدات البديلة تحمل أسماء الرموز الحقيقية، يؤدي إغفال إطار إلى فشل الربط بدلاً من تعطل التطبيق عند التشغيل.
تضبط البيئة أيضاً مسارات التضمين، وتوجّه gcc وcc إلى المترجم المثبت، وتصدّر علامات الربط التي تستدعي وقت تشغيل Objective-C وCoreFoundation. يقرأ Makefile المرفق تلك الإعدادات، لذلك لا يحتاج المشروع الجديد إلى إعداد سلسلة أدوات خاصة به. ومنذ ذلك الحين، تُبنى تطبيقاتك على الهاتف نفسه.
build/%.o: src/%.m | build
@echo " compile $<"
@gcc -c $(IOS_CFLAGS) $< -o $@
# Compiling and linking take different flags, so they are separate steps.
# A missing -framework is a link error here, which is the point: link stubs
# with real symbol names are what make it one instead of a crash at launch.
build/$(NAME): $(OBJECTS)
@echo " link $@"
@gcc $(OBJECTS) -o $@ $(IOS_LDFLAGS) $(addprefix -framework ,$(FRAMEWORKS))
@# Unsigned is SIGKILL at launch, not a warning.
@ldid -S $@- تأتي
IOS_CFLAGSوIOS_LDFLAGSمن/etc/profile.d/airbuild.sh، الذي تثبّته البيئة، لذلك لا يحدد أي مشروع اسم SDK أو مسار مترجم. - يوقّع الملف الثنائي على الهاتف أيضاً. وإذا لم يكن موقّعاً، يُنهى عند التشغيل.
إنشاء تطبيقك الأول وتشغيله
يبدأ المشروع الجديد من قالب UIKit المرفق. ينسخه AirBuild3G إلى دليل المشروع ويضع ملفاته في سياق النموذج. يبدأ النموذج من مشروع يمكن بناؤه بالفعل، لذا يمكن لطلبك الأول وصف التطبيق بدلاً من طلب Makefile.
تطبيق أول جيد هو زر يزيد رقماً على الشاشة. فهو صغير بما يكفي لقراءة كامل شفرته، ومع ذلك يختبر التحرير والتجميع والتحزيم والتشغيل على الهاتف. في طلبك، حدد iOS 4 هدفاً واطلب من النموذج بناء التطبيق وتثبيته. يجب أن يلتزم كود الواجهة بواجهات API التي يدعمها إصدار iOS على الهاتف. وجود تعريف في ملفات الترويسة لا يعني أن API موجودة في وقت التشغيل.
لدى النموذج أربع أدوات. يشغّل Execute الأوامر في دليل المشروع. يكتب EditFile ملفاً كاملاً. يستبدل PatchFile مقطعاً مطابقاً تماماً. يشغّل CheckBuild الأمر make بعد تحميل إعدادات البيئة ويعيد نهاية مخرجاته. تعود نتائج الأدوات إلى النموذج، فيستطيع متابعة العمل أو تعديل تغيير فاشل. تظل عملية الاستدلال لدى خدمة النماذج طوال الوقت.
استدعاء أداة
نتيجة الأداة
Execute EditFile PatchFile CheckBuildتتكرر الحلقة حتى يرد النموذج دون استدعاء أداة، أو تضغط على إيقاف، أو تكتمل 16 جولة.
بعد تثبيت التطبيق الجديد، افتحه على الهاتف. في تطبيق العدّاد، يختبر الضغط على الزر ومشاهدة الرقم يتغير ما طلبته بالضبط. يمكن للطلبات اللاحقة تغيير التخطيط أو إضافة تفاعل آخر، ويواصل النموذج العمل بالاعتماد على الملفات الحالية ونتائج الأدوات السابقة.
يحتفظ AirBuild3G بكل مشروع ومحادثته على الهاتف، ويحفظهما بعد كل جولة أدوات. ويتذكر أيضاً المشروع المفتوح، لأن مغادرة التطبيق أو انخفاض الذاكرة قد ينهي عمليته. عندما تعيد فتح AirBuild3G، يعود إلى ذلك المشروع مع محادثته وملفاته. تتابع رسالتك التالية من المحادثة المحفوظة، وتبقى الشفرة المصدرية موجودة للتعديل والبناء التاليين.
على هذه الشاشة، يصل حتى ذلك العدّاد بين التطبيقات التي استخدمتها من قبل وتلك التي يمكنك إنشاؤها الآن.
شيء جديد على شاشة مألوفة
وصل iPhone 3G في 2008، مع افتتاح App Store. كان بإمكان تطبيق جديد أن يحوّله إلى آلة موسيقية أو دفتر ملاحظات أو لعبة تحملها في كل مكان. وكان زر الشاشة الرئيسية يعيدك إلى شبكة من الأيقونات، تدعوك كل واحدة منها إلى تجربة شيء آخر.
عندما تستخدم ذلك الهاتف اليوم، تشعر كم مضى على 2008. لم تكبر الشاشة. ولم يصبح المعالج أسرع. ما زالت الواجهة الأصلية تطلب من التطبيقات استخدام بضعة عناصر تحكم ومساحة صغيرة بعناية. وأثناء العمل ضمن تلك الحدود، تلاحظ تفاصيل يسهل إغفالها على جهاز أحدث: هل يستجيب الزر، وهل تمر المحادثة بسلاسة، وهل تظهر الشاشة التالية في الوقت المناسب.
يمنح AirBuild3G تلك الشاشة المألوفة مهمة أخرى. يساعدك نموذج حديث على كتابة تطبيق، ويبنيه مترجم من حقبة الهاتف نفسه. ويمكن أن تبقى الأيقونات القديمة في مكانها.
وبجوارها، توجد مساحة لتطبيق لم يكن موجوداً من قبل.