• Elf.dll dosyasını açın. Hata ayıklama bilgilerini (DWARF) manuel olarak (ARM mikrodenetleyicileri için) içeren bir ELF dosyası oluşturuyoruz. Genel dosya yapısı

    Bu incelememizde bu formatın sadece 32-bit versiyonundan bahsedeceğiz çünkü henüz 64-bit versiyonuna ihtiyacımız yok.

    Herhangi bir ELF dosyası (bu formattaki nesne modülleri dahil) aşağıdaki bölümlerden oluşur:

    • ELF dosya başlığı;
    • Program bölümleri tablosu (nesne modüllerinde bulunmayabilir);
    • ELF dosya bölümleri;
    • Bölüm tablosu (yürütme modülünde bulunmayabilir);
    • Performans nedeniyle ELF formatı bit alanlarını kullanmaz. Ve tüm yapılar genellikle 4 bayt hizalıdır.

    Şimdi ELF dosya başlıklarında kullanılan türlere bakalım:

    Şimdi dosya başlığına bakalım:

    #define EI_NIDENT 16 struct elf32_hdr ( unsigned char e_ident; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; /* Giriş noktası */ Elf32_Off e_phoff; Elf32_Off e_shoff; Elf32_Word e_fla gs ; Elf32_Half e_ehsize; Elf32_Half e_shnum;

    e_ident dizisi sistem hakkında bilgi içerir ve birçok alt alandan oluşur.

    Yapı ( unsigned char ei_magic; unsigned char ei_class; unsigned char ei_data; unsigned char ei_version; unsigned char ei_pad; )

    • ei_magic - tüm ELF dosyaları için sabit değer, (0x7f, "E", "L", "F")'ye eşittir
    • ei_class - ELF dosya sınıfı (1 - 32 bit, 2 - 64 bit bunları dikkate almıyoruz)
    • ei_data - bu dosyanın bayt sırasını belirler (bu sıra platforma bağlıdır ve ileri (LSB veya 1) veya ters (MSB veya 2) olabilir) Intel işlemciler için yalnızca 1 değerine izin verilir.
    • ei_version oldukça kullanışsız bir alandır ve 1'e (EV_CURRENT) eşit değilse dosyanın hatalı olduğu kabul edilir.

    İşletim sistemleri kimlik bilgilerini ei_pad alanında saklar. Bu alan boş olabilir. Bizim için de önemi yok.

    e_type başlık alanı birden fazla değer içerebilir; çalıştırılabilir dosyalar için ET_EXEC 2'ye eşit olmalıdır

    e_machine - bu yürütülebilir dosyanın üzerinde çalışabileceği işlemciyi belirler (Bizim için EM_386'nın kabul edilebilir değeri 3'tür)

    e_version alanı başlıktaki ei_version alanına karşılık gelir.

    e_entry alanı, program başlamadan önce eip'e yerleştirilen programın başlangıç ​​adresini tanımlar.

    e_phoff alanı, programları belleğe yüklemek için kullanılan program bölüm tablosunun bulunduğu dosyanın başından itibaren uzaklığı belirtir.

    Tüm alanların amacını listelemeyeceğim; yükleme için hepsine ihtiyaç yoktur. Sadece iki tanesini daha anlatacağım.

    e_phentsize alanı, program bölümü tablosundaki girişin boyutunu belirtir.

    Ve e_phnum alanı program bölümü tablosundaki giriş sayısını belirler.

    Bölüm tablosu (program bölümleri değil) programları bağlamak için kullanılır. dikkate almayacağız. Ayrıca dinamik olarak bağlantılı modülleri de dikkate almayacağız. Bu konu oldukça karmaşıktır ve ilk tanışmaya uygun değildir. :)

    Şimdi program bölümleri hakkında. Program bölümü tablosu giriş formatı aşağıdaki gibidir:

    Struct 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_flags; Elf32_Word p_align; );

    Alanlar hakkında daha fazla bilgi edinin.

    • p_type - program bölümünün türünü belirler. Birkaç değer alabilir, ancak biz yalnızca biriyle ilgileniyoruz. PT_LOAD(1). Bölüm bu türden ise belleğe yüklenmesi amaçlanmaktadır.
    • p_offset - bu bölümün başladığı dosyadaki uzaklığı belirler.
    • p_vaddr - bu bölümün belleğe yüklenmesi gereken sanal adresi tanımlar.
    • p_paddr - bu bölümün yüklenmesi gereken fiziksel adresi tanımlar. Bu alan isteğe bağlıdır ve yalnızca bazı platformlarda anlamlıdır.
    • p_filesz - dosyadaki bölümün boyutunu belirler.
    • p_memsz - bellek bölümünün boyutunu belirler. Bu değer öncekinden daha büyük olabilir. p_flag alanı bellekteki bölümlere erişim türünü belirler. Bazı bölümler yapılabilir, bazıları yazılabilir. Hepsi mevcut sistemlerde okunmaya müsaittir.

    ELF formatı yükleniyor.

    Başlığı biraz çözdük. Şimdi ELF formatında ikili bir dosya yüklemek için bir algoritma vereceğim. Algoritma şematiktir ve çalışan bir program olarak düşünülmemelidir.

    Int LoadELF (unsigned 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 != "L" || EH->e_ident != "F" || EH->e_ident != ELFCLASS32 || EH->e_ident != ELFDATA2LSB || e_ident != EV_CURRENT || // version EH->e_type != ET_EXEC || // yazın EH->e_machine != EM_386 || // platform EH->e_version != EV_CURRENT) // ve yine her ihtimale karşı sürüm return ELF_WRONG; EPH = (struct elf32_phdr *)(bin + EH->e_phoff); while (EH->e_phnum--) ( if (EPH->p_type == PT_LOAD) memcpy (EPH->p_vaddr, bin + EPH-) >p_offset, EPH->p_filesz); EPH = (struct elf32_phdr *)((unsigned char *)EPH + EH->e_phentsize)); )

    Cidden, EPH->p_flags alanlarını analiz etmeye ve uygun sayfalara erişim haklarını ayarlamaya değer; burada basitçe kopyalama işe yaramaz, ancak bu artık formatla değil, hafıza tahsisiyle ilgilidir. Bu nedenle şimdi bunun hakkında konuşmayacağız.

    PE formatı.

    Birçok yönden ELF formatına benzer ve şaşırtıcı olmayan bir şekilde indirilebilecek bölümlere de sahip olmalıdır.

    Microsoft'taki diğer her şey gibi :) PE formatı da EXE formatını temel alır. Dosya yapısı aşağıdaki gibidir:

    • 00h - EXE başlığı (Bakmıyorum, Dos kadar eski. :)
    • 20h - OEM başlığı (önemli bir şey yok);
    • 3сh - dosyadaki (dword) gerçek PE başlığının ofseti.
    • saplama hareket tablosu;
    • Taslak;
    • PE başlığı;
    • nesne tablosu;
    • dosya nesneleri;

    stub, gerçek modda çalışan ve bazı ön eylemleri gerçekleştiren bir programdır. Yok olabilir ama bazen ihtiyaç duyulabilir.

    Biz biraz farklı bir şeyle ilgileniyoruz, PE başlığı.

    Yapısı şu şekildedir:

    Struct pe_hdr ( unsigned long pe_sign; unsigned short pe_cputype; unsigned short pe_objnum; unsigned long pe_time; unsigned long pe_cofftbl_off; unsigned long pe_cofftbl_size; unsigned short pe_nthdr_size; unsigned short pe_flags; unsigned short pe_magic; unsigned short pe_link_ver; unsigned long pe_code_ imzasız uzun; pe_idata_size ; imzasız uzun pe_udata_size; imzasız uzun pe_obj_align // ve diğer birçok önemsiz şey.

    Orada bir sürü şey var. Bu başlığın boyutunun 248 byte olduğunu söylemek yeterli olacaktır.

    Ve asıl önemli olan bu alanların çoğunun kullanılmamasıdır. (Kim böyle inşa ediyor?) Hayır, elbette oldukça iyi bilinen bir amaçları var, ancak örneğin test programım pe_code_base, pe_code_size vb. alanlarında sıfırlar içeriyor, ancak iyi çalışıyor. Sonuç, dosyanın nesne tablosuna göre yüklendiğidir. İşte bunun hakkında konuşacağız.

    Nesne tablosu PE başlığından hemen sonra gelir. Bu tablodaki girişler aşağıdaki formattadır:

    Struct pe_ohdr ( unsigned char o_name; unsigned long o_vsize; unsigned long o_vaddr; unsigned long o_psize; unsigned long o_poff; unsigned char o_reserved; unsigned long o_flags; );

    • o_name - bölümün adı, yüklemeye kesinlikle kayıtsızdır;
    • o_vsize - bellek bölümünün boyutu;
    • o_vaddr - ImageBase'e göre hafıza adresi;
    • o_psize - dosyadaki bölüm boyutu;
    • o_poff - dosyadaki bölüm ofseti;
    • o_flags - bölüm bayrakları;

    Bayraklara daha yakından bakmakta fayda var.

    • 00000004h - 16 bitlik ofsetlere sahip kod için kullanılır
    • 00000020h - kod bölümü
    • 00000040h - başlatılan veri bölümü
    • 00000080h - başlatılmamış veri bölümü
    • 00000200h - yorumlar veya diğer türde bilgiler
    • 00000400h - yer paylaşımı bölümü
    • 00000800h - program görselinin bir parçası olmayacak
    • 00001000h - genel veriler
    • 00500000h - aksi belirtilmedikçe varsayılan hizalama
    • 02000000h - bellekten kaldırılabilir
    • 04000000h - önbelleğe alınmadı
    • 08000000h - çağrılmadı
    • 10000000 saat - paylaşıldı
    • 20000000 saat - yapılabilir
    • 40000000h - okunabilir
    • 80000000h - yazabilirsiniz

    Yine paylaşılan ve yer paylaşımlı bölümlerden bahsetmeyeceğim, kod, veri ve erişim haklarıyla ilgileniyoruz.

    Genel olarak bu bilgi ikili dosyayı indirmek için zaten yeterlidir.

    PE formatı yükleniyor.

    int LoadPE (unsigned char *bin) ( struct elf32_hdr *PH = (struct pe_hdr *) (bin + *((unsigned long *)&bin)); // Elbette kombinasyon net değil... sadece dword'ü alın ofset 0x3c'de / / Ve resim dosyasındaki PE başlık adresini hesaplayın struct elf32_phdr *POH; if (PH == NULL || // PH->pe_sign işaretçisini kontrol edin != 0x4550 || // PE imzası ("P" , "E", 0, 0) PH->pe_cputype != 0x14c || // i386 (PH->pe_flags & 2) == 0) // dosya çalıştırılamıyor! return PE_WRONG while (PH-); >pe_obj_num--) ( if ((POH->p_flags & 0x60) != 0) // kod veya başlatılan veri memcpy'sinden biri (PE->pe_image_base + POH->o_vaddr, bin + POH- >o_poff, POH->o_psize ); POH = (struct pe_ohdr *)((unsigned char *)POH + sizeof (struct pe_ohdr));

    Bu yine hazır bir program değil, bir yükleme algoritmasıdır.

    Ve yine birçok nokta konunun kapsamını aştığı için değinilmiyor.

    Ancak şimdi mevcut sistem özelliklerinden biraz bahsetmeye değer.

    Sistem özellikleri.

    İşlemcilerde bulunan koruma araçlarının esnekliğine rağmen (tanımlayıcı tablolar düzeyinde koruma, segment düzeyinde koruma, sayfa düzeyinde koruma), mevcut sistemlerde (hem Windows hem de Unix) yalnızca sayfa koruması tam olarak kullanılır; kodun yazılmasını koruyabilmesine rağmen verileri yürütülmekten koruyamaz. (Belki de sistemdeki güvenlik açıklarının çokluğunun nedeni budur?)

    Tüm bölümler doğrusal adres sıfırdan adreslenir ve doğrusal belleğin sonuna kadar uzanır. Süreç sınırlandırması yalnızca sayfa tabloları düzeyinde yapılır.

    Bu bağlamda, tüm modüller başlangıç ​​adreslerinden değil, segmentte yeterince büyük bir ofsetle bağlanır. Windows'ta segmentteki temel adres 0x400000, Unix'te (Linux veya FreeBSD) - 0x8048000'dir.

    Bazı özellikler aynı zamanda hafızanın sayfa düzeniyle de ilgilidir.

    ELF dosyaları, bölümlerin sınırları ve boyutları dosyanın 4 kilobaytlık bloklarına denk gelecek şekilde bağlanır.

    PE formatında, formatın kendisi 512 baytlık bölümleri hizalamanıza izin vermesine rağmen, bölümlerin hizalanması 4k'de kullanılır; Windows'ta daha küçük bir hizalama doğru kabul edilmez.

    ELF dosyasıyla ilgili sorununuzu çözmenize yardımcı olduğumuzu umuyoruz. Listemizden bir uygulamayı nereden indirebileceğinizi bilmiyorsanız, bağlantıya tıklayın (programın adı budur) - Gerekli uygulamanın güvenli kurulum sürümünü nereden indireceğiniz konusunda daha ayrıntılı bilgi bulacaksınız.

    Bu sayfayı ziyaret etmeniz, özellikle bu veya benzeri soruları yanıtlamanıza yardımcı olacaktır:

    • ELF uzantılı bir dosya nasıl açılır?
    • ELF dosyasını başka bir formata nasıl dönüştürebilirim?
    • ELF dosya biçimi uzantısı nedir?
    • ELF dosyasını hangi programlar destekler?

    Bu sayfadaki materyalleri inceledikten sonra yukarıda sunulan soruların herhangi birine hala tatmin edici bir yanıt alamadıysanız, bu, burada ELF dosyasıyla ilgili sunulan bilgilerin eksik olduğu anlamına gelir. İletişim formunu kullanarak bizimle iletişime geçin ve hangi bilgileri bulamadığınızı yazın.

    Başka ne sorunlara neden olabilir?

    ELF dosyasını açamamanızın daha birçok nedeni olabilir (yalnızca ilgili uygulamanın olmaması değil).
    İlk önce- ELF dosyası, onu desteklemek için yüklenen uygulamaya yanlış bağlanmış (uyumsuz) olabilir. Bu durumda bu bağlantıyı kendiniz değiştirmeniz gerekir. Bunu yapmak için düzenlemek istediğiniz ELF dosyasına sağ tıklayın ve seçeneğine tıklayın. "Birlikte açmak için" ve ardından listeden yüklediğiniz programı seçin. Bu işlemden sonra ELF dosyasının açılmasıyla ilgili sorunlar tamamen ortadan kalkmalıdır.
    ikinci olarak- açmak istediğiniz dosya zarar görmüş olabilir. Bu durumda en iyisi yeni versiyonunu bulmak veya aynı kaynaktan tekrar indirmek olacaktır (belki de önceki oturumda bazı nedenlerden dolayı ELF dosyasının indirilmesi tamamlanmadı ve doğru şekilde açılamadı) .

    yardım ister misiniz?

    ELF dosya uzantısı hakkında ek bilginiz varsa bunu sitemizin kullanıcılarıyla paylaşırsanız minnettar oluruz. Aşağıdaki formu kullanarak ELF dosyasıyla ilgili bilgilerinizi bize gönderin.

    Standart geliştirme araçları, programınızı hata ayıklama bilgilerini ekleme özelliğine sahip bir ELF (Yürütülebilir ve Bağlanabilir Format) dosyasında derler. Format spesifikasyonu okunabilir. Ayrıca her mimarinin ARM özellikleri gibi kendine has özellikleri bulunmaktadır. Bu formata kısaca bir göz atalım.
    ELF formatındaki yürütülebilir bir dosya aşağıdaki parçalardan oluşur:
    1. Başlık (ELF Başlığı)
    Dosya ve ana özellikleri hakkında genel bilgiler içerir.
    2. Program Başlığı Tablosu
    Bu, dosya bölümleri ve bellek bölümleri arasındaki yazışma tablosudur; önyükleyiciye her bölümün hangi bellek alanına yazılacağını bildirir.
    3. Bölümler
    Bölümler dosyadaki tüm bilgileri içerir (program, veriler, hata ayıklama bilgileri vb.)
    Her bölümün bir türü, adı ve diğer parametreleri vardır. ".text" bölümü genellikle kodu saklar, ".symtab" - program sembollerinin bir tablosu (dosyaların, prosedürlerin ve değişkenlerin adları), ".strtab" - bir dizeler tablosu, ".debug_" ön ekine sahip bölümler - hata ayıklama bilgileri vb. .d. Ayrıca dosyada indeksi 0 olan boş bir bölüm bulunmalıdır.
    4. Bölüm Başlık Tablosu
    Bu, bir dizi bölüm başlığını içeren bir tablodur.
    Format, ELF Oluşturma bölümünde daha ayrıntılı olarak ele alınmaktadır.

    CÜCE Genel Bakış

    DWARF standartlaştırılmış bir hata ayıklama bilgi formatıdır. Standart resmi web sitesinden indirilebilir. Ayrıca formata ilişkin harika bir kısa genel bakış da mevcut: DWARF Hata Ayıklama Formatına Giriş (Michael J. Eager).
    Hata ayıklama bilgilerine neden ihtiyaç duyulur? İzin veriyor:
    • kesme noktalarını fiziksel bir adrese değil, kaynak kod dosyasındaki satır numarasına veya işlevin adına ayarlayın
    • fonksiyon parametrelerinin yanı sıra global ve yerel değişkenlerin değerlerini görüntüleyin ve değiştirin
    • çağrı yığınını görüntüle (geri izleme)
    • programı tek bir montaj talimatına göre değil, kaynak kod satırlarına göre adım adım yürütün
    Bu bilgi bir ağaç yapısında saklanır. Her ağaç düğümünün bir ebeveyni vardır, çocukları olabilir ve buna DIE (Hata Ayıklama Bilgi Girişi) adı verilir. Her düğümün kendi etiketi (tipi) ve düğümü tanımlayan niteliklerin (özellikler) bir listesi vardır. Nitelikler, veriler veya diğer düğümlere bağlantılar gibi her şeyi içerebilir. Ayrıca ağacın dışında saklanan bilgiler de vardır.
    Düğümler iki ana türe ayrılır: verileri tanımlayan düğümler ve kodu tanımlayan düğümler.
    Verileri açıklayan düğümler:
    1. Veri tipleri:
      • C'deki int türü gibi temel veri türleri (DW_TAG_base_type türündeki düğüm).
      • Bileşik veri türleri (işaretçiler vb.)
      • Diziler
      • Yapılar, sınıflar, birlikler, arayüzler
    2. Veri nesneleri:
      • sabitler
      • fonksiyon parametreleri
      • değişkenler
      • vesaire.
    Her veri nesnesinin, verinin bulunduğu adresin nasıl hesaplandığını belirten bir DW_AT_location özelliği vardır. Örneğin, bir değişken sabit bir adrese sahip olabilir, bir kayıt defterinde veya yığında olabilir veya bir sınıfın veya nesnenin üyesi olabilir. Bu adres oldukça karmaşık bir şekilde hesaplanabilir, bu nedenle standart, özel bir dahili yığın makinesinin operatör dizisini içerebilen Konum İfadeleri olarak adlandırılır.
    Kodu açıklayan düğümler:
    1. Prosedürler (işlevler) - DW_TAG_subprogram etiketine sahip düğümler. Azalan düğümler, değişkenlerin (işlev parametreleri ve yerel işlev değişkenleri) açıklamalarını içerebilir.
    2. Derleme Birimi. Program bilgilerini içerir ve diğer tüm düğümlerin ebeveynidir.
    Yukarıda anlatılan bilgiler ".debug_info" ve ".debug_abbrev" bölümlerinde yer almaktadır.
    Diğer bilgiler:
    • Satır numaraları hakkında bilgi (".debug_line" bölümü)
    • Makrolar hakkında bilgi (".debug_macinfo" bölümü)
    • Çağrı Çerçevesi Bilgisi (".debug_frame" bölümü)

    ELF'in yaratılışı

    Elfutils paketindeki libelf kütüphanesini kullanarak EFL formatında dosyalar oluşturacağız. İnternette iftira kullanımıyla ilgili iyi bir makale var - Örnekle LibELF (maalesef dosyaların oluşturulmasını çok kısaca anlatıyor) ve belgeler.
    Bir dosya oluşturmak birkaç aşamadan oluşur:
    1. İftira başlatılıyor
    2. Dosya Başlığı Oluşturma (ELF Başlığı)
    3. Program Başlığı Tablosu Oluşturma
    4. Bölüm Oluşturma
    5. Bir dosya yaz
    Aşamalara daha yakından bakalım
    İftira başlatılıyor
    Öncelikle elf_version(EV_CURRENT) fonksiyonunu çağırmanız ve sonucu kontrol etmeniz gerekecektir. EV_NONE'a eşitse bir hata oluşmuştur ve başka işlemler gerçekleştirilemez. Daha sonra ihtiyacımız olan dosyayı diskte oluşturmamız, tanımlayıcısını almamız ve elf_begin işlevine aktarmamız gerekiyor:
    Elf * elf_begin(int fd, Elf_Cmd cmd, Elf *elf)
    • fd - yeni açılan dosyanın tanımlayıcısı
    • cmd - modu (bilgi okumak için ELF_C_READ, yazmak için ELF_C_WRITE veya okuma/yazma için ELF_C_RDWR), açık dosya moduna karşılık gelmelidir (bizim durumumuzda ELF_C_WRITE)
    • elf - yalnızca arşiv dosyalarıyla (.a) çalışmak için gereklidir, bizim durumumuzda 0'ı geçmeniz gerekir
    Fonksiyon, tüm iftira fonksiyonlarında kullanılacak olan oluşturulan tanıtıcıya bir işaretçi döndürür, hata durumunda 0 döndürülür.
    Başlık oluşturma
    elf32_newehdr işlevi tarafından yeni bir dosya başlığı oluşturulur:
    Elf32_Ehdr * elf32_newehdr(Elf *elf);
    • elf - elf_begin işlevi tarafından döndürülen tanıtıcı
    Hata durumunda 0 değerini veya yapıya yönelik bir işaretçiyi (ELF dosyasının başlığı) döndürür:
    #define EI_NIDENT 16 typedef yapısı ( unsigned char e_ident; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff; Elf32_Off e_shoff; Elf32_Word e_flags; Elf32_Half e_ehs ize; Elf32_Half e_phentsize; Elf32_Half e_shntsize;

    Alanlarından bazıları standart bir şekilde doldurulmuş, bazılarını ise doldurmamız gerekiyor:

    • e_ident - tanımlama bayt dizisi, aşağıdaki endekslere sahiptir:
      • EI_MAG0, EI_MAG1, EI_MAG2, EI_MAG3 - bu 4 bayt, elf32_newehdr işlevinin bizim için zaten yaptığı 0x7f, "ELF" karakterlerini içermelidir
      • EI_DATA - dosyadaki veri kodlamasının türünü belirtir: ELFDATA2LSB veya ELFDATA2MSB. ELFDATA2LSB'yi şu şekilde kurmanız gerekir: e_ident = ELFDATA2LSB
      • EI_VERSION - dosya başlığı sürümü, bizim için zaten ayarlanmış
      • EI_PAD - dokunmayın
    • e_type - dosya türü, ET_NONE olabilir - tür yok, ET_REL - yeri değiştirilebilen dosya, ET_EXEC - yürütülebilir dosya, ET_DYN - paylaşılan nesne dosyası vb. Dosya türünü ET_EXEC olarak ayarlamamız gerekiyor
    • e_machine - bu dosya için gerekli mimari örneğin EM_386 - Intel mimarisi için, ARM için buraya EM_ARM (40) yazmamız gerekiyor - ARM Mimarisi için ELF'e bakın
    • e_version - dosya sürümü EV_CURRENT olarak ayarlanmalıdır
    • e_entry - giriş noktası adresi, bizim için gerekli değil
    • e_phoff - program başlık dosyasındaki ofset, e_shoff - bölüm başlığının ofseti, doldurmayın
    • e_flags - mimarimiz için (Cortex-M3) işlemciye özel bayraklar 0x05000000 (ABI sürüm 5) olarak ayarlanmalıdır.
    • e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum - dokunmayın
    • e_shstrndx - bölüm başlıklarına sahip satır tablosunun bulunduğu bölümün numarasını içerir. Henüz bölümümüz olmadığından bu sayıyı daha sonra belirleyeceğiz
    Program Başlığı Oluşturma
    Daha önce de belirtildiği gibi, Program Başlığı Tablosu, önyükleyiciye her bölümün nereye yazılacağını söyleyen, dosya bölümleri ve bellek bölümleri arasındaki yazışma tablosudur. Başlık elf32_newphdr işlevi kullanılarak oluşturulur:
    Elf32_Phdr * elf32_newphdr(Elf *elf, size_t sayısı);
    • elf bizim kolumuzdur
    • count - oluşturulacak tablo öğelerinin sayısı. Yalnızca bir bölümümüz (program kodlu) olacağından sayım 1'e eşit olacaktır.
    Hata durumunda 0 değerini veya program başlığını gösteren bir işaretçiyi döndürür.
    Başlık tablosundaki her öğe aşağıdaki yapıyla tanımlanır:
    TYPEDEF YAPISI (ELF32_WORD P_TYPE; ELF32_OFF P_OFFSET; ELF32_ADDR P_VADDR; ELF32_ADDR P_PADDDR; ELF32_WORD P_Filesz; ELF32_WORD P_MEMSZZ; ELF32_WORD P_FL AGS;
    • p_type - segment (bölüm) türü, burada PT_LOAD - yükleme segmentini belirtmeliyiz
    • p_offset - belleğe yüklenecek bölümün verilerinin başladığı dosyadaki ofsetler. Bizim için bu, dosya başlığı ve program başlığından hemen sonra yer alacak olan .text bölümüdür; ofseti bu başlıkların uzunluklarının toplamı olarak hesaplayabiliriz. Herhangi bir türün uzunluğu elf32_fsize işlevi kullanılarak elde edilebilir:
      size_t elf32_fsize(Elf_Type türü, size_t sayısı, imzasız int sürümü); yazın - buraya ELF_T_xxx sabiti, ELF_T_EHDR ve ELF_T_PHDR boyutlarına ihtiyacımız olacak; count - istenen türdeki ve sürümdeki öğelerin sayısı - EV_CURRENT olarak ayarlanmalıdır
    • p_vaddr, p_paddr - bölümün içeriğinin yükleneceği sanal ve fiziksel adres. Sanal adreslerimiz olmadığından, onu fiziksel olana eşit olarak ayarlıyoruz, en basit durumda - 0, çünkü programımızın yükleneceği yer burasıdır.
    • p_filesz, p_memsz - dosya ve bellekteki bölüm boyutu. Aynısı bizde de var ama henüz program kodunun olduğu bölüm olmadığından daha sonra yükleyeceğiz
    • p_flags - yüklenen bellek bölümü için izinler. PF_R - okuma, PF_W - yazma, PF_X - yürütme veya bunların bir kombinasyonu olabilir. p_flags'i PF_R + PF_X olarak ayarlayın
    • p_align - segment hizalaması, elimizde 4 tane var
    Bölüm Oluşturma
    Başlıkları oluşturduktan sonra bölümleri oluşturmaya başlayabilirsiniz. elf_newscn işlevi kullanılarak boş bir bölüm oluşturulur:
    Elf_Scn * elf_newscn(Elf *elf);
    • elf - elf_begin işlevi tarafından daha önce döndürülen tanıtıcı
    İşlev, hata durumunda bölüme bir işaretçi veya 0 döndürür.
    Bölüm oluşturduktan sonra bölüm başlığını doldurmanız ve bölüm veri tanımlayıcısı oluşturmanız gerekir.
    elf32_getshdr işlevini kullanarak bölüm başlığına bir işaretçi getirebiliriz:
    Elf32_Shdr * elf32_getshdr(Elf_Scn *scn);
    • scn, elf_newscn işlevinden aldığımız bölümün işaretçisidir.
    Bölüm başlığı şuna benzer:
    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; Elf32_Word sh_addraalign; 32_Word sh_ entsize; ) Elf32_Shdr;
    • sh_name - bölüm adı - bölüm başlıklarının dize tablosundaki konum (section.shstrtab) - aşağıdaki "Dize tabloları" konusuna bakın
    • sh_type - bölüm içerik türü, program kodlu bir bölüm için SHT_PROGBITS'i ayarlamanız gerekir, dize tablosu içeren bölümler için - SHT_STRTAB, sembol tablosu için - SHT_SYMTAB
    • sh_flags birleştirilebilen bölüm bayraklarıdır ve bunlardan yalnızca üçüne ihtiyacımız var:
      • SHF_ALLOC - bölümün belleğe yükleneceği anlamına gelir
      • SHF_EXECINSTR - bölüm çalıştırılabilir kodu içerir
      • SHF_STRINGS - bölüm bir dize tablosu içerir
      Buna göre programın .text bölümü için SHF_ALLOC + SHF_EXECINSTR bayraklarını ayarlamanız gerekir.
    • sh_addr - bölümün belleğe yükleneceği adres
    • sh_offset - dosyadaki bölümün ofseti - ona dokunmayın, kütüphane onu bizim için yükleyecektir
    • sh_size - bölüm boyutu - dokunmayın
    • sh_link - bağlantılı bölümün numarasını içerir; bölümü karşılık gelen satır tablosuna bağlamak gerekir (aşağıya bakın)
    • sh_info - bölüm türüne bağlı olarak ek bilgiler, 0 olarak ayarlanır
    • sh_addraalign - adres hizalama, dokunmayın
    • sh_entsize - eğer bir bölüm aynı uzunlukta birkaç elemandan oluşuyorsa, böyle bir elemanın uzunluğunu belirtir, dokunmayın
    Başlığı doldurduktan sonra elf_newdata işleviyle bir bölüm veri tanımlayıcısı oluşturmanız gerekir:
    Elf_Data * elf_newdata(Elf_Scn *scn);
    • scn, yeni bölümün yeni alınan işaretçisidir.
    İşlev, hata durumunda 0 değerini veya doldurulması gereken Elf_Data yapısına yönelik bir işaretçiyi döndürür:
    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 - bölüme yazılacak verinin işaretçisi
    • d_type - veri türü, ELF_T_BYTE her yerde bizim için uygundur
    • d_size - veri boyutu
    • d_off - bölümdeki ofset, 0'a ayarlandı
    • d_align - hizalama, 1'e ayarlanabilir - hizalama yok
    • d_version - sürüm, EV_CURRENT olarak ayarlanmalıdır
    Özel bölümler
    Amaçlarımız doğrultusunda, gerekli minimum bölüm setini oluşturmamız gerekecek:
    • .text - program kodunu içeren bölüm
    • .symtab - dosya sembolü tablosu
    • .strtab, .symtab bölümündeki sembollerin adlarını içeren bir dize tablosudur; çünkü .symtab, adları değil dizinlerini saklar.
    • .shstrtab - bölüm adlarını içeren dize tablosu
    Tüm bölümler bir önceki bölümde anlatıldığı gibi oluşturulmuştur ancak her özel bölümün kendine has özellikleri vardır.
    Bölüm.metin
    Bu bölüm çalıştırılabilir kod içerir, dolayısıyla bu kodun yükleneceği adrese sh_type'ı SHT_PROGBITS, sh_flags'ı SHF_EXECINSTR + SHF_ALLOC, sh_addr'ı ayarlamanız gerekir.
    Bölüm.symtab
    Bu bölüm, programın tüm sembollerinin (işlevlerinin) ve bunların tanımlandığı dosyaların bir açıklamasını içerir. Her biri 16 bayt uzunluğunda olan aşağıdaki öğelerden oluşur:
    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 - sembol adı (table.strtab dizesindeki dizin)
    • st_value - değer (bir işlev için giriş adresi veya bir dosya için 0). Cortex-M3, Thumb-2 komut setine sahip olduğundan bu adresin tek olması gerekir (gerçek adres + 1)
    • st_size - işlev kodu uzunluğu (dosya için 0)
    • st_info - sembol türü ve kapsamı. Bu alanın değerini belirleyen bir makro var
      #define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf))
      burada b kapsamdır ve t karakter türüdür
      Kapsam STB_LOCAL (sembol diğer nesne dosyalarından görünmez) veya STB_GLOBAL (görünür) olabilir. İşleri basitleştirmek için STB_GLOBAL kullanıyoruz.
      Sembol türü - işlev için STT_FUNC, dosya için STT_FILE
    • st_other - 0'a ayarla
    • st_shndx - sembolün tanımlandığı bölümün dizini (bölüm index.text) veya dosya için SHN_ABS.
      Scn tanımlayıcısındaki bölüm dizini elf_ndxscn kullanılarak belirlenebilir:
      size_t elf_ndxscn(Elf_Scn *scn);

    Bu bölüm her zamanki gibi oluşturulur, yalnızca sh_type'in SHT_SYMTAB olarak ayarlanması ve sh_link alanına index.strtab bölümünün yazılması gerekir, böylece bu bölümler bağlantılı hale gelir.
    Bölüm.strtab
    Bu bölüm .symtab bölümündeki tüm sembollerin adlarını içerir. Normal bir bölüm gibi oluşturulmuştur ancak sh_type, SHT_STRTAB olarak, sh_flags ise SHF_STRINGS olarak ayarlanmalıdır, böylece bu bölüm bir dize tablosu haline gelir.
    Bir bölüme ilişkin veriler, kaynak metinden bir diziye geçirilerek toplanabilir; işaretçi daha sonra bölüm veri tanımlayıcısına (d_buf) yazılır.
    Bölüm.shstrtab
    Bölüm, kendi başlığı da dahil olmak üzere dosyanın tüm bölümlerinin başlıklarını içeren bir dizeler tablosudur. .strtab bölümüyle aynı şekilde oluşturulur. Oluşturulduktan sonra indeksi dosya başlığının e_shstrndx alanına yazılmalıdır.
    Dize tabloları
    Dize tabloları boş baytla biten ardışık satırlar içerir; bu tablodaki ilk bayt da 0 olmalıdır. Tablodaki bir satırın dizini, tablonun başlangıcından itibaren bayt cinsinden uzaklıktır, dolayısıyla ilk satır "isim" dizin 1'e sahiptir, sonraki satır " var" ise dizin 6'ya sahiptir.
    Dizin 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
    Bir dosya yaz
    Yani başlıklar ve bölümler zaten oluşturuldu, şimdi bunların bir dosyaya yazılması ve iftira ile çalışmanın bitirilmesi gerekiyor. Kayıt elf_update işlevi tarafından gerçekleştirilir:
    off_t elf_update(Elf *elf, Elf_Cmd cmd);
    • elf - tutamak
    • cmd - komutun yazılabilmesi için ELF_C_WRITE değerine eşit olması gerekir.
    Fonksiyon hata durumunda -1 değerini döndürür. Hata metni, hata içeren satıra bir işaretçi döndürecek olan elf_errmsg(-1) işlevi çağrılarak elde edilebilir.
    Tanımlayıcımızı ilettiğimiz elf_end fonksiyonu ile kütüphane ile çalışmayı bitiriyoruz. Geriye kalan tek şey önceden açılan dosyayı kapatmaktır.
    Ancak oluşturulan dosyamız bir sonraki bölümde ekleyeceğimiz hata ayıklama bilgilerini içermiyor.

    CÜCE'nin yaratılışı

    Belgeleri içeren bir pdf dosyasıyla birlikte gelen kitaplığı kullanarak hata ayıklama bilgileri oluşturacağız (libdwarf2p.1.pdf - DWARF'e Yapımcı Kitaplığı Arayüzü).
    Hata ayıklama bilgilerinin oluşturulması aşağıdaki adımlardan oluşur:
    1. Düğüm oluşturma (DIE - Hata Ayıklama Bilgi Girişi)
    2. Düğüm Nitelikleri Oluşturma
    3. Veri Türleri Oluşturma
    4. Prosedürlerin (fonksiyonların) oluşturulması
    Aşamalara daha yakından bakalım
    Libdwarf yapımcısı başlatılıyor
    .symtab bölümündeki sembolleri oluştururken aynı zamanda derleme zamanında hata ayıklama bilgileri de oluşturacağız, bu nedenle kitaplık, libelf başlatıldıktan, ELF başlığı ve program başlığı oluşturulduktan sonra ve bölümler oluşturulmadan önce başlatılmalıdır.
    Başlatma için dwarf_producer_init_c fonksiyonunu kullanacağız. Kitaplığın, belgelerde açıklanan bazı nüanslarda farklılık gösteren birkaç başlatma işlevi daha (dwarf_producer_init, dwarf_producer_init_b) vardır. Prensip olarak bunlardan herhangi birini kullanabilirsiniz.

    Dwarf_P_Debug dwarf_producer_init_c(Dwarf_Unsigned flags, Dwarf_Callback_Func_c func, Dwarf_Handler hatası, Dwarf_Ptr hatası, void * user_data, Dwarf_Error *hata)

    • bayraklar - bazı parametreleri belirleyen birkaç sabitin "veya" kombinasyonu, örneğin bilgi bit derinliği, bayt sırası (küçük-endian, büyük-endian), yer değiştirme formatı, bunlara kesinlikle ihtiyacımız var DW_DLC_WRITE ve DW_DLC_SYMBOLIC_RELOCATIONS
    • func, hata ayıklama bilgileri içeren ELF bölümleri oluşturulurken çağrılacak bir geri çağırma işlevidir. Daha fazla ayrıntı için aşağıdaki "Hata ayıklama bilgilerini içeren bölümler oluşturma" bölümüne bakın.
    • errhand, hata oluştuğunda çağrılacak bir işlevin işaretçisidir. 0 gönderebilirsiniz
    • errarg - errhand işlevine aktarılacak veriler 0 olarak ayarlanabilir
    • user_data - func işlevine aktarılacak veriler 0'a ayarlanabilir
    • hata - hata kodunu döndürdü
    İşlev, sonraki tüm işlevlerde kullanılan bir tanımlayıcı olan Dwarf_P_Debug'u veya hata durumunda -1'i döndürür ve hata, hata kodunu içerir (hata iletisinin metnini, bu kodu ileterek dwarf_errmsg işlevini kullanarak kodundan alabilirsiniz) ona)
    Düğüm Oluşturma (DIE - Hata Ayıklama Bilgi Girişi)
    Yukarıda açıklandığı gibi hata ayıklama bilgileri bir ağaç yapısı oluşturur. Bu ağacın bir düğümünü oluşturmak için şunlara ihtiyacınız vardır:
    • dwarf_new_die işleviyle oluşturun
    • ona özellikler ekleyin (her özellik türü, aşağıda açıklanacak olan kendi işlevi tarafından eklenir)
    Düğüm dwarf_new_die işlevi kullanılarak oluşturulur:
    Dwarf_P_Die dwarf_new_die(Dwarf_P_Debug dbg, Dwarf_Tag new_tag, Dwarf_P_Die ebeveyni, Dwarf_P_Die alt çocuğu, Dwarf_P_Die left_sibling, Dwarf_P_Die right_sibling, Dwarf_Error *hata)
    • new_tag - düğüm etiketi (tür) - libdwarf.h dosyasında bulunabilen sabit DW_TAG_xxxx
    • ebeveyn, çocuk, sol_kardeş, sağ_kardeş - sırasıyla düğümün ebeveyni, çocuğu, sol ve sağ komşuları. Bu parametrelerin tamamının belirtilmesine gerek yoktur; bir tanesini belirleyip geri kalanların yerine 0 koymak yeterlidir. Eğer tüm parametreler 0 ise düğüm ya kök ya da izole olacaktır.
    • hata - oluştuğunda hata kodunu içerecektir
    İşlev, hata durumunda DW_DLV_BADADDR'yi veya başarı durumunda Dwarf_P_Die düğüm tanıtıcısını döndürür
    Düğüm Nitelikleri Oluşturma
    Düğüm nitelikleri oluşturmak için dwarf_add_AT_xxxx'in bütün bir işlev ailesi vardır. Bazen hangi fonksiyonun gerekli özniteliği yaratması gerektiğini belirlemek zordur, bu yüzden kütüphanenin kaynak kodunu birkaç kez araştırdım. İşlevlerden bazıları burada, bazıları ise aşağıda uygun bölümlerde açıklanacaktır. Hepsi bir sahip parametresini (özniteliğin ekleneceği düğüme yönelik bir tanıtıcı) kabul eder ve hata parametresinde bir hata kodu döndürür.
    dwarf_add_AT_name işlevi, bir düğüme bir "name" niteliği (DW_AT_name) ekler. Çoğu düğümün bir adı olmalıdır (örneğin prosedürler, değişkenler, sabitler), bazılarının ise bir adı olmayabilir (örneğin Derleme Birimi)
    Dwarf_P_Attribute dwarf_add_AT_name(Dwarf_P_Die sahibi, char *name, Dwarf_Error *hata)
    • ad - gerçek öznitelik değeri (düğüm adı)

    dwarf_add_AT_signed_const, dwarf_add_AT_unsigned_const işlevleri, belirtilen özniteliği ve onun imzalı (imzasız) değerini düğüme ekler. İmzalı ve imzasız nitelikler, sabit değerleri, boyutları, satır numaralarını vb. belirtmek için kullanılır. İşlev formatı:
    Dwarf_P_Attribute dwarf_add_AT_(un)signed_const(Dwarf_P_Debug dbg, Dwarf_P_Die sahibi, Dwarf_Half öznitelik, Dwarf_Signed değer, Dwarf_Error *hata)
    • dbg - Kitaplığın başlatılması sırasında elde edilen Dwarf_P_Debug tanımlayıcısı
    • attr - değeri ayarlanan özellik - libdwarf.h dosyasında bulunabilen DW_AT_xxxx sabiti
    • değer - özellik değeri
    Hata durumunda DW_DLV_BADADDR'yi veya başarı durumunda bir öznitelik tanıtıcısını döndürün.
    Derleme Birimi Oluşturma
    Herhangi bir ağacın bir kökü olmalıdır - bizim durumumuzda bu, program hakkında bilgiler içeren bir derleme birimidir (örneğin, ana dosyanın adı, kullanılan programlama dili, derleyicinin adı, sembollerin büyük/küçük harf duyarlılığı () değişkenler, işlevler), programın ana işlevi, başlangıç ​​adresi vb.). Prensip olarak hiçbir özelliğe gerek yoktur. Örneğin ana dosya ve derleyici hakkında bilgiler oluşturalım.
    Ana dosya bilgileri
    Ana dosya hakkındaki bilgileri depolamak için name niteliğini (DW_AT_name) kullanın, "Düğüm Nitelikleri Oluşturma" bölümünde gösterildiği gibi dwarf_add_AT_name işlevini kullanın.
    Derleyici bilgileri
    dwarf_add_AT_producer işlevini kullanıyoruz:
    Dwarf_P_Attribute dwarf_add_AT_name(Dwarf_P_Die sahibi, char *producer_string, Dwarf_Error *error)
    • yapımcı_string - bilgi metnini içeren dize
    Hata durumunda DW_DLV_BADADDR'yi veya başarı durumunda bir öznitelik tanıtıcısını döndürür.
    Ortak Bilgi Girişi Oluşturma
    Tipik olarak, bir işlev (altprogram) çağrıldığında, onun parametreleri ve dönüş adresi yığına aktarılır (ancak her derleyici bunu farklı şekilde yapabilir), tüm bunlara Çağrı Çerçevesi adı verilir. Hata ayıklayıcının, bir işlevden dönüş adresini doğru bir şekilde belirlemek ve bir geri izleme (bizi geçerli işleve ve bu işlevlerin parametrelerine yönlendiren bir işlev çağrıları zinciri) oluşturmak için çerçeve formatı hakkında bilgiye ihtiyacı vardır. Yığında depolanan işlemci kayıtlarının belirlenmesi de yaygındır. Yığın üzerinde yer ayıran ve işlemci kayıtlarını kaydeden koda işlev prologu, kayıtları ve yığını geri yükleyen koda ise sonsöz adı verilir.
    Bu bilgi büyük ölçüde derleyiciye bağlıdır. Örneğin önsöz ve sonsözün işlevin en başında ve sonunda olması gerekmez; bazen çerçeve kullanılır, bazen kullanılmaz; işlemci kayıtları diğer kayıtlarda vb. saklanabilir.
    Bu nedenle hata ayıklayıcının, işlemci kayıtlarının değerlerini nasıl değiştirdiğini ve prosedüre girerken bunların nereye kaydedileceğini bilmesi gerekir. Bu bilgiye Çağrı Çerçevesi Bilgisi adı verilir - çerçeve formatı hakkında bilgi. Programdaki her adres için (kod içeren), bellekteki çerçeve adresi (Kanonik Çerçeve Adresi - CFA) ve işlemci kayıtları hakkındaki bilgiler gösterilir; örneğin, şunları belirtebilirsiniz:
    • dava prosedürde saklanmaz
    • kayıt prosedürdeki değerini değiştirmez
    • kayıt yığında CFA+n adresinde saklanır
    • kayıt başka bir kayıtta saklanır
    • kayıt, oldukça açık olmayan bir şekilde hesaplanabilen bir adreste hafızada saklanır
    • vesaire.
    Kodda her adres için bilgilerin belirtilmesi gerektiğinden çok hacimlidir ve .debug_frame bölümünde sıkıştırılmış biçimde saklanır. Adresten adrese çok az değiştiği için sadece değişiklikleri DW_CFA_xxxx talimatları şeklinde kodlanır. Her talimat bir değişikliği belirtir; örneğin:
    • DW_CFA_set_loc - programdaki mevcut adresi işaret eder
    • DW_CFA_advance_loc - işaretçiyi belirli sayıda bayt ilerletir
    • DW_CFA_def_cfa - yığın çerçevesinin adresini gösterir (sayısal sabit)
    • DW_CFA_def_cfa_register - yığın çerçevesinin adresini belirtir (işlemci kaydından alınır)
    • DW_CFA_def_cfa_expression - yığın çerçeve adresinin nasıl hesaplanacağını belirtir
    • DW_CFA_same_value - kaydın değişmediğini gösterir
    • DW_CFA_register - kaydın başka bir kayıtta saklandığını belirtir
    • vesaire.
    .debug_frame bölümünün öğeleri iki türden olabilen girişlerdir: Ortak Bilgi Girişi (CIE) ve Çerçeve Açıklama Girişi (FDE). CIE, birçok FDE kaydında ortak olan bilgileri içerir; kabaca söylemek gerekirse, belirli bir prosedür türünü tanımlar. FDE'ler her özel prosedürü açıklar. Bir prosedüre girerken, hata ayıklayıcı önce CIE'den, ardından FDE'den gelen talimatları yürütür.
    Derleyicim, CFA'nın sp kaydında (r13) olduğu prosedürler oluşturur. Tüm prosedürler için bir CIE oluşturalım. Bunun için bir işlev var, 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_bytes, Dwarf_Unsigned init_bytes_len, Dwarf_Error *error);
    • artırıcı, UTF-8 kodlu bir dizedir ve varlığı, CIE veya FDE için platforma bağlı ek bilgilerin bulunduğunu gösterir. Boş bir satır koy
    • code_align - bayt cinsinden kod hizalaması (2 tane var)
    • data_align - çerçevedeki verilerin hizalanması (-4'e ayarla, bu, tüm parametrelerin yığında 4 bayt aldığı ve bellekte büyüdüğü anlamına gelir)
    • ret_addr_reg - prosedürden dönüş adresini içeren bir kayıt (14 tane var)
    • init_bytes - DW_CFA_xxxx talimatlarını içeren bir dizi. Ne yazık ki bu diziyi oluşturmanın uygun bir yolu yoktur. Bunu manuel olarak oluşturabilir veya C derleyicisi tarafından oluşturulan elf dosyasına bakabilirsiniz, ben de öyle yaptım. Benim durumumda 3 bayt içeriyor: 0x0C, 0x0D, 0, bu da DW_CFA_def_cfa: r13 ofs 0 anlamına gelir (CFA r13 kaydındadır, ofset 0'dır)
    • init_bytes_len - init_bytes dizisinin uzunluğu
    İşlev, hata durumunda DW_DLV_NOCOUNT değerini veya her prosedür için bir FDE oluştururken kullanılması gereken bir CIE tanıtıcısını döndürür; buna daha sonra "FDE prosedürü oluşturma" bölümünde bakacağız.
    Veri Türleri Oluşturma
    Prosedürler ve değişkenler oluşturabilmeniz için öncelikle veri türlerine karşılık gelen düğümler oluşturmanız gerekir. Pek çok veri türü vardır, ancak hepsi temel türlere dayanmaktadır (int, double vb. gibi temel türler), diğer türler ise temel türlerden oluşturulmuştur.
    Temel tür, DW_TAG_base_type etiketine sahip düğümdür. Aşağıdaki özelliklere sahip olması gerekir:
    • "ad" (DW_AT_name)
    • “kodlama” (DW_AT_encoding) - bu temel türü ne tür verilerin tanımladığı anlamına gelir (örneğin, DW_ATE_boolean - mantıksal, DW_ATE_float - kayan nokta, DW_ATE_signed - işaretli tam sayı, DW_ATE_unsigned - işaretsiz tam sayı, vb.)
    • “boyut” (DW_AT_byte_size - bayt cinsinden boyut veya DW_AT_bit_size - bit cinsinden boyut)
    Bir düğüm ayrıca başka isteğe bağlı nitelikler de içerebilir.
    Örneğin, 32 bit işaretli bir tamsayı taban türü "int" oluşturmak için, DW_TAG_base_type etiketine sahip bir düğüm oluşturmamız ve onun niteliklerini DW_AT_name - "int", DW_AT_encoding - DW_ATE_signed, DW_AT_byte_size - 4 olarak ayarlamamız gerekecek.
    Temel türler oluşturulduktan sonra onlardan türevler türetebilirsiniz. Bu tür düğümler, temel türlerine bir referans olan DW_AT_type niteliğini içermelidir. Örneğin, int işaretçisi - DW_TAG_pointer_type etiketine sahip bir düğüm, DW_AT_type özelliğinde önceden oluşturulan "int" türüne bir bağlantı içermelidir.
    Başka bir düğüme referansı olan bir nitelik, dwarf_add_AT_reference işlevi tarafından oluşturulur:
    Dwarf_P_Attribute dwarf_add_AT_reference(Dwarf_P_Debug dbg, Dwarf_P_Die sahibi, Dwarf_Half özniteliği, Dwarf_P_Die otherdie, Dwarf_Error *hata)
    • attr - özellik, bu durumda DW_AT_type
    • otherdie - başvurulan türün düğümünün tanıtıcısı
    Prosedür Oluşturma
    Prosedürler oluşturmak için bir tür hata ayıklama bilgisini daha açıklamam gerekiyor - Satır Numarası Bilgisi. Her makine talimatını belirli bir kaynak kodu satırıyla eşleştirmeye ve ayrıca programın satır satır hata ayıklamasına izin vermeye yarar. Bu bilgiler .debug_line bölümünde saklanır. Yeterli alanımız olsaydı, her talimat için aşağıdaki gibi sütunlara sahip bir satır olacak şekilde bir matris olarak depolanırdı:
    • kaynak dosya adı
    • bu dosyadaki satır numarası
    • dosyadaki sütun numarası
    • talimatın bir ifadenin başlangıcı mı yoksa bir ifade bloğu mu olduğu
    • vesaire.
    Böyle bir matris çok büyük olacaktır, dolayısıyla sıkıştırılması gerekir. İlk olarak, yinelenen satırlar silinir ve ikinci olarak, satırların kendisi değil, yalnızca içlerindeki değişiklikler kaydedilir. Bu değişiklikler sonlu durumlu bir makine için komutlara benziyor ve bilginin kendisi zaten bu makine tarafından "yürütülecek" bir program olarak kabul ediliyor. Bu programın komutları örneğin şuna benzer: DW_LNS_advance_pc - program sayacını belirli bir adrese ilerletir, DW_LNS_set_file - prosedürün tanımlandığı dosyayı ayarlar, DW_LNS_const_add_pc - program sayacını birkaç bayt ilerletir, vb.
    Bu bilgiyi bu kadar düşük bir seviyede oluşturmak zordur, dolayısıyla libdwarf bu görevi kolaylaştırmak için çeşitli işlevler sağlar.
    Her talimatın dosya adını saklamak pahalıdır, bu nedenle ad yerine dizini özel bir tabloda saklanır. Bir dosya dizini oluşturmak için dwarf_add_file_decl işlevini kullanmanız gerekir:
    Dwarf_Unsigned dwarf_add_file_decl(Dwarf_P_Debug dbg, char *name, Dwarf_Unsigned dir_idx, Dwarf_Unsigned time_mod, Dwarf_Unsigned uzunluk, Dwarf_Error *hata)
    • ad - dosya adı
    • dir_idx - dosyanın bulunduğu klasörün dizini. Dizin dwarf_add_directory_decl işlevi kullanılarak elde edilebilir. Tam yollar kullanılıyorsa, klasör dizini olarak 0'ı ayarlayabilir ve dwarf_add_directory_decl öğesini hiçbir şekilde kullanamazsınız.
    • time_mod - dosya değişiklik zamanı belirtilmeyebilir (0)
    • uzunluk - dosya boyutu, ayrıca isteğe bağlı (0)
    İşlev, hata durumunda dosya dizinini veya DW_DLV_NOCOUNT değerini döndürür.
    Satır numaraları hakkında bilgi oluşturmak için aşağıda bakacağımız dwarf_add_line_entry_b, dwarf_lne_set_address, dwarf_lne_end_sequence olmak üzere üç fonksiyon bulunmaktadır.
    Bir prosedür için hata ayıklama bilgilerinin oluşturulması birkaç aşamada gerçekleşir:
    • .symtab bölümünde prosedür sembolü oluşturma
    • niteliklere sahip bir prosedür düğümü oluşturma
    • FDE prosedürü oluşturma
    • prosedür parametreleri oluşturma
    • satır numarası bilgisi oluşturma
    Prosedür Sembolü Oluşturma
    Prosedür sembolü yukarıda Bölüm.symtab bölümünde anlatıldığı gibi oluşturulur. İçinde prosedürlerin sembolleri, bu prosedürlerin kaynak kodunun bulunduğu dosyaların sembolleri ile serpiştirilmiştir. Önce bir dosya sembolü, sonra bir prosedür oluşturuyoruz. Bu, dosyayı geçerli hale getirir ve bir sonraki prosedür geçerli dosyadaysa dosya sembolünün yeniden oluşturulmasına gerek kalmaz.
    Özniteliklerle Prosedür Düğümü Oluşturma
    İlk olarak, dwarf_new_die işlevini kullanarak ("Düğümler Oluşturma" bölümüne bakın), etiket olarak DW_TAG_alt programını ve ebeveyn olarak Derleme Birimi'ni (eğer bu global bir prosedürse) veya karşılık gelen DIE'yi (yerelse) belirterek bir düğüm oluştururuz. Daha sonra nitelikleri oluşturuyoruz:
    • prosedür adı (işlev dwarf_add_AT_name, bkz. “Düğüm nitelikleri oluşturma”)
    • dosyada prosedür kodunun başladığı satır numarası (öznitelik DW_AT_decl_line), işlev dwarf_add_AT_unsigned_const (bkz. “Düğüm nitelikleri oluşturma”)
    • prosedürün başlangıç ​​adresi (DW_AT_low_pc niteliği), dwarf_add_AT_targ_address işlevi, aşağıya bakın
    • prosedürün son adresi (DW_AT_high_pc özelliği), dwarf_add_AT_targ_address işlevi, aşağıya bakın
    • prosedür tarafından döndürülen sonucun türü (DW_AT_type özelliği önceden oluşturulmuş bir türe bağlantıdır, bkz. “Veri türlerinin oluşturulması”). Prosedür hiçbir şey döndürmezse bu özelliğin oluşturulmasına gerek yoktur.
    DW_AT_low_pc ve DW_AT_high_pc nitelikleri, bu amaç için özel olarak tasarlanmış dwarf_add_AT_targ_address_b işlevi kullanılarak oluşturulmalıdır:
    Dwarf_P_Attribute dwarf_add_AT_targ_address_b(Dwarf_P_Debug dbg, Dwarf_P_Die sahibi, Dwarf_Half özniteliği, Dwarf_Unsigned pc_value, Dwarf_Unsigned sym_index, Dwarf_Error *error)
    • öznitelik - nitelik (DW_AT_low_pc veya DW_AT_high_pc)
    • pc_value - adres değeri
    • sym_index - table.symtab'daki prosedür sembolünün dizini. İsteğe bağlı, 0 geçilebilir
    İşlev hata durumunda DW_DLV_BADADDR değerini döndürecektir.
    FDE prosedürü oluşturma
    Yukarıda "Ortak Bilgi Girişi Oluşturma" bölümünde tartışıldığı gibi, her prosedür için bir çerçeve tanımlayıcı oluşturmanız gerekir; bu, birkaç aşamada gerçekleşir:
    • yeni bir FDE oluşturma (bkz. Ortak Bilgi Girişi Oluşturma)
    • oluşturulan FDE'yi genel listeye ekleme
    • oluşturulan FDE'ye talimatlar ekleme
    dwarf_new_fde işlevini kullanarak yeni bir FDE oluşturabilirsiniz:
    Dwarf_P_Fde dwarf_new_fde(Dwarf_P_Debug dbg, Dwarf_Error *hata)
    İşlev, hata durumunda yeni FDE'ye veya DW_DLV_BADADDR'ye bir tanıtıcı döndürecektir.
    dwarf_add_frame_fde kullanarak listeye yeni bir FDE ekleyebilirsiniz:
    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_len, Dwarf_Unsigned sym_idx, Dwarf_Error* hatası)
    • fde - yeni alınan tanıtıcı
    • die - DIE prosedürleri (bkz. Niteliklerle prosedür düğümü oluşturma)
    • cie - CIE tanımlayıcısı (bkz. Ortak Bilgi Girişi Oluşturma)
    • virt_addr - prosedürümüzün başlangıç ​​adresi
    • code_len - bayt cinsinden prosedür uzunluğu
    İşlev hata durumunda DW_DLV_NOCOUNT değerini döndürür.
    Tüm bunlardan sonra FDE'mize DW_CFA_xxxx talimatlarını ekleyebilirsiniz. Bu, dwarf_add_fde_inst ve dwarf_fde_cfa_offset işlevleri tarafından yapılır. İlki verilen talimatı listeye ekler:
    Dwarf_P_Fde dwarf_add_fde_inst(Dwarf_P_Fde fde, Dwarf_Small op, Dwarf_Unsigned val1, Dwarf_Unsigned val2, Dwarf_Error *hata)
    • op - talimat kodu (DW_CFA_хххх)
    • val1, val2 - talimat parametreleri (her talimat için farklıdır, bkz. Standart, bölüm 6.4.2 Çağrı Çerçevesi Talimatları)
    dwarf_fde_cfa_offset işlevi DW_CFA_offset komutunu ekler:
    Dwarf_P_Fde dwarf_fde_cfa_offset(Dwarf_P_Fde fde, Dwarf_Unsigned reg, Dwarf_Signed offset, Dwarf_Error *hata)
    • fde - oluşturulan FDE'yi yönetir
    • reg - çerçeveye yazılan kayıt
    • ofset - çerçevedeki uzaklığı (bayt cinsinden değil, çerçeve öğelerinde, bkz. Ortak Bilgi Girişi Oluşturma, data_align)
    Örneğin derleyici, önsözünde lr (r14) kaydını yığın çerçevesinde saklayan bir prosedür oluşturur. İlk adım, ilk parametresi 1'e eşit olan DW_CFA_advance_loc talimatını eklemektir; bu, bilgisayar kaydının 2 bayt ilerletilmesi anlamına gelir (bkz. Ortak Bilgi Girişi Oluşturma, code_align), ardından parametre 4 ile DW_CFA_def_cfa_offset'i ekleyin (veri ofsetini 4 bayt kare) ve reg=14 offset=1 parametresiyle dwarf_fde_cfa_offset işlevini çağırın; bu, r14 kaydının CFA'dan -4 baytlık bir uzaklıkla çerçeveye yazılması anlamına gelir.
    Prosedür Parametreleri Oluşturma
    Prosedür parametreleri oluşturmak, sıradan değişkenler oluşturmaya benzer; bkz. "Değişkenler ve Sabitler Oluşturma"
    Satır numaraları hakkında bilgi oluşturma
    Bu bilgiler şu şekilde oluşturulur:
    • prosedürün başında dwarf_lne_set_address fonksiyonuyla bir talimat bloğu başlatıyoruz
    • her kod satırı (veya makine talimatı) için kaynak kodu hakkında bilgi yaratırız (dwarf_add_line_entry)
    • prosedürün sonunda talimat bloğunu dwarf_lne_end_sequence işleviyle tamamlıyoruz
    dwarf_lne_set_address işlevi, bir talimat bloğunun başlayacağı adresi ayarlar:
    Dwarf_Unsigned dwarf_lne_set_address(Dwarf_P_Debug dbg, Dwarf_Addr kapalı, Dwarf_Unsigned symidx, Dwarf_Error *hata)
    • offs - prosedür adresi (ilk makine talimatının adresi)
    • sym_idx - sembol dizini (isteğe bağlı, 0 belirtebilirsiniz)

    dwarf_add_line_entry_b işlevi, .debug_line bölümüne kaynak kodu satırları hakkında bilgi ekler. Her makine talimatı için bu işlevi çağırıyorum:
    Dwarf_Unsigned dwarf_add_line_entry_b(Dwarf_P_Debug dbg, Dwarf_Unsigned file_index, Dwarf_Addr code_offset, Dwarf_Unsigned lineno, Dwarf_Signed sütun_number, Dwarf_Bool is_source_stmt_begin, Dwarf_Bool is_basic_block_begin, Dwarf_Bool is_epilogue _begin, Dwarf_Bool is_prologue_end, Dwarf_Unsigned isa, Dwarf_Unsigned ayırıcı, Dwarf_Error *hata)
    • file_index - dwarf_add_file_decl işlevi tarafından daha önce elde edilen kaynak kod dosyasının dizini (bkz. "Prosedürler oluşturma")
    • code_offset - geçerli makine komutunun adresi
    • lineno - kaynak kod dosyasındaki satır numarası
    • sütun_numarası - kaynak kod dosyasındaki sütun numarası
    • is_source_stmt_begin - 1 eğer geçerli talimat lineno satırındaki kodda ilk ise (her zaman 1 kullanırım)
    • is_basic_block_begin - 1 eğer geçerli talimat ifade bloğundaki ilk ise (her zaman 0 kullanırım)
    • is_epilogue_begin - 1 eğer mevcut talimat prosedür sonsözünde ilk ise (gerekli değil, her zaman 0'ım var)
    • is_prologue_end - 1 eğer mevcut talimat prosedürün önsözündeki son talimat ise (gerekli!)
    • isa - talimat seti mimarisi. ARM Cortex M3 için DW_ISA_ARM_thumb belirttiğinizden emin olun!
    • ayrımcı. Kaynak kodunun bir konumu (dosya, satır, sütun) farklı makine talimatlarına karşılık gelebilir. Bu durumda, bu tür talimat setleri için farklı ayırıcıların kurulması gerekir. Böyle bir durum yoksa 0 olmalıdır
    İşlev 0 (başarılı) veya DW_DLV_NOCOUNT (hata) değerini döndürür.
    Son olarak dwarf_lne_end_sequence işlevi prosedürü tamamlar:
    Dwarf_Unsigned dwarf_lne_end_sequence(Dwarf_P_Debug dbg, Dwarf_Addr adresi; Dwarf_Error *hata)
    • adres - mevcut makine talimatının adresi
    0 (başarılı) veya DW_DLV_NOCOUNT (hata) değerini döndürür.
    Bu, prosedürün oluşturulmasını tamamlar.
    Değişkenler ve Sabitler Oluşturma
    Genel olarak değişkenler oldukça basittir. Bir adı, verilerinin bulunduğu bir bellek konumu (veya işlemci kaydı) ve bu verinin türü vardır. Değişken genelse, üst öğesi Derleme Birimi olmalıdır; yerelse karşılık gelen düğüm olmalıdır (bu özellikle prosedür parametreleri için geçerlidir; bunların üst öğeleri prosedürün kendisi olmalıdır). Ayrıca değişken bildiriminin hangi dosya, satır ve sütunda olduğunu da belirleyebilirsiniz.
    En basit durumda, bir değişkenin değeri sabit bir adreste bulunur, ancak birçok değişken yığına veya kayıt defterine bir prosedür girilirken dinamik olarak oluşturulur, bazen değerin adresinin hesaplanması oldukça önemsiz olabilir. Standart, bir değişkenin değerinin nerede bulunduğunu (konum ifadeleri) açıklayan bir mekanizma sağlar. Bir adres ifadesi, Fort benzeri bir yığın makinesi için bir dizi talimattır (DW_OP_xxxx sabitleri); aslında, dalları, prosedürleri ve aritmetik işlemlerini içeren ayrı bir dildir. Bu dili bütünüyle incelemeyeceğiz; aslında sadece birkaç talimatla ilgileneceğiz:
    • DW_OP_addr - değişkenin adresini gösterir
    • DW_OP_fbreg - değişkenin temel yazmaçtan uzaklığını gösterir (genellikle yığın işaretçisi)
    • DW_OP_reg0… DW_OP_reg31 - değişkenin ilgili kayıt defterinde saklandığını gösterir
    Bir adres ifadesi oluşturmak için öncelikle boş bir ifade oluşturmanız (dwarf_new_expr), buna talimatlar eklemeniz (dwarf_add_expr_addr, dwarf_add_expr_gen, vb.) ve bunu DW_AT_location özelliğinin (dwarf_add_AT_location_expression) değeri olarak düğüme eklemeniz gerekir.
    Boş bir adres ifadesi oluşturma işlevi, hata durumunda tanıtıcısını veya 0'ı döndürür:
    Dwarf_Expr dwarf_new_expr(Dwarf_P_Debug dbg, Dwarf_Error *hata)
    Bir ifadeye talimat eklemek için dwarf_add_expr_gen işlevini kullanmanız gerekir:
    Dwarf_Unsigned dwarf_add_expr_gen(Dwarf_P_Expr ifade, Dwarf_Small işlem kodu, Dwarf_Unsigned val1, Dwarf_Unsigned val2, Dwarf_Error *hata)
    • işlem kodu - işlem kodu, sabit DW_OP_хххх
    • val1, val2 - talimat parametreleri (bkz. Standart)

    Bir değişkenin adresini açıkça ayarlamak için öncekinin yerine dwarf_add_expr_addr işlevi kullanılmalıdır:
    Dwarf_Unsigned dwarf_add_expr_addr(Dwarf_P_Expr ifade, Dwarf_Unsigned adres, Dwarf_Signed sym_index, Dwarf_Error *hata)
    • expr, talimatın eklendiği adres ifadesinin tanıtıcısıdır
    • adres - değişken adres
    • sym_index - table.symtab'daki sembolün dizini. İsteğe bağlı, 0 geçilebilir
    İşlev ayrıca hata durumunda DW_DLV_NOCOUNT değerini döndürür.
    Son olarak dwarf_add_AT_location_expr işlevini kullanarak oluşturulan adres ifadesini düğüme ekleyebilirsiniz:
    Dwarf_P_Attribute dwarf_add_AT_location_expr(Dwarf_P_Debug dbg, Dwarf_P_Die sahibi, Dwarf_Half öznitelik, Dwarf_P_Expr loc_expr, Dwarf_Error *hata)
    • sahibi - ifadenin eklendiği düğüm
    • attr - özellik (bizim durumumuzda DW_AT_location)
    • loc_expr - önceden oluşturulmuş bir adres ifadesini yönetir
    İşlev, hata durumunda öznitelik tanıtıcısını veya DW_DLV_NOCOUNT değerini döndürür.
    Değişkenler (aynı zamanda prosedür parametreleri) ve sabitler, sırasıyla DW_TAG_variable, DW_TAG_formal_parameter ve DW_TAG_const_type etiketlerine sahip sıradan düğümlerdir. Aşağıdaki nitelikleri gerektirirler:
    • değişken/sabit adı (işlev dwarf_add_AT_name, bkz. “Düğüm nitelikleri oluşturma”)
    • değişkenin bildirildiği dosyadaki satır numarası (öznitelik DW_AT_decl_line), işlev dwarf_add_AT_unsigned_const (bkz. “Düğüm nitelikleri oluşturma”)
    • dosya adı dizini (DW_AT_decl_file niteliği), dwarf_add_AT_unsigned_const işlevi (bkz. “Düğüm nitelikleri oluşturma”)
    • değişken/sabit veri türü (DW_AT_type özelliği önceden oluşturulmuş bir türe bağlantıdır, bkz. “Veri türleri oluşturma”)
    • adres ifadesi (yukarıya bakın) - bir değişken veya prosedür parametresi için gereklidir
    • veya değer - bir sabit için (DW_AT_const_value niteliği, bkz. “Düğüm nitelikleri oluşturma”)
    Hata ayıklama bilgilerini içeren bölümler oluşturma
    Hata ayıklama bilgi ağacının tüm düğümlerini oluşturduktan sonra onunla elf bölümleri oluşturmaya başlayabilirsiniz. Bu iki aşamada gerçekleşir:
    • öncelikle her bölüm için bir kez gerekli elf bölümlerini oluşturmak için yazdığımız fonksiyonu çağıracak dwarf_transform_to_disk_form fonksiyonunu çağırmamız gerekiyor
    • her bölüm için dwarf_get_section_bytes işlevi bize ilgili bölüme yazılması gereken verileri döndürecektir.
    İşlev
    dwarf_transform_to_disk_form (Dwarf_P_Debug dbg, Dwarf_Error* hatası)
    oluşturduğumuz hata ayıklama bilgilerini ikili formata dönüştürür ancak diske hiçbir şey yazmaz. Hata durumunda oluşturulan elf bölümlerinin sayısını veya DW_DLV_NOCOUNT değerini döndürür. Bu durumda her bölüm için kütüphaneyi başlatırken dwarf_producer_init_c fonksiyonuna ilettiğimiz geri çağırma fonksiyonu çağrılacaktır. Bu fonksiyonu kendimiz yazmalıyız. Spesifikasyonu aşağıdaki gibidir:
    typedef int (*Dwarf_Callback_Func_c)(char* adı, int boyutu, Dwarf_Unsigned türü, Dwarf_Unsigned bayrakları, Dwarf_Unsigned bağlantısı, Dwarf_Unsigned bilgisi, Dwarf_Unsigned* sect_name_index, void * user_data, int* hatası)
    • name - oluşturulacak elf bölümünün adı
    • boyut - bölüm boyutu
    • tür - bölüm türü
    • bayraklar - bölüm bayrakları
    • bağlantı - bölüm bağlantı alanı
    • bilgi - bölüm bilgi alanı
    • sect_name_index - yer değiştirmelerle bölümün dizinini döndürmeniz gerekir (isteğe bağlı)
    • user_data - kütüphane başlatma fonksiyonunda ayarladığımız şekilde bize iletilir
    • hata - burada hata kodunu gönderebilirsiniz
    Bu fonksiyonda şunları yapmalıyız:
    • yeni bir bölüm oluşturun (elf_newscn işlevi, bkz. Bölüm Oluşturma)
    • bir bölüm başlığı oluşturun (işlev elf32_getshdr, age.)
    • doğru şekilde doldurun (bkz. aynı eser). Bu kolaydır çünkü bölüm başlığı alanları fonksiyonumuzun parametrelerine karşılık gelir. Eksik sh_addr, sh_offset, sh_entsize alanlarını 0'a ve sh_addralign'ı 1'e ayarlayın
    • oluşturulan bölümün dizinini döndürün (elf_ndxscn işlevi, bkz. “Section.symtab”) veya hata durumunda -1'i döndürün (hata kodunu hata olarak ayarlayarak)
    • Ayrıca ".rel" bölümünü de atlamalıyız (bizim durumumuzda), fonksiyondan dönerken 0 değerini döndürmeliyiz
    Tamamlandığında dwarf_transform_to_disk_form işlevi oluşturulan bölümlerin sayısını döndürecektir. Aşağıdaki adımları izleyerek her bölümü 0'dan başlayarak bir döngü halinde geçmemiz gerekecek:
    • dwarf_get_section_bytes işlevini kullanarak bir bölüme yazılacak verileri oluşturun:
      Dwarf_Ptr dwarf_get_section_bytes(Dwarf_P_Debug dbg, Dwarf_Signed dwarf_section, Dwarf_Signed *elf_section_index, Dwarf_Unsigned *uzunluk, Dwarf_Error* hatası)
      • dwarf_section - bölüm numarası. 0..n aralığında olmalıdır; burada n, dwarf_transform_to_disk_form işlevi tarafından bize döndürülen sayıdır
      • elf_section_index - verinin yazılması gereken bölümün indeksini döndürür
      • uzunluk - bu verinin uzunluğu
      • hata - kullanılmadı
      İşlev, alınan verilere veya 0'a bir işaretçi döndürür (durumda)
      oluşturulacak başka bölüm kalmadığında)
    • geçerli bölüm için bir veri tanımlayıcı oluşturun (elf_newdata işlevi, bkz. Bölüm Oluşturma) ve aşağıdakileri ayarlayarak doldurun (oraya bakın):
      • d_buf - önceki fonksiyondan aldığımız verilere işaret eden bir işaretçi
      • d_size - bu verinin boyutu (ibid.)
    Kütüphaneyle çalışmayı bitirme
    Bölümler oluşturulduktan sonra dwarf_producer_finish işlevini kullanarak libdwarf ile çalışmayı tamamlayabilirsiniz:
    Dwarf_Unsigned dwarf_producer_finish(Dwarf_P_Debug dbg, Dwarf_Error* hatası)
    İşlev hata durumunda DW_DLV_NOCOUNT değerini döndürür.
    Lütfen bu aşamada diske kayıt yapılmadığını unutmayın. Kayıt, “ELF Oluşturma - Dosya Yazma” bölümündeki işlevler kullanılarak yapılmalıdır.

    Çözüm

    Bu kadar.
    Tekrar ediyorum, hata ayıklama bilgilerinin oluşturulması çok geniş bir konudur ve çok fazla konuya değinmedim, sadece perdeyi kaldırdım. Dileyenler süresiz olarak daha derine inebilirler.
    Sorularınız varsa cevaplamaya çalışacağım.

    Bilgisayarınıza yüklediyseniz antivirüs programı Olabilmek bilgisayarınızdaki tüm dosyaları ve her dosyayı ayrı ayrı tarayın. Herhangi bir dosyayı, dosyaya sağ tıklayarak ve dosyayı virüslere karşı taramak için uygun seçeneği seçerek tarayabilirsiniz.

    Örneğin, bu şekilde vurgulanmıştır dosyam.elf dosyası, daha sonra bu dosyaya sağ tıklayıp dosya menüsündeki seçeneği seçmeniz gerekir. "AVG ile tara". Bu seçeneği seçtiğinizde AVG Antivirus dosyayı açacak ve virüslere karşı tarayacaktır.


    Bazen bunun sonucunda bir hata meydana gelebilir hatalı yazılım kurulumu Kurulum işlemi sırasında karşılaşılan bir sorundan kaynaklanıyor olabilir. Bu, işletim sisteminize müdahale edebilir ELF dosyanızı doğru uygulama yazılımına bağlayın sözde etkileyen "dosya uzantısı ilişkileri".

    Bazen basit Dolphin'i (emülatör) yeniden yükleme ELF'i Dolphin'e (emülatör) doğru şekilde bağlayarak sorununuzu çözebilir. Diğer durumlarda, dosya ilişkilendirmeleriyle ilgili sorunlar şunlardan kaynaklanabilir: kötü yazılım programlama geliştirici ve daha fazla yardım için geliştiriciyle iletişime geçmeniz gerekebilir.


    Tavsiye: En son yamalara ve güncellemelere sahip olduğunuzdan emin olmak için Dolphin'i (emülatör) en son sürüme güncellemeyi deneyin.


    Bu çok açık görünebilir, ancak çoğu zaman ELF dosyasının kendisi soruna neden oluyor olabilir. Bir dosyayı e-posta eki yoluyla aldıysanız veya bir web sitesinden indirdiyseniz ve indirme işlemi kesintiye uğradıysa (elektrik kesintisi veya başka bir nedenden dolayı), dosya zarar görebilir. Mümkünse ELF dosyasının yeni bir kopyasını almayı ve tekrar açmayı deneyin.


    Dikkatlice: Hasar görmüş bir dosya, bilgisayarınızdaki önceki veya mevcut kötü amaçlı yazılımlara ikincil zarar verebilir; bu nedenle, bilgisayarınızı güncel bir antivirüs programıyla güncel tutmanız önemlidir.


    Dosyanız ELF ise Bilgisayarınızdaki donanımla ilgili ihtiyacınız olabilecek dosyayı açmak için aygıt sürücülerini güncelle Bu ekipmanla ilgili.

    Bu sorun genellikle medya dosyası türleriyle ilişkilendirilir, bilgisayarın içindeki donanımın başarıyla açılmasına bağlıdır; ses kartı veya video kartı. Örneğin, bir ses dosyasını açmaya çalışıyorsanız ancak açamıyorsanız, ses kartı sürücülerini güncelle.


    Tavsiye: Bir ELF dosyasını açmaya çalıştığınızda aşağıdaki mesajı alırsanız .SYS dosyası hata mesajı sorun muhtemelen şu olabilir bozuk veya güncel olmayan aygıt sürücüleriyle ilişkili güncellenmesi gerekenler. Bu işlem, aşağıdaki gibi sürücü güncelleme yazılımı kullanılarak kolaylaştırılabilir: DriverDoc.


    Adımlar sorunu çözmezse ve hala ELF dosyalarını açarken sorun yaşıyorsunuz; bunun nedeni şunlar olabilir: kullanılabilir sistem kaynaklarının eksikliği. ELF dosyalarının bazı sürümlerinin bilgisayarınızda düzgün şekilde açılması için önemli miktarda kaynak (örneğin bellek/RAM, işlem gücü) gerekebilir. Oldukça eski bir bilgisayar donanımı ve aynı zamanda çok daha yeni bir işletim sistemi kullanıyorsanız bu sorun oldukça yaygındır.

    Bu sorun, işletim sistemi (ve arka planda çalışan diğer hizmetler) nedeniyle bilgisayar bir görevi yerine getirmekte sorun yaşadığında ortaya çıkabilir. bir ELF dosyasını açmak için çok fazla kaynak tüketmek. Nintendo Wii Oyun Dosyasını açmadan önce bilgisayarınızdaki tüm uygulamaları kapatmayı deneyin. Bilgisayarınızdaki mevcut tüm kaynakları serbest bırakarak, ELF dosyanızı açmayı denemek için en iyi konumda olacaksınız.


    Eğer sen yukarıda açıklanan tüm adımları tamamladınız ve ELF dosyanız hala açılmıyor, çalıştırmanız gerekebilir ekipman güncellemesi. Çoğu durumda, donanımın eski sürümlerini kullanırken bile, işlem gücü çoğu kullanıcı uygulaması için fazlasıyla yeterli olabilir (3D oluşturma, finansal/bilimsel modelleme veya yoğun multimedya çalışması). Böylece, büyük olasılıkla bilgisayarınızda yeterli bellek yoktur(genellikle "RAM" veya rastgele erişim belleği olarak adlandırılır) bir dosyayı açma görevini gerçekleştirmek için kullanılır.