Poznamky k debugovacimu formatu DWARF3 (draft7) =============================================== 1) Intro - DWARF3 - format informaci generovanych prekladaci, linkery a asemblery, ktery je pouzivan pro debugovani na urovni zdrojaku. Nezavisly na prekladaci/debuggeru - jde o vytvoreni metody sdeleni "obrazu" zdrojaku debuggeru. - designovan pro potreby symbolickeho debugovani na urovni zdrojaku ruznych jazyku (nezavisle, unifikovane) pomoci debugovaich informaci nezavislych na jazyku. Language-specific veci (virtualni C++ funkce) se resi zavedenim specialnich atributu, ktere pouzivaji pouze dane jazyky. - Dve casti - informacni obsah samotnych debugovacich zaznamu (sekce 2 - 6 v draftu) sekce 2 - celkova struktura informaci a atributu, ktera je spolecna vetsine nebo vsem debugging info entries sekce 3,4,5 - popisuji specificke debugging info entries a ukazuji zpusob jak sdeluji debuggeru nezbytne informace o zdrojovem programu sekce 6 - popisuje debugovaci informace neobsazene v debugging info entries - ulozeni (zakodovani) techto informaci v object fajlu (sekce 7) - Vendor extensibility - DWARF3 se nesnazi pokryvat vsechny jazyky ani vsechny aspekty ktere muzou byt pri debugovani potreba - nechava vendorum moznost definovat sve vlastni debugiing info tagy, atributy, ... tim ze cast "namespace" rezervuje pro tyto ucely a garantuje, ze nikdy v budoucnu nebudou pouzity 2) Obecny popis, definice, ... - Debugging info entry (DIE): DWARF pouziva sadu DIE k definovani low-level representace zdrojaku. Kazda DIE je popsana identifikacnim TAGem a sklada se ze serie atributu. TAG specifikuje tridu do ktere entry patri a atributy definuji charakteristicke vlastnosti DIE. (DIE od DWARF3 by se mely vyskytovat v sekci .debug_info object fajlu) - TAGy (s prefixem DW_TAG_): access_declaration, array_type, base_type, catch_block, class_type, common_block, compile_unit, const_type, constant, entry_point, enumaration_type, enumerator, file_type, formal_parameter, friend, imported_declaration, imported_module, imported_unit, inheritance, inlined_subroutine, interface_type, label, lexical_block, member, module, mutable_type, namelist, namelist_item, namespace, packed_type, partial_unit, pointer_type, ptr_to_member_type, reference_type, restrict_type, set_type, string_type, structure_type, subprogram, subrange_type, subroutine_type, template_type_parameter, template_value_parameter, thrown_type, try_block, typedef, union_type, unspecified_parameters, unspecified_type, variable, variant, variant_part, volatile_type, with_stmt - Atributy: kazdy atribut je charakterizovan nazvem atributu, v zadne DIE se nesmi vyskytovat vice nez jeden atribut stejneho jmena. Na poradi atributu v ramci DIE nezalezi. Definovana jmena atributu a jejich "related use"(?) je definovano na obrazku "2" - strany 16 - 21 \ povolene hodnoty atributu nalezi do jedne nebo vice trid attribute value forms. Kazda form class muze byt reprezentovana jednim nebo vice zpusoby. Napriklad hodnoty nekterych atributu se skladaji z jednoho "kusu" konstantnich dat. "Constant data" je trida hodnoty atributu, kterou dany atribut muze mit (existuje vice reprezentaci "constant data": 1,2,4,8 bajtu a promenna velikost). Konkretni reprezentace dane instance atributu je zakodovana spolecne s jmenem atributu. \ atribute value forms mohou nalezet do nasledujicich trid: address (odkazuje na adresu v adres space programu), block (neomezeny pocet neinterpretovanych dat), constant (1,2,4,8 bajtu nebo neomezena data v LEB128 formatu (viz dale, sekce 7.6)), flag (presence/absence atributu), lineptr (odkazuje na misto v sekci DWARFu ktera udrzuje informace o cislech radku), loclistptr (odkazuje na misto v sekci DWARFu ktera udrzuje location listy, ktere popisuji objekty jejichz umisteni se muze behem jejich zivota menit), macptr (odkazuje na umisteni v sekci DWARFu, ktera udrzuje informace o definovanych makrech), rangelistptr (odkazuje na umisteni v sekci DWARFu, ktera udrzuje (nespojite) adresni rozsahy), reference (odkazuje na jednu z DIE ktera popisuje program. Teto reference existuji dva typy - 1) offset relativni k zacatku kompilacni jednotky (objectu) ve ktere se reference vyskytuje a musi odkazovat na entry v teto jednotce 2) offset DIE v libovolne kompilacni jednotce krome te ve ktere se reference vyskytuje (huh?) string (null-terminated sting bajtu. stringy mohou byt reprezentovany primo v DIE jako offset do separatni tabulky stringu). - Relationships of DIE: k ruznym ucelum lze pouzivat to, ze jedne DIE je dovoleno "vlastnit" jine DIE. (lze tak popsat statickou blokovou strukturu v souboru, ukazat cleny struktury/tridy (unie) a asociovat deklarace se zdrojovymi soubory ci zdrojove soubory se sdilenymi objekty). Je to prirozene, protoze debugging information jsou reprezentovany jako "strom" ("relace", "vlastnicti" je samo o sobe definovano jako strom, existuji mezi entitami i jine relace - napriklad z DIE representujici promennou do jine DIE reprezentujici jeji typ -> ve skutecnosti to s temito relacemi neni strom, ale graf). Strom je "chapan" prefixove - u kazde DIE je definovano jestli ma nebo nema potomky (viz sekce 7.5.3) - u tech co nemaji je to co nasleduje sourozenec, jinak je to prvni potomek. Dalsi potomci jsou definovani jako sourozenci prvniho. Pro urychleni "vodorovneho" prohledavani muze DIE obsahovat atribut DW_AT_sibling, ktery ukazuje na sourozence (preskakuje potomky). - DWARF expressions: popisuji vypocet hodnoty nebo jmena v prubehu debugovani programu. Jsou vyjadrene v terminech DWARFovskych operaci ktere operuji nad zasobnikem hodnot. Vsechny DWARFovske operace jsou kodovany jako proud opcodes [s operandy]. Krome nasledujicih obecnych operaci jeste existuji operace s nazvy registru, (ktere jsou specificke pro "location expressions"), viz dale (2.5.1) \ Obecne operace: kazda obecna operace reprezentuje postfixovou operaci na zasobnikovem stroji. Kazda polozka na zasobniku je "velikost" (?) adresy na target stroji. Hodnota na vrcholu zasobniku po skonceni vypoctu DWARF expression je vysledek (adresa objektu, hodnota hranice pole, delka dynamickeho retezce, ...) Vsechny nasledujici operaci pushuji hodnotu na DWARF zasobnik \\ DW_OP_lit0, ... DW_OP_lit31 - koduji neznamenkovou literalni hodnotu 0-31 \\ DW_OP_addr - tato operace ma jeden operand ktery koduje adresu stroje a jejiz velikost je rovna velikosti adresy na target machine (?) \\ DW_OP_constXY - poskytuje konstanty (ruzne varianty co do poctu bytu a znamenkovosti/beznamenkovosti 1u, 1s, 8u, 8s, ...) \ Adresovani pomoci registru: operace ukladaji na zasobnik hodnotu ktera je vysledkem secteni obsahu registru s danym offsetem \\ DW_OP_fbreg - tato operace posktyuje LEB128 offset od adresy specifikovane location description v atributu DW_AT_frame_base aktualni funkce (typicky esp) \\ DW_OP_breg0, ... DW_OP_breg31 - poskytuji LEB128 offset od zadaneho registru \\ DW_OP_bregx - tato operace ma dva operandy: registr definovany pomoci LEB128 cisla a LEB128 offset (?jak se lisi od predchoziho?) \ Operace se stackem: manipuluje se zasobnikem DWARFu. Operace ktere indexuji zasobnik predpokladaji, ze vrch zasobniku ma index 0. Operace se stackem jsou celkem ocekavatelne (je jich dost, ne jen push/pop), popsany v sekci 2.4.1.3 Nahodny vyber zajimavejsich: \\ DW_OP_deref - pop, dereferencovat jako adresu, to co je na adrese pushnout. Existuji rozsirenejsi verze teto operace (vice bajtu, umi skladat adresu ze dvou hodnot (segment offset), atd. \\ DW_OP_push_object_address - ulozi na zasobnik adresu objektu ktery se zrovna vyhodnocuje. Tento objekt muze korespondovat s nezavislou promennou popsanou vlastnim DIE, nebo to muze byt polozka pole, struktury nebo tridy jejiz adresa byla drive dynamicky urcena. \ Aritmeticke a logicke operace - jasne \ Control flow operace: \\ DW_OP_{le,ge,eq,lt,gt,ne} (porovnani jsou signed) \\ DW_OP_skip - nepodmineny skok - operand je 2byte integer - na kolikatou expression od aktualni skocit \\ DW_OP_bra - podmineny skok podle topu stacku \\ DW_OP_call - podle offsetu. \\ DW_OP_call_ref - operand je offset do DIE v .debug_info ktera muze byt v jinem executable nez je operator. Relokaci resi consumer. \ Specialni operace: \\ DW_OP_piece - LED128 number je operand. To cislo urcuje velikost (bytes) casti objektu referencovaneho DWARF expression jejiz vysledek je na zasobniku (udajne pri kvuli kompilatorum, ktere jednu promennou ukladaji ve vice registrech nebo zcasti v pameti a z casti v registrech a tato operace umoznuje popsat na jak velkou cast promenne se odkazujeme .. ??) \\ DW_OP_nop - Location descriptors (poskytovat consumerovi moznost najit umisteni promenne, zjistit hranice dynamickych poli a retezcu a najit base adresu stack frame. Take musi umet (optimalizace, nove architektury ?) najit objetk jehoz umisteni se behem zivota muze menit. Informace o umisteni poskytuji Location descriptory. Muzou jich existovat dva druhy: - Location expressions - reprezentace nezavisla na jazyku, pouziva DWARF expressions (viz vyse) - "navod" pro consumera (debugger) k vypocteni umisteni. Postacuji pokud se objekt behem zivota nehybe a je bud staticky nebo ma zivotnost stejnou jako jeho nadrizeny blok. - Locations lists - pro ostatni. - Register name operators: \ pomoci DW_OP_reg0, DW_OP_reg31 je mozne pojmenovat registr. Existuje i DW_OP_regx, ktery ma jako operand LEB128 integer. Tyto operace mohou byt pouzivane pouze v Location Expressions (viz dale) - Location expression se sklada z zadne nebo vice DWARF expressions (viz vyse). Deli se na \\ Register names - vyskytuji se vzdy samostatne a indikuji ze dany objekt se vyskytuje v danem registru. \\ Addressing operations jsou pravidla pro vypocet adresy objektu v pameti. #include examply na strane 34 - Locations Lists se pouzivaji misto location expressions v pripade, ze objekt jehoz umisteni je popisovano muze behem sveho zivota umisteni menit. Listy jsou udrzovany v separatni sekci .debug_loc a pozna se podle location atribute, jehoz hodnota je reprezentovana jako ofset od zacatku .debug_loc k prvnimu bajtu listu pro objekt, o ktery jde. Kazdy zaznam v location listu je bud location list entry(LLE), base address selection entry nebo konec (ma v polozkach nuly). LLE se sklada z \ Pocatecni adresy - relativni vuci applicable base addr jednotky ktera tento list referencuje. Oznacuje zacatek adr. rozsahu, kde je tato location platna. \ Koncovou adresu - taktez relativni viz vyse. Oznacuje prvni adresu na ktere location neni platna. \ Location expression, ktera popisuje location objektu v dobe platnosti kde applicable base addr je urcena nejblizsim predchozi base addr selection entry (BAS, viz dale) a pokud takova neexistuje, pak applicable base addr je base address kompilacni jednotky (viz 3.1) Base Addr Selection Entry (BASE) se sklada z: \ Hodnoty nejvyssi reprezentovatelne adresy (32bit, 64bit) \ Adresy, ktera definuje vhodnout base address ktera se ma pouzit pri interpretaci pocatecni a koncove relativni adresy (viz vyse, LLE) po sobe jdoucich zaznamu v location listu. (pozn: BASE ovlivnuje pouze list ve kterem je obsazena) - Types and declarations: intuitivni - jakakoliv DIE popisujici deklaraci majici typ ma atribut DW_AT_type, jehoz hodnota je reference na jine DIE. Toto referencovane DIE muze popisovat zakladni typ nebo user-defined typ. Pripadne muze referencovana DIE take popisovat modifikator typu: constant, packed, pointer, reference, volatile. Vice o DIE popisujicich base a user-defined typy v sekci 5. - Accesibility of decls: Kvuli C++, Ada a dalsim. Jde o atribut DW_AT_accesibility, ktery muze nabyvat hodnot DW_ACCESS_public, DW_ACCESS_private a DW_ACCESS_protected. Zrejme. - Visibility of decls: Atribut oznacujici jestli je dana declarace videt i mimo entitu ve ktere jsou deklarovane. Atribut DW_AT_visibility nabyvajici hodnot DW_VIS_{local,exported,qualified} - Virtuality of Declarators: DW_AT_virtuality = {DW_VIRTUALITY_{none,[pure|]virutal}} - Artifical Entries: Kompilator muze chtit generovat debugovaci informace i pro objekty ktere nebyly deklarovany ve zdrojaku (napriklad formalni parametry, this v C++). atribut DW_AT_artifival (flag) - Target specific addressing: Umi (nepovinne) segmentaci pomoci DW_AT_segment atributu (ktery muze zdedit od rodice, nema-li vlastni) Jelikoz nektere architektury podporuji vice ruznych trid adres (vliv na dereferencovani pointeru a volani funkci) muze kazda DIE reprezentujici pointer nebo referenci nebo funkci (nebo jeji typ) mit atribut DW_AT_address_class, ktery nabyva integeru pro ruzne typy adresace (16bit off no segment, 16bit off 16bit seg, 32bit off no seg 32bit off 16 seg na Intelu) - Decl coordinates: je uzitecne asociovat deklaraci s jejim vyskytem ve zdrojaku. K tomu je pro deklarace objektu, modulu, podprogramu nebo typu atribut DW_AT_decl_file, DW_AT_decl_line a DW_AT_decl_column (hodnoty jsou constanty). - Jmena identifikatoru: DW_AT_name je atribut, jehoz hodnota je string odpovidajici nazvu identifikatoru ve zdrojaku (nemanglovane) - Data location: Jakakoliv DIE popisujici datovy objekt muze mit atribut DW_AT_location, jehoz hodnota je location description (viz vyse) - DIE popisujici entitu ktera ma machine code adresu nebo rozsah adres (compilation units, podprogramy. try/catch bloky, bloky, labely) muzou mit DW_AT_low_pc pro jednu adresu nebo jeste DW_AT_high_pc pro rozsah adres, pripadne DW_AT_ranges pro nespojity rozsah adres - v tom pripade ma tento atribut jako hodnotu class rangelist a indikuje zacatek range listu, ktery je v separatni sekci .debug_ranges, a hodnota DW_AT_ranges pak udava ofset od zacatku teto sekce. rangelist pak funguje podobne jako BASE (viz vyse). - Entry address: DW_entry_pc indikuje prvni spustitelnou instrukci v danem rozsahu adres. - Staticke a dynamicke vlastnosti typu (nektere veci se urcuji az pri runtime). Strana 43 (???) 3) Program Scope Entries Ted pujde o DIE ktere se vztahuji k ruznym urovnim programoveho scope: kompilacni, modulovy, podprogramovy, etc. (ohranicene casti textu programu) - Compilation and Importing Entries: object file se muze skladat z jedne nebo vice compilation units (muzou byt normal nebo partial). partial compilation unit se vztahuje k jedne nebo vice compilation units ktere ji importuji (?) \ Normal and Partial Compilation Unit Entries Pri bezne kompilaci se pouziva single compilation unit s tagem DW_TAG_compile_unit a reprezentuje cely object file a DW_TAG_partial_unit se nepouzije (pouziva se pouze pri nejakych DWARFovskych optimalizacich pri kompresi mista - viz Appendix E). Compilation Unit Entry (CUE) vlastni vsechny DIE ktere reprezentuji deklarace vytvorene v odpovidajici kompilaci. CUE mohou mit mnoho ruznych atributu (rozsah adres, viz vyse (kap. 2), DW_AT_name (cesta k souboru ze ktereho kompilace vznikla), DW_AT_language (programovaci jazyk), DW_AT_stmt_list (hodnota je offset do .debug_line sekce kde jsou ulozeny informace o cislech radku pro tuto compilation unit (viz dale, 6.2)), DW_AT_macro_info (jako u line info, odkaz do .debug_macinfo, pro makra, sekce 6.3), DW_AT_comp_dir (cwd), DW_AT_producer (vendor specific informace kompilatoru, DW_AT_identifier_case (oznacuje jaka ma byt case sensitivita pro identifikatory v teto compilation unit), DW_AT_base_types (atribut jehoz hodnota je reference a ktery ukazuje na jinou compilation unit, ve ktere jsou definovany base type entries ktere pouziva tato compilation unit - aby consumer (debugger) mohl najit definici base typu pro takove compilation units, ktere tyto definice samy neobsahuji), DW_AT_use_UTF8. DW_AT_low_pc je base address pro danou compilation unit. Pokud je toto nedefinovano, pak jsou vsechny struktury DWARFu ktere se odkazuji na base address (viz vyse) neplatne. \ Imported Unit Entries blah (?) - Modules, Namespace and Importing Entries: Moduly a Namespaces - zpusob jak sdruzit entity ktere maji neco spolecneho a spravovat jmena techto entit. \ Module: nektere jazyky maji koncept "modulu". DW_TAG_module. Modul vlastni sve DIE ktere popisuji entitiy programu a jejich platnost konci s koncem modulu. Rozsah je definovan bud DW_AT_low_pc a DW_AT_high_pc, nebo DW_AT_ranges pro nespojite adresni rozsahy (vis vyse). ... blah blah moduly \ Namespace: DIE s atributem DW_TAG_namespace s DW_AT_extension, ktery odkazuje na predchozi extension (pokud neni, tak na puvodni DW_TAG_namespace). Namespace & namespace extension mohou vlastnit jine DIE popisujici entity jejichz platnost konci s koncem namespace. Pokud typ, promenna nebo funkce deklarovana v namespace je definovana mimo telo namespace decl, ma atribut DW_AT_specification jehoz hodnota je reference na DIE reprezentujici prislusnou deklaraci typu/promenne/fce Globalni namespace nema v DWARFu specialni zaznam, globalni itemy se deklaruje bez reference do namespace. Priklad C++ namespace && DWARF - appendix D.3 \ Imported (renamed) Declaration Entries: DIE s DW_TAG_imported_declaration. Kazda importovana entry ma DW_AT_import, ktery ma jako hodnotu referenci na DIE importovane deklarace. Umoznuje prejmenovani (atribut DW_AT_name) (C++ namespace alias (nastavene _name a reference na puvodni namespace) \ Imported Module Entries - dovoluje importovat cely module (DW_TAG_imported_module) - Imported Module Decl muze vlastnit Imported Declaration Entries - ty ktere z importovaneho "modulu" pritecou i do "akutalniho". V C++ se tim da udelat treba 'using' - import atribute bude ukazovat na puvodni namespace. - Subroutine & Entry Point Entries \ Obecne informace Tagy DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point slouzi k popisu DIE pro podporgramy a entry pointy. DW_AT_name prislusne DIE ma stejnou hodnotu jako ve zdrojaku nazev podprogramu. DW_TAG_external (flag) muze znacit viditelnost vne z compilation unit. DIE podporogramu muze take obsahovat atribut DW_AT_calling_convention (s hodnotami normal (je bezpecne volat standardni volaci konvenci dane architektury), nocall znaci, ze pro debugger nemusi byt bezpecne ji volat, protoze neodpovida standardni volaci konvenci. _program je tam kvuli fortranu :) \ Navratove typy Pokud se jedna o funkci vracejici hodnotu, ma DIE atribut DW_AT_type oznacujici prislusny typ. \ Umisteni Klasika, viz vyse - bud muze mit DW_AT_low_pc a DW_AT_high_pc nebo DW_AT_ranges (viz vyse) - tyto atributy urcuji adresu kodu. Muze mit take atribut DW_AT_entry_pc, ktery oznacuje adresu prvni spustitelne instrukce. Podprogramy a entry pointy mohou mit take DW_AT_segment a DW_AT_address_class - viz vyse. \ Low-level inforamce Je take mozne mit v DIE DW_AT_return_addr atribut, jehoz hodnota je location description. Vypocitana location je misto kde je ulozena RET adresa. Take muze byt pritomen DW_AT_frame_base atribut, jehoz hodnota je location description a vypocitana location je frame daneho podprogramu (muze se hodit pro procedury, ktere potrebuji location listy (viz vyse) k ziskani locations lokalnich promennych - DW_AT_frame_base pak muze drzet location list a location descriptions promennych mohou byt jednoduche location expressions, ktere zahrnuji ve vypoctu frame base (a nejaky offset uvnitr nej). Take se muze hodit v nested rutinach (nepotrebujeme, DW_AT_static_link asi taky nebudeme potrebvoat). \ Types Thrown by Exceptions Jak vime :) v C++ muze funkce deklarovat mnozinu typu pro nez muze generovat vyjimku. Tyto typy jsou reprezentovany v samostatnych DIE, ktere maji atribut DW_AT_type, jehoz hodnota je reference na DIE popisujici typ. Tyto DIE jsou potomky DIE popisujici prislusnou rutinu. \ Instance function temlpates DWARF nerepresentuje genericky template, ale misto toho reprezentuje kazdou instanciaci zvlast. Instanciace template je reprezentovana DIE s DW_AT_subprogram. Se tremi vyjimkami je to stejne jako DIE pro obycejnou subroutine. Vyjimky: \\ Formalni parametru vyskytujici se v definici template je representovan DIE s tagem DW_TAG_template_type_parameter a DW_AT_name (jmeno formalniho parametru tak jak je ve zdrojaku). DW_AT_type urcuje typ kterym byl replacnut formalni typ pri instanciaci. \\ Pokud prekladac vygeneroval separatni compilation unit pro instanciaci name atribut pro DIE representujici tuto compilation unit by mel byt prazdny nebo by tam nemel byt vubec (??) \\ Pokud DIE reprezentujici instanciaci (nebo nektery jeji potomek) obsahuji atributy "declaration coordinate" (?), mely by tyto atributy odkazovat tam kde byla template definovana, ne na zdroje ktere vygeneroval kvuli instanciaci prekladac (??) \ Inline subroutines Jednoduche - v DIE je pouze DW_AT_inline atribut, ktery nabyva 4 hodnot (zda bylo/nebylo inlinovano a jaky byl hint ve zdrojaku - 4 moznosti). \\ Abstraktni instance Z DIE obsahujicich atribut DW_AT_inline a jejich potomku se stavi takzvany "asbtract instance tree" (a temto jednotlivym DIE se rika "abstract instance entry"). Jakakoliv DIE ktera patri do abstract instance tree by nemela ze zrejmeho duvodu (tyto DIE nereprezentuji zadnou "konkretni" instanci, ve skutecnosti v runtime neexistuji) obsahovat atributy, ktere se lisi mezi ruznymi inline a outline expanzeni (DW_AT_low_pc, DW_AT_high_pc, DW_AT_location, ...). (na druhou stranu v Appendixu D.6.3 je protipriklad ) \\ Konkretni inlinovane instance Kazda inline expanze je reprezentovana DIE s attr DW_TAG_inlined_subroutine. (takovy zaznam by mel byt vzdy primy potomek DIE ktera reprezentuje scope ve kterem k inlinovani dochazi). Klasicky definovany rozsah adres. Potomci DIE s DW_TAG_inlined_subroutine se nazyvaji "concrete inlined instance root" ... "concrete inlined instance tree", atd. Kazdy concerete tree je jednoznacne asociovan s prave jednim abstract instance tree (obracene "s prave jednim" zrejme neplati). DW_AT_abstract_orogin atribut odkazuje na asociovany abstract tree a mozna chybejici atributy ziskat odtamtud. Podrobnejsi detaily viz 3.3.8.2 \\ Out-of-line instance inline subrutin Nekdy prekladace potrebuji generovat executable instance inline subroutin jinde nez na mistech kde se tyhle subrutiny ve skutecnosti volaji. Takove concrete instaces se nazyvaji "concrete out-of-line instances" (V C++ napriklad zjisteni adresy funkce ktera je definovana jako inline muze vynutit vygenerovani konkretni out-of-line isntance dane funkce). DWARF takovouto out-of-line instanci reprezentuje uplne stejne jako v pripade concrete inlined instace (DW_AT_abstract_origin, ...) \ Trampolines Reprezentovano DW_TAG_subprogram nebo DW_TAG_inlined_subroutine s atributem DW_AT_trampoline a atribut indikuje kam trampolina predava kontrolu. Toto muze byt specifikovano v atributu vice ruznymi moznostmi (class reference - DIE ciloveho subprogramu, class address - relokovana adresa ciloveho suprogramu, class string - jmeno (zamanglovane) ciloveho podporoamu, flag - divnost (pokud true, tak tato DIE je sice trampoline, ale nevime k cemu). (v C++ trampoliny napriklad k implementaci odvozenych virtualnich member funkci na upravu parametru this). Ve DWARFu je to pravdepodobne proto, aby se vedelo co je trampolina a kdyz se na ni nastavi breakpoint, aby se ve skutecnosti nastavil az na samotny podprogram (a skryl se tak pred userem kompilatorem generovany kod). - Lexical block ({ }) DW_TAG_lexical block, urceny rozsah nebo nesouvisly rozsah (viz vyse). - Label Je vlastnen scopem do ktereho label patri. DW_TAG_label a ma atribut DW_AT_low_pc (relokovana adresa prvni instrukce labelu). DW_AT_name - Try/catch bloky Obycejne dva bloky, s oznacenim jestli se jedna o try/catch (DW_TAG_{try,catch}_block), rozsah adres jako u obycejneho bloku. Catch block ma alespon jednoho potomka, ktery reprezentuje typ vyjimky akceptovane v tomto catch bloku. 4) "Data Object" a "object list" entries V teto sekci jsou popsany jednotlive datove objekty - promenne, parametry, konstanty, seznamy techto objektu ktere mohou byt seskupene do jedne deklarace (priklad uveden jako common block ... spis struct/class, ne?) - Data Object Entries - promenne, formalni parametry a constants jsou reprezentovany DIE s tagy DW_TAG_variable a DW_TAG_formal_parameter, resp. DW_TAG_constant. Flagy urcujici vlastnosti jsou celkem primocare, viz 4.1 (atributu je hodne, ale jednoduchych (resi se static data, external, atd.) Pokud scope objektu zacina pozdeji nez je low_pc nejblizsiho vyssiho scope, tak i samotny objekt muze mit DW_AT_start_scope. To je napriklad kvuli float x = 1.1; int foo() { float f = x; float x = 2.2; return 0; } (do f se musi priradit 1.1). 5) Type Entries V teto sekci jsou popsany DIE popisujici base types, modified types a user-defined types - Base Type Entries Base typem se rozumi typ ktery neni definovany pomoci jinych typu. DW_TAG_base_type flag, DW_AT_name, DW_AT_encoding, DW_AT_byte_size (muze mit i DW_AT_bit_size a DW_AT_bit_offset, pokud hodnota daneho objectu nezabira tolik kolik je DW_AT_byte_size (Cckovsky int by na 32bit arch mel DW_AT_name "int", DW_AT_encoding DW_AT_signed a DW_AT_byte_size 4 - Unspecified Type Entries DW_TAG_unspecified_type (v Cecku void) a muze mit DW_AT_name - Type modifier entries (const, volatile, atd.). Jednoduche, viz 5.3 - Typedef DW_TAG_typedef - easy (DW_AT_name a DW_AT_type pokud je to definice). - Array DW_TAG_array_type (tag), DW_AT_name. DW_AT_ordering urcuje jestli je to row-major nebo col-major, DW_AT_type, DW_AT_byte_size. Dalsi array-specific tagy jsou DW_allocated, DW_AT_associated a DW_data_location a budou zmineny pozdeji (5.14) - Structure, Union, Class a Interface(? se nas netyka ?) \ Obecny popis DW_TAG_{union,structure,class}_type, DW_AT_name. Jednotlive polozky jsou vlastnene timto DIE a jsou ve stejnem poradi jako ve zdrojaku. Pokud jde o nekompletni zalezitost (deklaraci), je nastaven flag DW_AT_declaration, a pozdejsi definice ma atribut DW_AT_specification, jehoz hodnota je reference na odpovidajici deklaraci (a atributy neni nutne duplikovat) Pozn: V C/C++ deklarace ktere se vyskytuji uvnitr deklarace struct/union/class jsou brany zaroven jako jejich definice (krome static). Funkcni deklarace jsou definice prave kdyz se jejich telo vyskytuje uvnitr struktury/class/unie Pokud je definice mimo, pak DIE odpovidajici definici ma DW_AT_specification, coz je reference na deklarujici DIE uvnitr struktury. (??! zajimave je, ze deklarace nebude mit informace o location definice ??!) \ Derived Classes Typ class ktery popisuje odvozenou tridu vlastni DIE vsechny DIE ze kterych je odvozen (tedy je to opacne nez ja si predstavuju "odvozovaci strom"). Kazdy takovy (jaky? z tectu neni jasne) takovy DIE ma nastaven DW_TAG_inheritance. Kazdy inheritance entry ma DW_AT_type, ktery referencuje DIE ktery popisuje tridu od ktere je aktualni odvozena. Inheritance entry pro tridu ktera je odvozena z jine tridy ma take DW_AT_data_member_location, jehoz hodnota popisuje zacatek zdedeneho typu relativni k zacatku adresy odvozene tridy. (huh ??) - muze to byt constant (offset) nebo location expression. (je tam poznamka ze interpretace teto hodnoty je stejna jako u data members, coz je popsano pozdeji v sekci 5.6.6, tak uvidime) Pokud trida na kterou je odkazovano z inheritance entry slouzi jako virtualni trida (v C++), pak ma inheritance entry DW_AT_virtuality atribut a data member location atribut bude typicky netrivialni location expression :/ \ Access declarators - jasny (potomci class/strct type entry, podle DW_AT_name se to roslisuje o kterou polozku jde a DW_AT_accessibility ma v sobe ulozena prava). \ Friend - DIE ktery je potomek class/struct entry a ma DW_AT_friend, jehoz hodnota je reference na DIE popisujici frienda. \ Data Member Entries - reprezentovat DIE s DW_TAG_member, DW_AT_name a DW_AT_type, DW_AT_accessibility. Data member struct/union/classu ma DW_AT_data_member_location, jehoz hodnota popisuje umisteni tohoto data member vzhledem k base address struct/union/class (? nebo tridy ktera je nejblize ?). const - offset od zacatku. Jinak location description. ?Mlha kolem vnorenych struktur a optimalizace size? #include priklad na bitova pole \ Member function entries Jasne - DW_TAG_subprogram, DW_TAG_accessibility. Entry pro virtualni funkci ma take DW_AT_virtuality a DW_AT_vtable_elem_location, jehoz atribut obsahuje location description na "slot" pro tuto funkci ve virtualni tabulce metod nalezici k teto tride. Opet _specification pokud definice venku. \ Class Template Instatiations DWARF opet nereprezentuje generickou definici template, ale misto toho obsahuje reprezentaci kazde jednotlive instanciace. Je to celkem jasne a podobne funkcnim templatum. - Enumeration type: easy. DW_TAG_enum, kazdy enumeration type je DIE, ktery je potomek tohoto. ..... zbytek kapitoly 5 jsou porad dokola variace na toto tema (subroutine type, set (group of values of ordinal type), subrange, pointer na member typ, (priklad vypoctu adresy member pointeru ktery debugger dela na DWARFovske zasobnikove masine) 6) Other debugging info V tehle sekci jsou popsany debugging infa ktera nejsou reprezentovana pomoci DIE v sekci .debug_info - Accelerated access: Debugger casto potrebuje vyhledavat informace pro entity, ktere se mohou nachazet v jinych compilation units nez ve kterych je debugovany program zrovna zastaven. Podle adresy, podle jmena. Pro urychleni muze producent DWARFovskych informaci poskytnout 3 ruzne druhy tabulek ktere obsahuji informace o DIE vlastnenych danou compilation unit entr. \ Lookup podle jmena: 2 tabulky ve vlastnich sekcich - .debug_pubnames pro objekty a funkce a .debug_pubtypes pro typy. Kazda tabulka se sklada z mnoziny zaznamu promenne delky, kazda mnozina popisuje jmena globalnich objektu a funkci (globalnich typu), jejichz definice jsou representovany DIE vlastnenymi jednou kompilacni jednotkou (?hm, hm, hm, promyslet?). Kazda mnozina zacina hlavickou a pak offsetem do sekce .debug_info kompilacni jednotky, kterou tato mnozina referencuje a delkou obsahu v .debug_info, ktery od tohoto offsetu reprezentuje danou compilation unit. Po hlavicce nasleduji pary offset/name (odpovidajici DW_AT_name hledaneho objektu). null terminated. V pripade C++ member dat/funkcni class/struct/unionu, jmeno v .debug_pubnames neni jen jmeno dle DW_AT_name te referencovane debugging entry, ale plne kvalifikovane jmeno. \ Lookup podle adresy - pouziva se tabulka v sekci .debug_aranges (ranges?). Dal je to zrejme. \ Line number - source-level debugger potrebuje asociovat lokace ve zdrojaku s adresami konkretnich instrukci (breakpoint na radek, step line-line, etc.) Bylo zmineno nekde vyse, ze informace o cislech radek pro dany compilation unit je reprezentovano v .debug_line sekci object fajlu a je referencovano odpovidajici DIE. Naivni implementace by byla ulozit .debug_line obrovskou matici s jednou radkou pro kazdou instrukci ve vyslednem kodu. Sloupce by matice mela pro \\ source file name \\ source line number \\ source col number \\ flag, zda-li tato instrukce zacina statement ve zdrojaku \\ flag, zda-li tato instrukce zacina basic blok \\ .... Bylo by to velke. Jedna optimalizace kterou DWARF pouziva je, ze sjednoti po sobe jdouci radky ktere ve zdrojaku maji stejne misto. Dalsi optimalizaci je nejaky byte-coded language pro stavovy stroj a misto matice se do object fajlu uklada tento proud bajtu misto matice. Consumer (debugger), musi tenhle automat spustit a vygenerovat si podle nej tu matici (resp. jeji odpovidajici cast ktera ho zajima). TODO FIXME: Celkem mlha, POCHOPIT (Sekce 6.2), bojim bojim - Macro information Ve standardne zkompilovanem zdrojaku neni po makrech ani vidu (zadna reprezentae v target architekture). Standardnimi konstrukcemi DWARFu je tedy obtizne je reprezentovat. Macro information table poskytuje zpusob jak zachovat originalni zdrojak (pred preprocesingem a expanzi maker) v debugging info. Informace o makrech pro danou compilation unit jsou v .debug_macinfo. Informace o makrech pro kazdou kompilacni jednotku jsou reprezentovany jako serie zaznamu "macinfo". Kazdy zaznam v macinfo se sklada z "type code" a az dvou dalsich operandu. Serie je null-terminated. \\ Macinfo type codes - DW_MACINFO_define, DW_MACINFO_undef, DW_MACINFO_start_file, DW_MACINFO_end_file, DW_MACINFO_vendor_ext \\\ def a undef - dva operandy. Prvni koduje cislo radky ve zdrojaku na ktere se prislusny def/undef objevil. Druhy operand je null-terminated string, v pripade proste jmeno undefovaneho preprocesovaicho symbolu. V pripade define je to taky jmeno, ihned nasledovano formalnimi parametry makra (vcerna zavorek), nasledovanym definition strigem. Kazdy \\\ DW_MACINFO_start_file entry ma dva operandy. Prvni koduje cislo radky ve zdrojaku kde se vyskytlo #include. Druhy enkoduje source filename index. Ten koresponduje s cislem souboru v "line number information table" pro danou kompilacni jednotku. Tento index neprimo urcuje filename ktery se na dane filename includuje. - Call frame information Debuggery potrebuji videt/menit (jakekoliv) subroutine activation na zasobniku. V activation je predevsim: - code location v teto rutine (breakpoint, RET) - call frame address (CFA) - typicky jen sejvnute esp z mista callu v predchozim frame - ulozene registry ktere pouziva subrutina Aby se debugger mohl podivat/modifikovat "neaktualni" (ne na vrchu) aktivacni zaznam, musi "rozvinout" zasobnik zpatky dokud nenajde aktivacni zaznam ktery hleda. A taky ho pak musi umet zpatky "zavinout" :) do puvodni podoby. => je potreba vedet kde jsou registry a jak spocitat adresu prechoziho stackframe. DWARF se snazi to udelat arch-independent. Problemy: \ Epilog neni vzdy az na konci (vice exit pointu) \ Prekladace nekdy muzou premistovat kody obnovujici registry \ Prekladace nemuseji nutne pouzivat frame pointer (ebp na intelu) \ Zpusob jak spocitat adresu call frame se meni s postupem prologem/epilogem (samotna adresa se nemeni) \ Nektere subrutiny nemuseji mit nutne call frame (?) \ Nekdy je registr sejvnuty do registru ktery se nesejvuje (z definice) \ Nektere architektury maji instrukce na register management (se zanechanim dodatecne informace na zasobniku (popa/pusha?)) \ Nektere architekruty zachazeji specialne s RET - napriklad call zarucuje ze posledni dva bity jsou nulove a instrukce ret je ignoruje. S temito dvema bity se musi zachazet specialne (proc ze?) - DWARF podporuje "virtualni rozvijeni" tim, ze definuje arch-independent zpusob k zaznamenavani jak procedury ukladaji a obnovuji registry behem zivota. Velka tabulka a-la: LOC CFA R0 R1 ... RN L0 L1 ... LN Prvni sloupec obsahuje adresu pro kazdou location v programu, na ktere se vy vyskytuje nejaky code (v shared .o relativni offset). Zbyvajici sloupce obsahuji pravidla jak "virtualne rozvijet", prirazena ke kazdemu mistu v kodu. Prvni sloupec obsahuje pravidlo k vypoctu CFA (registr a offset ktere se sectou, nebo DWARF expression (viz hodne vyse)). Zbyvajici sloupce jsou registry - dany sloupec poskytuje informaci jestli byl dany registr ulozen a praidlo jak zjistit hodnotu registru v predchozim frame. Pravidla: \ undefined - nema v predchozim frame hodnotu (volany ho neuchovava) \ same val - neni modifikovan (volany ho uchovava ale nezmenil) \ offset(N) - predchozi hodnota je na CFA+N \ register(R) - predchzi hodnota je v registru R \ expression(E) - predchozi hodnota je na adrese kterou debugger ziska vyhodnocenim DWARFovskeho vyrazu E. \ architectural - da se dodefinovat arch-specific Kdyby byla ta tabulka takovahle, tak by byla obrovska - ale nastesti se s roustoucimi adresami informace malo meni, a tak se komprimuje a ukladaji se jen zmeny. Tyto virtul unwinding informace jsou ulozeny v separatni tabulce v sekci .debug_frame. Jsou dvojiho druhu: Commin Information Entry (CIE) a Frame Description Entry (FDE). CIE v sobe udrzuje informaci sdilenou vice FDE. V kazde neprazdne .debug_frame sekci je alespon jedna CIE. Obsahuje nasledujici polozky: 1) lengtth 2) CIE_id 3) version 4) augmentation 5) code_alignment_factor 6) data_alignment_factor 7) return_address_register - indikuje ktery sloupec v taulce reprezentuje RET. (nemusi odpovidat zadnemu skutecnemu registru) 8) initial_instructions - skvence pravidel kteer se provadeji k inicializaci kazdeho sloupce v tabulce 9) padding (aby to splnovalo len) FDE obsahuje nasledujici polozky: 1) length 2) CIE_pointer - konstantni offset do .debug_frame na CIE asociovanou s timto FDE 3) initial_location - konstanta ukazujici na prvni instrukci asocioavou s toutou entry 4) delka instrukci asocivanych s touto entry, v bajtech 5) instrukce - sekvence instrukci definujicich tabulku 6) padding Nasleduje seznam instrukci podle kterych debugger stavi vyse zminovanou tabulku. Popisovat se mi je nechce, navic jsem to zatim uplne presne nepochopil - proste jde o to postavit tu "rozdilovou" tabulku jen za pomoci instrukci - neco jako kontrukce matice korespondence kodu a cisel radek ve zdrojaku. V kapitole 6.4.3 je pak algoritmus jaky pouzivaji debuggery aby z techto instrukci postavili tabulku a v Appendixu D.5 je priklad. 7) Data representation V teto sekci je popsana binarni reprezentace samotne DIE, atributovych typu a vsech ostatni debugovacich zalezitosti popsanych v predeslych kapitolach. Tato cast je pomerne hutna, ale nikoliv myslenkove, ale jsou tam definovany hodnoty opcodu pro instrukce, presne popsan zpusob ulozeni datovych struktur v jednotlivych sekcich, reprezentaci cisel (LEB128 format), atd. Z teto sekce mam dojem, ze nelze udelat nejake vypisky - bylo by potreba ji vpodstate celou prepsat a navic do ni stejne ten kdo to pripadne bude implementovat bude porad koukat kde je jaky bajt, kde je jaky padding, atd. Proto ji prozatim do techto poznamek nezahrnuji. == Doporucuji si projit appendixy ve kterych jsou examply (velka cast appendixu v casti D osvetluje mnohe vyse popsane na prikladech). Melo by z toho jit pochopit i pocitani tabulek z instrukci (call frame a line/address korespondence), jsou tam na to celkem rozsahle a zda se ilustrativni priklady. Appendix E pojednava o prostorove kompresi dat DWARFu a explicitne zminuje, ze predevsim v pripade C++ muze debug info bez komprese dosahovat znacnych velikosti. Cela myslenka komprese je zalozena na "rozbiti" debug informaci do vice compilations units a nechat na linkeru aby reduntantni sections pri linkovani object fajlu vyhodil a je popsan algoritmus ktery dava navod jak toto rozdelovani delat (na prikladu C++ a fortranu) == -- jikos