michael@0: // -*- mode: c++ -*- michael@0: michael@0: // Copyright (c) 2010 Google Inc. All Rights Reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // Original author: Jim Blandy michael@0: michael@0: // dwarf2reader::CompilationUnit is a simple and direct parser for michael@0: // DWARF data, but its handler interface is not convenient to use. In michael@0: // particular: michael@0: // michael@0: // - CompilationUnit calls Dwarf2Handler's member functions to report michael@0: // every attribute's value, regardless of what sort of DIE it is. michael@0: // As a result, the ProcessAttributeX functions end up looking like michael@0: // this: michael@0: // michael@0: // switch (parent_die_tag) { michael@0: // case DW_TAG_x: michael@0: // switch (attribute_name) { michael@0: // case DW_AT_y: michael@0: // handle attribute y of DIE type x michael@0: // ... michael@0: // } break; michael@0: // ... michael@0: // } michael@0: // michael@0: // In C++ it's much nicer to use virtual function dispatch to find michael@0: // the right code for a given case than to switch on the DIE tag michael@0: // like this. michael@0: // michael@0: // - Processing different kinds of DIEs requires different sets of michael@0: // data: lexical block DIEs have start and end addresses, but struct michael@0: // type DIEs don't. It would be nice to be able to have separate michael@0: // handler classes for separate kinds of DIEs, each with the members michael@0: // appropriate to its role, instead of having one handler class that michael@0: // needs to hold data for every DIE type. michael@0: // michael@0: // - There should be a separate instance of the appropriate handler michael@0: // class for each DIE, instead of a single object with tables michael@0: // tracking all the dies in the compilation unit. michael@0: // michael@0: // - It's not convenient to take some action after all a DIE's michael@0: // attributes have been seen, but before visiting any of its michael@0: // children. The only indication you have that a DIE's attribute michael@0: // list is complete is that you get either a StartDIE or an EndDIE michael@0: // call. michael@0: // michael@0: // - It's not convenient to make use of the tree structure of the michael@0: // DIEs. Skipping all the children of a given die requires michael@0: // maintaining state and returning false from StartDIE until we get michael@0: // an EndDIE call with the appropriate offset. michael@0: // michael@0: // This interface tries to take care of all that. (You're shocked, I'm sure.) michael@0: // michael@0: // Using the classes here, you provide an initial handler for the root michael@0: // DIE of the compilation unit. Each handler receives its DIE's michael@0: // attributes, and provides fresh handler objects for children of michael@0: // interest, if any. The three classes are: michael@0: // michael@0: // - DIEHandler: the base class for your DIE-type-specific handler michael@0: // classes. michael@0: // michael@0: // - RootDIEHandler: derived from DIEHandler, the base class for your michael@0: // root DIE handler class. michael@0: // michael@0: // - DIEDispatcher: derived from Dwarf2Handler, an instance of this michael@0: // invokes your DIE-type-specific handler objects. michael@0: // michael@0: // In detail: michael@0: // michael@0: // - Define handler classes specialized for the DIE types you're michael@0: // interested in. These handler classes must inherit from michael@0: // DIEHandler. Thus: michael@0: // michael@0: // class My_DW_TAG_X_Handler: public DIEHandler { ... }; michael@0: // class My_DW_TAG_Y_Handler: public DIEHandler { ... }; michael@0: // michael@0: // DIEHandler subclasses needn't correspond exactly to single DIE michael@0: // types, as shown here; the point is that you can have several michael@0: // different classes appropriate to different kinds of DIEs. michael@0: // michael@0: // - In particular, define a handler class for the compilation michael@0: // unit's root DIE, that inherits from RootDIEHandler: michael@0: // michael@0: // class My_DW_TAG_compile_unit_Handler: public RootDIEHandler { ... }; michael@0: // michael@0: // RootDIEHandler inherits from DIEHandler, adding a few additional michael@0: // member functions for examining the compilation unit as a whole, michael@0: // and other quirks of rootness. michael@0: // michael@0: // - Then, create a DIEDispatcher instance, passing it an instance of michael@0: // your root DIE handler class, and use that DIEDispatcher as the michael@0: // dwarf2reader::CompilationUnit's handler: michael@0: // michael@0: // My_DW_TAG_compile_unit_Handler root_die_handler(...); michael@0: // DIEDispatcher die_dispatcher(&root_die_handler); michael@0: // CompilationUnit reader(sections, offset, bytereader, &die_dispatcher); michael@0: // michael@0: // Here, 'die_dispatcher' acts as a shim between 'reader' and the michael@0: // various DIE-specific handlers you have defined. michael@0: // michael@0: // - When you call reader.Start(), die_dispatcher behaves as follows, michael@0: // starting with your root die handler and the compilation unit's michael@0: // root DIE: michael@0: // michael@0: // - It calls the handler's ProcessAttributeX member functions for michael@0: // each of the DIE's attributes. michael@0: // michael@0: // - It calls the handler's EndAttributes member function. This michael@0: // should return true if any of the DIE's children should be michael@0: // visited, in which case: michael@0: // michael@0: // - For each of the DIE's children, die_dispatcher calls the michael@0: // DIE's handler's FindChildHandler member function. If that michael@0: // returns a pointer to a DIEHandler instance, then michael@0: // die_dispatcher uses that handler to process the child, using michael@0: // this procedure recursively. Alternatively, if michael@0: // FindChildHandler returns NULL, die_dispatcher ignores that michael@0: // child and its descendants. michael@0: // michael@0: // - When die_dispatcher has finished processing all the DIE's michael@0: // children, it invokes the handler's Finish() member function, michael@0: // and destroys the handler. (As a special case, it doesn't michael@0: // destroy the root DIE handler.) michael@0: // michael@0: // This allows the code for handling a particular kind of DIE to be michael@0: // gathered together in a single class, makes it easy to skip all the michael@0: // children or individual children of a particular DIE, and provides michael@0: // appropriate parental context for each die. michael@0: michael@0: #ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__ michael@0: #define COMMON_DWARF_DWARF2DIEHANDLER_H__ michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "common/dwarf/types.h" michael@0: #include "common/dwarf/dwarf2enums.h" michael@0: #include "common/dwarf/dwarf2reader.h" michael@0: #include "common/using_std_string.h" michael@0: michael@0: namespace dwarf2reader { michael@0: michael@0: // A base class for handlers for specific DIE types. The series of michael@0: // calls made on a DIE handler is as follows: michael@0: // michael@0: // - for each attribute of the DIE: michael@0: // - ProcessAttributeX() michael@0: // - EndAttributes() michael@0: // - if that returned true, then for each child: michael@0: // - FindChildHandler() michael@0: // - if that returns a non-NULL pointer to a new handler: michael@0: // - recurse, with the new handler and the child die michael@0: // - Finish() michael@0: // - destruction michael@0: class DIEHandler { michael@0: public: michael@0: DIEHandler() { } michael@0: virtual ~DIEHandler() { } michael@0: michael@0: // When we visit a DIE, we first use these member functions to michael@0: // report the DIE's attributes and their values. These have the michael@0: // same restrictions as the corresponding member functions of michael@0: // dwarf2reader::Dwarf2Handler. michael@0: // michael@0: // Since DWARF does not specify in what order attributes must michael@0: // appear, avoid making decisions in these functions that would be michael@0: // affected by the presence of other attributes. The EndAttributes michael@0: // function is a more appropriate place for such work, as all the michael@0: // DIE's attributes have been seen at that point. michael@0: // michael@0: // The default definitions ignore the values they are passed. michael@0: virtual void ProcessAttributeUnsigned(enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: uint64 data) { } michael@0: virtual void ProcessAttributeSigned(enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: int64 data) { } michael@0: virtual void ProcessAttributeReference(enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: uint64 data) { } michael@0: virtual void ProcessAttributeBuffer(enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: const char* data, michael@0: uint64 len) { } michael@0: virtual void ProcessAttributeString(enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: const string& data) { } michael@0: virtual void ProcessAttributeSignature(enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: uint64 signture) { } michael@0: michael@0: // Once we have reported all the DIE's attributes' values, we call michael@0: // this member function. If it returns false, we skip all the DIE's michael@0: // children. If it returns true, we call FindChildHandler on each michael@0: // child. If that returns a handler object, we use that to visit michael@0: // the child; otherwise, we skip the child. michael@0: // michael@0: // This is a good place to make decisions that depend on more than michael@0: // one attribute. DWARF does not specify in what order attributes michael@0: // must appear, so only when the EndAttributes function is called michael@0: // does the handler have a complete picture of the DIE's attributes. michael@0: // michael@0: // The default definition elects to ignore the DIE's children. michael@0: // You'll need to override this if you override FindChildHandler, michael@0: // but at least the default behavior isn't to pass the children to michael@0: // FindChildHandler, which then ignores them all. michael@0: virtual bool EndAttributes() { return false; } michael@0: michael@0: // If EndAttributes returns true to indicate that some of the DIE's michael@0: // children might be of interest, then we apply this function to michael@0: // each of the DIE's children. If it returns a handler object, then michael@0: // we use that to visit the child DIE. If it returns NULL, we skip michael@0: // that child DIE (and all its descendants). michael@0: // michael@0: // OFFSET is the offset of the child; TAG indicates what kind of DIE michael@0: // it is. michael@0: // michael@0: // The default definition skips all children. michael@0: virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag) { michael@0: return NULL; michael@0: } michael@0: michael@0: // When we are done processing a DIE, we call this member function. michael@0: // This happens after the EndAttributes call, all FindChildHandler michael@0: // calls (if any), and all operations on the children themselves (if michael@0: // any). We call Finish on every handler --- even if EndAttributes michael@0: // returns false. michael@0: virtual void Finish() { }; michael@0: }; michael@0: michael@0: // A subclass of DIEHandler, with additional kludges for handling the michael@0: // compilation unit's root die. michael@0: class RootDIEHandler: public DIEHandler { michael@0: public: michael@0: RootDIEHandler() { } michael@0: virtual ~RootDIEHandler() { } michael@0: michael@0: // We pass the values reported via Dwarf2Handler::StartCompilationUnit michael@0: // to this member function, and skip the entire compilation unit if it michael@0: // returns false. So the root DIE handler is actually also michael@0: // responsible for handling the compilation unit metadata. michael@0: // The default definition always visits the compilation unit. michael@0: virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, michael@0: uint8 offset_size, uint64 cu_length, michael@0: uint8 dwarf_version) { return true; } michael@0: michael@0: // For the root DIE handler only, we pass the offset, tag and michael@0: // attributes of the compilation unit's root DIE. This is the only michael@0: // way the root DIE handler can find the root DIE's tag. If this michael@0: // function returns true, we will visit the root DIE using the usual michael@0: // DIEHandler methods; otherwise, we skip the entire compilation michael@0: // unit. michael@0: // michael@0: // The default definition elects to visit the root DIE. michael@0: virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag) { return true; } michael@0: }; michael@0: michael@0: class DIEDispatcher: public Dwarf2Handler { michael@0: public: michael@0: // Create a Dwarf2Handler which uses ROOT_HANDLER as the handler for michael@0: // the compilation unit's root die, as described for the DIEHandler michael@0: // class. michael@0: DIEDispatcher(RootDIEHandler *root_handler) : root_handler_(root_handler) { } michael@0: // Destroying a DIEDispatcher destroys all active handler objects michael@0: // except the root handler. michael@0: ~DIEDispatcher(); michael@0: bool StartCompilationUnit(uint64 offset, uint8 address_size, michael@0: uint8 offset_size, uint64 cu_length, michael@0: uint8 dwarf_version); michael@0: bool StartDIE(uint64 offset, enum DwarfTag tag); michael@0: void ProcessAttributeUnsigned(uint64 offset, michael@0: enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: uint64 data); michael@0: void ProcessAttributeSigned(uint64 offset, michael@0: enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: int64 data); michael@0: void ProcessAttributeReference(uint64 offset, michael@0: enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: uint64 data); michael@0: void ProcessAttributeBuffer(uint64 offset, michael@0: enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: const char* data, michael@0: uint64 len); michael@0: void ProcessAttributeString(uint64 offset, michael@0: enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: const string &data); michael@0: void ProcessAttributeSignature(uint64 offset, michael@0: enum DwarfAttribute attr, michael@0: enum DwarfForm form, michael@0: uint64 signature); michael@0: void EndDIE(uint64 offset); michael@0: michael@0: private: michael@0: michael@0: // The type of a handler stack entry. This includes some fields michael@0: // which don't really need to be on the stack --- they could just be michael@0: // single data members of DIEDispatcher --- but putting them here michael@0: // makes it easier to see that the code is correct. michael@0: struct HandlerStack { michael@0: // The offset of the DIE for this handler stack entry. michael@0: uint64 offset_; michael@0: michael@0: // The handler object interested in this DIE's attributes and michael@0: // children. If NULL, we're not interested in either. michael@0: DIEHandler *handler_; michael@0: michael@0: // Have we reported the end of this DIE's attributes to the handler? michael@0: bool reported_attributes_end_; michael@0: }; michael@0: michael@0: // Stack of DIE attribute handlers. At StartDIE(D), the top of the michael@0: // stack is the handler of D's parent, whom we may ask for a handler michael@0: // for D itself. At EndDIE(D), the top of the stack is D's handler. michael@0: // Special cases: michael@0: // michael@0: // - Before we've seen the compilation unit's root DIE, the stack is michael@0: // empty; we'll call root_handler_'s special member functions, and michael@0: // perhaps push root_handler_ on the stack to look at the root's michael@0: // immediate children. michael@0: // michael@0: // - When we decide to ignore a subtree, we only push an entry on michael@0: // the stack for the root of the tree being ignored, rather than michael@0: // pushing lots of stack entries with handler_ set to NULL. michael@0: std::stack die_handlers_; michael@0: michael@0: // The root handler. We don't push it on die_handlers_ until we michael@0: // actually get the StartDIE call for the root. michael@0: RootDIEHandler *root_handler_; michael@0: }; michael@0: michael@0: } // namespace dwarf2reader michael@0: #endif // COMMON_DWARF_DWARF2DIEHANDLER_H__