AirBuild
इंजीनियरिंग

पहला App Store, AI Coding के साथ नए रूप में।

2008 में, iPhone 3G और पहले App Store ने बहुत से लोगों को मोबाइल ऐप से परिचित कराया। हमने AirBuild3G इसलिए बनाया ताकि उस फ़ोन को कुछ नया करने को मिले। iPhone 3G या 3GS पर, आप किसी नए ऐप का वर्णन कर सकते हैं, coding model के साथ उसके source पर काम कर सकते हैं और उसी फ़ोन पर उसे compile करके चला सकते हैं। model आपकी चुनी हुई सेवा पर चलता है। आपकी project files और development tools फ़ोन पर ही रहते हैं।

AirBuild3G को Xcode 27 से Mac पर बनाया गया है। इससे बनाए गए ऐप एक अलग compiler का उपयोग करते हैं, जो फ़ोन पर इंस्टॉल होता है। दोनों को चलाने के लिए चार क्षेत्रों में बदलाव करने पड़े: device तैयार करना, linking, HTTPS और फ़ोन की अपनी toolchain। नीचे के भाग इस काम को आपके पहले ऐप तक क्रम से बताते हैं।

मूल हार्डवेयर तैयार करना

छोटा display और वास्तविक Home button iPhone 3G को तुरंत पहचानने योग्य बनाते हैं। इसके लिए फिर से कुछ बनाने का अर्थ है इसके मूल आकार और सीमाओं का सम्मान करना, उन controls तक जिनसे इसके ऐप परिचित लगते थे। AirBuild3G स्वयं iOS 4 पर उपलब्ध UIKit controls का उपयोग करता है, जिनमें grouped settings tables और navigation-bar actions शामिल हैं।

AirBuild3G iPhone 3G और 3GS पर चलता है। 3G को armv6 binaries चाहिए; 3GS armv7 का उपयोग करता है। firmware तय करता है कि हर फ़ोन कौन-सा environment package इंस्टॉल कर सकता है। हमारा संदर्भ armv6 device एक iPhone 3G है, जो custom firmware के साथ iOS 4.2.1 चला रहा है।

  1. तैयार करेंrestore / activate
  2. कनेक्ट करेंSSH + network
  3. इंस्टॉल करेंapp + environment package
  4. EnvironmentSettings → Environment
Setup के चार चरण हैं: फ़ोन तैयार करें, उससे कनेक्ट करें, AirBuild3G और environment package इंस्टॉल करें, फिर Settings → Environment में Install पर टैप करें।

ऐसे फ़ोन से शुरू करें जो अपनी Home screen तक boot होता हो और आपके Mac से SSH connections स्वीकार करता हो। Debian packages पाने और इंस्टॉल करने के लिए उसमें चालू network connection और package manager होना चाहिए। उसका hardware model और firmware version तय करते हैं कि कौन-से packages भेजने हैं, और उसका network address तय करता है कि कहाँ भेजने हैं। आपको भरोसेमंद USB cable और power भी चाहिए।

कुछ पुराने फ़ोन को activation और system access के लिए अतिरिक्त चरणों की आवश्यकता होती है। समर्थित hardware के लिए Legacy iOS Kit का उपयोग करें और अपने फ़ोन के model और firmware के लिए उसके निर्देशों का पालन करें। शुरू करने से पहले फ़ोन का backup लें। AirBuild3G स्वयं फ़ोन को activate नहीं करता।

TLS support के लिए AirBuild3G को Substrate भी चाहिए। 3G पर build में armv6 शामिल होना चाहिए: केवल armv7 वाला package वहाँ नहीं चल सकता, चाहे उसका version कुछ भी हो। Legacy iOS Kit से तैयार किए गए फ़ोन में पहले से compatible build हो सकता है। यदि Substrate काम करता है, तो उसे वहीं रहने दें।

Settings → Environment भी इसी नियम का पालन करता है। यह development tools जोड़ता है और काम कर रहे package manager, SSH service तथा दूसरे base components को नहीं बदलता। उन components को एक साथ upgrade करने पर ऐसी binaries इंस्टॉल हो सकती हैं जिन्हें पुराना system चला नहीं सकता।

Xcode 27 से बनाना

Xcode 27 का compiler अब भी armv6 और armv7 code बनाता है और iOS 4.0 deployment target स्वीकार करता है। Xcode के साथ केवल iOS 27 SDK आता है, इसलिए हम legacy SDK के headers के साथ compile करते हैं। make command line पर SDK का path LEGACY_SDK के रूप में दें या उसे export करें। environment packages बनाने वाली scripts भी इसे पढ़ती हैं, और केवल Makefile के भीतर सेट किया गया value उन तक कभी नहीं पहुँचता।

linker अधिक सख्त है। यह armv6 को link target के रूप में अस्वीकार करता है, iOS 4.3 से नीचे के किसी भी deployment target को स्वीकार नहीं करता और legacy SDK की libraries को किसी अज्ञात platform के लिए बनी हुई बताता है। उन libraries के लिए हम इसके बजाय बनाए गए stubs से link करते हैं और headers के लिए legacy SDK रखते हैं।

  1. Compile किया गयाclang
    arch
    armv6
    न्यूनतम iOS
    4.0
    code
    e92d40f0 …
  2. Link किया गयाrelabel, फिर ld
    arch
    armv6armv7
    न्यूनतम iOS
    4.05.0
    code
    e92d40f0 …
  3. भेजा गयावापस relabel, patch
    arch
    armv7armv6
    न्यूनतम iOS
    5.04.0
    code
    e92d40f0 …
    make verify
Clang armv6 instructions बनाता है। link से पहले और बाद में build केवल metadata को दोबारा लिखता है, फिर परिणाम की जाँच करता है।

build दोनों slices को iOS 4.0 के लिए compile करता है, उन्हें iOS 5.0 के लिए link करता है और फिर उनके minimum version को वापस 4.0 पर patch करता है। armv6 के लिए यह हर object file को armv7 के रूप में relabel भी करता है ताकि linker उन्हें स्वीकार करे, फिर linked slice को वापस armv6 के रूप में relabel करता है। Relabeling metadata को बदलता है, instructions को नहीं। compiled code और linker द्वारा जोड़े गए symbol stubs को पहले से armv6 पर चलना चाहिए।

tools/relabel_arch.py
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)
  1. हर architecture के लिए Mach-O का cpusubtype: 6 armv6 है, 9 armv7 है।
  2. पूरा relabel: offset 8 पर चार bytes, यानी header का cpusubtype field। instructions बिना बदले आगे जाती हैं।
build इस script को link से पहले हर armv6 object पर और उसके बाद linked slice पर चलाता है।

build -no_pie को रखता है क्योंकि 4.3 से पहले iOS में ASLR नहीं था। यह Objective-C category merging को भी बंद करता है, क्योंकि वह merged methods से Thumb bit हटाकर 3GS पर AirBuild3G को crash कर देता। make verify architectures, load commands, armv6 instructions और armv7 methods पर Thumb bit की जाँच करता है। सफल link इनमें से कुछ भी साबित नहीं करता।

AirBuild3G और उसका privileged helper बनाने के लिए make चलाएँ। make payload हर pinned package को local cache में download करके verify करता है, और make package उससे AirBuild3G का package और दोनों environment packages offline बनाता है। packaging के लिए LEGACY_SDK भी चाहिए, क्योंकि हर environment package में फ़ोन के लिए SDK archive होता है।

make install-device DEVICE=root@<ip> AirBuild3G और iOS 4 environment package इंस्टॉल करता है, Home screen को refresh करता है और ऐप को restart करता है। iOS 5 या उसके बाद के version वाले 3GS के लिए, इसके बजाय tools/deploy_device.sh में तीसरे argument के रूप में ios6 दें। ऐप mobile user के रूप में चलता है, और उसके पास मौजूद छोटा helper root की आवश्यकता वाले सभी काम चलाता है। environment package अब फ़ोन पर है; Settings → Environment उसमें मौजूद development tools इंस्टॉल करता है।

Model Service से कनेक्ट करना

Settings में, Endpoint के अंतर्गत आप तीन values दर्ज करते हैं: Base URL, API Key और Model। Requests फ़ोन से सीधे उस endpoint पर जाती हैं। हम बीच में कोई server नहीं चलाते, और कोई account या device registration नहीं है। आपकी model service model चलाती है, usage का bill देती है और बताती है कि क्या दर्ज करना है। यदि आपका फ़ोन हमारी ओर से उपहार था, तो वह इसके बजाय air.build से कनेक्ट होता है।

मूल iOS 4, SecureTransport के ज़रिए केवल TLS 1.0 इस्तेमाल करता है, जबकि मौजूदा endpoint को TLS 1.2 की आवश्यकता हो सकती है। हम CFNetwork के लिए Substrate tweak, TLSFix से TLS 1.2 जोड़ते हैं।

फ़ोन को अब भी server की पहचान verify करनी होती है। उसका पुराना trust store किसी मौजूदा endpoint की certificate chain हमेशा नहीं बना सकता, और TLS 1.2 जोड़ने से वह store update नहीं होता। connection विफल होने पर encryption और certificate trust को अलग-अलग समस्याएँ मानें।

AirBuild3G default रूप से certificates को validate करता है। request विफल होने पर पहले Base URL, API Key, Model और certificate chain की जाँच करें। Trust Any Certificate setting एक स्पष्ट अपवाद है। इसे चालू करने से server की पहचान की जाँच हट जाती है और आपकी API key ऐसे किसी भी व्यक्ति के सामने आ सकती है जो endpoint की नकल कर सके। endpoint आपके अपने network पर न हो तो इसे बंद रखें। AirBuild3G हर trust evaluation पर setting पढ़ता है, इसलिए इसे बंद करने का असर अगली request पर होता है।

src/ABLTrustAll.m
static OSStatus ABLSecTrustEvaluate(SecTrustRef trust, SecTrustResultType *result) {
	…
	OSStatus status = ABLOriginalSecTrustEvaluate(trust, result);
	if (!ABLTrustAnyCertificate()) {
		return status;
	}
	if (result != NULL) {
		*result = kSecTrustResultUnspecified;
	}
	return errSecSuccess;
}
  1. फ़ोन का अपना evaluation पहले चलता है।
  2. Trust Any Certificate default रूप से बंद है, इसलिए वह जवाब बिना बदले वापस जाता है। हर call पर setting को NSUserDefaults से पढ़ा जाता है।
SecTrustEvaluate पर hook, Trust Any Certificate चालू न होने पर, फ़ोन का अपना जवाब लौटाता है।

reply की streaming भी फ़ोन की क्षमता में होनी चाहिए। AirBuild3G background thread पर stream को receive और decode करता है तथा interface को केवल पूरा हो चुका text देता है। transcript हर token पर दोबारा मापने और scroll करने के बजाय तय अंतराल पर refresh होता है, जिससे reply आने के दौरान layout का काम कम रहता है। reply दिखने का अर्थ है कि connection काम करता है। model द्वारा लिखे गए code को compile करने के लिए अब भी फ़ोन पर environment चाहिए।

ऐसा Compiler इंस्टॉल करना जिसे फ़ोन चला सके

Settings → Environment, environment package से compiler, SDK headers, link libraries और signing tools इंस्टॉल करता है। iOS 4 package में armv6 के लिए GCC है; iOS 5 और उसके बाद के version के package में armv7 के लिए clang है। हर फ़ोन केवल अपने firmware से मेल खाने वाला package इंस्टॉल करता है।

हर package को download address और checksum से pin किया गया है, और installation किसी repository से latest versions माँगने के बजाय उन्हीं files का उपयोग करता है। pinned version पर पहले से इंस्टॉल packages छोड़ दिए जाते हैं, इसलिए Environment को दूसरी बार चलाने पर वह तुरंत पूरा हो जाता है। इसके बाद इंस्टॉल किए गए packages hold कर दिए जाते हैं, ताकि Cydia ऐसे upgrades देना बंद करे जो toolchain को तोड़ देते हैं।

iPhone 3G के लिए environment, iphone-gcc 4.2 के 2008 build का उपयोग करता है। 2009 build precompiled headers के लिए एक gigabyte reserve करता है, जिससे उसका एक हिस्सा armv6 फ़ोन के address space से बाहर चला जाता है। उसके compiler backends कुछ भी compile करने से पहले crash हो जाते हैं। दोनों builds स्वयं को Apple build 5555 बताते हैं, लेकिन केवल 2008 वाला उस address space में समाता है।

armv6 सीमा0x30000000

2008 iphone-gcc · हर segment 6 MB से कमचलता है

512 MB array · test binaryचलती है

768 MB array · test binarySIGBUS

2009 iphone-gcc · 1 GB precompiled-header arenaSIGBUS

2008 compiler armv6 सीमा के भीतर समाता है; 2009 build main से पहले विफल हो जाता है क्योंकि उसका reserved arena सीमा पार कर जाता है। 512 MB और 768 MB की test arrays सीमा के दोनों ओर आती हैं।

SDK headers में भी बदलाव चाहिए: यह GCC उनकी block declarations को parse नहीं कर सकता। iOS 4 headers को package करने से पहले हम उन declarations के साथ हर ऐसी declaration हटाते हैं जो किसी हटाए गए type पर निर्भर है। clang package Apple के headers को बिना बदले रखता है, क्योंकि clang blocks का समर्थन करता है। हर package में एक signing tool भी होता है, जो उसके compiler से बनी चीज़ को sign कर सकता है।

फ़ोन अपने system frameworks को dyld shared cache में रखता है, इसलिए framework directories में linker के पढ़ने के लिए library files नहीं होतीं। हम ऐसे generated stubs देते हैं जिनमें exported symbols और असली library names होते हैं। linker हर reference को stub से resolve करता है और दर्ज करता है कि वह किस library से आया है; launch के समय dyld उसे shared cache में असली implementation से bind करता है। चूँकि stubs में असली symbol names होते हैं, किसी framework को छोड़ देने पर ऐप launch के समय crash होने के बजाय link विफल हो जाता है।

environment include paths भी सेट करता है, gcc और cc को इंस्टॉल किए गए compiler की ओर point करता है और ऐसे link flags export करता है जो Objective-C runtime और CoreFoundation को शामिल करते हैं। bundled Makefile उन settings को पढ़ता है, इसलिए नए project को अपनी ओर से toolchain setup की आवश्यकता नहीं होती। इसके बाद आपके ऐप फ़ोन पर ही बनते हैं।

template/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 $@
  1. IOS_CFLAGS और IOS_LDFLAGS, /etc/profile.d/airbuild.sh से आते हैं, जिसे environment इंस्टॉल करता है, इसलिए कोई project SDK या compiler path का नाम नहीं देता।
  2. binary को फ़ोन पर ही sign किया जाता है। बिना sign किए, launch के समय वह बंद कर दी जाती है।
वह Makefile जिससे नया project शुरू होता है। फ़ोन केवल अपनी इंस्टॉल की हुई toolchain से compile, link और sign करता है।

अपना पहला ऐप बनाना और चलाना

नया project bundled UIKit template से शुरू होता है। AirBuild3G उसे project directory में copy करता है और उसकी files को model के context में रखता है। model ऐसे project से शुरू करता है जो पहले से build होता है, इसलिए आपकी पहली request में Makefile माँगने के बजाय ऐप का वर्णन किया जा सकता है।

पहला अच्छा ऐप ऐसा button है जो screen पर किसी number को बढ़ाता है। वह पूरा पढ़ने के लिए पर्याप्त छोटा है, फिर भी उससे editing, compiling, packaging और फ़ोन पर running सभी की जाँच हो जाती है। अपनी request में iOS 4 को target बताएँ और model से ऐप को build और install करने के लिए कहें। उसके interface code को उन्हीं APIs तक सीमित रहना चाहिए जिन्हें फ़ोन का iOS लागू करता है। headers में declaration होने का अर्थ यह नहीं कि API run time पर मौजूद है।

model के पास चार tools हैं। Execute project directory में commands चलाता है। EditFile पूरी file लिखता है। PatchFile किसी सटीक passage को बदलता है। CheckBuild, environment की settings load करके make चलाता है और उसके output का अंतिम हिस्सा लौटाता है। Tool results model को वापस मिलते हैं, जो काम जारी रख सकता है या विफल बदलाव को सुधार सकता है। inference पूरे समय आपकी model service पर रहता है।

RemoteModelआपका endpoint

tool call

tool result

फ़ोन परToolsExecute EditFile PatchFile CheckBuild

यह loop तब तक दोहरता है जब तक model बिना tool call के reply न करे, आप Stop पर टैप न करें या 16 rounds पूरे न हो जाएँ।

tool का एक round। model दूर से चलता है और उसके tools फ़ोन पर चलते हैं; loop जारी रहने पर हर result अगली request में जाता है।

नया ऐप इंस्टॉल हो जाने के बाद उसे फ़ोन पर खोलें। counter के लिए button पर टैप करके number को बदलते देखना ठीक उसी चीज़ की जाँच करता है जो आपने माँगी थी। बाद की requests layout बदल सकती हैं या कोई और interaction जोड़ सकती हैं, और model मौजूदा files तथा पहले के tool results पर आगे काम करता है।

AirBuild3G हर project और उसकी बातचीत को फ़ोन पर रखता है, और tool के हर round के बाद save करता है। यह यह भी याद रखता है कि कौन-सा project खुला है, क्योंकि ऐप से बाहर जाने या memory कम पड़ने पर उसका process समाप्त हो सकता है। AirBuild3G को दोबारा खोलने पर वह बातचीत और files सहित उसी project पर लौटता है। आपका अगला message save की गई बातचीत से आगे बढ़ता है, और अगले edit तथा build के लिए source वहीं रहता है।

इस screen पर, वह counter भी उन ऐप को जोड़ता है जिन्हें आप कभी इस्तेमाल करते थे और जिन्हें अब बना सकते हैं।

परिचित Screen पर कुछ नया

iPhone 3G, 2008 में आया था, ठीक उसी समय जब App Store खुला था। कोई नया ऐप उसे ऐसा instrument, notebook या game बना सकता था जिसे आप हर जगह साथ ले जाते। Home button आपको icons के grid पर वापस लाता था, जहाँ हर icon कुछ और आज़माने का निमंत्रण था।

आज उस फ़ोन पर आपको महसूस होता है कि 2008 कितना पहले था। display बड़ा नहीं हुआ है। processor तेज़ नहीं हुआ है। मूल interface अब भी ऐप से कुछ controls और थोड़ी जगह का सावधानी से उपयोग करने को कहता है। उन सीमाओं में काम करते हुए आप ऐसे विवरणों पर ध्यान देते हैं जिन्हें किसी नए device पर अनदेखा करना आसान है: button प्रतिक्रिया देता है या नहीं, बातचीत आसानी से scroll होती है या नहीं, अगली screen सही समय पर दिखती है या नहीं।

AirBuild3G उस परिचित screen को एक और काम देता है। मौजूदा model ऐप लिखने में आपकी मदद करता है, और फ़ोन के अपने दौर का compiler उसे बनाता है। पुराने icons वहीं रह सकते हैं।

उनके साथ ऐसे ऐप के लिए भी जगह है जो पहले मौजूद नहीं था।

GitHub पर AirBuild3G

AirBuild टीम की ओर से सप्रेम