آغازگری که دروازه‌های بسته را شکست

پشت هر برنامه‌ای که امروز روی دستگاه‌هایتان اجرا می‌شود، یک بدهی پنهان نهفته است. پیش از آنکه حتی یک خط کد متن‌باز به یک فایل اجرایی تبدیل شود، چیزی باید آن را به دستورالعمل‌های باینری ترجمه کند. در حدود چهار دههٔ اخیر، این نقش را به‌طور قاطع مجموعهٔ کامپایلرهای جی‌ان‌یو یا همان GCC بر عهده داشته‌است. این ابزار فراتر از یک نرم‌افزار کمکی است؛ ستون فقرات اکوسیستم نرم‌افزار آزاد محسوب می‌شود و ایدهٔ ریشه‌ایِ دسترس‌پذیری کد منبع را به واقعیتی ملموس تبدیل کرد. با این حال، تاریخچهٔ آن با یک معمای عمیق در زمینهٔ امنیت نرم‌افزار گره خورده‌است.

ریچارد استالمن در دههٔ 1980 میلادی برای پروژهٔ جی‌ان‌یو (یک تلاش برای ساخت یک سیستم‌عامل شبیه یونیکس و کاملاً آزاد) به این نتیجه رسید که بازار نرم‌افزار به‌شدت توسط شرکت‌های کدبسته تسخیر شده‌است. در آن دوران، داشتن کد منبع به‌تنهایی کافی نبود؛ تبدیل آن کدها به برنامهٔ اجرایی مستلزم پرداخت هزینه‌های کلان به صاحبان کامپایلرهای تجاری بود. استالمن با توسعهٔ کامپایلری که به‌طور تخصصی برای زبان C طراحی شد، این وابستگی را حذف کرد. اولین نسخه که در سال 1987 منتشر گردید، بسیار پایه‌ای بود اما مسیر را برای انقلابی که بعدها رخ داد هموار ساخت. ابتدا این مخفف به‌صورت کامپایلر C جی‌ان‌یو شناخته می‌شد، اما به‌تدریج با اضافه شدن پشتیبانی از زبان‌های دیگر، نام آن به مجموعهٔ کامپایلرهای جی‌ان‌یو تغییر یافت تا نشان‌دهندهٔ ماهیت گستردهٔ آن باشد.

از یک زبان ساده تا اکوسیستم چندوجهی

رشد جی‌سی‌سی دقیقاً هم‌مسیر گسترش نرم‌افزار آزاد حرکت کرد. در انتهای دههٔ 1980، پشتیبانی از ++C از طریق پروژه‌ای مستقل به‌نام G++ ادغام شد. دههٔ 90 میلادی شاهد افزودن پشتیبانی از زبان‌های فورتان، آدا و اُبجکتیو-سی بود که جی‌سی‌سی را به یک ابزار همه‌کاره تبدیل کرد. در سال 1997، کنترل پروژه به یک کمیتهٔ هدایت مستقل واگذار شد تا تضمین گردد که توسعهٔ این ابزار تحت اصول مشارکت باز باقی بماند. نسل‌های بعدی با افزودن تکنیک‌های پیشرفتهٔ بهینه‌سازی و حمایت کامل از استانداردهای مدرن مثل ++C-20 تکمیل شدند.

امروز جی‌سی‌سی از طریق فرانت‌اندهای تخصصی خود طیف وسیعی از زبان‌ها را پوشش می‌دهد:

  • g++: فرانت‌اند ++C که مدیریت استثناها و الگوها را ساده می‌کند.
  • gccgo: فرانت‌اند زبان Go که به‌لطف بهینه‌سازی‌های جی‌سی‌سی، جایگزینی قدرتمند برای ابزارهای رسمی گوگل به‌شمار می‌رود.
  • gfortran: موتور پردازش زبان فورتان که استانداردهای دوازدهم به بعد را پشتیبانی می‌کند.
  • gccobjc: فرانت‌اند اُبجکتیو-سی که هنوز هم در پروژه‌های بین‌پلتفرمی کاربرد دارد.
معماری داخلی و فرانت‌اندهای مختلف مجموعه کامپایلرهای جی‌ان‌یو

معماری سه‌لایه که جهان را درنوردید

تأثیرگذاری بی‌نظیر جی‌سی‌سی مدیون طراحی مدولار و سه‌بخشی آن است. لایهٔ نخست یا بخش جلویی کد منبع را پارس می‌کند و آن را به یک نمایش میانی مستقل از زبان تبدیل می‌کند. در لایهٔ میانی، موتور بهینه‌سازی بدون توجه به زبانِ مبدأ، تکنیک‌هایی مانند حذف کدهای مرده، گسترش حلقه‌ها و تجمیع ثابت‌ها را اعمال می‌کند. این یعنی تمام زبان‌های پشتیبانی‌شده از یک ماشین بهینه‌سازی مشترک بهره می‌برند. در نهایت، لایهٔ پشتی این نمایش میانی را به کد باینری هدف برای پلتفرم‌هایی نظیر x86، ARM یا RISC-V تبدیل می‌کند و به تخصیص رجیستر و زمان‌بندی دستورالعمل‌ها می‌پردازد.

این معماری انعطاف‌پذیر باعث شد غول‌های فناوری به جی‌سی‌سی متکی شوند. شرکت‌های بزرگی مانند رد‌هت، گوگل (در ساخت اندروید و هستهٔ لینوکس) و اینتل، سال‌هاست که از این ابزار در زیرساخت‌های حیاتی خود استفاده می‌کنند. اپل نیز پیش از توسعهٔ کلانگ، در ساخت سیستم‌عامل‌های دسکتاپ و موبایل خود مستقیماً از جی‌سی‌سی بهره می‌برد. جزئیات فنی این معماری را می‌توان در مستندات ویکی‌پدیا دربارهٔ GCC بررسی کرد.

فرآیند کامپایل و تبدیل کد به ماشین توسط بخش‌های مختلف جی‌سی‌سی

پارادوکس اعتماد و ریشه‌های امنیتی

اینجا دقیقاً محل قرارگیری یکی از چالش‌برانگیزترین بحث‌های امنیت نرم‌افزار قرار دارد. جی‌سی‌سی خود به زبان‌های C و ++C نوشته شده‌است. این یعنی برای ساختن خودِ آن نیازی به یک کامپایلر دیگر است که قبلاً روی سیستم نصب شده‌باشد. توزیع‌های لینوکس معمولاً از باینری‌های آمادهٔ موجود برای کامپایل کردن نسخه‌های جدید استفاده می‌کنند، اما این چرخه سوالی جدی را ایجاد می‌کند: آیا می‌توان به خروجی چنین باینری‌هایی اعتماد کرد؟

این دقت‌نظر دقیقاً همان موضوعی بود که کن تامپسون، برندهٔ جایزهٔ تورینگ، در سخنرانی خود در سال 1984 با نام «تأملاتی دربارهٔ اعتماد به اعتماد» مطرح کرد. تامپسون نشان داد که یک کامپایلر آلوده قادر است کدهای جعلی را در دل باینری‌ها پنهان کند. وقتی همان باینری آلوده دوباره برای ساخت نسخهٔ بعدی استفاده شود، این کدهای مخرب در تمام چرخه‌های بعدی تکرار می‌شوند و شناسایی آن‌ها تقریباً غیرممکن خواهد ماند. حتی اگر کد منبع کاملاً شفاف باشد، اگر موتور ترجمهٔ آن دستکاری شده‌باشد، هیچ‌کس نمی‌تواند تضمین دهد که خروجی نهایی سالم است. توضیحات کامل این مفهوم را در مقالهٔ کن تامپسون در ویکی‌پدیا مطالعه کنید.

جامعهٔ متن‌باز برای غلبه بر این معمای بوت‌استرپ، راهکارهای نوینی را توسعه داده‌است. «ساخت‌های تکرارپذیر» یا Reproducible Builds به توسعه‌دهندگان اجازه می‌دهد تا با فراهم‌سازی دقیق محیط کامپایل، تضمین کنند که باینری حاصل دقیقاً همان چیزی خواهد بود که از کد منبع انتظار می‌رود. علاوه بر این، پلتفرم‌های مدرن از «کامپایل دوگانهٔ چندمنبعی» استفاده می‌کنند که در آن باینری‌ها توسط چندین کامپایلر متفاوت از زبان‌های مختلف ساخته و با هم تطبیق داده می‌شوند. این رویکرد، احتمال تزریق باگ‌های هدفمند یا بدافزارهای پنهان را به‌شدت کاهش می‌دهد.

پیش‌بینی می‌شود که با ورود هوش مصنوعی به چرخهٔ بهینه‌سازی کامپایلرها و افزایش پیچیدگی معماری‌های سخت‌افزاری، ابزارهایی مانند جی‌سی‌سی نقش پیشران‌تری در شفاف‌سازی فرآیندهای ساخت خواهند داشت. تأکید بر شفافیت، ساخت‌های تکرارپذیر و معماری‌های باز، آیندهٔ صنعت نرم‌افزار را از حالت تکیه بر اعتماد کورکورانه به سمت استانداردهای قابل‌اثبات هدایت خواهد کرد.