• فایل elf را باز کنید. ما یک فایل ELF با اطلاعات اشکال زدایی (DWARF) به صورت دستی (برای میکروکنترلرهای ARM) ایجاد می کنیم. ساختار فایل جهانی

    در این بررسی ما فقط در مورد نسخه 32 بیتی این فرمت صحبت خواهیم کرد، زیرا هنوز به نسخه 64 بیتی نیاز نداریم.

    هر فایل ELF (از جمله ماژول های شی با این فرمت) از بخش های زیر تشکیل شده است:

    • هدر فایل ELF.
    • جدول بخش های برنامه (ممکن است در ماژول های شی وجود نداشته باشد).
    • بخش های فایل ELF.
    • جدول بخش (ممکن است در ماژول اجرا وجود نداشته باشد).
    • به دلایل عملکرد، فرمت ELF از فیلدهای بیتی استفاده نمی کند. و همه ساختارها معمولاً 4 بایت تراز هستند.

    حال بیایید به انواع مورد استفاده در هدر فایل ELF نگاه کنیم:

    حالا بیایید به هدر فایل نگاه کنیم:

    #define EI_NIDENT 16 struct elf32_hdr ( char e_ident بدون علامت; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; /* Entry point Elf _Word e_fla gs;

    آرایه e_ident حاوی اطلاعاتی در مورد سیستم است و از چندین زیر فیلد تشکیل شده است.

    ساختار ( char ei_magic بدون امضا؛ char ei_class بدون امضا؛ char ei_data بدون امضا؛ char ei_نسخه بدون امضا؛ char ei_pad بدون امضا؛ )

    • ei_magic - مقدار ثابت برای همه فایل‌های ELF، برابر با (0x7f، "E"، "L"، "F")
    • ei_class - کلاس فایل ELF (1 - 32 بیت، 2 - 64 بیت که ما در نظر نمی گیریم)
    • ei_data - ترتیب بایت را برای این فایل تعیین می کند (این ترتیب به پلتفرم بستگی دارد و می تواند به جلو (LSB یا 1) یا معکوس (MSB یا 2) باشد) برای پردازنده های اینتل فقط مقدار 1 مجاز است.
    • ei_version یک فیلد نسبتاً بی استفاده است و اگر برابر با 1 (EV_CURRENT) نباشد، فایل نادرست در نظر گرفته می شود.

    سیستم عامل ها اطلاعات شناسایی خود را در قسمت ei_pad ذخیره می کنند. ممکن است این فیلد خالی باشد. برای ما هم فرقی نمی کند.

    فیلد هدر e_type می تواند حاوی چندین مقدار باشد، برای فایل های اجرایی باید ET_EXEC برابر با 2 باشد.

    e_machine - پردازنده ای را تعیین می کند که این فایل اجرایی می تواند روی آن اجرا شود (برای ما، مقدار قابل قبول EM_386 3 است)

    فیلد e_version با فیلد ei_version از هدر مطابقت دارد.

    فیلد e_entry آدرس شروع برنامه را تعریف می کند که قبل از شروع برنامه در eip قرار می گیرد.

    فیلد e_phoff مقدار افست را از ابتدای فایل مشخص می کند که جدول بخش برنامه مورد استفاده برای بارگذاری برنامه ها در حافظه در آن قرار دارد.

    من هدف همه فیلدها را لیست نمی کنم. من فقط دو مورد دیگر را شرح می دهم.

    فیلد e_phentsize اندازه ورودی را در جدول بخش برنامه مشخص می کند.

    و قسمت e_phnum تعداد ورودی های جدول بخش برنامه را مشخص می کند.

    جدول بخش (نه بخش های برنامه) برای پیوند دادن برنامه ها استفاده می شود. ما آن را در نظر نخواهیم گرفت. ما همچنین ماژول های پیوند شده پویا را در نظر نخواهیم گرفت. این موضوع کاملاً پیچیده است و برای اولین آشنایی مناسب نیست. :)

    حالا در مورد بخش های برنامه. فرمت ورود جدول بخش برنامه به شرح زیر است:

    ساختار elf32_phdr (Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_memsz; Elf_3_Word p_memsz;

    درباره زمینه ها بیشتر بدانید

    • p_type - نوع بخش برنامه را تعیین می کند. می تواند چندین مقدار داشته باشد، اما ما فقط به یک مورد علاقه داریم. PT_LOAD (1). اگر بخش از این نوع باشد، در نظر گرفته شده است که در حافظه بارگذاری شود.
    • p_offset - افست را در فایلی که این بخش از آن شروع می شود تعیین می کند.
    • p_vaddr - آدرس مجازی را که این بخش باید در حافظه بارگذاری شود را مشخص می کند.
    • p_paddr - آدرس فیزیکی که این بخش باید در آن بارگذاری شود را مشخص می کند. این فیلد اختیاری است و فقط در برخی از پلتفرم ها معنا دارد.
    • p_filesz - اندازه بخش را در فایل تعیین می کند.
    • p_memsz - اندازه بخش حافظه را تعیین می کند. این مقدار ممکن است بیشتر از مقدار قبلی باشد. فیلد p_flag نوع دسترسی به بخش‌های حافظه را تعیین می‌کند. برخی از بخش ها را می توان انجام داد، برخی را می توان نوشت. همه برای خواندن در سیستم های موجود در دسترس هستند.

    در حال بارگیری قالب ELF.

    عنوان را کمی فهمیدیم. حالا یک الگوریتم برای بارگذاری یک فایل باینری با فرمت ELF ارائه می کنم. الگوریتم شماتیک است و نباید به عنوان یک برنامه کاری در نظر گرفته شود.

    Int LoadELF (char *bin بدون علامت) (struct elf32_hdr *EH = (struct elf32_hdr *)bin؛ struct elf32_phdr *EPH; if (EH->e_ident != 0x7f || // Control MAGIC EH->e_ident != "E" ||. EH->e_ident != EH->e_ident != ELFCLASS32 || e_ident != EV_CURRENT ||. // نسخه EH->e_type != ET_EXEC || // تایپ کنید EH->e_machine != EM_386 || // پلت فرم EH->e_CURRENT) //. بازگشت ELF_WRONG = (struct elf32_phdr *)(bin + EH->e_phnum--) (اگر (EPH->p_type == PT_LOAD) memcpy (EPH->p_vaddr, bin + EPH-); >p_offset، EPH->p_filesz = (struct elf32_phdr *)((char unsigned *)EPH + EH->e_phentsize)); )

    به طور جدی، ارزش آنالیز فیلدهای EPH->p_flags و تنظیم حقوق دسترسی به صفحات مناسب را دارد، و به سادگی کپی کردن در اینجا کار نخواهد کرد، اما این دیگر به قالب مربوط نمی شود، بلکه به تخصیص حافظه مربوط می شود. بنابراین، ما در حال حاضر در این مورد صحبت نمی کنیم.

    فرمت PE

    از بسیاری جهات شبیه فرمت ELF است و جای تعجب نیست که بخش هایی نیز برای دانلود در دسترس باشد.

    مانند هر چیز دیگری در مایکروسافت :) فرمت PE بر اساس فرمت EXE است. ساختار فایل به شرح زیر است:

    • 00h - هدر EXE (من به آن نگاه نمی کنم، به اندازه Dos قدیمی است. :)
    • 20h - هدر OEM (هیچ چیز قابل توجهی در آن نیست).
    • 3сh - افست هدر PE واقعی در فایل (dword).
    • میز حرکت خرد;
    • خرد
    • هدر PE؛
    • جدول شی
    • اشیاء فایل؛

    stub برنامه ای است که در حالت واقعی اجرا می شود و برخی از اقدامات اولیه را انجام می دهد. ممکن است وجود نداشته باشد، اما گاهی اوقات ممکن است نیاز باشد.

    ما به چیزی کمی متفاوت علاقه مند هستیم، هدر PE.

    ساختار آن به این صورت است:

    Struct pe_hdr ( pe_sign بلند بدون علامت; pe_cputype کوتاه بدون امضا; pe_objnum کوتاه بدون امضا; pe_time طولانی بدون امضا; pe_cofftbl_off طولانی بدون امضا; pe_nthdr_size کوتاه بدون امضا; pe_nthdr_size; link_ver; اندازه طولانی بدون امضا pe_idata_size ;

    چیزهای زیادی آنجاست کافی است بگوییم که حجم این هدر 248 بایت است.

    و نکته اصلی این است که اکثر این زمینه ها استفاده نمی شوند. (چه کسی اینطوری میسازد؟) نه، آنها البته هدفی دارند که کاملاً شناخته شده است، اما برنامه آزمایشی من مثلاً در فیلدهای pe_code_base، pe_code_size و غیره صفر دارد، اما خوب کار می کند. نتیجه این است که فایل بر اساس جدول شی بارگذاری می شود. این چیزی است که ما در مورد آن صحبت خواهیم کرد.

    جدول شی بلافاصله بعد از هدر PE دنبال می شود. ورودی های این جدول دارای فرمت زیر هستند:

    Struct pe_ohdr ( char o_name بدون علامت; o_vsize طولانی بدون امضا؛ o_vaddr طولانی بدون امضا؛ o_psize طولانی بدون امضا؛ طولانی o_poff بدون امضا؛ char o_reserved بدون امضا؛ o_flags طولانی بدون امضا؛ );

    • o_name - نام بخش، نسبت به بارگیری کاملاً بی تفاوت است.
    • o_vsize - اندازه بخش حافظه؛
    • o_vaddr - آدرس حافظه نسبت به ImageBase.
    • o_psize - اندازه بخش در فایل.
    • o_poff - افست بخش در فایل.
    • o_flags - پرچم های بخش;

    ارزش نگاهی دقیق تر به پرچم ها را دارد.

    • 00000004h - برای کد با افست 16 بیتی استفاده می شود
    • 00000020h - بخش کد
    • 00000040h - بخش داده های اولیه
    • 00000080h - بخش داده های بدون مقدار اولیه
    • 00000200h - نظرات یا هر نوع اطلاعات دیگری
    • 00000400h - بخش پوشش
    • 00000800h - بخشی از تصویر برنامه نخواهد بود
    • 00001000h - داده های عمومی
    • 00500000h - تراز پیش‌فرض مگر اینکه خلاف آن مشخص شده باشد
    • 02000000h - می تواند از حافظه تخلیه شود
    • 04000000h - ذخیره نشده است
    • 08000000h - صفحه بندی نشده است
    • 10000000 ساعت - به اشتراک گذاشته شده است
    • 20000000 ساعت - قابل انجام است
    • 40000000h - قابل خواندن است
    • 80000000h - می توانید بنویسید

    باز هم، من در مورد بخش های مشترک و همپوشانی صحبت نمی کنم، ما به کد، داده ها و حقوق دسترسی علاقه مندیم.

    به طور کلی، این اطلاعات از قبل برای دانلود فایل باینری کافی است.

    در حال بارگیری فرمت PE.

    int LoadPE (unsigned char *bin) (struct elf32_hdr *PH = (struct pe_hdr *) (bin + *((unsigned long *)&bin)); // البته ترکیب مشخص نیست... فقط dword را بگیرید در افست 0x3c / / و محاسبه آدرس هدر PE در تصویر فایل elf32_phdr *POH اگر (PH == NULL || // کنترل نشانگر PH->pe_sign != 0x4550 || // PE signature ("P"; , "E" 0, 0) PH->pe_cputype != 0x14c | >pe_obj_num--) (اگر ((POH->p_flags & 0x60) != 0) // کد یا memcpy داده اولیه (PE->pe_image_base + POH->o_vaddr، bin + POH- >o_poff، POH->o_psize POH = (struct pe_ohdr *)((char unsigned *)POH + sizeof (struct pe_ohdr));

    این دوباره یک برنامه آماده نیست، بلکه یک الگوریتم بارگذاری است.

    و باز هم به بسیاری از نکات پرداخته نمی شود، زیرا از محدوده موضوع فراتر می رود.

    اما اکنون ارزش آن را دارد که کمی در مورد ویژگی های سیستم موجود صحبت کنیم.

    ویژگی های سیستم

    با وجود انعطاف‌پذیری ابزارهای حفاظتی موجود در پردازنده‌ها (محافظت در سطح جداول توصیفگر، حفاظت در سطح بخش، حفاظت در سطح صفحه)، در سیستم‌های موجود (هم ویندوز و هم یونیکس) فقط از محافظت از صفحه به طور کامل استفاده می‌شود که اگرچه می تواند کد را از نوشتن محافظت کند، اما نمی تواند از داده ها در برابر اجرا محافظت کند. (شاید این دلیل انبوه آسیب پذیری های سیستم باشد؟)

    همه بخش ها از آدرس خطی صفر آدرس دهی می شوند و تا انتهای حافظه خطی گسترش می یابند. تحدید حدود فرآیند فقط در سطح جداول صفحه انجام می شود.

    در این راستا، همه ماژول ها نه از آدرس های شروع، بلکه با یک افست به اندازه کافی بزرگ در بخش مرتبط هستند. در ویندوز، آدرس پایه در بخش 0x400000، در Unix (Linux یا FreeBSD) - 0x8048000 است.

    برخی از ویژگی ها نیز به سازماندهی صفحه حافظه مربوط می شود.

    فایل های ELF به گونه ای پیوند داده شده اند که مرزها و اندازه بخش ها در بلوک های 4 کیلوبایتی فایل قرار می گیرند.

    و در قالب PE، علیرغم این واقعیت که خود فرمت به شما امکان می دهد بخش های 512 بایتی را تراز کنید، ترازبندی بخش ها در 4k استفاده می شود.

    امیدواریم به شما کمک کرده باشیم تا مشکل خود را با فایل ELF حل کنیم. اگر نمی دانید از کجا می توانید یک برنامه را از لیست ما دانلود کنید، روی پیوند کلیک کنید (این نام برنامه است) - اطلاعات دقیق تری در مورد جایی که می توانید نسخه نصب امن برنامه مورد نیاز را دانلود کنید، پیدا خواهید کرد.

    بازدید از این صفحه به شما کمک می کند به طور خاص به این سوالات یا سوالات مشابه پاسخ دهید:

    • چگونه یک فایل با پسوند ELF باز کنیم؟
    • چگونه یک فایل ELF را به فرمت دیگری تبدیل کنیم؟
    • پسوند فرمت فایل ELF چیست؟
    • چه برنامه هایی از فایل ELF پشتیبانی می کنند؟

    اگر پس از مشاهده مطالب موجود در این صفحه، هنوز پاسخ رضایت بخشی به هیچ یک از سوالات ارائه شده در بالا دریافت نکرده اید، به این معنی است که اطلاعات ارائه شده در اینجا در مورد فایل ELF ناقص است. با استفاده از فرم تماس با ما تماس بگیرید و اطلاعاتی را که پیدا نکردید بنویسید.

    چه چیز دیگری می تواند مشکلاتی ایجاد کند؟

    ممکن است دلایل بیشتری وجود داشته باشد که چرا نمی توانید فایل ELF را باز کنید (نه فقط عدم وجود برنامه مربوطه).
    اولا- فایل ELF ممکن است به اشتباه (ناسازگار) با برنامه نصب شده برای پشتیبانی از آن پیوند داده شده باشد. در این صورت باید خودتان این اتصال را تغییر دهید. برای این کار روی فایل ELF که می خواهید ویرایش کنید کلیک راست کرده و روی گزینه کلیک کنید "برای باز کردن با"و سپس برنامه ای را که نصب کرده اید از لیست انتخاب کنید. پس از این عمل، مشکلات باز کردن فایل ELF باید به طور کامل ناپدید شوند.
    دوما- فایلی که می خواهید باز کنید ممکن است به سادگی آسیب دیده باشد. در این صورت بهتر است نسخه جدیدی از آن را پیدا کنید یا دوباره آن را از همان منبع دانلود کنید (شاید به دلایلی در جلسه قبل دانلود فایل ELF تمام نشد و به درستی باز نشد) .

    ایا میخواهید کمک کنید؟

    اگر اطلاعات بیشتری در مورد پسوند فایل ELF دارید، اگر آن را با کاربران سایت ما به اشتراک بگذارید سپاسگزار خواهیم بود. از فرم زیر استفاده کنید و اطلاعات خود را در مورد فایل ELF برای ما ارسال کنید.

    ابزارهای توسعه استاندارد برنامه شما را در یک فایل ELF (قالب اجرایی و پیوند پذیر) با قابلیت گنجاندن اطلاعات اشکال زدایی کامپایل می کنند. مشخصات قالب قابل خواندن است. علاوه بر این، هر معماری ویژگی های خاص خود را دارد، مانند ویژگی های ARM. بیایید نگاهی کوتاه به این قالب بیندازیم.
    یک فایل اجرایی با فرمت ELF از بخش های زیر تشکیل شده است:
    1. سرصفحه (ELF Header)
    حاوی اطلاعات کلی در مورد فایل و مشخصات اصلی آن است.
    2. جدول سربرگ برنامه
    این جدول مطابقت بین بخش‌های فایل و بخش‌های حافظه است و به بوت‌لودر می‌گوید که هر بخش را در کدام ناحیه بنویسد.
    3. بخش ها
    بخش ها حاوی تمام اطلاعات موجود در فایل (برنامه، داده ها، اطلاعات اشکال زدایی و غیره) هستند.
    هر بخش دارای یک نوع، نام و پارامترهای دیگر است. بخش ".text" معمولا کد را ذخیره می کند، ".symtab" - جدولی از نمادهای برنامه (نام فایل ها، رویه ها و متغیرها)، "strtab. - جدولی از رشته ها، بخش هایی با پیشوند ".debug_" - اشکال زدایی اطلاعات و غیره .d. علاوه بر این، فایل باید دارای یک بخش خالی با شاخص 0 باشد.
    4. جدول سرصفحه بخش
    این جدول حاوی آرایه ای از سرصفحه های بخش است.
    این قالب با جزئیات بیشتری در بخش ایجاد ELF مورد بحث قرار گرفته است.

    بررسی اجمالی کوتوله

    DWARF یک فرمت اطلاعات اشکال زدایی استاندارد است. استاندارد را می توان از وب سایت رسمی دانلود کرد. همچنین یک نمای کلی کوتاه عالی از قالب وجود دارد: مقدمه ای بر فرمت اشکال زدایی DWARF (مایکل جی. اشتیاق).
    چرا اشکال زدایی اطلاعات مورد نیاز است؟ آن اجازه می دهد:
    • نقاط شکست را نه یک آدرس فیزیکی، بلکه روی شماره خط در فایل کد منبع یا نام تابع تنظیم کنید
    • نمایش و تغییر مقادیر متغیرهای سراسری و محلی و همچنین پارامترهای تابع
    • نمایش پشته تماس (backtrace)
    • برنامه را گام به گام نه بر اساس یک دستورالعمل اسمبلی، بلکه بر اساس خطوط کد منبع اجرا کنید
    این اطلاعات در یک ساختار درختی ذخیره می شود. هر گره درختی یک والد دارد، می تواند فرزند داشته باشد و DIE (Entry اطلاعات اشکال زدایی) نامیده می شود. هر گره دارای برچسب (نوع) و لیستی از ویژگی ها (خواص) است که گره را توصیف می کند. ویژگی ها می توانند هر چیزی را شامل شوند، مانند داده ها یا پیوندهایی به گره های دیگر. علاوه بر این، اطلاعاتی در خارج از درخت ذخیره می شود.
    گره ها به دو نوع اصلی تقسیم می شوند: گره هایی که داده ها را توصیف می کنند و گره هایی که کد را توصیف می کنند.
    گره هایی که داده ها را توصیف می کنند:
    1. انواع داده ها:
      • انواع داده های پایه (گره با نوع DW_TAG_base_type)، مانند نوع int در C.
      • انواع داده های ترکیبی (اشاره گر و غیره)
      • آرایه ها
      • ساختارها، کلاس ها، اتحادیه ها، رابط ها
    2. اشیاء داده:
      • ثابت ها
      • پارامترهای تابع
      • متغیرها
      • و غیره.
    هر شی داده دارای یک ویژگی DW_AT_location است که نحوه محاسبه آدرسی که داده ها در آن قرار دارند را مشخص می کند. به عنوان مثال، یک متغیر می تواند یک آدرس ثابت داشته باشد، در یک ثبات یا پشته باشد، یا عضو یک کلاس یا شی باشد. این آدرس را می توان به روشی نسبتاً پیچیده محاسبه کرد، بنابراین استاندارد اصطلاحات موقعیت مکانی را ارائه می دهد که می تواند شامل دنباله ای از عملگرهای یک ماشین پشته داخلی خاص باشد.
    گره هایی که کد را توصیف می کنند:
    1. رویه ها (توابع) - گره هایی با برچسب DW_TAG_subprogram. گره های نزول می توانند شامل توضیحات متغیرها - پارامترهای تابع و متغیرهای تابع محلی باشند.
    2. واحد تالیف. حاوی اطلاعات برنامه است و والد تمام گره های دیگر است.
    اطلاعاتی که در بالا توضیح داده شد در بخش‌های ".debug_info" و ".debug_abbrev" قرار دارند.
    اطلاعات دیگر:
    • اطلاعات مربوط به شماره خطوط (بخش ".debug_line")
    • اطلاعات در مورد ماکروها (بخش ".debug_macinfo")
    • اطلاعات چارچوب تماس (بخش "debug_frame.")

    ایجاد ELF

    ما فایل هایی را با فرمت EFL با استفاده از کتابخانه libelf از بسته elfutils ایجاد خواهیم کرد. مقاله خوبی در اینترنت در مورد استفاده از libelf وجود دارد - LibELF توسط مثال (متاسفانه، ایجاد فایل ها را به طور خلاصه توضیح می دهد) و همچنین مستندات.
    ایجاد یک فایل شامل چند مرحله است:
    1. شروع افترا زدن
    2. ایجاد سربرگ فایل (ELF Header)
    3. ایجاد جدول هدر برنامه
    4. ایجاد بخش ها
    5. یک فایل بنویسید
    بیایید نگاهی دقیق تر به مراحل بیندازیم
    شروع افترا زدن
    ابتدا باید تابع elf_version (EV_CURRENT) را فراخوانی کرده و نتیجه را بررسی کنید. اگر برابر با EV_NONE باشد، خطایی رخ داده است و اقدامات بعدی قابل انجام نیست. سپس باید فایل مورد نیاز خود را روی دیسک ایجاد کنیم، توصیفگر آن را دریافت کرده و به تابع elf_begin ارسال کنیم:
    Elf * elf_begin(int fd، Elf_Cmd cmd، Elf *elf)
    • fd - توصیف کننده فایل تازه باز شده
    • cmd - حالت (ELF_C_READ برای خواندن اطلاعات، ELF_C_WRITE برای نوشتن یا ELF_C_RDWR برای خواندن/نوشتن)، باید با حالت فایل باز مطابقت داشته باشد (ELF_C_WRITE در مورد ما)
    • elf - فقط برای کار با فایل های آرشیو (a) مورد نیاز است، در مورد ما باید 0 را پاس کنید
    تابع یک اشاره گر را به دسته ایجاد شده برمی گرداند که در تمام توابع libelf استفاده می شود، در صورت خطا 0 برمی گردد.
    ایجاد عنوان
    یک هدر فایل جدید توسط تابع elf32_newehdr ایجاد می شود:
    Elf32_Ehdr * elf32_newehdr(Elf *elf);
    • elf - دسته بازگردانده شده توسط تابع elf_begin
    0 در صورت خطا یا یک اشاره گر به ساختار - سرصفحه فایل ELF:
    #define EI_NIDENT 16 typedef struct ( char e_ident بدون علامت؛ Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff;Elf3_3;Elf32_Off _Half e_ehs ize;

    برخی از فیلدهای آن به صورت استاندارد پر شده است، برخی را باید پر کنیم:

    • e_ident - آرایه بایت شناسایی، دارای شاخص های زیر است:
      • EI_MAG0، EI_MAG1، EI_MAG2، EI_MAG3 - این 4 بایت باید شامل کاراکترهای 0x7f"ELF" باشد که تابع elf32_newehdr قبلاً برای ما انجام داده است.
      • EI_DATA - نوع کدگذاری داده در فایل را نشان می دهد: ELFDATA2LSB یا ELFDATA2MSB. شما باید ELFDATA2LSB را به این صورت نصب کنید: e_ident = ELFDATA2LSB
      • EI_VERSION - نسخه هدر فایل، از قبل برای ما تنظیم شده است
      • EI_PAD - دست نزنید
    • e_type - نوع فایل، می تواند ET_NONE - بدون نوع، ET_REL - فایل قابل جابجایی، ET_EXEC - فایل اجرایی، ET_DYN - فایل شی مشترک و غیره باشد. باید نوع فایل را روی ET_EXEC قرار دهیم
    • e_machine - معماری مورد نیاز برای این فایل، به عنوان مثال EM_386 - برای معماری اینتل، برای ARM باید EM_ARM (40) را در اینجا بنویسیم - برای معماری ARM به ELF مراجعه کنید.
    • e_version - نسخه فایل، باید روی EV_CURRENT تنظیم شود
    • e_entry - آدرس نقطه ورود، برای ما ضروری نیست
    • e_phoff - افست در فایل هدر برنامه، e_shoff - افست هدر بخش، پر نکنید
    • e_flags - پرچم‌های مخصوص پردازنده، برای معماری ما (Cortex-M3) باید روی 05000000 تنظیم شود (نسخه ABI 5)
    • e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum - دست نزنید
    • e_shstrndx - شامل تعداد قسمتی است که جدول ردیف ها با سرفصل های بخش در آن قرار دارد. از آنجایی که هنوز هیچ بخش نداریم، بعداً این شماره را تنظیم خواهیم کرد
    ایجاد سربرگ برنامه
    همانطور که قبلاً ذکر شد، جدول سرصفحه برنامه جدولی از مطابقت بین بخش های فایل و بخش های حافظه است که به بوت لودر می گوید که هر بخش را کجا بنویسد. عنوان با استفاده از تابع elf32_newphdr ایجاد شده است:
    Elf32_Phdr * elf32_newphdr(Elf *elf, size_t count);
    • جن دستگیره ماست
    • count - تعداد عناصر جدولی که باید ایجاد شود. از آنجایی که فقط یک بخش (با کد برنامه) خواهیم داشت، تعداد برابر با 1 خواهد بود.
    خطای 0 یا اشاره گر به سربرگ برنامه را برمی گرداند.
    هر عنصر در جدول هدر با ساختار زیر توصیف می شود:
    TYPEDEF STRUCT (ELF32_WORD P_TYPE؛ ELF32_OFF P_OFFSET؛ ELF32_ADDR P_VADDR؛ ELF32_ADDR P_PADDDR؛ ELF32_WORD P_Filesz؛ ELF32_WORD P_MEMSZ3;
    • p_type - نوع قطعه (بخش)، در اینجا باید PT_LOAD را مشخص کنیم - بخش بارگذاری
    • p_offset - در فایلی که داده های قسمتی که در حافظه بارگذاری می شود شروع می شود، آفست می شود. برای ما، این بخش متن است که بلافاصله بعد از هدر فایل و سربرگ برنامه قرار خواهد گرفت. طول هر نوع را می توان با استفاده از تابع elf32_fsize بدست آورد:
      size_t elf32_fsize (نوع Elf_Type، تعداد size_t، نسخه int بدون امضا)؛ نوع - در اینجا ثابت ELF_T_xxx، ما به اندازه های ELF_T_EHDR و ELF_T_PHDR نیاز خواهیم داشت. count - تعداد عناصر نوع مورد نظر، نسخه - باید روی EV_CURRENT تنظیم شود
    • p_vaddr, p_paddr - آدرس مجازی و فیزیکی که محتویات بخش در آن بارگذاری می شود. از آنجایی که ما آدرس مجازی نداریم، آن را برابر با آدرس فیزیکی، در ساده ترین حالت - 0 قرار می دهیم، زیرا برنامه ما در اینجا بارگذاری می شود.
    • p_filesz، p_memsz - اندازه بخش در فایل و حافظه. ما آنها را به همین ترتیب داریم، اما چون هنوز بخشی با کد برنامه وجود ندارد، بعداً آنها را نصب خواهیم کرد
    • p_flags - مجوزهای بخش حافظه بارگذاری شده. می تواند PF_R - خواندن، PF_W - نوشتن، PF_X - اجرا یا ترکیبی از آنها باشد. p_flags را روی PF_R + PF_X قرار دهید
    • p_align - تراز قطعه، ما 4 داریم
    ایجاد بخش ها
    پس از ایجاد سرفصل ها، می توانید شروع به ایجاد بخش کنید. یک بخش خالی با استفاده از تابع elf_newscn ایجاد می شود:
    Elf_Scn * elf_newscn(Elf *elf);
    • elf - دسته ای که قبلاً توسط تابع elf_begin برگردانده شده است
    تابع یک اشاره گر به بخش یا 0 در خطا برمی گرداند.
    پس از ایجاد یک بخش، باید هدر بخش را پر کنید و یک توصیفگر داده بخش ایجاد کنید.
    ما می توانیم با استفاده از تابع elf32_getshdr یک اشاره گر به هدر بخش دریافت کنیم:
    Elf32_Shdr * elf32_getshdr(Elf_Scn *scn);
    • scn یک اشاره گر به قسمتی است که از تابع elf_newscn دریافت کردیم.
    هدر بخش به شکل زیر است:
    typedef struct (Elf32_Word sh_name; Elf32_Word sh_type; Elf32_Word sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; Elf32_Word sh_size; Elf32_Word sh_link; Elf32_Word sh_info_Word; sh_entsize ) Elf32_Shdr;
    • sh_name - نام بخش - افست در جدول رشته ای سرصفحه های بخش (section.shstrtab) - "جدول رشته" را در زیر ببینید
    • sh_type - نوع محتوای بخش، برای بخش با کد برنامه باید SHT_PROGBITS را تنظیم کنید، برای بخش هایی با جدول رشته ای - SHT_STRTAB، برای یک جدول نمادها - SHT_SYMTAB
    • sh_flags پرچم‌های بخش‌هایی هستند که می‌توان آنها را ترکیب کرد و ما فقط به سه مورد از آنها نیاز داریم:
      • SHF_ALLOC - به این معنی است که بخش در حافظه بارگذاری می شود
      • SHF_EXECINSTR - بخش شامل کدهای اجرایی است
      • SHF_STRINGS - بخش شامل یک جدول از رشته ها است
      بر این اساس، برای بخش متن با برنامه باید پرچم های SHF_ALLOC + SHF_EXECINSTR را تنظیم کنید.
    • sh_addr - آدرسی که بخش در حافظه بارگذاری می شود
    • sh_offset - افست بخش در فایل - آن را لمس نکنید، کتابخانه آن را برای ما نصب می کند
    • sh_size - اندازه بخش - دست نزنید
    • sh_link - شامل شماره بخش پیوند شده است که برای پیوند دادن بخش با جدول ردیف مربوطه آن لازم است (به زیر مراجعه کنید)
    • sh_info - اطلاعات اضافی بسته به نوع بخش، روی 0 تنظیم کنید
    • sh_addralign - تراز آدرس، لمس نکنید
    • sh_entsize - اگر یک بخش از چندین عنصر با طول یکسان تشکیل شده باشد، طول چنین عنصری را نشان می دهد، لمس نکنید
    پس از پر کردن هدر، باید یک توصیفگر داده بخش با تابع elf_newdata ایجاد کنید:
    Elf_Data * elf_newdata(Elf_Scn *scn);
    • scn اشاره گر تازه دریافت شده به بخش جدید است.
    این تابع در صورت خطا 0 یا یک اشاره گر به ساختار Elf_Data که باید پر شود برمی گرداند:
    typedef struct ( void* d_buf; Elf_Type d_type; size_t d_size; off_t d_off; size_t d_align; unsigned d_version; ) Elf_Data;
    • d_buf - اشاره گر به داده هایی که باید در بخش نوشته شوند
    • d_type - نوع داده، ELF_T_BYTE برای ما در همه جا مناسب است
    • d_size - اندازه داده ها
    • d_off - offset در بخش، روی 0 تنظیم شده است
    • d_align - alignment، می تواند روی 1 تنظیم شود - بدون تراز
    • d_version - نسخه، باید روی EV_CURRENT تنظیم شود
    بخش های ویژه
    برای اهداف ما، ما باید حداقل مجموعه مورد نیاز از بخش ها را ایجاد کنیم:
    • متن - بخش با کد برنامه
    • .symtab - جدول نماد فایل
    • strtab. یک جدول رشته ای است که شامل نام نمادها از بخش .symtab است، زیرا دومی نه خود نام ها، بلکه شاخص های آنها را ذخیره می کند.
    • .shstrtab - جدول رشته ای حاوی نام بخش ها
    همه بخش ها همانطور که در قسمت قبل توضیح داده شد ایجاد می شوند، اما هر بخش ویژه ویژگی های خاص خود را دارد.
    بخش. متن
    این بخش حاوی کدهای اجرایی است، بنابراین شما باید sh_type را روی SHT_PROGBITS، sh_flags را روی SHF_EXECINSTR + SHF_ALLOC، sh_addr را به آدرسی که این کد بارگذاری می شود، تنظیم کنید.
    Section.symtab
    این بخش شامل توضیحاتی در مورد تمام نمادها (توابع) برنامه و فایل هایی است که در آنها توضیح داده شده است. از عناصر زیر تشکیل شده است که هر کدام 16 بایت طول دارند:
    typedef struct ( Elf32_Word st_name; Elf32_Addr st_value; Elf32_Word st_size; unsigned char st_info; unsigned char st_other; Elf32_Half st_shndx; ) Elf32_Sym;
    • st_name - نام نماد (شاخص در string table.strtab)
    • st_value - مقدار (آدرس ورودی برای یک تابع یا 0 برای یک فایل). از آنجایی که Cortex-M3 یک مجموعه دستورالعمل Thumb-2 دارد، این آدرس باید فرد باشد (آدرس واقعی + 1)
    • st_size - طول کد تابع (0 برای فایل)
    • st_info - نوع نماد و دامنه آن. یک ماکرو برای تعیین مقدار این فیلد وجود دارد
      #define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf))
      که در آن b دامنه و t نوع کاراکتر است
      دامنه می تواند STB_LOCAL (نماد از فایل های شی دیگر قابل مشاهده نیست) یا STB_GLOBAL (قابل مشاهده) باشد. برای ساده کردن کارها، از STB_GLOBAL استفاده می کنیم.
      نوع نماد - STT_FUNC برای تابع، STT_FILE برای فایل
    • st_other - روی 0 تنظیم کنید
    • st_shndx - نمایه بخشی که نماد برای آن تعریف شده است (بخش index.text)، یا SHN_ABS برای فایل.
      شاخص یک بخش بر اساس توصیفگر scn آن را می توان با استفاده از elf_ndxscn تعیین کرد:
      size_t elf_ndxscn(Elf_Scn *scn);

    این بخش به روش معمول ایجاد می شود، فقط sh_type باید روی SHT_SYMTAB تنظیم شود و بخش index.strtab باید در فیلد sh_link نوشته شود، بنابراین این بخش ها لینک می شوند.
    Section.strtab
    این بخش شامل نام تمام نمادها از قسمت .symtab است. مانند یک بخش عادی ایجاد شده است، اما sh_type باید روی SHT_STRTAB تنظیم شود، sh_flag روی SHF_STRINGS، بنابراین این بخش تبدیل به یک جدول رشته می شود.
    داده‌های یک بخش را می‌توان با عبور از متن مبدأ به آرایه‌ای جمع‌آوری کرد، نشانگر آن سپس در توصیفگر داده‌های بخش (d_buf) نوشته می‌شود.
    Section.shstrtab
    بخش، جدولی از رشته‌ها است که شامل سرصفحه‌های تمام بخش‌های فایل، از جمله هدر خودش است. به همان روشی که بخش strtab. ایجاد می شود. پس از ایجاد، فهرست آن باید در قسمت e_shstrndx سربرگ فایل نوشته شود.
    جداول رشته ای
    جداول رشته‌ای حاوی ردیف‌های متوالی هستند که با بایت تهی ختم می‌شوند، اولین بایت در این جدول نیز باید 0 باشد. شاخص یک ردیف در جدول به سادگی برحسب بایت از ابتدای جدول است، بنابراین اولین ردیف "name" است. دارای شاخص 1 است، ردیف بعدی "var" دارای شاخص 6 است.
    فهرست 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \0 n a m e \0 v a r \0
    یک فایل بنویسید
    بنابراین، هدرها و بخش‌ها قبلاً تشکیل شده‌اند، اکنون باید در یک فایل نوشته شوند و کار با libelf به پایان برسد. ضبط توسط تابع elf_update انجام می شود:
    off_t elf_update(Elf *elf، Elf_Cmd cmd);
    • جن - دسته
    • دستور cmd - برای نوشتن باید برابر با ELF_C_WRITE باشد.
    تابع در صورت خطا، -1 را برمی گرداند. متن خطا را می توان با فراخوانی تابع elf_errmsg(-1) بدست آورد که نشانگر را به خط دارای خطا برمی گرداند.
    کار با کتابخانه را با تابع elf_end به پایان می‌رسانیم که توصیفگر خود را به آن منتقل می‌کنیم. تنها چیزی که باقی می ماند این است که فایلی که قبلاً باز شده را ببندید.
    با این حال، فایل تولید شده ما حاوی اطلاعات اشکال زدایی نیست که در بخش بعدی اضافه خواهیم کرد.

    ایجاد کوتوله

    ما اطلاعات اشکال زدایی را با استفاده از کتابخانه ایجاد خواهیم کرد که همراه با یک فایل pdf همراه با مستندات (libdwarf2p.1.pdf - A Producer Library Interface to DWARF).
    ایجاد اطلاعات اشکال زدایی شامل مراحل زیر است:
    1. ایجاد گره ها (DIE - Debugging Information Entry)
    2. ایجاد ویژگی های گره
    3. ایجاد انواع داده ها
    4. ایجاد رویه ها (توابع)
    بیایید نگاهی دقیق تر به مراحل بیندازیم
    راه اندازی تولید کننده libdwarf
    ما همزمان با ایجاد نمادها در بخش symtab. اطلاعات اشکال زدایی را ایجاد می کنیم، بنابراین کتابخانه باید پس از مقداردهی اولیه libelf، ایجاد هدر ELF و هدر برنامه و قبل از ایجاد بخش ها مقداردهی شود.
    برای مقداردهی اولیه از تابع dwarf_producer_init_c استفاده خواهیم کرد. این کتابخانه چندین تابع مقداردهی اولیه دیگر دارد (dwarf_producer_init، dwarf_producer_init_b)، که در برخی تفاوت های ظریف توضیح داده شده در مستندات متفاوت است. در اصل، شما می توانید از هر یک از آنها استفاده کنید.

    Dwarf_P_Debug dwarf_producer_init_c(پرچم های Dwarf_Unsigned، تابع Dwarf_Callback_Func_c، خطای Dwarf_Handler، خطای Dwarf_Ptr، void * user_data، Dwarf_Error *error)

    • flags - ترکیبی از "یا" از چندین ثابت که برخی از پارامترها را تعیین می کند، به عنوان مثال، عمق بیت اطلاعات، دنباله بایت (کوچک اندین، بزرگ-اندین)، قالب جابجایی، که ما قطعا به DW_DLC_WRITE و DW_DLC_SYMBOLIC_RELOCATIONS نیاز داریم.
    • func یک تابع تماس است که هنگام ایجاد بخش های ELF با اطلاعات اشکال زدایی فراخوانی می شود. برای جزئیات بیشتر، به بخش «ایجاد بخش هایی با اطلاعات اشکال زدایی» زیر مراجعه کنید.
    • errhand یک اشاره گر به یک تابع است که در صورت بروز خطا فراخوانی می شود. شما می توانید 0 ارسال کنید
    • errarg - داده هایی که به تابع errhand ارسال می شوند را می توان روی 0 تنظیم کرد
    • user_data - داده هایی که به تابع func ارسال می شوند را می توان روی 0 تنظیم کرد
    • خطا - کد خطا برگردانده شده است
    تابع Dwarf_P_Debug را برمی گرداند - توصیفگر مورد استفاده در تمام توابع بعدی، یا -1 در صورت بروز خطا، و خطا حاوی کد خطا خواهد بود (می توانید متن پیام خطا را با استفاده از کد آن با استفاده از تابع dwarf_errmsg دریافت کنید و این کد را ارسال کنید. به آن)
    ایجاد گره ها (DIE - ورود اطلاعات اشکال زدایی)
    همانطور که در بالا توضیح داده شد، اطلاعات اشکال زدایی یک ساختار درختی را تشکیل می دهد. برای ایجاد یک گره از این درخت، شما نیاز دارید:
    • آن را با استفاده از تابع dwarf_new_die ایجاد کنید
    • ویژگی هایی را به آن اضافه کنید (هر نوع مشخصه با عملکرد خاص خود اضافه می شود که در زیر توضیح داده خواهد شد)
    گره با استفاده از تابع dwarf_new_die ایجاد می شود:
    Dwarf_P_Die dwarf_new_die(Dwarf_P_Debug dbg, Dwarf_Tag new_tag, Dwarf_P_Die, والد Dwarf_P_Die, Dwarf_P_Die child, Dwarf_P_Die left_sibling, Dwarf_P_Die right_sibling, Error *errorf)
    • new_tag - برچسب گره (نوع) - ثابت DW_TAG_xxxx که در فایل libdwarf.h یافت می شود
    • والد، فرزند، چپ_خواهر، راست_خواهر - به ترتیب همسایه های والد، فرزند، چپ و راست گره. لازم نیست همه این پارامترها را مشخص کنید و به جای بقیه، 0 را تعیین کنید، گره یا ریشه یا ایزوله می شود
    • error - زمانی که رخ دهد حاوی کد خطا خواهد بود
    این تابع در صورت خطا DW_DLV_BADADDR یا دسته گره Dwarf_P_Die را در صورت موفقیت برمی گرداند.
    ایجاد ویژگی های گره
    برای ایجاد ویژگی های گره، یک خانواده کامل از توابع dwarf_add_AT_xxxx وجود دارد. گاهی اوقات تعیین اینکه کدام تابع باید ویژگی مورد نیاز را ایجاد کند دشوار است، بنابراین من حتی چندین بار در کد منبع کتابخانه جستجو کردم. برخی از توابع در اینجا توضیح داده خواهد شد، برخی در زیر در بخش های مربوطه. همه آنها یک پارامتر مالکیت را می پذیرند - یک دسته به گره ای که ویژگی به آن اضافه می شود، و یک کد خطا را در پارامتر خطا برمی گردانند.
    تابع dwarf_add_AT_name یک ویژگی "name" (DW_AT_name) را به یک گره اضافه می کند. اکثر گره ها باید یک نام داشته باشند (به عنوان مثال، رویه ها، متغیرها، ثابت ها)، برخی ممکن است نامی نداشته باشند (به عنوان مثال، Compilation Unit)
    Dwarf_P_Attribute dwarf_add_AT_name (Dwarf_P_Die milkdie، char *name، Dwarf_Error *error)
    • نام - مقدار مشخصه واقعی (نام گره)

    توابع dwarf_add_AT_signed_const، dwarf_add_AT_unsigned_const ویژگی مشخص شده و مقدار امضا شده (بدون علامت) آن را به گره اضافه می کنند. از ویژگی های علامت دار و بدون علامت برای تعیین مقادیر ثابت، اندازه ها، شماره خطوط و غیره استفاده می شود. فرمت تابع:
    Dwarf_P_Attribute dwarf_add_AT_(un)signed_const(Dwarf_P_Debug dbg, Dwarf_P_Die milkdie, Dwarf_Half attr, Dwarf_Signed value, Dwarf_Error *error)
    • dbg - توصیفگر Dwarf_P_Debug که در طول اولیه سازی کتابخانه به دست آمده است
    • attr - مشخصه ای که مقدار آن تنظیم شده است - ثابت DW_AT_xxxx که در فایل libdwarf.h یافت می شود.
    • ارزش - مقدار ویژگی
    DW_DLV_BADADDR را در صورت خطا یا یک دسته ویژگی را در صورت موفقیت برگردانید.
    ایجاد یک واحد کامپایل
    هر درختی باید ریشه داشته باشد - در مورد ما، این یک واحد کامپایل است که حاوی اطلاعاتی در مورد برنامه است (به عنوان مثال، نام فایل اصلی، زبان برنامه نویسی مورد استفاده، نام کامپایلر، حساسیت حروف کوچک و بزرگ نمادها ( متغیرها، توابع)، تابع اصلی برنامه، آدرس شروع و غیره و غیره). در اصل، هیچ ویژگی مورد نیاز نیست. به عنوان مثال، بیایید اطلاعاتی در مورد فایل اصلی و کامپایلر ایجاد کنیم.
    اطلاعات فایل اصلی
    برای ذخیره اطلاعات مربوط به فایل اصلی، از ویژگی نام (DW_AT_name) استفاده کنید، از تابع dwarf_add_AT_name همانطور که در بخش "ایجاد ویژگی های گره" نشان داده شده است استفاده کنید.
    اطلاعات کامپایلر
    ما از تابع dwarf_add_AT_producer استفاده می کنیم:
    Dwarf_P_Attribute dwarf_add_AT_name (Dwarf_P_Die milkdie، char *producer_string، Dwarf_Error *error)
    • producer_string - رشته با متن اطلاعات
    DW_DLV_BADADDR را در صورت خطا برمی‌گرداند یا یک دسته ویژگی را در صورت موفقیت نشان می‌دهد.
    ایجاد یک ورودی اطلاعات مشترک
    به طور معمول، هنگامی که یک تابع (زیر روال) فراخوانی می شود، پارامترها و آدرس برگشتی آن به پشته فشار داده می شود (اگرچه هر کامپایلر ممکن است این کار را متفاوت انجام دهد)، به همه این ها Call Frame می گویند. دیباگر به اطلاعاتی در مورد قالب فریم نیاز دارد تا بتواند آدرس برگشتی از یک تابع را به درستی تعیین کند و یک بک ردیابی ایجاد کند - زنجیره ای از فراخوانی های تابع که ما را به تابع فعلی و پارامترهای این توابع هدایت می کند. همچنین معمول است که رجیسترهای پردازنده را که در پشته ذخیره می شوند، مشخص کنید. کدی که روی پشته فضای ذخیره می‌کند و رجیسترهای پردازنده را ذخیره می‌کند، یک پیش‌پرده تابع، کدی که ثبات‌ها را بازیابی می‌کند و پشته را اپیلوگ می‌گویند.
    این اطلاعات به شدت به کامپایلر وابسته است. برای مثال، لازم نیست که مقدمه و پایان نامه در ابتدا و انتهای کارکرد باشد. گاهی اوقات از قاب استفاده می شود، گاهی اوقات نه. ثبات های پردازنده را می توان در رجیسترهای دیگر و غیره ذخیره کرد.
    بنابراین، دیباگر باید بداند که ثبت‌های پردازنده چگونه مقدار خود را تغییر می‌دهند و در هنگام ورود به رویه در کجا ذخیره می‌شوند. این اطلاعات Call Frame Information نامیده می شود - اطلاعات مربوط به فرمت فریم. برای هر آدرس در برنامه (شامل کد)، آدرس فریم در حافظه (آدرس قاب متعارف - CFA) و اطلاعات مربوط به رجیسترهای پردازنده نشان داده شده است، به عنوان مثال، می توانید نشان دهید که:
    • مورد در رویه حفظ نشده است
    • رجیستر ارزش خود را در رویه تغییر نمی دهد
    • ثبات در پشته در آدرس CFA+n ذخیره می شود
    • ثبت در رجیستر دیگری ذخیره می شود
    • رجیستر در برخی از آدرس ها در حافظه ذخیره می شود که می تواند به روشی نسبتاً غیر واضح محاسبه شود
    • و غیره.
    از آنجایی که اطلاعات باید برای هر آدرس در کد مشخص شود، بسیار حجیم است و به صورت فشرده در قسمت .debug_frame ذخیره می شود. از آنجایی که از آدرسی به آدرس دیگر تغییر کمی می کند، فقط تغییرات آن در قالب دستورالعمل های DW_CFA_xxxx کدگذاری می شود. هر دستورالعمل یک تغییر را نشان می دهد، به عنوان مثال:
    • DW_CFA_set_loc - به آدرس فعلی در برنامه اشاره می کند
    • DW_CFA_advance_loc - نشانگر را با تعداد مشخصی بایت جلو می برد
    • DW_CFA_def_cfa - آدرس قاب پشته را نشان می دهد (ثابت عددی)
    • DW_CFA_def_cfa_register - آدرس قاب پشته را نشان می دهد (برگرفته از ثبت پردازنده)
    • DW_CFA_def_cfa_expression - نحوه محاسبه آدرس قاب پشته را مشخص می کند
    • DW_CFA_same_value - نشان می دهد که رجیستر تغییر نکرده است
    • DW_CFA_register - نشان می دهد که رجیستر در رجیستر دیگری ذخیره شده است
    • و غیره.
    عناصر بخش .debug_frame ورودی هایی هستند که می توانند دو نوع باشند: Common Information Entry (CIE) و Frame Description Entry (FDE). CIE حاوی اطلاعاتی است که در بسیاری از سوابق FDE مشترک است و نوع خاصی از رویه را توصیف می کند. FDE ها هر رویه خاص را توصیف می کنند. هنگام وارد کردن یک رویه، دیباگر ابتدا دستورات را از CIE و سپس از FDE اجرا می کند.
    کامپایلر من رویه هایی را ایجاد می کند که CFA در ثبات sp (r13) باشد. بیایید یک CIE برای همه رویه ها ایجاد کنیم. یک تابع برای این وجود دارد، dwarf_add_frame_cie:
    Dwarf_Unsigned dwarf_add_frame_cie(Dwarf_P_Debug dbg, char *augmenter, Dwarf_Small code_align, Dwarf_Small data_align, Dwarf_Small ret_addr_reg, Dwarf_Ptr init_UrrorfignE, Dwarf_Ptr init_UrrorfignE, Dwarf_Ptr init_UrrorfignE, Dwarf_Ptr init_UrrorfignE, Dwarf_Small code_align;
    • Augmenter یک رشته رمزگذاری شده UTF-8 است که وجود آن نشان می دهد که اطلاعات اضافی وابسته به پلتفرم برای CIE یا FDE وجود دارد. یک خط خالی بگذارید
    • code_align - تراز کد در بایت (ما 2 داریم)
    • data_align - تراز داده ها در قاب (مجموعه -4، به این معنی که تمام پارامترها 4 بایت در پشته می گیرند و در حافظه کم می شوند)
    • ret_addr_reg - یک ثبات حاوی آدرس برگشتی از رویه (ما 14 داریم)
    • init_bytes - آرایه ای حاوی دستورالعمل های DW_CFA_xxxx. متاسفانه هیچ راه مناسبی برای تولید این آرایه وجود ندارد. می توانید آن را به صورت دستی تولید کنید یا در فایل elf که توسط کامپایلر C تولید شده است نگاه کنید، کاری که من انجام دادم. برای مورد من شامل 3 بایت است: 0x0C، 0x0D، 0، که مخفف DW_CFA_def_cfa: r13 از 0 است (CFA در ثبات r13 است، افست 0 است)
    • init_bytes_len - طول آرایه init_bytes
    تابع DW_DLV_NOCOUNT در صورت خطا یا یک دسته CIE را برمی گرداند که باید هنگام ایجاد یک FDE برای هر رویه استفاده شود، که بعداً در بخش "ایجاد رویه FDE" به آن خواهیم پرداخت.
    ایجاد انواع داده ها
    قبل از اینکه بتوانید رویه ها و متغیرها را ایجاد کنید، ابتدا باید گره هایی ایجاد کنید که با انواع داده ها مطابقت دارند. انواع داده های زیادی وجود دارد، اما همه آنها بر اساس انواع پایه هستند (انواع ابتدایی مانند int، double و غیره)، انواع دیگر از انواع پایه ساخته شده اند.
    نوع پایه گرهی با تگ DW_TAG_base_type است. باید دارای ویژگی های زیر باشد:
    • "name" (DW_AT_name)
    • "encoding" (DW_AT_encoding) - به این معنی است که چه نوع داده ای این نوع پایه را توصیف می کند (به عنوان مثال، DW_ATE_boolean - منطقی، DW_ATE_float - ممیز شناور، DW_ATE_signed - عدد صحیح امضا شده، DW_ATE_unsigned - عدد صحیح بدون علامت، و غیره)
    • "اندازه" (DW_AT_byte_size - اندازه به بایت یا DW_AT_bit_size - اندازه به بیت)
    یک گره ممکن است دارای ویژگی های اختیاری دیگری نیز باشد.
    به عنوان مثال، برای ایجاد یک پایه عدد صحیح امضا شده 32 بیتی از نوع "int"، باید یک گره با تگ DW_TAG_base_type ایجاد کنیم و ویژگی های آن را DW_AT_name - "int"، DW_AT_encoding - DW_ATE_signed، DW_AT_byte_size - 4 تنظیم کنیم.
    پس از ایجاد انواع پایه، می توانید مشتقاتی را از آنها استخراج کنید. چنین گره هایی باید دارای ویژگی DW_AT_type باشند - اشاره ای به نوع پایه آنها. به عنوان مثال، یک اشاره گر به int - یک گره با تگ DW_TAG_pointer_type باید در ویژگی DW_AT_type پیوندی به نوع "int" ایجاد شده قبلی داشته باشد.
    یک ویژگی با ارجاع به گره دیگر توسط تابع dwarf_add_AT_reference ایجاد می شود:
    Dwarf_P_Attribute dwarf_add_AT_reference(Dwarf_P_Debug dbg، Dwarf_P_Die milkdie، Dwarf_Half attr، Dwarf_P_Die otherdie، Dwarf_Error *خطا)
    • attr - ویژگی، در این مورد DW_AT_type
    • otherdie - یک دسته برای گره از نوع مورد اشاره
    ایجاد رویه ها
    برای ایجاد رویه ها، باید یک نوع دیگر از اطلاعات اشکال زدایی را توضیح دهم - اطلاعات شماره خط. این برنامه برای نگاشت هر دستورالعمل ماشین به خط خاصی از کد منبع و همچنین امکان اشکال زدایی خط به خط برنامه را به کار می گیرد. این اطلاعات در قسمت .debug_line ذخیره می شود. اگر فضای کافی داشتیم، به عنوان یک ماتریس، یک ردیف برای هر دستورالعمل با ستون هایی مانند این ذخیره می شد:
    • نام فایل کد منبع
    • شماره خط در این فایل
    • شماره ستون در فایل
    • دستورالعمل شروع یک دستور باشد یا بلوکی از دستورات
    • و غیره.
    چنین ماتریسی بسیار بزرگ است، بنابراین باید فشرده شود. اولاً خطوط تکراری حذف می شوند و ثانیاً خود خطوط ذخیره نمی شوند، بلکه فقط تغییرات آنها ذخیره می شود. این تغییرات مانند دستوراتی برای یک ماشین حالت محدود به نظر می رسد و خود اطلاعات قبلاً برنامه ای در نظر گرفته می شود که توسط این ماشین "اجرا" می شود. دستورات این برنامه به عنوان مثال به این صورت است: DW_LNS_advance_pc - پیشخوان برنامه را به یک آدرس خاص پیش ببرید، DW_LNS_set_file - فایلی را که رویه در آن تعریف شده است تنظیم کنید، DW_LNS_const_add_pc - شمارنده برنامه را چندین بایت پیش ببرید و غیره.
    تولید این اطلاعات در چنین سطح پایین دشوار است، بنابراین libdwarf چندین عملکرد را برای آسان کردن این کار ارائه می دهد.
    ذخیره کردن نام فایل برای هر دستورالعمل گران است، بنابراین به جای نام، فهرست آن در یک جدول خاص ذخیره می شود. برای ایجاد یک فهرست فایل، باید از تابع dwarf_add_file_decl استفاده کنید:
    Dwarf_Unsigned dwarf_add_file_decl(Dwarf_P_Debug dbg, char *name, Dwarf_Unsigned dir_idx, Dwarf_Unsigned time_mod, Dwarf_Unsigned length, Dwarf_Error *error)
    • نام - نام فایل
    • dir_idx - فهرست پوشه ای که فایل در آن قرار دارد. شاخص را می توان با استفاده از تابع dwarf_add_directory_decl به دست آورد. اگر از مسیرهای کامل استفاده می شود، می توانید 0 را به عنوان فهرست پوشه تنظیم کنید و به هیچ وجه از dwarf_add_directory_decl استفاده نکنید.
    • time_mod - زمان تغییر فایل، ممکن است مشخص نشده باشد (0)
    • طول - اندازه فایل، همچنین اختیاری (0)
    تابع فهرست فایل یا DW_DLV_NOCOUNT را در صورت خطا برمی گرداند.
    برای ایجاد اطلاعات در مورد شماره خطوط، سه تابع dwarf_add_line_entry_b، dwarf_lne_set_address، dwarf_lne_end_sequence وجود دارد که در زیر به آنها خواهیم پرداخت.
    ایجاد اطلاعات اشکال زدایی برای یک رویه در چند مرحله انجام می شود:
    • ایجاد نماد رویه در قسمت symtab
    • ایجاد یک گره رویه با ویژگی ها
    • ایجاد یک رویه FDE
    • ایجاد پارامترهای رویه
    • ایجاد اطلاعات شماره خط
    ایجاد نماد رویه
    نماد رویه همانطور که در بالا در بخش Section.symtab توضیح داده شد ایجاد می شود. در آن، نمادهای رویه‌ها با نمادهای فایل‌هایی که کد منبع این رویه‌ها در آنها قرار دارد، آمیخته می‌شوند. ابتدا یک نماد فایل و سپس یک رویه ایجاد می کنیم. این کار فایل را جاری می کند و اگر رویه بعدی در فایل فعلی باشد، نیازی به ایجاد مجدد نماد فایل نیست.
    ایجاد یک گره رویه با ویژگی ها
    ابتدا، با استفاده از تابع dwarf_new_die یک گره ایجاد می کنیم (به بخش "ایجاد گره ها" مراجعه کنید)، DW_TAG_subprogram را به عنوان برچسب، و واحد کامپایل (اگر این یک رویه جهانی است) یا DIE مربوطه (اگر محلی) را به عنوان والد مشخص می کنیم. سپس صفات را ایجاد می کنیم:
    • نام رویه (تابع dwarf_add_AT_name، به «ایجاد ویژگی‌های گره» مراجعه کنید)
    • شماره خط در فایلی که کد رویه شروع می‌شود (ویژگی DW_AT_decl_line)، تابع dwarf_add_AT_unsigned_const (به «ایجاد ویژگی‌های گره» مراجعه کنید)
    • آدرس شروع رویه (ویژگی DW_AT_low_pc)، تابع dwarf_add_AT_targ_address، زیر را ببینید
    • آدرس نهایی رویه (ویژگی DW_AT_high_pc)، تابع dwarf_add_AT_targ_address، زیر را ببینید
    • نوع نتیجه برگردانده شده توسط رویه (ویژگی DW_AT_type پیوندی به نوع ایجاد شده قبلی است، به «ایجاد انواع داده» مراجعه کنید). اگر رویه چیزی را برنگرداند، نیازی به ایجاد این ویژگی نیست
    ویژگی های DW_AT_low_pc و DW_AT_high_pc باید با استفاده از تابع dwarf_add_AT_targ_address_b که مخصوص این منظور طراحی شده است ایجاد شوند:
    Dwarf_P_Attribute dwarf_add_AT_targ_address_b(Dwarf_P_Debug dbg, Dwarf_P_Die milkdie, Dwarf_Half attr, Dwarf_Unsigned pc_value, Dwarf_Unsigned sym_index, *error_error)
    • attr - ویژگی (DW_AT_low_pc یا DW_AT_high_pc)
    • pc_value - مقدار آدرس
    • sym_index - شاخص نماد رویه در table.symtab. اختیاری، 0 را می توان پاس کرد
    تابع در صورت خطا DW_DLV_BADADDR را برمی گرداند.
    ایجاد یک رویه FDE
    همانطور که در بالا در بخش "ایجاد یک ورودی اطلاعات مشترک" بحث شد، برای هر روش باید یک توصیفگر فریم ایجاد کنید، که در چند مرحله اتفاق می افتد:
    • ایجاد یک FDE جدید (به ایجاد یک ورودی اطلاعات مشترک مراجعه کنید)
    • پیوستن FDE ایجاد شده به لیست کلی
    • افزودن دستورالعمل ها به FDE ایجاد شده
    می توانید با استفاده از تابع dwarf_new_fde یک FDE جدید ایجاد کنید:
    Dwarf_P_Fde dwarf_new_fde (Dwarf_P_Debug dbg، Dwarf_Error *error)
    تابع در صورت خطا یک دسته را به FDE یا DW_DLV_BADADDR جدید برمی گرداند.
    می توانید یک FDE جدید را با استفاده از dwarf_add_frame_fde به لیست پیوست کنید:
    Dwarf_Unsigned dwarf_add_frame_fde(Dwarf_P_Debug dbg, Dwarf_P_Fde fde, Dwarf_P_Die die, Dwarf_Unsigned cie, Dwarf_Addr virt_addr, Dwarf_Unsigned code_war_Urror, Dwarf_Unsigned code_war_Urror, Dwarf_P_Fde, Dwarf_P_Die,
    • fde - دسته که به تازگی دریافت شده است
    • die - رویه های DIE (به ایجاد یک گره رویه با ویژگی ها مراجعه کنید)
    • cie - توصیفگر CIE (به ایجاد یک ورودی اطلاعات مشترک مراجعه کنید)
    • virt_addr - آدرس شروع رویه ما
    • code_len - طول روش بر حسب بایت
    تابع در صورت خطا DW_DLV_NOCOUNT را برمی گرداند.
    پس از همه اینها، می توانید دستورالعمل های DW_CFA_xxxx را به FDE ما اضافه کنید. این کار توسط توابع dwarf_add_fde_inst و dwarf_fde_cfa_offset انجام می شود. اولین مورد دستور داده شده را به لیست اضافه می کند:
    Dwarf_P_Fde dwarf_add_fde_inst(Dwarf_P_Fde fde، Dwarf_Small op، Dwarf_Unsigned val1، Dwarf_Unsigned val2، Dwarf_Error *error)
    • کد دستور عملیات (DW_CFA_хххх)
    • val1، val2 - پارامترهای دستورالعمل (برای هر دستورالعمل متفاوت است، به بخش استاندارد، بخش 6.4.2 دستورالعمل های فریم تماس مراجعه کنید)
    تابع dwarf_fde_cfa_offset دستور DW_CFA_offset را اضافه می کند:
    Dwarf_P_Fde dwarf_fde_cfa_offset(Dwarf_P_Fde fde, Dwarf_Unsigned reg, Dwarf_Signed offset, Dwarf_Error *error)
    • fde - دسته به FDE ایجاد شده
    • reg - ثبتی که روی فریم نوشته می شود
    • offset - افست آن در قاب (نه در بایت، بلکه در عناصر قاب، به ایجاد یک ورودی اطلاعات مشترک، data_align مراجعه کنید)
    برای مثال، کامپایلر رویه‌ای ایجاد می‌کند که پیش‌گفتار آن رجیستر lr (r14) را در قاب پشته ذخیره می‌کند. اولین گام این است که دستور DW_CFA_advance_loc را با پارامتر اول برابر با 1 اضافه کنید، به این معنی که رجیستر کامپیوتر را 2 بایت ارتقا دهید (به ایجاد یک ورودی اطلاعات مشترک، code_align مراجعه کنید)، سپس DW_CFA_def_cfa_offset را با پارامتر 4 اضافه کنید (تعدیل داده را در قسمت تنظیم کنید. فریم در 4 بایت) و تابع dwarf_fde_cfa_offset را با پارامتر reg=14 offset=1 فراخوانی کنید، به این معنی که رجیستر r14 را در فریم با افست -4 بایت از CFA بنویسید.
    ایجاد پارامترهای رویه
    ایجاد پارامترهای رویه مشابه ایجاد متغیرهای معمولی است، به «ایجاد متغیرها و ثابت ها» مراجعه کنید.
    ایجاد اطلاعات در مورد شماره خطوط
    این اطلاعات به این صورت ایجاد می شود:
    • در ابتدای رویه، بلوکی از دستورالعمل ها را با تابع dwarf_lne_set_address شروع می کنیم
    • برای هر خط کد (یا دستورالعمل ماشین) اطلاعاتی درباره کد منبع ایجاد می کنیم (dwarf_add_line_entry)
    • در پایان روش، بلوک دستورالعمل ها را با تابع dwarf_lne_end_sequence تکمیل می کنیم.
    تابع dwarf_lne_set_address آدرسی را تنظیم می کند که در آن بلوکی از دستورالعمل ها شروع می شود:
    Dwarf_Unsigned dwarf_lne_set_address (Dwarf_P_Debug dbg, Dwarf_Addr offs, Dwarf_Unsigned symidx, Dwarf_Error *error)
    • offs - آدرس رویه (آدرس اولین دستورالعمل ماشین)
    • sym_idx - شاخص نماد (اختیاری، می توانید 0 را مشخص کنید)

    تابع dwarf_add_line_entry_b اطلاعات مربوط به خطوط کد منبع را به بخش .debug_line اضافه می کند. من این تابع را برای هر دستورالعمل ماشین صدا می زنم:
    Dwarf_Unsigned dwarf_add_line_entry_b(Dwarf_P_Debug dbg, Dwarf_Unsigned file_index, Dwarf_Addr code_offset, Dwarf_Unsigned lineno, Dwarf_Signed column_number, Dwarf_Bool is_be_gin_beolm Bool is_epilogue _begin, Dwarf_Bool is_prologue_end, Dwarf_Unsigned isa, Dwarf_Unsigned discriminator, Dwarf_Error *error)
    • file_index - فهرست فایل کد منبع که قبلاً توسط تابع dwarf_add_file_decl بدست آمده است (به "ایجاد رویه ها" مراجعه کنید)
    • code_offset - آدرس دستورالعمل فعلی ماشین
    • lineno - شماره خط در فایل کد منبع
    • column_number - شماره ستون در فایل کد منبع
    • is_source_stmt_begin - 1 اگر دستور فعلی اولین مورد در کد خط lineno باشد (من همیشه از 1 استفاده می کنم)
    • is_basic_block_begin - 1 اگر دستور فعلی اولین دستور بلوک باشد (من همیشه از 0 استفاده می کنم)
    • is_epilogue_begin - 1 اگر دستورالعمل فعلی اولین مورد در اپیلوگ رویه باشد (لازم نیست، من همیشه 0 دارم)
    • is_prologue_end - 1 اگر دستور فعلی آخرین دستور در مقدمه رویه باشد (الزامی است!)
    • isa - معماری مجموعه دستورالعمل. حتماً برای ARM Cortex M3 DW_ISA_ARM_thumb را مشخص کنید!
    • تبعیض کننده یک موقعیت (فایل، خط، ستون) از کد منبع می تواند با دستورالعمل های مختلف ماشین مطابقت داشته باشد. در این مورد، باید برای مجموعه‌هایی از این دستورالعمل‌ها، تمایزکننده‌های مختلفی نصب شود. اگر چنین مواردی وجود نداشته باشد، باید 0 باشد
    تابع 0 (موفقیت) یا DW_DLV_NOCOUNT (خطا) را برمی گرداند.
    در نهایت، تابع dwarf_lne_end_sequence این روش را تکمیل می کند:
    Dwarf_Unsigned dwarf_lne_end_sequence (Dwarf_P_Debug dbg, Dwarf_Addr address; Dwarf_Error *error)
    • آدرس - آدرس دستورالعمل فعلی ماشین
    0 (موفقیت) یا DW_DLV_NOCOUNT (خطا) را برمی گرداند.
    این ایجاد رویه را کامل می کند.
    ایجاد متغیرها و ثابت ها
    به طور کلی، متغیرها بسیار ساده هستند. آنها دارای یک نام، یک مکان حافظه (یا ثبت پردازنده) هستند که داده های آنها در آن قرار دارد و نوع آن داده ها. اگر متغیر سراسری است، والد آن باید واحد کامپایل باشد، اگر محلی باشد، گره مربوطه (این به ویژه برای پارامترهای رویه صادق است، والد آنها باید خود رویه باشد). همچنین می توانید تعیین کنید که اعلان متغیر در کدام فایل، ردیف و ستون باشد.
    در ساده ترین حالت، مقدار یک متغیر در یک آدرس ثابت قرار دارد، اما بسیاری از متغیرها به صورت پویا هنگام وارد کردن یک رویه در پشته یا ثبات ایجاد می شوند، گاهی اوقات محاسبه آدرس مقدار می تواند کاملاً غیر ضروری باشد. این استاندارد مکانیزمی را برای توصیف محل قرارگیری مقدار یک متغیر - عبارات مکان ارائه می کند. یک عبارت آدرس مجموعه ای از دستورالعمل ها (ثابت های DW_OP_xxxx) برای یک ماشین پشته مانند Fort است، در واقع یک زبان جداگانه با شاخه ها، رویه ها و عملیات حسابی است. ما این زبان را به طور کامل بررسی نمی کنیم، در واقع فقط به چند دستورالعمل علاقه مند خواهیم بود:
    • DW_OP_addr - آدرس متغیر را نشان می دهد
    • DW_OP_fbreg - نشان دهنده افست متغیر از ثبات پایه (معمولا اشاره گر پشته) است.
    • DW_OP_reg0… DW_OP_reg31 - نشان می دهد که متغیر در ثبات مربوطه ذخیره می شود.
    برای ایجاد یک عبارت آدرس، ابتدا باید یک عبارت خالی (dwarf_new_expr) ایجاد کنید، دستورالعمل ها را به آن اضافه کنید (dwarf_add_expr_addr، dwarf_add_expr_gen، و غیره) و آن را به عنوان مقدار ویژگی DW_AT_location (dwarf_add_AT_location_expression) به گره اضافه کنید.
    تابع ایجاد یک عبارت آدرس خالی، دسته یا 0 خود را در خطا برمی گرداند:
    Dwarf_Expr dwarf_new_expr(Dwarf_P_Debug dbg, Dwarf_Error *error)
    برای افزودن دستورالعمل به یک عبارت، باید از تابع dwarf_add_expr_gen استفاده کنید:
    Dwarf_Unsigned dwarf_add_expr_gen(Dwarf_P_Expr expr، Dwarf_Small opcode، Dwarf_Unsigned val1، Dwarf_Unsigned val2، Dwarf_Error *خطا)
    • opcode - کد عملیات، ثابت DW_OP_хххх
    • val1، val2 - پارامترهای دستورالعمل (به استاندارد مراجعه کنید)

    برای تنظیم صریح آدرس یک متغیر، تابع dwarf_add_expr_addr باید به جای تابع قبلی استفاده شود:
    Dwarf_Unsigned dwarf_add_expr_addr(Dwarf_P_Expr expr, Dwarf_Unsigned address, Dwarf_Signed sym_index, Dwarf_Error *error)
    • expr یک دسته برای عبارت آدرس است که دستورالعمل به آن اضافه می شود
    • آدرس - آدرس متغیر
    • sym_index - شاخص نماد در table.symtab. اختیاری، 0 را می توان پاس کرد
    این تابع همچنین DW_DLV_NOCOUNT را در صورت خطا برمی گرداند.
    در نهایت، می توانید عبارت آدرس ایجاد شده را با استفاده از تابع dwarf_add_AT_location_expr به گره اضافه کنید:
    Dwarf_P_Attribute dwarf_add_AT_location_expr(Dwarf_P_Debug dbg، Dwarf_P_Die milkdie، Dwarf_Half attr، Dwarf_P_Expr loc_expr، Dwarf_Error *خطا)
    • milkdie - گره ای که عبارت به آن اضافه می شود
    • attr - ویژگی (در مورد ما DW_AT_location)
    • loc_expr - دسته به عبارت آدرسی که قبلا ایجاد شده است
    تابع دسته ویژگی یا DW_DLV_NOCOUNT را در صورت خطا برمی‌گرداند.
    متغیرها (و همچنین پارامترهای رویه) و ثابت ها به ترتیب گره های معمولی با برچسب DW_TAG_variable، DW_TAG_formal_parameter و DW_TAG_const_type هستند. آنها به ویژگی های زیر نیاز دارند:
    • نام متغیر/ثابت (تابع dwarf_add_AT_name، به «ایجاد ویژگی‌های گره» مراجعه کنید)
    • شماره خط در فایلی که متغیر در آن اعلان شده است (ویژگی DW_AT_decl_line)، تابع dwarf_add_AT_unsigned_const (به «ایجاد ویژگی‌های گره» مراجعه کنید)
    • فهرست نام فایل (ویژگی DW_AT_decl_file)، تابع dwarf_add_AT_unsigned_const (به «ایجاد ویژگی‌های گره» مراجعه کنید)
    • نوع داده متغیر/ثابت (ویژگی DW_AT_type پیوندی به نوع ایجاد شده قبلی است، به «ایجاد انواع داده» مراجعه کنید)
    • بیان آدرس (به بالا مراجعه کنید) - برای یک متغیر یا پارامتر رویه مورد نیاز است
    • یا مقدار - برای یک ثابت (ویژگی DW_AT_const_value، به "ایجاد ویژگی های گره" مراجعه کنید)
    ایجاد بخش هایی با اطلاعات اشکال زدایی
    پس از ایجاد تمام گره های درخت اطلاعات اشکال زدایی، می توانید شروع به ایجاد بخش های elf با آن کنید. این در دو مرحله اتفاق می افتد:
    • ابتدا باید تابع dwarf_transform_to_disk_form را فراخوانی کنیم، که تابعی را که نوشته ایم برای ایجاد بخش های الف لازم یک بار برای هر بخش فراخوانی می کند.
    • برای هر بخش، تابع dwarf_get_section_bytes داده هایی را به ما برمی گرداند که باید در بخش مربوطه نوشته شوند.
    تابع
    dwarf_transform_to_disk_form (Dwarf_P_Debug dbg, Dwarf_Error* error)
    اطلاعات اشکال زدایی را که ایجاد کردیم به فرمت باینری تبدیل می کند، اما چیزی روی دیسک نمی نویسد. تعداد بخش‌های الف ایجاد شده یا DW_DLV_NOCOUNT در صورت خطا را برمی‌گرداند. در این حالت برای هر بخش تابع callback فراخوانی می شود که هنگام مقداردهی اولیه کتابخانه آن را به تابع dwarf_producer_init_c منتقل کردیم. این تابع را باید خودمان بنویسیم. مشخصات آن به شرح زیر است:
    typedef int (*Dwarf_Callback_Func_c)(کاراکتر* نام، اندازه int، نوع Dwarf_Unsigned، پرچم‌های Dwarf_Unsigned، پیوند Dwarf_Unsigned، اطلاعات Dwarf_Unsigned، Dwarf_Unsigned* بخش_نام_شاخص، خالی *خطای_کاربری*)،
    • name - نام بخش جن که باید ایجاد شود
    • اندازه - اندازه بخش
    • نوع - نوع بخش
    • پرچم - پرچم بخش
    • پیوند - فیلد پیوند بخش
    • اطلاعات - قسمت اطلاعات قسمت
    • sect_name_index - باید نمایه بخش را با جابجایی ها برگردانید (اختیاری)
    • user_data - به همان روشی که آن را در تابع مقداردهی اولیه کتابخانه تنظیم کرده ایم به ما ارسال می شود
    • error - در اینجا می توانید کد خطا را ارسال کنید
    در این تابع باید:
    • یک بخش جدید ایجاد کنید (عملکرد elf_newscn، به ایجاد بخش ها مراجعه کنید)
    • یک هدر بخش ایجاد کنید (عملکرد elf32_getshdr، همانجا)
    • آن را به درستی پر کنید (رجوع کنید به همان). این آسان است زیرا فیلدهای هدر بخش با پارامترهای تابع ما مطابقت دارند. فیلدهای گمشده sh_addr، sh_offset، sh_entsize را روی 0 و sh_addralign را روی 1 تنظیم کنید.
    • نمایه بخش ایجاد شده (عملکرد elf_ndxscn، به بخش "Section.symtab" مراجعه کنید) یا -1 در مورد خطا (با تنظیم کد خطا روی خطا) را برگردانید.
    • همچنین باید از بخش ".rel" بگذریم (در مورد ما) و هنگام بازگشت از تابع، 0 را برگردانیم
    پس از تکمیل، تابع dwarf_transform_to_disk_form تعداد بخش های ایجاد شده را برمی گرداند. ما باید هر بخش را در یک حلقه از 0 طی مراحل زیر طی کنیم:
    • با استفاده از تابع dwarf_get_section_bytes داده هایی را برای نوشتن در یک بخش ایجاد کنید:
      Dwarf_Ptr dwarf_get_section_bytes (Dwarf_P_Debug dbg, Dwarf_Signed dwarf_section, Dwarf_Signed *elf_section_index, Dwarf_unsigned *length, Dwarf_Error* خطا)
      • dwarf_section - شماره بخش. باید در محدوده 0..n باشد، جایی که n عددی است که توسط تابع dwarf_transform_to_disk_form به ما برگردانده شده است.
      • elf_section_index - ایندکس بخشی را که داده ها باید روی آن نوشته شوند را برمی گرداند
      • طول - طول این داده ها
      • خطا - استفاده نشده است
      تابع یک اشاره گر به داده های دریافتی یا 0 (در مورد
      زمانی که هیچ بخش دیگری برای ایجاد باقی نمانده است)
    • یک توصیفگر داده برای بخش فعلی ایجاد کنید (عملکرد elf_newdata، به ایجاد بخش ها مراجعه کنید) و آن را پر کنید (آنجا را ببینید) با تنظیم:
      • d_buf - یک اشاره گر به داده هایی که از تابع قبلی دریافت کرده ایم
      • d_size - اندازه این داده ها (همانجا)
    اتمام کار با کتابخانه
    پس از تشکیل بخش ها، می توانید با استفاده از تابع dwarf_producer_finish کار با libdwarf را به پایان برسانید:
    Dwarf_Unsigned dwarf_producer_finish(Dwarf_P_Debug dbg, Dwarf_Error* error)
    تابع DW_DLV_NOCOUNT را در صورت خطا برمی گرداند.
    لطفاً توجه داشته باشید که در این مرحله ضبط روی دیسک انجام نمی شود. ضبط باید با استفاده از عملکردهای بخش "ایجاد ELF - نوشتن یک فایل" انجام شود.

    نتیجه

    همین.
    تکرار می کنم، ایجاد اطلاعات اشکال زدایی موضوع بسیار گسترده ای است و من به بسیاری از موضوعات دست نزدم و فقط حجاب را برداشتم. کسانی که آرزو دارند می توانند به طور نامحدود عمیق تر شوند.
    اگر سوالی دارید، سعی می کنم به آنها پاسخ دهم.

    اگر روی کامپیوتر خود نصب کرده اید برنامه آنتی ویروسمی توان تمام فایل های موجود در رایانه خود و همچنین هر فایل را به صورت جداگانه اسکن کنید. شما می توانید هر فایلی را با کلیک راست روی فایل و انتخاب گزینه مناسب برای اسکن فایل برای ویروس ها اسکن کنید.

    به عنوان مثال، در این شکل برجسته شده است فایل my-file.elf، سپس باید روی این فایل کلیک راست کرده و در منوی فایل گزینه را انتخاب کنید "اسکن با AVG". با انتخاب این گزینه، AVG Antivirus باز می شود و فایل را برای ویروس ها اسکن می کند.


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

    گاهی ساده نصب مجدد دلفین (شبیه ساز)ممکن است با پیوند صحیح ELF با Dolphin (شبیه ساز) مشکل شما را حل کند. در موارد دیگر، مشکلات مرتبط با فایل ممکن است ناشی از برنامه نویسی بد نرم افزارتوسعه دهنده و ممکن است لازم باشد برای کمک بیشتر با توسعه دهنده تماس بگیرید.


    توصیه:سعی کنید Dolphin (Emulator) را به آخرین نسخه به روز کنید تا مطمئن شوید که آخرین وصله ها و به روز رسانی ها را دارید.


    این ممکن است خیلی بدیهی به نظر برسد، اما اغلب خود فایل ELF ممکن است باعث این مشکل شده باشد. اگر فایلی را از طریق یک پیوست ایمیل دریافت کردید یا آن را از یک وب سایت دانلود کردید و روند دانلود قطع شد (مانند قطع برق یا دلایل دیگر)، فایل ممکن است آسیب ببیند. در صورت امکان، یک کپی جدید از فایل ELF تهیه کنید و دوباره آن را باز کنید.


    با دقت:یک فایل آسیب دیده می تواند باعث آسیب جانبی به بدافزارهای قبلی یا موجود در رایانه شما شود، بنابراین مهم است که رایانه خود را با یک آنتی ویروس به روز به روز نگه دارید.


    اگر فایل شما ELF است مربوط به سخت افزار کامپیوتر شمابرای باز کردن فایلی که ممکن است نیاز داشته باشید به روز رسانی درایورهای دستگاهمرتبط با این تجهیزات

    این مشکل معمولاً با انواع فایل های رسانه ای مرتبط است، که به باز کردن موفقیت آمیز سخت افزار در داخل رایانه بستگی دارد، به عنوان مثال. کارت صدا یا کارت گرافیک. به عنوان مثال، اگر می خواهید یک فایل صوتی را باز کنید اما نمی توانید آن را باز کنید، ممکن است نیاز داشته باشید به روز رسانی درایورهای کارت صدا.


    توصیه:اگر زمانی که می خواهید یک فایل ELF را باز کنید دریافت می کنید پیغام خطای فایل SYS، مشکل احتمالاً می تواند باشد مرتبط با درایورهای دستگاه خراب یا قدیمیکه باید به روز شوند این فرآیند را می توان با استفاده از نرم افزارهای به روز رسانی درایور آسان تر کرد DriverDoc.


    اگر مراحل مشکل را حل نکردو شما همچنان در باز کردن فایل های ELF مشکل دارید، این ممکن است به دلیل آن باشد کمبود منابع سیستمی موجود. برخی از نسخه‌های فایل‌های ELF ممکن است به مقدار قابل توجهی از منابع (مانند حافظه/رم، قدرت پردازش) نیاز داشته باشند تا به درستی در رایانه شما باز شوند. اگر از سخت افزار کامپیوتر نسبتا قدیمی و در عین حال از یک سیستم عامل بسیار جدیدتر استفاده می کنید، این مشکل بسیار رایج است.

    این مشکل ممکن است زمانی رخ دهد که رایانه در انجام یک کار مشکل داشته باشد زیرا ممکن است سیستم عامل (و سایر سرویس‌های در حال اجرا در پس‌زمینه) برای باز کردن یک فایل ELF منابع زیادی مصرف کنید. سعی کنید قبل از باز کردن فایل بازی Nintendo Wii، تمام برنامه های رایانه شخصی خود را ببندید. با آزاد کردن تمام منابع موجود در رایانه خود، در بهترین موقعیت برای تلاش برای باز کردن فایل ELF خود خواهید بود.


    اگر شما تمام مراحل توضیح داده شده در بالا را انجام دادو فایل ELF شما هنوز باز نمی شود، ممکن است لازم باشد اجرا کنید به روز رسانی تجهیزات. در بیشتر موارد، حتی زمانی که از نسخه‌های قدیمی‌تر سخت‌افزار استفاده می‌کنید، قدرت پردازش همچنان می‌تواند برای اکثر برنامه‌های کاربر بیش از حد کافی باشد (مگر اینکه کارهای زیادی با CPU انجام دهید، مانند رندر سه بعدی، مدل‌سازی مالی/علمی، یا کار فشرده چند رسانه ای). بدین ترتیب، این احتمال وجود دارد که رایانه شما حافظه کافی نداشته باشد(که معمولاً "RAM" یا حافظه دسترسی تصادفی نامیده می شود) برای انجام وظیفه باز کردن یک فایل.