تصور کنید فایلهای اجرایی نرمافزارهای شما به جای بلوکهای مبهمی از کد ماشین، پایگاهدادههایی باشند که میتوانید با دستورات SQL آنها را تحلیل کنید. fzakaria نمونه اولیهای به نام SELF (Structured Executable & Linkable Format) را توسعه داده است که فرمت استاندارد صنعت یعنی ELF را با یک پایگاهداده SQLite جایگزین میکند؛ به این معنا که فایلی که شما با دستور chmod +x اجرایی میکنید، در واقع یک پایگاهداده کامل است.
دهههاست که دنیای لینوکس به فرمت ELF (Executable and Linkable Format) متکی است. این ساختار صلب و فشردهای است که در زمانی طراحی شد که فضای دیسک و پهنای باند شبکه بسیار محدود بود. برای تحلیل یک فایل ELF، ابزارهایی مثل readelf یا objdump باید بهصورت دستی آفستهای پیچیده را تجزیه کنند. این وضعیت باعث ایجاد اکوسیستمی پراکنده شده است که در آن هر ابزار باید تجزیهکننده (Parser) مخصوص به خود را پیادهسازی کند. نویسنده پروژه اشاره میکند که ELF در واقع پایگاهدادهای است که نمیخواهد این حقیقت را بپذیرد و برای رسیدن به سرعت بالا، مفاهیم اولیه پایگاهداده را بهصورت دستی پیاده کرده است.
همانطور که در تحلیلهای پیشین ما دربارهی معماریهای توزیعشده و مدیریت وابستگیها اشاره کردیم، حذف پیچیدگیهای لایهی پایین میتواند سرعت توسعه را بهشدت افزایش دهد. SELF با الهام از فلسفه NixOS — که کل سیستم را به عنوان یک پیکربندی اعلامی (Declarative) میبیند — با فایل اجرایی مانند یک مخزن داده ساختاریافته برخورد میکند. در این رویکرد، تغییر دادن یک باینری دیگر نیازمند جراحیهای خطرناک روی آفستها نیست، بلکه تبدیل به یک تراکنش (Transaction) در پایگاهداده میشود. این پژوهش که از دوران دکتری نویسنده آغاز شده، ابتدا منجر به ابزاری به نام sqlelf شد که اجازه میداد با دستوراتی مثل SELECT name FROM elf_symbols به جای ترکیب readelf و grep به نمادهای فایل دسترسی پیدا کنیم (به نقل از مقاله arXiv:2405.03883).

معماری یک فایل اجراییِ پایگاهدادهای
یک فایل SELF صرفاً پایگاهدادهای نیست که یک برنامه را توصیف کند، بلکه خودِ همان فایلی است که اجرا میشود. طبق مستندات پروژه، وقتی این فایل را با دستور file بررسی میکنید، به عنوان یک پایگاهداده SQLite 3.x با شناسه اپلیکیشن 0x53454c46 شناخته میشود. برای عملکرد صحیح، این فایل تنها به دو جدول اصلی نیاز دارد:
- self_meta: هدر ELF را به صورت جفتهای کلید-مقدار ذخیره میکند.
- segments: تصویر بارگذاری (Load Image) را در خود جای میدهد. هر ردیف در این جدول نماینده یک هدر برنامه است که بایتهای آن به صورت BLOB (یک نوع ذخیرهسازی دادههای حجیم و بدون ساختار) ذخیره شدهاند.
یکی از مهمترین تغییرات، جایگزینی جدول نمادها (Symbol Table) است. در ELF، جستوجوی نمادها به بخشهای پیچیدهای مثل .gnu.hash متکی است. SELF تمام اینها را با یک جدول symbols و یک ایندکس استاندارد B-tree در SQLite جایگزین میکند که همان کارایی را با ساختاری استاندارد فراهم میآورد.
جزئیات جدول نمادها
جدول symbols شامل ستونهایی برای نام، نسخه (مثلاً GLIBC_2.2.5)، مقدار، اندازه و نوع نماد است. این طراحی نیاز به جدول رشتهها (.dynstr) را از بین میبرد، زیرا SQLite بهطور پیشفرض رشتهها را مدیریت میکند. همچنین نسخهبندی نمادها که در ELF به ساختارهای پیچیدهای نیاز داشت، اکنون تنها یک ستون ساده در جدول است.
از باینریها به پرسوجوها
از آنجایی که فایل اجرایی اکنون یک پایگاهداده است، ابزارهای سنتی تحلیل باینری به پرسوجوهای ساده SQL تبدیل میشوند. به گزارش توسعهدهنده، ابزار ldd (که وابستگیهای کتابخانهای را لیست میکند) اکنون تبدیل به یک عملیات JOIN بین جداول symbols و segments شده است تا نام کتابخانههای مورد نیاز را پیدا کند.
برخی از معادلهای ابزاری در این سیستم عبارتاند از:
- nm -D --undefined: تبدیل به
SELECT name,version FROM imports LIMIT 3 - readelf -l: تبدیل به
SELECT type,vaddr,memsz,r,w,x FROM segments WHERE type='load' - ldd: یک پرسوجو روی جدول
needed
اصلاح یک باینری دیگر ریسکبرانگیز نیست. دستور strip که نمادهای دیباگ را برای کاهش حجم حذف میکند، اکنون به یک تراکنش DELETE و سپس دستور VACUUM (برای بهینهسازی فضای دیسک) تبدیل شده است. این کار میتواند حجم یک فایل را از ۵۷,۳۴۴ بایت به ۴۹,۱۵۲ بایت کاهش دهد بدون اینکه برنامه از کار بیفتد.
برای اجرای این سیستم در لینوکس، SELF از زیرسیستم binfmt_misc در هسته (Kernel) استفاده میکند. این سیستم طوری پیکربندی شده که بایتهای جادویی SQLite و شناسه خاص SELF را شناسایی کرده و سپس برنامه self-exec را فراخوانی کند. این مفسر کوچک که باید حتماً با فرمت ELF باشد تا از حلقه تکرار بینهایت جلوگیری شود، دادهها را از پایگاهداده میخواند، آنها را در حافظه نگاشت میکند و برنامه را اجرا میسازد.
پیوند پویا و بارگذار SQL
بیشترین مزیت این رویکرد در پیوند پویا (Dynamic Linking) ظاهر میشود. نویسنده دو مسیر را بررسی کرده است:
۱. rtld-audit: استفاده از رابط glibc برای رهگیری جستوجوی کتابخانهها. در اینجا، پاسخ به این سؤال که «کدام کتابخانه این نماد را دارد؟» به جای گشتن در فایلسیستم، از طریق یک پرسوجوی SQL داده میشود. این یعنی حتی اگر کتابخانه از روی دیسک حذف شود (rm libgreet.so.1)، برنامه همچنان میتواند با اسکن یک پایگاهداده سیستمی اجرا شود.
۲. self-ld: یک نمونه اثباتی از پیونددهنده پویا که کاملاً با SQL نوشته شده و جدول آفست جهانی (GOT) را با استفاده از پرسوجوهای پیچیده SQL اصلاح میکند.

موازنه عملکرد و اندازه
جایگزینی یک استاندارد جهانی هزینههایی دارد. حجم فایلهای SELF به دلیل سربار B-tree تقریباً دو برابر فایلهای ELF است. با این حال، در باینریهای بهینهشده (Stripped)، این تفاوت بسیار ناچیز است؛ برای مثال در coreutils تفاوت حجم کمتر از ۱٪ است.
تأخیر (Latency) چالش اصلی است. بنچمارکها نشان میدهند که برای هر اجرا، حدود ۵ میلیثانیه سربار ثابت برای باز کردن SQLite و شروع مفسر وجود دارد. همچنین یک مشکل جدی در بهرهوری حافظه وجود دارد: باینریهای SELF در حال حاضر از قابلیت نگاشت حافظه (Memory-mapping) ELF بهره نمیبرند. چون بایتها از B-tree کپی میشوند، دو پردازش که یک برنامه SELF را اجرا میکنند نمیتوانند صفحات متنی را در حافظه به اشتراک بگذارند که منجر به مصرف بیشتر رم میشود.
چشمانداز «سیستمعامل به مثابه پایگاهداده»
رادیکالترین کاربرد SELF، مفهوم «بستار» (Closure) است. یک پایگاهداده واحد SQLite میتواند یک برنامه و تمام وابستگیهای آن را در خود جای دهد. نویسنده ۷۲۳ فایل اجرایی و ۴۰۰ کتابخانه مشترک را در یک فایل userland.db جای داد. جالب اینجاست که حجم نهایی (۶۱۱.۹ مگابایت) از مجموع فایلهای ELF اصلی (۶۴۴.۴ مگابایت) کمتر شد؛ زیرا پایگاهداده بهطور طبیعی کتابخانههای مشترک و نمادهای تکراری را حذف (Deduplicate) میکند.
این معماری اجازه تغییرات اتمیک در سطح سیستم را میدهد. برای مثال، LD_PRELOAD دیگر یک متغیر محیطی نیست، بلکه ردیفی در یک جدول است. فعال یا غیرفعال کردن یک ردیابی (Trace) در کل سیستم، تنها با یک تراکنش SQL ساده (BEGIN و COMMIT) انجام میشود و در صورت بروز خطا، میتوان کل تغییرات را بهصورت اتمیک به حالت قبل بازگرداند (ROLLBACK).
گام بعدی شما
- اگر از NixOS استفاده میکنید، میتوانید محیط مجازی این پروژه را با دستور
nix run .#self-vmاجرا کنید تا تجربه اجرای یک پایگاهداده به جای باینری را داشته باشید. - برای درک عمیقتر، مقاله arXiv:2405.03883 را بخوانید تا متوجه شوید چگونه SQL میتواند جایگزین ابزارهای سنتی تحلیل باینری شود.
- بررسی کنید که آیا در پروژههای خود میتوانید از رویکرد دادهمحور برای مدیریت وابستگیهای پیچیده استفاده کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو