pax_global_header00006660000000000000000000000064130201567600014512gustar00rootroot0000000000000052 comment=d5ad1b95142d9499459d8ae3f921f9937056213f libelfin-0.3/000077500000000000000000000000001302015676000131405ustar00rootroot00000000000000libelfin-0.3/.dir-locals.el000066400000000000000000000002011302015676000155620ustar00rootroot00000000000000((c-mode (indent-tabs-mode . nil) (c-file-style . "bsd")) (c++-mode (indent-tabs-mode . nil) (c-file-style . "bsd")) ) libelfin-0.3/.travis.yml000066400000000000000000000002511302015676000152470ustar00rootroot00000000000000sudo: required # The default environment is Ubuntu 12.04, which only has GCC 4.6. dist: trusty language: cpp script: - make - make check compiler: - gcc - clang libelfin-0.3/LICENSE000066400000000000000000000020751302015676000141510ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2013 Austin T. Clements Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libelfin-0.3/Makefile000066400000000000000000000002671302015676000146050ustar00rootroot00000000000000all: $(MAKE) -C elf $(MAKE) -C dwarf install: $(MAKE) -C elf install $(MAKE) -C dwarf install clean: $(MAKE) -C elf clean $(MAKE) -C dwarf clean check: cd test && ./test.sh libelfin-0.3/README.md000066400000000000000000000035431302015676000144240ustar00rootroot00000000000000[Libelfin](https://github.com/aclements/libelfin/) is a from-scratch C++11 library for reading ELF binaries and DWARFv4 debug information. Quick start ----------- `make`, and optionally `make install`. You'll need GCC 4.7 or later. Features -------- * Native C++11 code and interface, designed from scratch to interact well with C++11 features, from range-based for loops to move semantics to enum classes. * Libelfin fully implements parsing for Debugging Information Entries (DIEs), the core data structure used by the DWARF format, as well as most DWARFv4 tables. * Supports all DWARFv4 DIE value types except location lists and macros. * Nearly complete evaluator for DWARFv4 expressions and location descriptions. * Complete interpreter for DWARFv4 line tables. * Iterators for easily and naturally traversing compilation units, type units, DIE trees, and DIE attribute lists. * Every enum value can be pretty-printed. * Large collection of type-safe DIE attribute fetchers. Non-features ------------ Libelfin implements a *syntactic* layer for DWARF and ELF, but not a *semantic* layer. Interpreting the information stored in DWARF DIE trees still requires a great deal of understanding of DWARF, but libelfin will make sense of the bytes for you. Using libelfin -------------- To build against `libdwarf++`, use, for example g++ -std=c++11 a.cc $(pkg-config --cflags --libs libdwarf++) To use a local build of libelfin, set `PKG_CONFIG_PATH`. For example, export PKG_CONFIG_PATH=$PWD/elf:$PWD/dwarf There are various example programs in `examples/`. Status ------ Libelfin is a good start. It's not production-ready and there are many parts of the DWARF specification it does not yet implement, but it's complete enough to be useful for many things and is a good deal more pleasant to use than every other debug info library I've tried. libelfin-0.3/dwarf/000077500000000000000000000000001302015676000142435ustar00rootroot00000000000000libelfin-0.3/dwarf/.gitignore000066400000000000000000000001201302015676000162240ustar00rootroot00000000000000*.o to_string.cc libdwarf++.a libdwarf++.so libdwarf++.so.* libdwarf++.pc /doc/ libelfin-0.3/dwarf/Doxyfile000066400000000000000000002234351302015676000157620ustar00rootroot00000000000000# Doxyfile 1.7.6.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = "My Project" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = internal.hh # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = *.cc # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = *::impl # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # style sheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the # mathjax.org site, so you can quickly see the result without installing # MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES libelfin-0.3/dwarf/Makefile000066400000000000000000000045761302015676000157170ustar00rootroot00000000000000# Changed when ABI backwards compatibility is broken. # Typically uses the major version. SONAME = 0 CXXFLAGS+=-g -O2 -Werror override CXXFLAGS+=-std=c++0x -Wall -fPIC all: libdwarf++.a libdwarf++.so.$(SONAME) libdwarf++.so libdwarf++.pc SRCS := dwarf.cc cursor.cc die.cc value.cc abbrev.cc \ expr.cc rangelist.cc line.cc attrs.cc \ die_str_map.cc elf.cc to_string.cc HDRS := dwarf++.hh data.hh internal.hh small_vector.hh ../elf/to_hex.hh CLEAN := libdwarf++.a: $(SRCS:.cc=.o) ar rcs $@ $^ CLEAN += libdwarf++.a $(SRCS:.cc=.o) $(SRCS:.cc=.o): $(HDRS) to_string.cc: ../elf/enum-print.py dwarf++.hh data.hh Makefile @echo "// Automatically generated by make at $$(date)" > to_string.cc @echo "// DO NOT EDIT" >> to_string.cc @echo >> to_string.cc @echo '#include "internal.hh"' >> to_string.cc @echo >> to_string.cc @echo 'DWARFPP_BEGIN_NAMESPACE' >> to_string.cc @echo >> to_string.cc python3 ../elf/enum-print.py < dwarf++.hh >> to_string.cc python3 ../elf/enum-print.py -s _ -u --hex -x hi_user -x lo_user < data.hh >> to_string.cc @echo 'DWARFPP_END_NAMESPACE' >> to_string.cc CLEAN += to_string.cc libdwarf++.so.$(SONAME): $(SRCS:.cc=.o) $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -Wl,-soname,$@ -o $@ $^ CLEAN += libdwarf++.so.* libdwarf++.so: ln -s $@.$(SONAME) $@ CLEAN += libdwarf++.so # Create pkg-config for local library and headers. This will be # transformed in to the correct global pkg-config by install. libdwarf++.pc: always @(VER=$$(git describe --match 'v*' | sed -e s/^v//); \ echo "libdir=$$PWD"; \ echo "includedir=$$PWD"; \ echo ""; \ echo "Name: libdwarf++"; \ echo "Description: C++11 DWARF library"; \ echo "Version: $$VER"; \ echo "Requires: libelf++ = $$VER"; \ echo "Libs: -L\$${libdir} -ldwarf++"; \ echo "Cflags: -I\$${includedir}") > $@ CLEAN += libdwarf++.pc .PHONY: always PREFIX?=/usr/local install: libdwarf++.a libdwarf++.so.$(SONAME) libdwarf++.so libdwarf++.pc install -d $(PREFIX)/lib/pkgconfig install -t $(PREFIX)/lib libdwarf++.a install -t $(PREFIX)/lib libdwarf++.so.$(SONAME) install -t $(PREFIX)/lib libdwarf++.so install -d $(PREFIX)/include/libelfin/dwarf install -t $(PREFIX)/include/libelfin/dwarf data.hh dwarf++.hh small_vector.hh sed 's,^libdir=.*,libdir=$(PREFIX)/lib,;s,^includedir=.*,includedir=$(PREFIX)/include,' libdwarf++.pc \ > $(PREFIX)/lib/pkgconfig/libdwarf++.pc clean: rm -f $(CLEAN) .DELETE_ON_ERROR: libelfin-0.3/dwarf/abbrev.cc000066400000000000000000000132761302015676000160240ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "internal.hh" using namespace std; DWARFPP_BEGIN_NAMESPACE static value::type resolve_type(DW_AT name, DW_FORM form) { switch (form) { case DW_FORM::addr: return value::type::address; case DW_FORM::block: case DW_FORM::block1: case DW_FORM::block2: case DW_FORM::block4: // Prior to DWARF 4, exprlocs didn't have their own // form and were represented as blocks. // XXX Should this be predicated on version? switch (name) { case DW_AT::location: case DW_AT::byte_size: case DW_AT::bit_offset: case DW_AT::bit_size: case DW_AT::string_length: case DW_AT::lower_bound: case DW_AT::return_addr: case DW_AT::bit_stride: case DW_AT::upper_bound: case DW_AT::count: case DW_AT::data_member_location: case DW_AT::frame_base: case DW_AT::segment: case DW_AT::static_link: case DW_AT::use_location: case DW_AT::vtable_elem_location: case DW_AT::allocated: case DW_AT::associated: case DW_AT::data_location: case DW_AT::byte_stride: return value::type::exprloc; default: return value::type::block; } case DW_FORM::data4: case DW_FORM::data8: // Prior to DWARF 4, section offsets didn't have their // own form and were represented as data4 or data8. // DWARF 3 clarified that types that accepted both // constants and section offsets were to treat data4 // and data8 as section offsets and other constant // forms as constants. // XXX Should this be predicated on version? switch (name) { case DW_AT::location: case DW_AT::stmt_list: case DW_AT::string_length: case DW_AT::return_addr: case DW_AT::start_scope: case DW_AT::data_member_location: case DW_AT::frame_base: case DW_AT::macro_info: case DW_AT::segment: case DW_AT::static_link: case DW_AT::use_location: case DW_AT::vtable_elem_location: case DW_AT::ranges: goto sec_offset; default: // Fall through break; } case DW_FORM::data1: case DW_FORM::data2: return value::type::constant; case DW_FORM::udata: return value::type::uconstant; case DW_FORM::sdata: return value::type::sconstant; case DW_FORM::exprloc: return value::type::exprloc; case DW_FORM::flag: case DW_FORM::flag_present: return value::type::flag; case DW_FORM::ref1: case DW_FORM::ref2: case DW_FORM::ref4: case DW_FORM::ref8: case DW_FORM::ref_addr: case DW_FORM::ref_sig8: case DW_FORM::ref_udata: return value::type::reference; case DW_FORM::string: case DW_FORM::strp: return value::type::string; case DW_FORM::indirect: // There's nothing meaningful we can do return value::type::invalid; case DW_FORM::sec_offset: sec_offset: // The type of this form depends on the attribute switch (name) { case DW_AT::stmt_list: return value::type::line; case DW_AT::location: case DW_AT::string_length: case DW_AT::return_addr: case DW_AT::data_member_location: case DW_AT::frame_base: case DW_AT::segment: case DW_AT::static_link: case DW_AT::use_location: case DW_AT::vtable_elem_location: return value::type::loclist; case DW_AT::macro_info: return value::type::mac; case DW_AT::start_scope: case DW_AT::ranges: return value::type::rangelist; default: throw format_error("DW_FORM_sec_offset not expected for attribute " + to_string(name)); } } throw format_error("unknown attribute form " + to_string(form)); } attribute_spec::attribute_spec(DW_AT name, DW_FORM form) : name(name), form(form), type(resolve_type(name, form)) { } bool abbrev_entry::read(cursor *cur) { attributes.clear(); // Section 7.5.3 code = cur->uleb128(); if (!code) return false; tag = (DW_TAG)cur->uleb128(); children = cur->fixed() == DW_CHILDREN::yes; while (1) { DW_AT name = (DW_AT)cur->uleb128(); DW_FORM form = (DW_FORM)cur->uleb128(); if (name == (DW_AT)0 && form == (DW_FORM)0) break; attributes.push_back(attribute_spec(name, form)); } attributes.shrink_to_fit(); return true; } DWARFPP_END_NAMESPACE libelfin-0.3/dwarf/attrs.cc000066400000000000000000000202071302015676000157100ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "dwarf++.hh" using namespace std; DWARFPP_BEGIN_NAMESPACE #define AT_ANY(name) \ value at_##name(const die &d) \ { \ return d[DW_AT::name]; \ } \ static_assert(true, "") #define AT_ADDRESS(name) \ taddr at_##name(const die &d) \ { \ return d[DW_AT::name].as_address(); \ } \ static_assert(true, "") #define AT_ENUM(name, type) \ type at_##name(const die &d) \ { \ return (type)d[DW_AT::name].as_uconstant(); \ } \ static_assert(true, "") #define AT_FLAG(name) \ bool at_##name(const die &d) \ { \ return d[DW_AT::name].as_flag(); \ } \ static_assert(true, "") #define AT_FLAG_(name) \ bool at_##name(const die &d) \ { \ return d[DW_AT::name##_].as_flag(); \ } \ static_assert(true, "") #define AT_REFERENCE(name) \ die at_##name(const die &d) \ { \ return d[DW_AT::name].as_reference(); \ } \ static_assert(true, "") #define AT_STRING(name) \ string at_##name(const die &d) \ { \ return d[DW_AT::name].as_string(); \ } \ static_assert(true, "") #define AT_UDYNAMIC(name) \ uint64_t at_##name(const die &d, expr_context *ctx) \ { \ return _at_udynamic(DW_AT::name, d, ctx); \ } \ static_assert(true, "") static uint64_t _at_udynamic(DW_AT attr, const die &d, expr_context *ctx, int depth = 0) { // DWARF4 section 2.19 if (depth > 16) throw format_error("reference depth exceeded for " + to_string(attr)); value v(d[attr]); switch (v.get_type()) { case value::type::constant: case value::type::uconstant: return v.as_uconstant(); case value::type::reference: return _at_udynamic(attr, v.as_reference(), ctx, depth + 1); case value::type::exprloc: return v.as_exprloc().evaluate(ctx).value; default: throw format_error(to_string(attr) + " has unexpected type " + to_string(v.get_type())); } } ////////////////////////////////////////////////////////////////// // 0x0X // AT_REFERENCE(sibling); // XXX location AT_STRING(name); AT_ENUM(ordering, DW_ORD); AT_UDYNAMIC(byte_size); AT_UDYNAMIC(bit_offset); AT_UDYNAMIC(bit_size); ////////////////////////////////////////////////////////////////// // 0x1X // // XXX stmt_list AT_ADDRESS(low_pc); taddr at_high_pc(const die &d) { value v(d[DW_AT::high_pc]); switch (v.get_type()) { case value::type::address: return v.as_address(); case value::type::constant: case value::type::uconstant: return at_low_pc(d) + v.as_uconstant(); default: throw format_error(to_string(DW_AT::high_pc) + " has unexpected type " + to_string(v.get_type())); } } AT_ENUM(language, DW_LANG); AT_REFERENCE(discr); AT_ANY(discr_value); // XXX Signed or unsigned AT_ENUM(visibility, DW_VIS); AT_REFERENCE(import); // XXX string_length AT_REFERENCE(common_reference); AT_STRING(comp_dir); AT_ANY(const_value); AT_REFERENCE(containing_type); // XXX default_value ////////////////////////////////////////////////////////////////// // 0x2X // DW_INL at_inline(const die &d) { // XXX Missing attribute is equivalent to DW_INL_not_inlined // (DWARF4 section 3.3.8) return (DW_INL)d[DW_AT::inline_].as_uconstant(); } AT_FLAG(is_optional); AT_UDYNAMIC(lower_bound); // XXX Language-based default? AT_STRING(producer); AT_FLAG(prototyped); // XXX return_addr // XXX start_scope AT_UDYNAMIC(bit_stride); AT_UDYNAMIC(upper_bound); ////////////////////////////////////////////////////////////////// // 0x3X // AT_REFERENCE(abstract_origin); AT_ENUM(accessibility, DW_ACCESS); // XXX const address_class AT_FLAG(artificial); // XXX base_types AT_ENUM(calling_convention, DW_CC); AT_UDYNAMIC(count); expr_result at_data_member_location(const die &d, expr_context *ctx, taddr base, taddr pc) { value v(d[DW_AT::data_member_location]); switch (v.get_type()) { case value::type::constant: case value::type::uconstant: return {expr_result::type::address, base + v.as_uconstant()}; case value::type::exprloc: return v.as_exprloc().evaluate(ctx, base); case value::type::loclist: // XXX throw std::runtime_error("not implemented"); default: throw format_error("DW_AT_data_member_location has unexpected type " + to_string(v.get_type())); } } // XXX decl_column decl_file decl_line AT_FLAG(declaration); // XXX discr_list AT_ENUM(encoding, DW_ATE); AT_FLAG(external); ////////////////////////////////////////////////////////////////// // 0x4X // // XXX frame_base die at_friend(const die &d) { return d[DW_AT::friend_].as_reference(); } AT_ENUM(identifier_case, DW_ID); // XXX macro_info AT_REFERENCE(namelist_item); AT_REFERENCE(priority); // XXX Computed might be useful // XXX segment AT_REFERENCE(specification); // XXX static_link AT_REFERENCE(type); // XXX use_location AT_FLAG(variable_parameter); // XXX 7.11 The value DW_VIRTUALITY_none is equivalent to the absence // of the DW_AT_virtuality attribute. AT_ENUM(virtuality, DW_VIRTUALITY); // XXX vtable_elem_location AT_UDYNAMIC(allocated); AT_UDYNAMIC(associated); ////////////////////////////////////////////////////////////////// // 0x5X // // XXX data_location AT_UDYNAMIC(byte_stride); AT_ADDRESS(entry_pc); AT_FLAG(use_UTF8); AT_REFERENCE(extension); rangelist at_ranges(const die &d) { return d[DW_AT::ranges].as_rangelist(); } // XXX trampoline // XXX const call_column, call_file, call_line AT_STRING(description); // XXX const binary_scale // XXX const decimal_scale AT_REFERENCE(small); // XXX const decimal_sign // XXX const digit_count ////////////////////////////////////////////////////////////////// // 0x6X // AT_STRING(picture_string); AT_FLAG_(mutable); AT_FLAG(threads_scaled); AT_FLAG_(explicit); AT_REFERENCE(object_pointer); AT_ENUM(endianity, DW_END); AT_FLAG(elemental); AT_FLAG(pure); AT_FLAG(recursive); AT_REFERENCE(signature); // XXX Computed might be useful AT_FLAG(main_subprogram); // XXX const data_bit_offset AT_FLAG(const_expr); AT_FLAG(enum_class); AT_STRING(linkage_name); rangelist die_pc_range(const die &d) { // DWARF4 section 2.17 if (d.has(DW_AT::ranges)) return at_ranges(d); taddr low = at_low_pc(d); taddr high = d.has(DW_AT::high_pc) ? at_high_pc(d) : (low + 1); return rangelist({{low, high}}); } DWARFPP_END_NAMESPACE libelfin-0.3/dwarf/cursor.cc000066400000000000000000000122541302015676000160730ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "internal.hh" #include #include using namespace std; DWARFPP_BEGIN_NAMESPACE int64_t cursor::sleb128() { // Appendix C uint64_t result = 0; unsigned shift = 0; while (pos < sec->end) { uint8_t byte = *(uint8_t*)(pos++); result |= (uint64_t)(byte & 0x7f) << shift; shift += 7; if ((byte & 0x80) == 0) { if (shift < sizeof(result)*8 && (byte & 0x40)) result |= -((uint64_t)1 << shift); return result; } } underflow(); return 0; } shared_ptr
cursor::subsection() { // Section 7.4 const char *begin = pos; section_length length = fixed(); format fmt; if (length < 0xfffffff0) { fmt = format::dwarf32; length += sizeof(uword); } else if (length == 0xffffffff) { length = fixed(); fmt = format::dwarf64; length += sizeof(uword) + sizeof(uint64_t); } else { throw format_error("initial length has reserved value"); } pos = begin + length; return make_shared
(sec->type, begin, length, sec->ord, fmt); } void cursor::skip_initial_length() { switch (sec->fmt) { case format::dwarf32: pos += sizeof(uword); break; case format::dwarf64: pos += sizeof(uword) + sizeof(uint64_t); break; default: throw logic_error("cannot skip initial length with unknown format"); } } section_offset cursor::offset() { switch (sec->fmt) { case format::dwarf32: return fixed(); case format::dwarf64: return fixed(); default: throw logic_error("cannot read offset with unknown format"); } } void cursor::string(std::string &out) { size_t size; const char *p = this->cstr(&size); out.resize(size); memmove(&out.front(), p, size); } const char * cursor::cstr(size_t *size_out) { // Scan string size const char *p = pos; while (pos < sec->end && *pos) pos++; if (pos == sec->end) throw format_error("unterminated string"); if (size_out) *size_out = pos - p; pos++; return p; } void cursor::skip_form(DW_FORM form) { section_offset tmp; // Section 7.5.4 switch (form) { case DW_FORM::addr: pos += sec->addr_size; break; case DW_FORM::sec_offset: case DW_FORM::ref_addr: case DW_FORM::strp: switch (sec->fmt) { case format::dwarf32: pos += 4; break; case format::dwarf64: pos += 8; break; case format::unknown: throw logic_error("cannot read form with unknown format"); } break; // size+data forms case DW_FORM::block1: tmp = fixed(); pos += tmp; break; case DW_FORM::block2: tmp = fixed(); pos += tmp; break; case DW_FORM::block4: tmp = fixed(); pos += tmp; break; case DW_FORM::block: case DW_FORM::exprloc: tmp = uleb128(); pos += tmp; break; // fixed-length forms case DW_FORM::flag_present: break; case DW_FORM::flag: case DW_FORM::data1: case DW_FORM::ref1: pos += 1; break; case DW_FORM::data2: case DW_FORM::ref2: pos += 2; break; case DW_FORM::data4: case DW_FORM::ref4: pos += 4; break; case DW_FORM::data8: case DW_FORM::ref_sig8: pos += 8; break; // variable-length forms case DW_FORM::sdata: case DW_FORM::udata: case DW_FORM::ref_udata: while (pos < sec->end && (*(uint8_t*)pos & 0x80)) pos++; pos++; break; case DW_FORM::string: while (pos < sec->end && *pos) pos++; pos++; break; case DW_FORM::indirect: skip_form((DW_FORM)uleb128()); break; default: throw format_error("unknown form " + to_string(form)); } } void cursor::underflow() { throw underflow_error("cannot read past end of DWARF section"); } DWARFPP_END_NAMESPACE libelfin-0.3/dwarf/data.hh000066400000000000000000000437431302015676000155100ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #ifndef _DWARFPP_DW_HH_ #define _DWARFPP_DW_HH_ #include #include DWARFPP_BEGIN_NAMESPACE // Integer representations (Section 7.26) typedef std::int8_t sbyte; typedef std::uint8_t ubyte; typedef std::uint16_t uhalf; typedef std::uint32_t uword; // Section offsets and lengths typedef std::uint64_t section_offset; typedef std::uint64_t section_length; // A target machine address. Targets may use smaller addresses; this // represents the largest supported address type. typedef std::uint64_t taddr; // DIE tags (Section 7, figure 18). typedef, friend, and namespace // have a trailing underscore because they are reserved words. enum class DW_TAG { array_type = 0x01, class_type = 0x02, entry_point = 0x03, enumeration_type = 0x04, formal_parameter = 0x05, imported_declaration = 0x08, label = 0x0a, lexical_block = 0x0b, member = 0x0d, pointer_type = 0x0f, reference_type = 0x10, compile_unit = 0x11, string_type = 0x12, structure_type = 0x13, subroutine_type = 0x15, typedef_ = 0x16, union_type = 0x17, unspecified_parameters = 0x18, variant = 0x19, common_block = 0x1a, common_inclusion = 0x1b, inheritance = 0x1c, inlined_subroutine = 0x1d, module = 0x1e, ptr_to_member_type = 0x1f, set_type = 0x20, subrange_type = 0x21, with_stmt = 0x22, access_declaration = 0x23, base_type = 0x24, catch_block = 0x25, const_type = 0x26, constant = 0x27, enumerator = 0x28, file_type = 0x29, friend_ = 0x2a, namelist = 0x2b, namelist_item = 0x2c, packed_type = 0x2d, subprogram = 0x2e, template_type_parameter = 0x2f, template_value_parameter = 0x30, thrown_type = 0x31, try_block = 0x32, variant_part = 0x33, variable = 0x34, volatile_type = 0x35, dwarf_procedure = 0x36, restrict_type = 0x37, interface_type = 0x38, namespace_ = 0x39, imported_module = 0x3a, unspecified_type = 0x3b, partial_unit = 0x3c, imported_unit = 0x3d, condition = 0x3f, shared_type = 0x40, type_unit = 0x41, rvalue_reference_type = 0x42, template_alias = 0x43, lo_user = 0x4080, hi_user = 0xffff, }; std::string to_string(DW_TAG v); // Child determination (Section 7, figure 19). enum class DW_CHILDREN : ubyte { no = 0x00, yes = 0x01, }; std::string to_string(DW_CHILDREN v); // Attribute names (Section 7, figure 20). inline, friend, mutable, // and explicit have a trailing underscore because they are reserved // words. enum class DW_AT { sibling = 0x01, // reference location = 0x02, // exprloc, loclistptr name = 0x03, // string ordering = 0x09, // constant byte_size = 0x0b, // constant, exprloc, reference bit_offset = 0x0c, // constant, exprloc, reference bit_size = 0x0d, // constant, exprloc, reference stmt_list = 0x10, // lineptr low_pc = 0x11, // address high_pc = 0x12, // address, constant language = 0x13, // constant discr = 0x15, // reference discr_value = 0x16, // constant visibility = 0x17, // constant import = 0x18, // reference string_length = 0x19, // exprloc, loclistptr common_reference = 0x1a, // reference comp_dir = 0x1b, // string const_value = 0x1c, // block, constant, string containing_type = 0x1d, // reference default_value = 0x1e, // reference inline_ = 0x20, // constant is_optional = 0x21, // flag lower_bound = 0x22, // constant, exprloc, reference producer = 0x25, // string prototyped = 0x27, // flag return_addr = 0x2a, // exprloc, loclistptr start_scope = 0x2c, // constant, rangelistptr bit_stride = 0x2e, // constant, exprloc, reference upper_bound = 0x2f, // constant, exprloc, reference abstract_origin = 0x31, // reference accessibility = 0x32, // constant address_class = 0x33, // constant artificial = 0x34, // flag base_types = 0x35, // reference calling_convention = 0x36, // constant count = 0x37, // constant, exprloc, reference data_member_location = 0x38, // constant, exprloc, loclistptr decl_column = 0x39, // constant decl_file = 0x3a, // constant decl_line = 0x3b, // constant declaration = 0x3c, // flag discr_list = 0x3d, // block encoding = 0x3e, // constant external = 0x3f, // flag frame_base = 0x40, // exprloc, loclistptr friend_ = 0x41, // reference identifier_case = 0x42, // constant macro_info = 0x43, // macptr namelist_item = 0x44, // reference priority = 0x45, // reference segment = 0x46, // exprloc, loclistptr specification = 0x47, // reference static_link = 0x48, // exprloc, loclistptr type = 0x49, // reference use_location = 0x4a, // exprloc, loclistptr variable_parameter = 0x4b, // flag virtuality = 0x4c, // constant vtable_elem_location = 0x4d, // exprloc, loclistptr // DWARF 3 allocated = 0x4e, // constant, exprloc, reference associated = 0x4f, // constant, exprloc, reference data_location = 0x50, // exprloc byte_stride = 0x51, // constant, exprloc, reference entry_pc = 0x52, // address use_UTF8 = 0x53, // flag extension = 0x54, // reference ranges = 0x55, // rangelistptr trampoline = 0x56, // address, flag, reference, string call_column = 0x57, // constant call_file = 0x58, // constant call_line = 0x59, // constant description = 0x5a, // string binary_scale = 0x5b, // constant decimal_scale = 0x5c, // constant small = 0x5d, // reference decimal_sign = 0x5e, // constant digit_count = 0x5f, // constant picture_string = 0x60, // string mutable_ = 0x61, // flag threads_scaled = 0x62, // flag explicit_ = 0x63, // flag object_pointer = 0x64, // reference endianity = 0x65, // constant elemental = 0x66, // flag pure = 0x67, // flag recursive = 0x68, // flag // DWARF 4 signature = 0x69, // reference main_subprogram = 0x6a, // flag data_bit_offset = 0x6b, // constant const_expr = 0x6c, // flag enum_class = 0x6d, // flag linkage_name = 0x6e, // string lo_user = 0x2000, hi_user = 0x3fff, }; std::string to_string(DW_AT v); // Attribute form encodings (Section 7, figure 21) enum class DW_FORM { addr = 0x01, // address block2 = 0x03, // block block4 = 0x04, // block data2 = 0x05, // constant data4 = 0x06, // constant data8 = 0x07, // constant string = 0x08, // string block = 0x09, // block block1 = 0x0a, // block data1 = 0x0b, // constant flag = 0x0c, // flag sdata = 0x0d, // constant strp = 0x0e, // string udata = 0x0f, // constant ref_addr = 0x10, // reference ref1 = 0x11, // reference ref2 = 0x12, // reference ref4 = 0x13, // reference ref8 = 0x14, // reference ref_udata = 0x15, // reference indirect = 0x16, // (Section 7.5.3) // DWARF 4 sec_offset = 0x17, // lineptr, loclistptr, macptr, rangelistptr exprloc = 0x18, // exprloc flag_present = 0x19, // flag ref_sig8 = 0x20, // reference }; std::string to_string(DW_FORM v); // DWARF operation encodings (Section 7.7.1 and figure 24) enum class DW_OP : ubyte { addr = 0x03, // [constant address (size target specific)] deref = 0x06, const1u = 0x08, // [1-byte constant] const1s = 0x09, // [1-byte constant] const2u = 0x0a, // [2-byte constant] const2s = 0x0b, // [2-byte constant] const4u = 0x0c, // [4-byte constant] const4s = 0x0d, // [4-byte constant] const8u = 0x0e, // [8-byte constant] const8s = 0x0f, // [8-byte constant] constu = 0x10, // [ULEB128 constant] consts = 0x11, // [SLEB128 constant] dup = 0x12, drop = 0x13, over = 0x14, pick = 0x15, // [1-byte stack index] swap = 0x16, rot = 0x17, xderef = 0x18, abs = 0x19, and_ = 0x1a, div = 0x1b, minus = 0x1c, mod = 0x1d, mul = 0x1e, neg = 0x1f, not_ = 0x20, or_ = 0x21, plus = 0x22, plus_uconst = 0x23, // [ULEB128 addend] shl = 0x24, shr = 0x25, shra = 0x26, xor_ = 0x27, skip = 0x2f, // [signed 2-byte constant] bra = 0x28, // [signed 2-byte constant] eq = 0x29, ge = 0x2a, gt = 0x2b, le = 0x2c, lt = 0x2d, ne = 0x2e, // Literals 0..31 = (lit0 + literal) lit0 = 0x30, lit31 = 0x4f, // Registers 0..31 = (reg0 + regnum) reg0 = 0x50, reg31 = 0x6f, // Base register 0..31 = (breg0 + regnum) breg0 = 0x70, // [SLEB128 offset] breg31 = 0x8f, // [SLEB128 offset] regx = 0x90, // [ULEB128 register] fbreg = 0x91, // [SLEB128 offset] bregx = 0x92, // [ULEB128 register, SLEB128 offset] piece = 0x93, // [ULEB128 size of piece addressed] deref_size = 0x94, // [1-byte size of data retrieved] xderef_size = 0x95, // [1-byte size of data retrieved] nop = 0x96, // DWARF 3 push_object_address = 0x97, call2 = 0x98, // [2-byte offset of DIE] call4 = 0x99, // [4-byte offset of DIE] call_ref = 0x9a, // [4- or 8-byte offset of DIE] form_tls_address = 0x9b, call_frame_cfa = 0x9c, bit_piece = 0x9d, // [ULEB128 size, ULEB128 offset] // DWARF 4 implicit_value = 0x9e, // [ULEB128 size, block of that size] stack_value = 0x9f, lo_user = 0xe0, hi_user = 0xff, }; std::string to_string(DW_OP v); // DW_AT::encoding constants (DWARF4 section 7.8 figure 25) enum class DW_ATE { address = 0x01, boolean = 0x02, complex_float = 0x03, float_ = 0x04, signed_ = 0x05, signed_char = 0x06, unsigned_ = 0x07, unsigned_char = 0x08, imaginary_float = 0x09, packed_decimal = 0x0a, numeric_string = 0x0b, edited = 0x0c, signed_fixed = 0x0d, unsigned_fixed = 0x0e, decimal_float = 0x0f, // DWARF 4 UTF = 0x10, lo_user = 0x80, hi_user = 0xff, }; std::string to_string(DW_ATE v); // DW_AT::decimal_sign constants (DWARF4 section 7.8 figure 26) enum class DW_DS { unsigned_ = 0x01, leading_overpunch = 0x02, trailing_overpunch = 0x03, leading_separate = 0x04, trailing_separate = 0x05, }; std::string to_string(DW_DS v); // DW_AT::endianity constants (DWARF4 section 7.8 figure 27) enum class DW_END { default_ = 0x00, big = 0x01, little = 0x02, lo_user = 0x40, hi_user = 0xff, }; std::string to_string(DW_END v); // DW_AT::accessibility constants (DWARF4 section 7.9 figure 28) enum class DW_ACCESS { public_ = 0x01, protected_ = 0x02, private_ = 0x03, }; std::string to_string(DW_ACCESS v); // DW_AT::visibility constants (DWARF4 section 7.10 figure 29) enum class DW_VIS { local = 0x01, exported = 0x02, qualified = 0x03, }; std::string to_string(DW_VIS v); // DW_AT::virtuality constants (DWARF4 section 7.11 figure 30) enum class DW_VIRTUALITY { none = 0x00, virtual_ = 0x01, pure_virtual = 0x02, }; std::string to_string(DW_VIRTUALITY v); // DW_AT::language constants (DWARF4 section 7.12 figure 31) enum class DW_LANG { C89 = 0x0001, // Lower bound 0 C = 0x0002, // Lower bound 0 Ada83 = 0x0003, // Lower bound 1 C_plus_plus = 0x0004, // Lower bound 0 Cobol74 = 0x0005, // Lower bound 1 Cobol85 = 0x0006, // Lower bound 1 Fortran77 = 0x0007, // Lower bound 1 Fortran90 = 0x0008, // Lower bound 1 Pascal83 = 0x0009, // Lower bound 1 Modula2 = 0x000a, // Lower bound 1 Java = 0x000b, // Lower bound 0 C99 = 0x000c, // Lower bound 0 Ada95 = 0x000d, // Lower bound 1 Fortran95 = 0x000e, // Lower bound 1 PLI = 0x000f, // Lower bound 1 ObjC = 0x0010, // Lower bound 0 ObjC_plus_plus = 0x0011, // Lower bound 0 UPC = 0x0012, // Lower bound 0 D = 0x0013, // Lower bound 0 Python = 0x0014, // Lower bound 0 lo_user = 0x8000, hi_user = 0xffff, }; std::string to_string(DW_LANG v); // DW_AT::identifier_case constants (DWARF4 section 7.14 figure 32) enum class DW_ID { case_sensitive = 0x00, up_case = 0x01, down_case = 0x02, case_insensitive = 0x03, }; std::string to_string(DW_ID v); // DW_AT::calling_convention constants (DWARF4 section 7.15 figure 33) enum class DW_CC { normal = 0x01, program = 0x02, nocall = 0x03, lo_user = 0x40, hi_user = 0xff, }; std::string to_string(DW_CC v); // DW_AT::inline constants (DWARF4 section 7.16 figure 34) enum class DW_INL { not_inlined = 0x00, inlined = 0x01, declared_not_inlined = 0x02, declared_inlined = 0x03, }; std::string to_string(DW_INL v); // DW_AT::ordering constants (DWARF4 section 7.17 figure 35) enum class DW_ORD { row_major = 0x00, col_major = 0x01, }; std::string to_string(DW_ORD v); // DW_AT::discr_list constants (DWARF4 section 7.18 figure 36) enum class DW_DSC { label = 0x00, range = 0x01, }; std::string to_string(DW_DSC v); // Line number standard opcodes (DWARF4 section 7.21 figure 37) enum class DW_LNS { copy = 0x01, advance_pc = 0x02, advance_line = 0x03, set_file = 0x04, set_column = 0x05, negate_stmt = 0x06, set_basic_block = 0x07, const_add_pc = 0x08, fixed_advance_pc = 0x09, // DWARF 3 set_prologue_end = 0x0a, set_epilogue_begin = 0x0b, set_isa = 0x0c, }; std::string to_string(DW_LNS v); // Line number extended opcodes (DWARF4 section 7.21 figure 38) enum class DW_LNE { end_sequence = 0x01, set_address = 0x02, define_file = 0x03, // DWARF 4 set_discriminator = 0x04, // DWARF 3 lo_user = 0x80, hi_user = 0xff, }; std::string to_string(DW_LNE v); DWARFPP_END_NAMESPACE #endif libelfin-0.3/dwarf/die.cc000066400000000000000000000125651302015676000153240ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "internal.hh" using namespace std; DWARFPP_BEGIN_NAMESPACE die::die(const unit *cu) : cu(cu), abbrev(nullptr) { } const unit & die::get_unit() const { return *cu; } section_offset die::get_section_offset() const { return cu->get_section_offset() + offset; } void die::read(section_offset off) { cursor cur(cu->data(), off); offset = off; abbrev_code acode = cur.uleb128(); if (acode == 0) { abbrev = nullptr; next = cur.get_section_offset(); return; } abbrev = &cu->get_abbrev(acode); tag = abbrev->tag; // XXX We can pre-compute almost all of this work in the // abbrev_entry. attrs.clear(); attrs.reserve(abbrev->attributes.size()); for (auto &attr : abbrev->attributes) { attrs.push_back(cur.get_section_offset()); cur.skip_form(attr.form); } next = cur.get_section_offset(); } bool die::has(DW_AT attr) const { if (!abbrev) return false; // XXX Totally lame for (auto &a : abbrev->attributes) if (a.name == attr) return true; return false; } value die::operator[](DW_AT attr) const { // XXX We can pre-compute almost all of this work in the // abbrev_entry. if (abbrev) { int i = 0; for (auto &a : abbrev->attributes) { if (a.name == attr) return value(cu, a.name, a.form, a.type, attrs[i]); i++; } } throw out_of_range("DIE does not have attribute " + to_string(attr)); } value die::resolve(DW_AT attr) const { // DWARF4 section 2.13, DWARF4 section 3.3.8 // DWARF4 is unclear about what to do when there's both a // DW_AT::specification and a DW_AT::abstract_origin. // Conceptually, though, a concrete inlined instance cannot // itself complete an external function that wasn't first // completed by its abstract instance, so we first try to // resolve abstract_origin, then we resolve specification. // XXX This traverses the abbrevs at least twice and // potentially several more times if (has(attr)) return (*this)[attr]; if (has(DW_AT::abstract_origin)) { die ao = (*this)[DW_AT::abstract_origin].as_reference(); if (ao.has(attr)) return ao[attr]; if (ao.has(DW_AT::specification)) { die s = ao[DW_AT::specification].as_reference(); if (s.has(attr)) return s[attr]; } } else if (has(DW_AT::specification)) { die s = (*this)[DW_AT::specification].as_reference(); if (s.has(attr)) return s[attr]; } return value(); } die::iterator die::begin() const { if (!abbrev || !abbrev->children) return end(); return iterator(cu, next); } die::iterator::iterator(const unit *cu, section_offset off) : d(cu) { d.read(off); } die::iterator & die::iterator::operator++() { if (!d.abbrev) return *this; if (!d.abbrev->children) { // The DIE has no children, so its successor follows // immediately d.read(d.next); } else if (d.has(DW_AT::sibling)) { // They made it easy on us. Follow the sibling // pointer. XXX Probably worth optimizing d = d[DW_AT::sibling].as_reference(); } else { // It's a hard-knock life. We have to iterate through // the children to find the next DIE. // XXX Particularly unfortunate if the user is doing a // DFS, since this will result in N^2 behavior. Maybe // a small cache of terminator locations in the CU? iterator sub(d.cu, d.next); while (sub->abbrev) ++sub; d.read(sub->next); } return *this; } const vector > die::attributes() const { vector > res; if (!abbrev) return res; // XXX Quite slow, especially when using this to traverse an // entire DIE tree since each DIE will produce a new vector // (whereas other vectors get reused). Might be worth a // custom iterator. int i = 0; for (auto &a : abbrev->attributes) { res.push_back(make_pair(a.name, value(cu, a.name, a.form, a.type, attrs[i]))); i++; } return res; } bool die::operator==(const die &o) const { return cu == o.cu && offset == o.offset; } bool die::operator!=(const die &o) const { return !(*this == o); } DWARFPP_END_NAMESPACE size_t std::hash::operator()(const dwarf::die &a) const { return hash()(a.cu) ^ hash()(a.get_unit_offset()); } libelfin-0.3/dwarf/die_str_map.cc000066400000000000000000000071361302015676000170470ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "internal.hh" #include #include using namespace std; // XXX Make this more readily available? namespace std { template<> struct hash { typedef size_t result_type; typedef dwarf::DW_TAG argument_type; result_type operator()(argument_type a) const { return (result_type)a; } }; } DWARFPP_BEGIN_NAMESPACE struct string_hash { typedef size_t result_type; typedef const char *argument_type; result_type operator()(const char *s) const { result_type h = 0; for (; *s; ++s) h += 33 * h + *s; return h; } }; struct string_eq { typedef bool result_type; typedef const char *first_argument_type; typedef const char *second_argument_type; bool operator()(const char *x, const char *y) const { return strcmp(x, y) == 0; } }; struct die_str_map::impl { impl(const die &parent, DW_AT attr, const initializer_list &accept) : attr(attr), accept(accept.begin(), accept.end()), pos(parent.begin()), end(parent.end()) { } unordered_map str_map; DW_AT attr; unordered_set accept; die::iterator pos, end; die invalid; }; die_str_map::die_str_map(const die &parent, DW_AT attr, const initializer_list &accept) : m(make_shared(parent, attr, accept)) { } die_str_map die_str_map::from_type_names(const die &parent) { return die_str_map (parent, DW_AT::name, // All DWARF type tags (this is everything that ends // with _type except thrown_type). {DW_TAG::array_type, DW_TAG::class_type, DW_TAG::enumeration_type, DW_TAG::pointer_type, DW_TAG::reference_type, DW_TAG::string_type, DW_TAG::structure_type, DW_TAG::subroutine_type, DW_TAG::union_type, DW_TAG::ptr_to_member_type, DW_TAG::set_type, DW_TAG::subrange_type, DW_TAG::base_type, DW_TAG::const_type, DW_TAG::file_type, DW_TAG::packed_type, DW_TAG::volatile_type, DW_TAG::restrict_type, DW_TAG::interface_type, DW_TAG::unspecified_type, DW_TAG::shared_type, DW_TAG::rvalue_reference_type}); } const die & die_str_map::operator[](const char *val) const { // Do we have this value? auto it = m->str_map.find(val); if (it != m->str_map.end()) return it->second; // Read more until we find the value or the end while (m->pos != m->end) { const die &d = *m->pos; ++m->pos; if (!m->accept.count(d.tag) || !d.has(m->attr)) continue; value dval(d[m->attr]); if (dval.get_type() != value::type::string) continue; const char *dstr = dval.as_cstr(); m->str_map[dstr] = d; if (strcmp(val, dstr) == 0) return m->str_map[dstr]; } // Not found return m->invalid; } DWARFPP_END_NAMESPACE libelfin-0.3/dwarf/dwarf++.hh000066400000000000000000001314671302015676000160310ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #ifndef _DWARFPP_HH_ #define _DWARFPP_HH_ #ifndef DWARFPP_BEGIN_NAMESPACE #define DWARFPP_BEGIN_NAMESPACE namespace dwarf { #define DWARFPP_END_NAMESPACE } #endif #include "data.hh" #include "small_vector.hh" #include #include #include #include #include #include DWARFPP_BEGIN_NAMESPACE // Forward declarations class dwarf; class loader; class compilation_unit; class type_unit; class die; class value; class expr; class expr_context; class expr_result; class rangelist; class line_table; // Internal type forward-declarations struct section; struct abbrev_entry; struct cursor; // XXX Audit for binary-compatibility // XXX Might be able to reduce private coupling by making class // section public (and clean it up and maybe rename it slice) and // provide methods to get the backing data of things. // // XXX Make slice generic, without formatting information? Still want // lightweight cursors, so maybe the cursor methods that need the // format should take a const reference to a format stored in the // compilation unit? // XXX operator==/!= and hash functions // XXX Indicate DWARF4 in all spec references // XXX Big missing support: .debug_aranges, .debug_frame, loclists, // macros ////////////////////////////////////////////////////////////////// // DWARF files // /** * An exception indicating malformed DWARF data. */ class format_error : public std::runtime_error { public: explicit format_error(const std::string &what_arg) : std::runtime_error(what_arg) { } explicit format_error(const char *what_arg) : std::runtime_error(what_arg) { } }; /** * DWARF section types. These correspond to the names of ELF * sections, though DWARF can be embedded in other formats. */ enum class section_type { abbrev, aranges, frame, info, line, loc, macinfo, pubnames, pubtypes, ranges, str, types, }; std::string to_string(section_type v); /** * A DWARF file. This class is internally reference counted and can * be efficiently copied. * * Objects retrieved from this object may depend on it; the caller is * responsible for keeping this object live as long as any retrieved * object may be in use. */ class dwarf { public: /** * Construct a DWARF file that is backed by sections read from * the given loader. */ explicit dwarf(const std::shared_ptr &l); /** * Construct a DWARF file that is initially not valid. */ dwarf() = default; dwarf(const dwarf&) = default; dwarf(dwarf&&) = default; ~dwarf(); dwarf& operator=(const dwarf &o) = default; dwarf& operator=(dwarf &&o) = default; bool operator==(const dwarf &o) const { return m == o.m; } bool operator!=(const dwarf &o) const { return m != o.m; } /** * Return true if this object represents a DWARF file. * Default constructed dwarf objects are not valid. */ bool valid() const { return !!m; } // XXX This allows the compilation units to be modified and // ties us to a vector. Probably should return an opaque // iterable collection over const references. /** * Return the list of compilation units in this DWARF file. */ const std::vector &compilation_units() const; /** * Return the type unit with the given signature. If the * signature does not correspond to a type unit, throws * out_of_range. */ const type_unit &get_type_unit(uint64_t type_signature) const; /** * \internal Retrieve the specified section from this file. * If the section does not exist, throws format_error. */ std::shared_ptr
get_section(section_type type) const; private: struct impl; std::shared_ptr m; }; /** * An interface for lazily loading DWARF sections. */ class loader { public: virtual ~loader() { } /** * Load the requested DWARF section into memory and return a * pointer to the beginning of it. This memory must remain * valid and unchanged until the loader is destroyed. If the * requested section does not exist, this should return * nullptr. If the section exists but cannot be loaded for * any reason, this should throw an exception. */ virtual const void *load(section_type section, size_t *size_out) = 0; }; /** * The base class for a compilation unit or type unit within a DWARF * file. A unit consists of a rooted tree of DIEs, plus additional * metadata that depends on the type of unit. */ class unit { public: virtual ~unit() = 0; bool operator==(const unit &o) const { return m == o.m; } bool operator!=(const unit &o) const { return m != o.m; } /** * Return true if this object is valid. Default constructed * unit objects are not valid. */ bool valid() const { return !!m; } /** * Return the dwarf file this unit is in. */ const dwarf &get_dwarf() const; /** * Return the byte offset of this unit's header in its * section (.debug_info or .debug_types). */ section_offset get_section_offset() const; /** * Return the root DIE of this unit. For a compilation unit, * this should be a DW_TAG::compilation_unit or * DW_TAG::partial_unit. */ const die &root() const; /** * \internal Return the data for this unit. */ const std::shared_ptr
&data() const; /** * \internal Return the abbrev for the specified abbrev * code. */ const abbrev_entry &get_abbrev(std::uint64_t acode) const; protected: friend struct ::std::hash; struct impl; std::shared_ptr m; }; /** * A compilation unit within a DWARF file. Most of the information * in a DWARF file is divided up by compilation unit. This class is * internally reference counted and can be efficiently copied. */ class compilation_unit : public unit { public: compilation_unit() = default; compilation_unit(const compilation_unit &o) = default; compilation_unit(compilation_unit &&o) = default; compilation_unit& operator=(const compilation_unit &o) = default; compilation_unit& operator=(compilation_unit &&o) = default; /** * \internal Construct a compilation unit whose header begins * offset bytes into the .debug_info section of file. */ compilation_unit(const dwarf &file, section_offset offset); /** * Return the line number table of this compilation unit. * Returns an invalid line table if this unit has no line * table. */ const line_table &get_line_table() const; }; /** * A type unit. Type units allow complex type information to be * shared between compilation units. */ class type_unit : public unit { public: type_unit() = default; type_unit(const type_unit &o) = default; type_unit(type_unit &&o) = default; type_unit &operator=(const type_unit &o) = default; type_unit &operator=(type_unit &&o) = default; /** * \internal Construct a type unit whose header begins offset * bytes into the .debug_types section of file. */ type_unit(const dwarf &file, section_offset offset); /** * Return the 64-bit unique signature that identifies this * type unit. This is how DIEs from other units refer to type * described by this unit. */ uint64_t get_type_signature() const; // XXX Can a type unit contain more than one top-level DIE? // The description of type_offset makes it sound like it // might. /** * Return the DIE of the type described by this type unit. * This may not be the root DIE of this unit if the type is * nested in namespaces or other structures. */ const die &type() const; }; ////////////////////////////////////////////////////////////////// // Debugging information entries (DIEs) // /** * A Debugging Information Entry, or DIE. The basic unit of * information in a DWARF file. */ class die { // XXX Make this class better for use in maps. Currently dies // are fairly big and expensive to copy, but most of that // information can be constructed lazily. This is also bad // for use in caches since it will keep the DWARF file alive. // OTOH, maybe caches need eviction anyway. public: DW_TAG tag; die() : cu(nullptr), abbrev(nullptr) { } die(const die &o) = default; die(die &&o) = default; die& operator=(const die &o) = default; die& operator=(die &&o) = default; /** * Return true if this object represents a DIE in a DWARF * file. Default constructed objects are not valid and some * methods return invalid DIEs to indicate failures. */ bool valid() const { return abbrev != nullptr; } /** * Return the unit containing this DIE. */ const unit &get_unit() const; /** * Return this DIE's byte offset within its compilation unit. */ section_offset get_unit_offset() const { return offset; } /** * Return this DIE's byte offset within its section. */ section_offset get_section_offset() const; /** * Return true if this DIE has the requested attribute. */ bool has(DW_AT attr) const; /** * Return the value of attr. Throws out_of_range if this DIE * does not have the specified attribute. It is generally * better to use the type-safe attribute getters (the global * functions beginning with at_*) when possible. */ value operator[](DW_AT attr) const; /** * Return the value of attr after resolving specification and * abstract origin references. If the attribute cannot be * resolved, returns an invalid value. Declaration DIEs can * "complete" a previous non-defining declaration DIE and * similarly inherit the non-defining declaration's attributes * (DWARF4 section 2.13) Likewise, any DIE that is a child of * a concrete inlined instance can specify another DIE as its * "abstract origin" and the original DIE will inherit the * attributes of its abstract origin (DWARF4 section 3.3.8.2). */ value resolve(DW_AT attr) const; class iterator; /** * Return an iterator over the children of this DIE. Note * that the DIEs returned by this iterator are temporary, so * if you need to store a DIE for more than one loop * iteration, you must copy it. */ iterator begin() const; iterator end() const; /** * Return a vector of the attributes of this DIE. */ const std::vector > attributes() const; bool operator==(const die &o) const; bool operator!=(const die &o) const; private: friend class unit; friend class type_unit; friend class value; // XXX If we can get the CU, we don't need this friend struct ::std::hash; const unit *cu; // The abbrev of this DIE. By convention, if this DIE // represents a sibling list terminator, this is null. This // object is kept live by the CU. const abbrev_entry *abbrev; // The beginning of this DIE, relative to the CU. section_offset offset; // Offsets of attributes, relative to cu's subsection. The // vast majority of DIEs tend to have six or fewer attributes, // so we reserve space in the DIE itself for six attributes. small_vector attrs; // The offset of the next DIE, relative to cu'd subsection. // This is set even for sibling list terminators. section_offset next; die(const unit *cu); /** * Read this DIE from the given offset in cu. */ void read(section_offset off); }; /** * An iterator over a sequence of sibling DIEs. */ class die::iterator { public: iterator() = default; iterator(const iterator &o) = default; iterator(iterator &&o) = default; iterator& operator=(const iterator &o) = default; iterator& operator=(iterator &&o) = default; const die &operator*() const { return d; } const die *operator->() const { return &d; } // XXX Make this less confusing by implementing operator== instead bool operator!=(const iterator &o) const { // Quick test of abbrevs. In particular, this weeds // out non-end against end, which is a common // comparison while iterating, though it also weeds // out many other things. if (d.abbrev != o.d.abbrev) return true; // Same, possibly NULL abbrev. If abbrev is NULL, // then next's are uncomparable, so we need to stop // now. We consider all ends to be the same, without // comparing cu's. if (d.abbrev == nullptr) return false; // Comparing two non-end abbrevs. return d.next != o.d.next || d.cu != o.d.cu; } iterator &operator++(); private: friend class die; iterator(const unit *cu, section_offset off); die d; }; inline die::iterator die::end() const { return iterator(); } /** * An exception indicating that a value is not of the requested type. */ class value_type_mismatch : public std::logic_error { public: explicit value_type_mismatch(const std::string &what_arg) : std::logic_error(what_arg) { } explicit value_type_mismatch(const char *what_arg) : std::logic_error(what_arg) { } }; /** * The value of a DIE attribute. * * This is logically a union of many different types. Each type has a * corresponding as_* methods that will return the value as that type * or throw value_type_mismatch if the attribute is not of the * requested type. * * Values of "constant" type are somewhat ambiguous and * context-dependent. Constant forms with specified signed-ness have * type "uconstant" or "sconstant", while other constant forms have * type "constant". If the value's type is "constant", it can be * retrieved using either as_uconstant or as_sconstant. * * Some other types can also be coerced. These are documented on the * individual as_* methods. * * There is no as_line; while there is an attribute for line tables, * line tables are really associated with compilation units (and * require additional context from the compilation unit). Use * compilation_unit::get_line_table instead. */ class value { public: enum class type { invalid, address, block, constant, uconstant, sconstant, exprloc, flag, line, loclist, mac, rangelist, reference, string }; /** * Construct a value with type `type::invalid`. */ value() : cu(nullptr), typ(type::invalid) { } value(const value &o) = default; value(value &&o) = default; value& operator=(const value &o) = default; value& operator=(value &&o) = default; /** * Return true if this object represents a valid value. * Default constructed line tables are not valid. */ bool valid() const { return typ != type::invalid; } /** * Return this value's byte offset within its compilation * unit. */ section_offset get_unit_offset() const { return offset; } /** * Return this value's byte offset within its section. */ section_offset get_section_offset() const; type get_type() const { return typ; } /** * Return this value's attribute encoding. This automatically * resolves indirect encodings, so this will never return * DW_FORM::indirect. Note that the mapping from forms to * types is non-trivial and often depends on the attribute * (especially prior to DWARF 4). */ DW_FORM get_form() const { return form; } /** * Return this value as a target machine address. */ taddr as_address() const; /** * Return this value as a block. The returned pointer points * directly into the section data, so the caller must ensure * that remains valid as long as the data is in use. * *size_out is set to the length of the returned block, in * bytes. * * This automatically coerces "exprloc" type values by * returning the raw bytes of the encoded expression. */ const void *as_block(size_t *size_out) const; /** * Return this value as an unsigned constant. This * automatically coerces "constant" type values by * interpreting their bytes as unsigned. */ uint64_t as_uconstant() const; /** * Return this value as a signed constant. This automatically * coerces "constant" type values by interpreting their bytes * as twos-complement signed values. */ int64_t as_sconstant() const; /** * Return this value as an expression. This automatically * coerces "block" type values by interpreting the bytes in * the block as an expression (prior to DWARF 4, exprlocs were * always encoded as blocks, though the library automatically * distinguishes these types based on context). */ expr as_exprloc() const; /** * Return this value as a boolean flag. */ bool as_flag() const; // XXX loclistptr, macptr /** * Return this value as a rangelist. */ rangelist as_rangelist() const; /** * For a reference type value, return the referenced DIE. * This DIE may be in a different compilation unit or could * be a DIE in a type unit. */ die as_reference() const; /** * Return this value as a string. */ std::string as_string() const; /** * Fill the given string buffer with the string value of this * value. This is useful to minimize allocation when reading * several string-type values. */ void as_string(std::string &buf) const; /** * Return this value as a NUL-terminated character string. * The returned pointer points directly into the section data, * so the caller must ensure that remains valid as long as the * data is in use. *size_out, if not NULL, is set to the * length of the returned string without the NUL-terminator. */ const char *as_cstr(size_t *size_out = nullptr) const; /** * Return this value as a section offset. This is applicable * to lineptr, loclistptr, macptr, and rangelistptr. */ section_offset as_sec_offset() const; private: friend class die; value(const unit *cu, DW_AT name, DW_FORM form, type typ, section_offset offset); void resolve_indirect(DW_AT name); const unit *cu; DW_FORM form; type typ; section_offset offset; }; std::string to_string(value::type v); std::string to_string(const value &v); ////////////////////////////////////////////////////////////////// // Expressions and location descriptions // /** * An exception during expression evaluation. */ class expr_error : public std::runtime_error { public: explicit expr_error(const std::string &what_arg) : std::runtime_error(what_arg) { } explicit expr_error(const char *what_arg) : std::runtime_error(what_arg) { } }; /** * A DWARF expression or location description. */ class expr { public: /** * Short-hand for evaluate(ctx, {}). */ expr_result evaluate(expr_context *ctx) const; /** * Short-hand for evaluate(ctx, {argument}). */ expr_result evaluate(expr_context *ctx, taddr argument) const; /** * Return the result of evaluating this expression using the * specified expression context. The expression stack will be * initialized with the given arguments such that the first * arguments is at the top of the stack and the last argument * at the bottom of the stack. * * Throws expr_error if there is an error evaluating the * expression (such as an unknown operation, stack underflow, * bounds error, etc.) */ expr_result evaluate(expr_context *ctx, const std::initializer_list &arguments) const; private: // XXX This will need more information for some operations expr(const unit *cu, section_offset offset, section_length len); friend class value; const unit *cu; section_offset offset; section_length len; }; /** * An interface that provides contextual information for expression * evaluation. Callers of expr::evaluate are expected to subclass * this in order to provide this information to the expression * evaluation engine. The default implementation throws expr_error * for all methods. */ class expr_context { public: virtual ~expr_context() { } /** * Return the value stored in register regnum. This is used * to implement DW_OP_breg* operations. */ virtual taddr reg(unsigned regnum) { throw expr_error("DW_OP_breg* operations not supported"); } /** * Implement DW_OP_deref_size. */ virtual taddr deref_size(taddr address, unsigned size) { throw expr_error("DW_OP_deref_size operations not supported"); } /** * Implement DW_OP_xderef_size. */ virtual taddr xderef_size(taddr address, taddr asid, unsigned size) { throw expr_error("DW_OP_xderef_size operations not supported"); } /** * Implement DW_OP_form_tls_address. */ virtual taddr form_tls_address(taddr address) { throw expr_error("DW_OP_form_tls_address operations not supported"); } }; /** * An instance of expr_context that throws expr_error for all methods. * This is equivalent to the default construction of expr_context, but * often more convenient to use. */ extern expr_context no_expr_context; // XXX Provide methods to check type and fetch value? /** * The result of evaluating a DWARF expression or location * description. */ class expr_result { public: enum class type { /** * value specifies the address in memory of an object. * This is also the result type used for general * expressions that do not refer to object locations. */ address, /** * value specifies a register storing an object. */ reg, /** * The object does not have a location. value is the * value of the object. */ literal, /** * The object does not have a location. Its value is * pointed to by the 'implicit' field. */ implicit, /** * The object is present in the source, but not in the * object code, and hence does not have a location or * a value. */ empty, }; /** * For location descriptions, the type of location this result * describes. */ type location_type; /** * For general-purpose expressions, the result of expression. * For address location descriptions, the address in memory of * the object. For register location descriptions, the * register storing the object. For literal location * descriptions, the value of the object. */ taddr value; /** * For implicit location descriptions, a pointer to a block * representing the value in the memory representation of the * target machine. */ const char *implicit; size_t implicit_len; // XXX Composite locations }; std::string to_string(expr_result::type v); ////////////////////////////////////////////////////////////////// // Range lists // /** * A DWARF range list describing a set of possibly non-contiguous * addresses. */ class rangelist { public: /** * \internal Construct a range list whose data begins at the * given offset in sec. cu_addr_size is the address size of * the associated compilation unit. cu_low_pc is the * DW_AT::low_pc attribute of the compilation unit containing * the referring DIE or 0 (this is used as the base address of * the range list). */ rangelist(const std::shared_ptr
&sec, section_offset off, unsigned cu_addr_size, taddr cu_low_pc); /** * Construct a range list from a sequence of {low, high} * pairs. */ rangelist(const std::initializer_list > &ranges); /** * Construct an empty range list. */ rangelist() = default; /** Copy constructor */ rangelist(const rangelist &o) = default; /** Move constructor */ rangelist(rangelist &&o) = default; rangelist& operator=(const rangelist &o) = default; rangelist& operator=(rangelist &&o) = default; class entry; typedef entry value_type; class iterator; /** * Return an iterator over the entries in this range list. * The ranges returned by this iterator are temporary, so if * you need to store a range for more than one loop iteration, * you must copy it. */ iterator begin() const; /** * Return an iterator to one past the last entry in this range * list. */ iterator end() const; /** * Return true if this range list contains the given address. */ bool contains(taddr addr) const; private: std::vector synthetic; std::shared_ptr
sec; taddr base_addr; }; /** * An entry in a range list. The range spans addresses [low, high). */ class rangelist::entry { public: taddr low, high; /** * Return true if addr is within this entry's bounds. */ bool contains(taddr addr) const { return low <= addr && addr < high; } }; /** * An iterator over a sequence of ranges in a range list. */ class rangelist::iterator { public: /** * \internal Construct an end iterator. */ iterator() : sec(nullptr), base_addr(0), pos(0) { } /** * \internal Construct an iterator that reads rangelist data * from the beginning of the given section and starts with the * given base address. */ iterator(const std::shared_ptr
&sec, taddr base_addr); /** Copy constructor */ iterator(const iterator &o) = default; /** Move constructor */ iterator(iterator &&o) = default; iterator& operator=(const iterator &o) = default; iterator& operator=(iterator &&o) = default; /** * Return the current range list entry. This entry is reused * internally, so the caller should copy it if it needs to * persist past the next increment. */ const rangelist::entry &operator*() const { return entry; } /** Dereference operator */ const rangelist::entry *operator->() const { return &entry; } /** Equality operator */ bool operator==(const iterator &o) const { return sec == o.sec && pos == o.pos; } /** Inequality operator */ bool operator!=(const iterator &o) const { return !(*this == o); } /** * Increment this iterator to point to the next range list * entry. */ iterator &operator++(); private: std::shared_ptr
sec; taddr base_addr; section_offset pos; rangelist::entry entry; }; ////////////////////////////////////////////////////////////////// // Line number tables // /** * A DWARF line number table. A line number table is a list of line * table entries, broken up into "sequences". Within a sequence, * entries are in order of increasing program counter ("address") and * an entry provides information for all program counters between the * entry's address and the address of the next entry. Each sequence * is terminated by a special entry with its * line_table::entry::end_sequence flag set. The line number table * also records the set of source files for a given compilation unit, * which can be referred to from other DIE attributes. */ class line_table { public: /** * \internal Construct a line number table whose header begins * at the given offset in sec. cu_addr_size is the address * size of the associated compilation unit. cu_comp_dir and * cu_name give the DW_AT::comp_dir and DW_AT::name attributes * of the associated compilation unit. */ line_table(const std::shared_ptr
&sec, section_offset offset, unsigned cu_addr_size, const std::string &cu_comp_dir, const std::string &cu_name); /** * Construct an invalid, empty line table. */ line_table() = default; /** Copy constructor */ line_table(const line_table &o) = default; /** Move constructor */ line_table(line_table &&o) = default; line_table &operator=(const line_table &o) = default; line_table &operator=(line_table &&o) = default; /** * Return true if this object represents an initialized line * table. Default constructed line tables are not valid. */ bool valid() const { return !!m; } class file; class entry; typedef entry value_type; class iterator; /** * Return an iterator to the beginning of this line number * table. If called on an invalid line table, this will * return an iterator equal to end(). */ iterator begin() const; /** * Return an iterator to one past the last entry in this line * number table. */ iterator end() const; /** * Return an iterator to the line table entry containing addr * (roughly, the entry with the highest address less than or * equal to addr, but accounting for end_sequence entries). * Returns end() if there is no such entry. */ iterator find_address(taddr addr) const; /** * Return the index'th file in the line table. These indexes * are typically used by declaration and call coordinates. If * index is out of range, throws out_of_range. */ const file *get_file(unsigned index) const; private: friend class iterator; struct impl; std::shared_ptr m; }; /** * A source file in a line table. */ class line_table::file { public: /** * The absolute path of this source file. */ std::string path; /** * The last modification time of this source file in an * implementation-defined encoding or 0 if unknown. */ uint64_t mtime; /** * The size in bytes of this source file or 0 if unknown. */ uint64_t length; /** * Construct a source file object. */ file(std::string path = "", uint64_t mtime = 0, uint64_t length = 0); }; /** * An entry in the line table. */ class line_table::entry { public: /** * The program counter value corresponding to a machine * instruction generated by the compiler. */ taddr address; /** * The index of an operation within a VLIW instruction. The * index of the first operation is 0. For non-VLIW * architectures, this will always be 0. */ unsigned op_index; /** * The source file containing this instruction. */ const line_table::file *file; /** * The index of the source file containing this instruction. */ unsigned file_index; /** * The source line number of this instruction, starting at 1. * This may be 0 if this instruction cannot be attributed to * any source line. */ unsigned line; /** * The column number within this source line, starting at 1. * The value 0 indicates that a statement begins at the "left * edge" of the line, whatever that means. */ unsigned column; /** * True if this instruction is a recommended breakpoint * location. Typically this is the beginning of a statement. */ bool is_stmt; /** * True if this instruction is the beginning of a basic block. */ bool basic_block; /** * True if this address is the first byte after the end of a * sequence of target machine instructions. In this case, all * other fields besides address are not meaningful. */ bool end_sequence; /** * True if this address is one where execution should be * suspended for an entry breakpoint of a function. */ bool prologue_end; /** * True if this address is one where execution should be * suspended for an exit breakpoint of a function. */ bool epilogue_begin; /** * The instruction set architecture of this instruction. The * meaning of this field is generally defined by an * architecture's ABI. */ unsigned isa; /** * A number that identifies the block containing the current * instruction if multiple blocks are associated with the same * source file, line, and column. */ unsigned discriminator; /** * Reset this line info object to the default initial values * for all fields. is_stmt has no default value, so the * caller must provide it. */ void reset(bool is_stmt); /** * Return a descriptive string of the form * "filename[:line[:column]]". */ std::string get_description() const; }; /** * An iterator over the entries in a line table. */ class line_table::iterator { public: /** * \internal Construct an iterator for the given line table * starting pos bytes into the table's section. */ iterator(const line_table *table, section_offset pos); /** Copy constructor */ iterator(const iterator &o) = default; /** Move constructor */ iterator(iterator &&o) = default; iterator &operator=(const iterator &o) = default; iterator &operator=(iterator &&o) = default; /** * Return the current line table entry. This entry is reused * internally, so the caller should copy it if it needs to * persist past the next increment. */ const line_table::entry &operator*() const { return entry; } /** Dereference operator */ const line_table::entry *operator->() const { return &entry; } /** Equality operator */ bool operator==(const iterator &o) const { return o.pos == pos && o.table == table; } /** Inequality operator */ bool operator!=(const iterator &o) const { return !(*this == o); } /** * Increment this iterator to point to the next line table * entry. */ iterator &operator++(); /** Post-increment operator */ iterator operator++(int) { iterator tmp(*this); ++(*this); return tmp; } private: const line_table *table; line_table::entry entry, regs; section_offset pos; /** * Process the next opcode. If the opcode "adds a row to the * table", update entry to reflect the row and return true. */ bool step(cursor *cur); }; ////////////////////////////////////////////////////////////////// // Type-safe attribute getters // // XXX More die at_abstract_origin(const die &d); DW_ACCESS at_accessibility(const die &d); uint64_t at_allocated(const die &d, expr_context *ctx); bool at_artificial(const die &d); uint64_t at_associated(const die &d, expr_context *ctx); uint64_t at_bit_offset(const die &d, expr_context *ctx); uint64_t at_bit_size(const die &d, expr_context *ctx); uint64_t at_bit_stride(const die &d, expr_context *ctx); uint64_t at_byte_size(const die &d, expr_context *ctx); uint64_t at_byte_stride(const die &d, expr_context *ctx); DW_CC at_calling_convention(const die &d); die at_common_reference(const die &d); std::string at_comp_dir(const die &d); value at_const_value(const die &d); bool at_const_expr(const die &d); die at_containing_type(const die &d); uint64_t at_count(const die &d, expr_context *ctx); expr_result at_data_member_location(const die &d, expr_context *ctx, taddr base, taddr pc); bool at_declaration(const die &d); std::string at_description(const die &d); die at_discr(const die &d); value at_discr_value(const die &d); bool at_elemental(const die &d); DW_ATE at_encoding(const die &d); DW_END at_endianity(const die &d); taddr at_entry_pc(const die &d); bool at_enum_class(const die &d); bool at_explicit(const die &d); die at_extension(const die &d); bool at_external(const die &d); die at_friend(const die &d); taddr at_high_pc(const die &d); DW_ID at_identifier_case(const die &d); die at_import(const die &d); DW_INL at_inline(const die &d); bool at_is_optional(const die &d); DW_LANG at_language(const die &d); std::string at_linkage_name(const die &d); taddr at_low_pc(const die &d); uint64_t at_lower_bound(const die &d, expr_context *ctx); bool at_main_subprogram(const die &d); bool at_mutable(const die &d); std::string at_name(const die &d); die at_namelist_item(const die &d); die at_object_pointer(const die &d); DW_ORD at_ordering(const die &d); std::string at_picture_string(const die &d); die at_priority(const die &d); std::string at_producer(const die &d); bool at_prototyped(const die &d); bool at_pure(const die &d); rangelist at_ranges(const die &d); bool at_recursive(const die &d); die at_sibling(const die &d); die at_signature(const die &d); die at_small(const die &d); die at_specification(const die &d); bool at_threads_scaled(const die &d); die at_type(const die &d); uint64_t at_upper_bound(const die &d, expr_context *ctx); bool at_use_UTF8(const die &d); bool at_variable_parameter(const die &d); DW_VIRTUALITY at_virtuality(const die &d); DW_VIS at_visibility(const die &d); /** * Return the PC range spanned by the code of a DIE. The DIE must * either have DW_AT::ranges or DW_AT::low_pc. It may optionally have * DW_AT::high_pc. */ rangelist die_pc_range(const die &d); ////////////////////////////////////////////////////////////////// // Utilities // /** * An index of sibling DIEs by some string attribute. This index is * lazily constructed and space-efficient. */ class die_str_map { public: /** * Construct the index of the attr attribute of all immediate * children of parent whose tags are in accept. */ die_str_map(const die &parent, DW_AT attr, const std::initializer_list &accept); die_str_map() = default; die_str_map(const die_str_map &o) = default; die_str_map(die_str_map &&o) = default; die_str_map& operator=(const die_str_map &o) = default; die_str_map& operator=(die_str_map &&o) = default; /** * Construct a string map for the type names of parent's * immediate children. * * XXX This should use .debug_pubtypes if parent is a compile * unit's root DIE, but it currently does not. */ static die_str_map from_type_names(const die &parent); /** * Return the DIE whose attribute matches val. If no such DIE * exists, return an invalid die object. */ const die &operator[](const char *val) const; /** * Short-hand for [value.c_str()]. */ const die &operator[](const std::string &val) const { return (*this)[val.c_str()]; } private: struct impl; std::shared_ptr m; }; ////////////////////////////////////////////////////////////////// // ELF support // namespace elf { /** * Translate an ELF section name info a DWARF section type. * If the section is a valid DWARF section name, sets *out to * the type and returns true. If not, returns false. */ bool section_name_to_type(const char *name, section_type *out); /** * Translate a DWARF section type into an ELF section name. */ const char *section_type_to_name(section_type type); template class elf_loader : public loader { Elf f; public: elf_loader(const Elf &file) : f(file) { } const void *load(section_type section, size_t *size_out) { auto sec = f.get_section(section_type_to_name(section)); if (!sec.valid()) return nullptr; *size_out = sec.size(); return sec.data(); } }; /** * Create a DWARF section loader backed by the given ELF * file. This is templatized to eliminate a static dependency * between the libelf++ and libdwarf++, though it can only * reasonably be used with elf::elf from libelf++. */ template std::shared_ptr > create_loader(const Elf &f) { return std::make_shared >(f); } }; DWARFPP_END_NAMESPACE ////////////////////////////////////////////////////////////////// // Hash specializations // namespace std { template<> struct hash { typedef size_t result_type; typedef const dwarf::unit &argument_type; result_type operator()(argument_type a) const { return hash()(a.m); } }; template<> struct hash { typedef size_t result_type; typedef const dwarf::die &argument_type; result_type operator()(argument_type a) const; }; } #endif libelfin-0.3/dwarf/dwarf.cc000066400000000000000000000256621302015676000156700ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "internal.hh" using namespace std; DWARFPP_BEGIN_NAMESPACE ////////////////////////////////////////////////////////////////// // class dwarf // struct dwarf::impl { impl(const std::shared_ptr &l) : l(l), have_type_units(false) { } std::shared_ptr l; std::shared_ptr
sec_info; std::shared_ptr
sec_abbrev; std::vector compilation_units; std::unordered_map type_units; bool have_type_units; std::map > sections; }; dwarf::dwarf(const std::shared_ptr &l) : m(make_shared(l)) { const void *data; size_t size; // Get required sections data = l->load(section_type::info, &size); if (!data) throw format_error("required .debug_info section missing"); m->sec_info = make_shared
(section_type::info, data, size, byte_order::lsb); // Sniff the endianness from the version field of the first // CU. This is always a small but non-zero integer. cursor endcur(m->sec_info); // Skip length. section_length length = endcur.fixed(); if (length == 0xffffffff) endcur.fixed(); // Get version in both little and big endian. uhalf version = endcur.fixed(); uhalf versionbe = (version >> 8) | ((version & 0xFF) << 8); if (versionbe < version) { m->sec_info = make_shared
(section_type::info, data, size, byte_order::msb); } data = l->load(section_type::abbrev, &size); if (!data) throw format_error("required .debug_abbrev section missing"); m->sec_abbrev = make_shared
(section_type::abbrev, data, size, m->sec_info->ord); // Get compilation units. Everything derives from these, so // there's no point in doing it lazily. cursor infocur(m->sec_info); while (!infocur.end()) { // XXX Circular reference. Given that we now require // the dwarf object to stick around for DIEs, maybe we // might as well require that for units, too. m->compilation_units.emplace_back( *this, infocur.get_section_offset()); infocur.subsection(); } } dwarf::~dwarf() { } const std::vector & dwarf::compilation_units() const { static std::vector empty; if (!m) return empty; return m->compilation_units; } const type_unit & dwarf::get_type_unit(uint64_t type_signature) const { if (!m->have_type_units) { cursor tucur(get_section(section_type::types)); while (!tucur.end()) { // XXX Circular reference type_unit tu(*this, tucur.get_section_offset()); m->type_units[tu.get_type_signature()] = tu; tucur.subsection(); } m->have_type_units = true; } if (!m->type_units.count(type_signature)) throw out_of_range("type signature 0x" + to_hex(type_signature)); return m->type_units[type_signature]; } std::shared_ptr
dwarf::get_section(section_type type) const { if (type == section_type::info) return m->sec_info; if (type == section_type::abbrev) return m->sec_abbrev; auto it = m->sections.find(type); if (it != m->sections.end()) return it->second; size_t size; const void *data = m->l->load(type, &size); if (!data) throw format_error(std::string(elf::section_type_to_name(type)) + " section missing"); m->sections[type] = std::make_shared
(section_type::str, data, size, m->sec_info->ord); return m->sections[type]; } ////////////////////////////////////////////////////////////////// // class unit // /** * Implementation of a unit. */ struct unit::impl { const dwarf file; const section_offset offset; const std::shared_ptr
subsec; const section_offset debug_abbrev_offset; const section_offset root_offset; // Type unit-only values const uint64_t type_signature; const section_offset type_offset; // Lazily constructed root and type DIEs die root, type; // Lazily constructed line table line_table lt; // Map from abbrev code to abbrev. If the map is dense, it // will be stored in the vector; otherwise it will be stored // in the map. bool have_abbrevs; std::vector abbrevs_vec; std::unordered_map abbrevs_map; impl(const dwarf &file, section_offset offset, const std::shared_ptr
&subsec, section_offset debug_abbrev_offset, section_offset root_offset, uint64_t type_signature = 0, section_offset type_offset = 0) : file(file), offset(offset), subsec(subsec), debug_abbrev_offset(debug_abbrev_offset), root_offset(root_offset), type_signature(type_signature), type_offset(type_offset), have_abbrevs(false) { } void force_abbrevs(); }; unit::~unit() { } const dwarf & unit::get_dwarf() const { return m->file; } section_offset unit::get_section_offset() const { return m->offset; } const die& unit::root() const { if (!m->root.valid()) { m->force_abbrevs(); m->root = die(this); m->root.read(m->root_offset); } return m->root; } const std::shared_ptr
& unit::data() const { return m->subsec; } const abbrev_entry & unit::get_abbrev(abbrev_code acode) const { if (!m->have_abbrevs) m->force_abbrevs(); if (!m->abbrevs_vec.empty()) { if (acode >= m->abbrevs_vec.size()) goto unknown; const abbrev_entry &entry = m->abbrevs_vec[acode]; if (entry.code == 0) goto unknown; return entry; } else { auto it = m->abbrevs_map.find(acode); if (it == m->abbrevs_map.end()) goto unknown; return it->second; } unknown: throw format_error("unknown abbrev code 0x" + to_hex(acode)); } void unit::impl::force_abbrevs() { // XXX Compilation units can share abbrevs. Parse each table // at most once. if (have_abbrevs) return; // Section 7.5.3 cursor c(file.get_section(section_type::abbrev), debug_abbrev_offset); abbrev_entry entry; abbrev_code highest = 0; while (entry.read(&c)) { abbrevs_map[entry.code] = entry; if (entry.code > highest) highest = entry.code; } // Typically, abbrev codes are assigned linearly, so it's more // space efficient and time efficient to store the table in a // vector. Convert to a vector if it's dense enough, by some // rough estimate of "enough". if (highest * 10 < abbrevs_map.size() * 15) { // Move the map into the vector abbrevs_vec.resize(highest + 1); for (auto &entry : abbrevs_map) abbrevs_vec[entry.first] = move(entry.second); abbrevs_map.clear(); } have_abbrevs = true; } ////////////////////////////////////////////////////////////////// // class compilation_unit // compilation_unit::compilation_unit(const dwarf &file, section_offset offset) { // Read the CU header (DWARF4 section 7.5.1.1) cursor cur(file.get_section(section_type::info), offset); std::shared_ptr
subsec = cur.subsection(); cursor sub(subsec); sub.skip_initial_length(); uhalf version = sub.fixed(); if (version < 2 || version > 4) throw format_error("unknown compilation unit version " + std::to_string(version)); // .debug_abbrev-relative offset of this unit's abbrevs section_offset debug_abbrev_offset = sub.offset(); ubyte address_size = sub.fixed(); subsec->addr_size = address_size; m = make_shared(file, offset, subsec, debug_abbrev_offset, sub.get_section_offset()); } const line_table & compilation_unit::get_line_table() const { if (!m->lt.valid()) { const die &d = root(); if (!d.has(DW_AT::stmt_list) || !d.has(DW_AT::name) || !d.has(DW_AT::comp_dir)) goto done; shared_ptr
sec; try { sec = m->file.get_section(section_type::line); } catch (format_error &e) { goto done; } m->lt = line_table(sec, d[DW_AT::stmt_list].as_sec_offset(), m->subsec->addr_size, at_comp_dir(d), at_name(d)); } done: return m->lt; } ////////////////////////////////////////////////////////////////// // class type_unit // type_unit::type_unit(const dwarf &file, section_offset offset) { // Read the type unit header (DWARF4 section 7.5.1.2) cursor cur(file.get_section(section_type::types), offset); std::shared_ptr
subsec = cur.subsection(); cursor sub(subsec); sub.skip_initial_length(); uhalf version = sub.fixed(); if (version != 4) throw format_error("unknown type unit version " + std::to_string(version)); // .debug_abbrev-relative offset of this unit's abbrevs section_offset debug_abbrev_offset = sub.offset(); ubyte address_size = sub.fixed(); subsec->addr_size = address_size; uint64_t type_signature = sub.fixed(); section_offset type_offset = sub.offset(); m = make_shared(file, offset, subsec, debug_abbrev_offset, sub.get_section_offset(), type_signature, type_offset); } uint64_t type_unit::get_type_signature() const { return m->type_signature; } const die & type_unit::type() const { if (!m->type.valid()) { m->force_abbrevs(); m->type = die(this); m->type.read(m->type_offset); } return m->type; } DWARFPP_END_NAMESPACE libelfin-0.3/dwarf/elf.cc000066400000000000000000000027071302015676000153260ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "dwarf++.hh" #include using namespace std; DWARFPP_BEGIN_NAMESPACE static const struct { const char *name; section_type type; } sections[] = { {".debug_abbrev", section_type::abbrev}, {".debug_aranges", section_type::aranges}, {".debug_frame", section_type::frame}, {".debug_info", section_type::info}, {".debug_line", section_type::line}, {".debug_loc", section_type::loc}, {".debug_macinfo", section_type::macinfo}, {".debug_pubnames", section_type::pubnames}, {".debug_pubtypes", section_type::pubtypes}, {".debug_ranges", section_type::ranges}, {".debug_str", section_type::str}, {".debug_types", section_type::types}, }; bool elf::section_name_to_type(const char *name, section_type *out) { for (auto &sec : sections) { if (strcmp(sec.name, name) == 0) { *out = sec.type; return true; } } return false; } const char * elf::section_type_to_name(section_type type) { for (auto &sec : sections) { if (sec.type == type) return sec.name; } return nullptr; } DWARFPP_END_NAMESPACE libelfin-0.3/dwarf/expr.cc000066400000000000000000000420411302015676000155310ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "internal.hh" using namespace std; DWARFPP_BEGIN_NAMESPACE expr_context no_expr_context; expr::expr(const unit *cu, section_offset offset, section_length len) : cu(cu), offset(offset), len(len) { } expr_result expr::evaluate(expr_context *ctx) const { return evaluate(ctx, {}); } expr_result expr::evaluate(expr_context *ctx, taddr argument) const { return evaluate(ctx, {argument}); } expr_result expr::evaluate(expr_context *ctx, const std::initializer_list &arguments) const { // The stack machine's stack. The top of the stack is // stack.back(). // XXX This stack must be in target machine representation, // since I see both (DW_OP_breg0 (eax): -28; DW_OP_stack_value) // and (DW_OP_lit1; DW_OP_stack_value). small_vector stack; // Create the initial stack. arguments are in reverse order // (that is, element 0 is TOS), so reverse it. stack.reserve(arguments.size()); for (const taddr *elt = arguments.end() - 1; elt >= arguments.begin(); elt--) stack.push_back(*elt); // Create a subsection for just this expression so we can // easily detect the end (including premature end). auto cusec = cu->data(); shared_ptr
subsec (make_shared
(cusec->type, cusec->begin + offset, len, cusec->ord, cusec->fmt, cusec->addr_size)); cursor cur(subsec); // Prepare the expression result. Some location descriptions // create the result directly, rather than using the top of // stack. expr_result result; // 2.6.1.1.4 Empty location descriptions if (cur.end()) { result.location_type = expr_result::type::empty; result.value = 0; return result; } // Assume the result is an address for now and should be // grabbed from the top of stack at the end. result.location_type = expr_result::type::address; // Execute! while (!cur.end()) { #define CHECK() do { if (stack.empty()) goto underflow; } while (0) #define CHECKN(n) do { if (stack.size() < n) goto underflow; } while (0) union { uint64_t u; int64_t s; } tmp1, tmp2, tmp3; static_assert(sizeof(tmp1) == sizeof(taddr), "taddr is not 64 bits"); // Tell GCC to warn us about missing switch cases, // even though we have a default case. #pragma GCC diagnostic push #pragma GCC diagnostic warning "-Wswitch-enum" DW_OP op = (DW_OP)cur.fixed(); switch (op) { // 2.5.1.1 Literal encodings case DW_OP::lit0...DW_OP::lit31: stack.push_back((unsigned)op - (unsigned)DW_OP::lit0); break; case DW_OP::addr: stack.push_back(cur.address()); break; case DW_OP::const1u: stack.push_back(cur.fixed()); break; case DW_OP::const2u: stack.push_back(cur.fixed()); break; case DW_OP::const4u: stack.push_back(cur.fixed()); break; case DW_OP::const8u: stack.push_back(cur.fixed()); break; case DW_OP::const1s: stack.push_back(cur.fixed()); break; case DW_OP::const2s: stack.push_back(cur.fixed()); break; case DW_OP::const4s: stack.push_back(cur.fixed()); break; case DW_OP::const8s: stack.push_back(cur.fixed()); break; case DW_OP::constu: stack.push_back(cur.uleb128()); break; case DW_OP::consts: stack.push_back(cur.sleb128()); break; // 2.5.1.2 Register based addressing case DW_OP::fbreg: // XXX throw runtime_error("DW_OP_fbreg not implemented"); case DW_OP::breg0...DW_OP::breg31: tmp1.u = (unsigned)op - (unsigned)DW_OP::breg0; tmp2.s = cur.sleb128(); stack.push_back((int64_t)ctx->reg(tmp1.u) + tmp2.s); break; case DW_OP::bregx: tmp1.u = cur.uleb128(); tmp2.s = cur.sleb128(); stack.push_back((int64_t)ctx->reg(tmp1.u) + tmp2.s); break; // 2.5.1.3 Stack operations case DW_OP::dup: CHECK(); stack.push_back(stack.back()); break; case DW_OP::drop: CHECK(); stack.pop_back(); break; case DW_OP::pick: tmp1.u = cur.fixed(); CHECKN(tmp1.u); stack.push_back(stack.revat(tmp1.u)); break; case DW_OP::over: CHECKN(2); stack.push_back(stack.revat(1)); break; case DW_OP::swap: CHECKN(2); tmp1.u = stack.back(); stack.back() = stack.revat(1); stack.revat(1) = tmp1.u; break; case DW_OP::rot: CHECKN(3); tmp1.u = stack.back(); stack.back() = stack.revat(1); stack.revat(1) = stack.revat(2); stack.revat(2) = tmp1.u; break; case DW_OP::deref: tmp1.u = subsec->addr_size; goto deref_common; case DW_OP::deref_size: tmp1.u = cur.fixed(); if (tmp1.u > subsec->addr_size) throw expr_error("DW_OP_deref_size operand exceeds address size"); deref_common: CHECK(); stack.back() = ctx->deref_size(stack.back(), tmp1.u); break; case DW_OP::xderef: tmp1.u = subsec->addr_size; goto xderef_common; case DW_OP::xderef_size: tmp1.u = cur.fixed(); if (tmp1.u > subsec->addr_size) throw expr_error("DW_OP_xderef_size operand exceeds address size"); xderef_common: CHECKN(2); tmp2.u = stack.back(); stack.pop_back(); stack.back() = ctx->xderef_size(tmp2.u, stack.back(), tmp1.u); break; case DW_OP::push_object_address: // XXX throw runtime_error("DW_OP_push_object_address not implemented"); case DW_OP::form_tls_address: CHECK(); stack.back() = ctx->form_tls_address(stack.back()); break; case DW_OP::call_frame_cfa: // XXX throw runtime_error("DW_OP_call_frame_cfa not implemented"); // 2.5.1.4 Arithmetic and logical operations #define UBINOP(binop) \ do { \ CHECKN(2); \ tmp1.u = stack.back(); \ stack.pop_back(); \ tmp2.u = stack.back(); \ stack.back() = tmp2.u binop tmp1.u; \ } while (0) case DW_OP::abs: CHECK(); tmp1.u = stack.back(); if (tmp1.s < 0) tmp1.s = -tmp1.s; stack.back() = tmp1.u; break; case DW_OP::and_: UBINOP(&); break; case DW_OP::div: CHECKN(2); tmp1.u = stack.back(); stack.pop_back(); tmp2.u = stack.back(); tmp3.s = tmp1.s / tmp2.s; stack.back() = tmp3.u; break; case DW_OP::minus: UBINOP(-); break; case DW_OP::mod: UBINOP(%); break; case DW_OP::mul: UBINOP(*); break; case DW_OP::neg: CHECK(); tmp1.u = stack.back(); tmp1.s = -tmp1.s; stack.back() = tmp1.u; break; case DW_OP::not_: CHECK(); stack.back() = ~stack.back(); break; case DW_OP::or_: UBINOP(|); break; case DW_OP::plus: UBINOP(+); break; case DW_OP::plus_uconst: tmp1.u = cur.uleb128(); CHECK(); stack.back() += tmp1.u; break; case DW_OP::shl: CHECKN(2); tmp1.u = stack.back(); stack.pop_back(); tmp2.u = stack.back(); // C++ does not define what happens if you // shift by more bits than the width of the // type, so we handle this case specially if (tmp1.u < sizeof(tmp2.u)*8) stack.back() = tmp2.u << tmp1.u; else stack.back() = 0; break; case DW_OP::shr: CHECKN(2); tmp1.u = stack.back(); stack.pop_back(); tmp2.u = stack.back(); // Same as above if (tmp1.u < sizeof(tmp2.u)*8) stack.back() = tmp2.u >> tmp1.u; else stack.back() = 0; break; case DW_OP::shra: CHECKN(2); tmp1.u = stack.back(); stack.pop_back(); tmp2.u = stack.back(); // Shifting a negative number is // implementation-defined in C++. tmp3.u = (tmp2.s < 0); if (tmp3.u) tmp2.s = -tmp2.s; if (tmp1.u < sizeof(tmp2.u)*8) tmp2.u >>= tmp1.u; else tmp2.u = 0; // DWARF implies that over-shifting a negative // number should result in 0, not ~0. if (tmp3.u) tmp2.s = -tmp2.s; stack.back() = tmp2.u; break; case DW_OP::xor_: UBINOP(^); break; #undef UBINOP // 2.5.1.5 Control flow operations #define SRELOP(relop) \ do { \ CHECKN(2); \ tmp1.u = stack.back(); \ stack.pop_back(); \ tmp2.u = stack.back(); \ stack.back() = (tmp2.s <= tmp1.s) ? 1 : 0; \ } while (0) case DW_OP::le: SRELOP(<=); break; case DW_OP::ge: SRELOP(>=); break; case DW_OP::eq: SRELOP(==); break; case DW_OP::lt: SRELOP(<); break; case DW_OP::gt: SRELOP(>); break; case DW_OP::ne: SRELOP(!=); break; case DW_OP::skip: tmp1.s = cur.fixed(); goto skip_common; case DW_OP::bra: tmp1.s = cur.fixed(); CHECK(); tmp2.u = stack.back(); stack.pop_back(); if (tmp2.u == 0) break; skip_common: cur = cursor(subsec, (int64_t)cur.get_section_offset() + tmp1.s); break; case DW_OP::call2: case DW_OP::call4: case DW_OP::call_ref: // XXX throw runtime_error(to_string(op) + " not implemented"); #undef SRELOP // 2.5.1.6 Special operations case DW_OP::nop: break; // 2.6.1.1.2 Register location descriptions case DW_OP::reg0...DW_OP::reg31: result.location_type = expr_result::type::reg; result.value = (unsigned)op - (unsigned)DW_OP::reg0; break; case DW_OP::regx: result.location_type = expr_result::type::reg; result.value = cur.uleb128(); break; // 2.6.1.1.3 Implicit location descriptions case DW_OP::implicit_value: result.location_type = expr_result::type::implicit; result.implicit_len = cur.uleb128(); cur.ensure(result.implicit_len); result.implicit = cur.pos; break; case DW_OP::stack_value: CHECK(); result.location_type = expr_result::type::literal; result.value = stack.back(); break; // 2.6.1.2 Composite location descriptions case DW_OP::piece: case DW_OP::bit_piece: // XXX throw runtime_error(to_string(op) + " not implemented"); case DW_OP::lo_user...DW_OP::hi_user: // XXX We could let the context evaluate this, // but it would need access to the cursor. throw expr_error("unknown user op " + to_string(op)); default: throw expr_error("bad operation " + to_string(op)); } #pragma GCC diagnostic pop #undef CHECK #undef CHECKN } if (result.location_type == expr_result::type::address) { // The result type is still and address, so we should // fetch it from the top of stack. if (stack.empty()) throw expr_error("final stack is empty; no result given"); result.value = stack.back(); } return result; underflow: throw expr_error("stack underflow evaluating DWARF expression"); } DWARFPP_END_NAMESPACE libelfin-0.3/dwarf/internal.hh000066400000000000000000000175641302015676000164150ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #ifndef _DWARFPP_INTERNAL_HH_ #define _DWARFPP_INTERNAL_HH_ #include "dwarf++.hh" #include "../elf/to_hex.hh" #include #include #include #include DWARFPP_BEGIN_NAMESPACE enum class format { unknown, dwarf32, dwarf64 }; enum class byte_order { lsb, msb }; /** * Return this system's native byte order. */ static inline byte_order native_order() { static const union { int i; char c[sizeof(int)]; } test = {1}; return test.c[0] == 1 ? byte_order::lsb : byte_order::msb; } /** * A single DWARF section or a slice of a section. This also tracks * dynamic information necessary to decode values in this section. */ struct section { section_type type; const char *begin, *end; const format fmt; const byte_order ord; unsigned addr_size; section(section_type type, const void *begin, section_length length, byte_order ord, format fmt = format::unknown, unsigned addr_size = 0) : type(type), begin((char*)begin), end((char*)begin + length), fmt(fmt), ord(ord), addr_size(addr_size) { } section(const section &o) = default; std::shared_ptr
slice(section_offset start, section_length len, format fmt = format::unknown, unsigned addr_size = 0) { if (fmt == format::unknown) fmt = this->fmt; if (addr_size == 0) addr_size = this->addr_size; return std::make_shared
( type, begin+start, std::min(len, (section_length)(end-begin)), ord, fmt, addr_size); } size_t size() const { return end - begin; } }; /** * A cursor pointing into a DWARF section. Provides deserialization * operations and bounds checking. */ struct cursor { // XXX There's probably a lot of overhead to maintaining the // shared pointer to the section from this. Perhaps the rule // should be that all objects keep the dwarf::impl alive // (directly or indirectly) and that keeps the loader alive, // so a cursor just needs a regular section*. std::shared_ptr
sec; const char *pos; cursor() : pos(nullptr) { } cursor(const std::shared_ptr
sec, section_offset offset = 0) : sec(sec), pos(sec->begin + offset) { } /** * Read a subsection. The cursor must be at an initial * length. After, the cursor will point just past the end of * the subsection. The returned section has the appropriate * DWARF format and begins at the current location of the * cursor (so this is usually followed by a * skip_initial_length). */ std::shared_ptr
subsection(); std::int64_t sleb128(); section_offset offset(); void string(std::string &out); const char *cstr(size_t *size_out = nullptr); void ensure(section_offset bytes) { if ((section_offset)(sec->end - pos) < bytes || pos >= sec->end) underflow(); } template T fixed() { ensure(sizeof(T)); static_assert(sizeof(T) <= 8, "T too big"); uint64_t val = 0; const unsigned char *p = (const unsigned char*)pos; if (sec->ord == byte_order::lsb) { for (unsigned i = 0; i < sizeof(T); i++) val |= ((uint64_t)p[i]) << (i * 8); } else { for (unsigned i = 0; i < sizeof(T); i++) val = (val << 8) | (uint64_t)p[i]; } pos += sizeof(T); return (T)val; } std::uint64_t uleb128() { // Appendix C // XXX Pre-compute all two byte ULEB's std::uint64_t result = 0; int shift = 0; while (pos < sec->end) { uint8_t byte = *(uint8_t*)(pos++); result |= (uint64_t)(byte & 0x7f) << shift; if ((byte & 0x80) == 0) return result; shift += 7; } underflow(); return 0; } taddr address() { switch (sec->addr_size) { case 1: return fixed(); case 2: return fixed(); case 4: return fixed(); case 8: return fixed(); default: throw std::runtime_error("address size " + std::to_string(sec->addr_size) + " not supported"); } } void skip_initial_length(); void skip_form(DW_FORM form); cursor &operator+=(section_offset offset) { pos += offset; return *this; } cursor operator+(section_offset offset) const { return cursor(sec, pos + offset); } bool operator<(const cursor &o) const { return pos < o.pos; } bool end() const { return pos >= sec->end; } bool valid() const { return !!pos; } section_offset get_section_offset() const { return pos - sec->begin; } private: cursor(const std::shared_ptr
sec, const char *pos) : sec(sec), pos(pos) { } void underflow(); }; /** * An attribute specification in an abbrev. */ struct attribute_spec { DW_AT name; DW_FORM form; // Computed information value::type type; attribute_spec(DW_AT name, DW_FORM form); }; typedef std::uint64_t abbrev_code; /** * An entry in .debug_abbrev. */ struct abbrev_entry { abbrev_code code; DW_TAG tag; bool children; std::vector attributes; abbrev_entry() : code(0) { } bool read(cursor *cur); }; /** * A section header in .debug_pubnames or .debug_pubtypes. */ struct name_unit { uhalf version; section_offset debug_info_offset; section_length debug_info_length; // Cursor to the first name_entry in this unit. This cursor's // section is limited to this unit. cursor entries; void read(cursor *cur) { // Section 7.19 std::shared_ptr
subsec = cur->subsection(); cursor sub(subsec); sub.skip_initial_length(); version = sub.fixed(); if (version != 2) throw format_error("unknown name unit version " + std::to_string(version)); debug_info_offset = sub.offset(); debug_info_length = sub.offset(); entries = sub; } }; /** * An entry in a .debug_pubnames or .debug_pubtypes unit. */ struct name_entry { section_offset offset; std::string name; void read(cursor *cur) { offset = cur->offset(); cur->string(name); } }; DWARFPP_END_NAMESPACE #endif libelfin-0.3/dwarf/line.cc000066400000000000000000000407311302015676000155060ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "internal.hh" #include using namespace std; DWARFPP_BEGIN_NAMESPACE // The expected number of arguments for standard opcodes. This is // used to check the opcode_lengths header field for compatibility. static const int opcode_lengths[] = { 0, // DW_LNS::copy 0, 1, 1, 1, 1, // DW_LNS::negate_stmt 0, 0, 0, 1, 0, // DW_LNS::set_epilogue_begin 0, 1 }; struct line_table::impl { shared_ptr
sec; // Header information section_offset program_offset; ubyte minimum_instruction_length; ubyte maximum_operations_per_instruction; bool default_is_stmt; sbyte line_base; ubyte line_range; ubyte opcode_base; vector standard_opcode_lengths; vector include_directories; vector file_names; // The offset in sec following the last read file name entry. // File name entries can appear both in the line table header // and in the line number program itself. Since we can // iterate over the line number program repeatedly, this keeps // track of how far we've gotten so we don't add the same // entry twice. section_offset last_file_name_end; // If an iterator has traversed the entire program, then we // know we've gathered all file names. bool file_names_complete; impl() : last_file_name_end(0), file_names_complete(false) {}; bool read_file_entry(cursor *cur, bool in_header); }; line_table::line_table(const shared_ptr
&sec, section_offset offset, unsigned cu_addr_size, const string &cu_comp_dir, const string &cu_name) : m(make_shared()) { // XXX DWARF2 and 3 give a weird specification for DW_AT_comp_dir string comp_dir, abs_path; if (cu_comp_dir.empty() || cu_comp_dir.back() == '/') comp_dir = cu_comp_dir; else comp_dir = cu_comp_dir + '/'; // Read the line table header (DWARF2 section 6.2.4, DWARF3 // section 6.2.4, DWARF4 section 6.2.3) cursor cur(sec, offset); m->sec = cur.subsection(); cur = cursor(m->sec); cur.skip_initial_length(); m->sec->addr_size = cu_addr_size; // Basic header information uhalf version = cur.fixed(); if (version < 2 || version > 4) throw format_error("unknown line number table version " + std::to_string(version)); section_length header_length = cur.offset(); m->program_offset = cur.get_section_offset() + header_length; m->minimum_instruction_length = cur.fixed(); m->maximum_operations_per_instruction = 1; if (version == 4) m->maximum_operations_per_instruction = cur.fixed(); if (m->maximum_operations_per_instruction == 0) throw format_error("maximum_operations_per_instruction cannot" " be 0 in line number table"); m->default_is_stmt = cur.fixed(); m->line_base = cur.fixed(); m->line_range = cur.fixed(); if (m->line_range == 0) throw format_error("line_range cannot be 0 in line number table"); m->opcode_base = cur.fixed(); static_assert(sizeof(opcode_lengths) / sizeof(opcode_lengths[0]) == 13, "opcode_lengths table has wrong length"); // Opcode length table m->standard_opcode_lengths.resize(m->opcode_base); m->standard_opcode_lengths[0] = 0; for (unsigned i = 1; i < m->opcode_base; i++) { ubyte length = cur.fixed(); if (length != opcode_lengths[i]) // The spec never says what to do if the // opcode length of a standard opcode doesn't // match the header. Do the safe thing. throw format_error( "expected " + std::to_string(opcode_lengths[i]) + " arguments for line number opcode " + std::to_string(i) + ", got " + std::to_string(length)); m->standard_opcode_lengths[i] = length; } // Include directories list string incdir; // Include directory 0 is implicitly the compilation unit // current directory m->include_directories.push_back(comp_dir); while (true) { cur.string(incdir); if (incdir.empty()) break; if (incdir.back() != '/') incdir += '/'; if (incdir[0] == '/') m->include_directories.push_back(move(incdir)); else m->include_directories.push_back(comp_dir + incdir); } // File name list string file_name; // File name 0 is implicitly the compilation unit file name. // cu_name can be relative to comp_dir or absolute. if (!cu_name.empty() && cu_name[0] == '/') m->file_names.emplace_back(cu_name); else m->file_names.emplace_back(comp_dir + cu_name); while (m->read_file_entry(&cur, true)); } line_table::iterator line_table::begin() const { if (!valid()) return iterator(nullptr, 0); return iterator(this, m->program_offset); } line_table::iterator line_table::end() const { if (!valid()) return iterator(nullptr, 0); return iterator(this, m->sec->size()); } line_table::iterator line_table::find_address(taddr addr) const { iterator prev = begin(), e = end(); if (prev == e) return prev; iterator it = prev; for (++it; it != e; prev = it++) { if (prev->address <= addr && it->address > addr && !prev->end_sequence) return prev; } prev = e; return prev; } const line_table::file * line_table::get_file(unsigned index) const { if (index >= m->file_names.size()) { // It could be declared in the line table program. // This is unlikely, so we don't have to be // super-efficient about this. Just force our way // through the whole line table program. if (!m->file_names_complete) { for (auto &ent : *this) (void)ent; } if (index >= m->file_names.size()) throw out_of_range ("file name index " + std::to_string(index) + " exceeds file table size of " + std::to_string(m->file_names.size())); } return &m->file_names[index]; } bool line_table::impl::read_file_entry(cursor *cur, bool in_header) { assert(cur->sec == sec); string file_name; cur->string(file_name); if (in_header && file_name.empty()) return false; uint64_t dir_index = cur->uleb128(); uint64_t mtime = cur->uleb128(); uint64_t length = cur->uleb128(); // Have we already processed this file entry? if (cur->get_section_offset() <= last_file_name_end) return true; last_file_name_end = cur->get_section_offset(); if (file_name[0] == '/') file_names.emplace_back(move(file_name), mtime, length); else if (dir_index < include_directories.size()) file_names.emplace_back( include_directories[dir_index] + file_name, mtime, length); else throw format_error("file name directory index out of range: " + std::to_string(dir_index)); return true; } line_table::file::file(string path, uint64_t mtime, uint64_t length) : path(path), mtime(mtime), length(length) { } void line_table::entry::reset(bool is_stmt) { address = op_index = 0; file = nullptr; file_index = line = 1; column = 0; this->is_stmt = is_stmt; basic_block = end_sequence = prologue_end = epilogue_begin = false; isa = discriminator = 0; } string line_table::entry::get_description() const { string res = file->path; if (line) { res.append(":").append(std::to_string(line)); if (column) res.append(":").append(std::to_string(column)); } return res; } line_table::iterator::iterator(const line_table *table, section_offset pos) : table(table), pos(pos) { if (table) { regs.reset(table->m->default_is_stmt); ++(*this); } } line_table::iterator & line_table::iterator::operator++() { cursor cur(table->m->sec, pos); // Execute opcodes until we reach the end of the stream or an // opcode emits a line table row bool stepped = false, output = false; while (!cur.end() && !output) { output = step(&cur); stepped = true; } if (stepped && !output) throw format_error("unexpected end of line table"); if (stepped && cur.end()) { // Record that all file names must be known now table->m->file_names_complete = true; } if (output) { // Resolve file name of entry if (entry.file_index < table->m->file_names.size()) entry.file = &table->m->file_names[entry.file_index]; else throw format_error("bad file index " + std::to_string(entry.file_index) + " in line table"); } pos = cur.get_section_offset(); return *this; } bool line_table::iterator::step(cursor *cur) { struct line_table::impl *m = table->m.get(); // Read the opcode (DWARF4 section 6.2.3) ubyte opcode = cur->fixed(); if (opcode >= m->opcode_base) { // Special opcode (DWARF4 section 6.2.5.1) ubyte adjusted_opcode = opcode - m->opcode_base; unsigned op_advance = adjusted_opcode / m->line_range; signed line_inc = m->line_base + (signed)adjusted_opcode % m->line_range; regs.line += line_inc; regs.address += m->minimum_instruction_length * ((regs.op_index + op_advance) / m->maximum_operations_per_instruction); regs.op_index = (regs.op_index + op_advance) % m->maximum_operations_per_instruction; entry = regs; regs.basic_block = regs.prologue_end = regs.epilogue_begin = false; regs.discriminator = 0; return true; } else if (opcode != 0) { // Standard opcode (DWARF4 sections 6.2.3 and 6.2.5.2) // // According to the standard, any opcode between the // highest defined opcode for a given DWARF version // and opcode_base should be treated as a // vendor-specific opcode. However, the de facto // standard seems to be to process these as standard // opcodes even if they're from a later version of the // standard than the line table header claims. uint64_t uarg; #pragma GCC diagnostic push #pragma GCC diagnostic warning "-Wswitch-enum" switch ((DW_LNS)opcode) { case DW_LNS::copy: entry = regs; regs.basic_block = regs.prologue_end = regs.epilogue_begin = false; regs.discriminator = 0; break; case DW_LNS::advance_pc: // Opcode advance (as for special opcodes) uarg = cur->uleb128(); advance_pc: regs.address += m->minimum_instruction_length * ((regs.op_index + uarg) / m->maximum_operations_per_instruction); regs.op_index = (regs.op_index + uarg) % m->maximum_operations_per_instruction; break; case DW_LNS::advance_line: regs.line = (signed)regs.line + cur->sleb128(); break; case DW_LNS::set_file: regs.file_index = cur->uleb128(); break; case DW_LNS::set_column: regs.column = cur->uleb128(); break; case DW_LNS::negate_stmt: regs.is_stmt = !regs.is_stmt; break; case DW_LNS::set_basic_block: regs.basic_block = true; break; case DW_LNS::const_add_pc: uarg = (255 - m->opcode_base) / m->line_range; goto advance_pc; case DW_LNS::fixed_advance_pc: regs.address += cur->fixed(); regs.op_index = 0; break; case DW_LNS::set_prologue_end: regs.prologue_end = true; break; case DW_LNS::set_epilogue_begin: regs.epilogue_begin = true; break; case DW_LNS::set_isa: regs.isa = cur->uleb128(); break; default: // XXX Vendor extensions throw format_error("unknown line number opcode " + to_string((DW_LNS)opcode)); } return ((DW_LNS)opcode == DW_LNS::copy); } else { // opcode == 0 // Extended opcode (DWARF4 sections 6.2.3 and 6.2.5.3) assert(opcode == 0); uint64_t length = cur->uleb128(); section_offset end = cur->get_section_offset() + length; opcode = cur->fixed(); switch ((DW_LNE)opcode) { case DW_LNE::end_sequence: regs.end_sequence = true; entry = regs; regs.reset(m->default_is_stmt); break; case DW_LNE::set_address: regs.address = cur->address(); regs.op_index = 0; break; case DW_LNE::define_file: m->read_file_entry(cur, false); break; case DW_LNE::set_discriminator: // XXX Only DWARF4 regs.discriminator = cur->uleb128(); break; case DW_LNE::lo_user...DW_LNE::hi_user: // XXX Vendor extensions throw runtime_error("vendor line number opcode " + to_string((DW_LNE)opcode) + " not implemented"); default: // XXX Prior to DWARF4, any opcode number // could be a vendor extension throw format_error("unknown line number opcode " + to_string((DW_LNE)opcode)); } #pragma GCC diagnostic pop if (cur->get_section_offset() > end) throw format_error("extended line number opcode exceeded its size"); cur += end - cur->get_section_offset(); return ((DW_LNE)opcode == DW_LNE::end_sequence); } } DWARFPP_END_NAMESPACE libelfin-0.3/dwarf/rangelist.cc000066400000000000000000000056021302015676000165450ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "internal.hh" using namespace std; DWARFPP_BEGIN_NAMESPACE rangelist::rangelist(const std::shared_ptr
&sec, section_offset off, unsigned cu_addr_size, taddr cu_low_pc) : sec(sec->slice(off, ~0, format::unknown, cu_addr_size)), base_addr(cu_low_pc) { } rangelist::rangelist(const initializer_list > &ranges) { synthetic.reserve(ranges.size() * 2 + 2); for (auto &range : ranges) { synthetic.push_back(range.first); synthetic.push_back(range.second); } synthetic.push_back(0); synthetic.push_back(0); sec = make_shared
( section_type::ranges, (const char*)synthetic.data(), synthetic.size() * sizeof(taddr), native_order(), format::unknown, sizeof(taddr)); base_addr = 0; } rangelist::iterator rangelist::begin() const { if (sec) return iterator(sec, base_addr); return end(); } rangelist::iterator rangelist::end() const { return iterator(); } bool rangelist::contains(taddr addr) const { for (auto ent : *this) if (ent.contains(addr)) return true; return false; } rangelist::iterator::iterator(const std::shared_ptr
&sec, taddr base_addr) : sec(sec), base_addr(base_addr), pos(0) { // Read in the first entry ++(*this); } rangelist::iterator & rangelist::iterator::operator++() { // DWARF4 section 2.17.3 taddr largest_offset = ~(taddr)0; if (sec->addr_size < sizeof(taddr)) largest_offset += 1 << (8 * sec->addr_size); // Read in entries until we reach a regular entry of an // end-of-list. Note that pos points to the beginning of the // entry *following* the current entry, so that's where we // start. cursor cur(sec, pos); while (true) { entry.low = cur.address(); entry.high = cur.address(); if (entry.low == 0 && entry.high == 0) { // End of list sec.reset(); pos = 0; break; } else if (entry.low == largest_offset) { // Base address change base_addr = entry.high; } else { // Regular entry. Adjust by base address. entry.low += base_addr; entry.high += base_addr; pos = cur.get_section_offset(); break; } } return *this; } DWARFPP_END_NAMESPACE libelfin-0.3/dwarf/small_vector.hh000066400000000000000000000113621302015676000172610ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #ifndef _DWARFPP_SMALL_VECTOR_HH_ #define _DWARFPP_SMALL_VECTOR_HH_ DWARFPP_BEGIN_NAMESPACE /** * A vector-like class that only heap allocates above a specified * size. */ template class small_vector { public: typedef T value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef size_t size_type; small_vector() : base((T*)buf), end(base), cap((T*)&buf[sizeof(T[Min])]) { } small_vector(const small_vector &o) : base((T*)buf), end(base), cap((T*)&buf[sizeof(T[Min])]) { *this = o; } small_vector(small_vector &&o) : base((T*)buf), end(base), cap((T*)&buf[sizeof(T[Min])]) { if ((char*)o.base == o.buf) { // Elements are inline; have to copy them base = (T*)buf; end = base; cap = (T*)&buf[sizeof(T[Min])]; *this = o; o.clear(); } else { // Elements are external; swap pointers base = o.base; end = o.end; cap = o.cap; o.base = (T*)o.buf; o.end = o.base; o.cap = (T*)&o.buf[sizeof(T[Min])]; } } ~small_vector() { clear(); if ((char*)base != buf) delete[] (char*)base; } small_vector &operator=(const small_vector &o) { size_type osize = o.size(); clear(); reserve(osize); for (size_type i = 0; i < osize; i++) new (&base[i]) T(o[i]); end = base + osize; return *this; } size_type size() const { return end - base; } bool empty() const { return base == end; } void reserve(size_type n) { if (n <= (size_type)(cap - base)) return; size_type target = cap - base; if (target == 0) target = 1; while (target < n) target <<= 1; char *newbuf = new char[sizeof(T[target])]; T *src = base, *dest = (T*)newbuf; for (; src < end; src++, dest++) { new(dest) T(*src); dest->~T(); } if ((char*)base != buf) delete[] (char*)base; base = (T*)newbuf; end = dest; cap = base + target; } reference operator[](size_type n) { return base[n]; } const_reference operator[](size_type n) const { return base[n]; } reference at(size_type n) { return base[n]; } const_reference at(size_type n) const { return base[n]; } /** * "Reverse at". revat(0) is equivalent to back(). revat(1) * is the element before back. Etc. */ reference revat(size_type n) { return *(end - 1 - n); } const_reference revat(size_type n) const { return *(end - 1 - n); } reference front() { return base[0]; } const_reference front() const { return base[0]; } reference back() { return *(end-1); } const_reference back() const { return *(end-1); } void push_back(const T& x) { reserve(size() + 1); new (end) T(x); end++; } void push_back(T&& x) { reserve(size() + 1); new (end) T(std::move(x)); end++; } void pop_back() { end--; end->~T(); } void clear() { for (T* p = base; p < end; ++p) p->~T(); end = base; } private: char buf[sizeof(T[Min])]; T *base, *end, *cap; }; DWARFPP_END_NAMESPACE #endif libelfin-0.3/dwarf/value.cc000066400000000000000000000241371302015676000156750ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "internal.hh" #include using namespace std; DWARFPP_BEGIN_NAMESPACE value::value(const unit *cu, DW_AT name, DW_FORM form, type typ, section_offset offset) : cu(cu), form(form), typ(typ), offset(offset) { if (form == DW_FORM::indirect) resolve_indirect(name); } section_offset value::get_section_offset() const { return cu->get_section_offset() + offset; } taddr value::as_address() const { if (form != DW_FORM::addr) throw value_type_mismatch("cannot read " + to_string(typ) + " as address"); cursor cur(cu->data(), offset); return cur.address(); } const void * value::as_block(size_t *size_out) const { // XXX Blocks can contain all sorts of things, including // references, which couldn't be resolved by callers in the // current minimal API. cursor cur(cu->data(), offset); switch (form) { case DW_FORM::block1: *size_out = cur.fixed(); break; case DW_FORM::block2: *size_out = cur.fixed(); break; case DW_FORM::block4: *size_out = cur.fixed(); break; case DW_FORM::block: case DW_FORM::exprloc: *size_out = cur.uleb128(); break; default: throw value_type_mismatch("cannot read " + to_string(typ) + " as block"); } cur.ensure(*size_out); return cur.pos; } uint64_t value::as_uconstant() const { cursor cur(cu->data(), offset); switch (form) { case DW_FORM::data1: return cur.fixed(); case DW_FORM::data2: return cur.fixed(); case DW_FORM::data4: return cur.fixed(); case DW_FORM::data8: return cur.fixed(); case DW_FORM::udata: return cur.uleb128(); default: throw value_type_mismatch("cannot read " + to_string(typ) + " as uconstant"); } } int64_t value::as_sconstant() const { cursor cur(cu->data(), offset); switch (form) { case DW_FORM::data1: return cur.fixed(); case DW_FORM::data2: return cur.fixed(); case DW_FORM::data4: return cur.fixed(); case DW_FORM::data8: return cur.fixed(); case DW_FORM::sdata: return cur.sleb128(); default: throw value_type_mismatch("cannot read " + to_string(typ) + " as sconstant"); } } expr value::as_exprloc() const { cursor cur(cu->data(), offset); size_t size; // Prior to DWARF 4, exprlocs were encoded as blocks. switch (form) { case DW_FORM::exprloc: case DW_FORM::block: size = cur.uleb128(); break; case DW_FORM::block1: size = cur.fixed(); break; case DW_FORM::block2: size = cur.fixed(); break; case DW_FORM::block4: size = cur.fixed(); break; default: throw value_type_mismatch("cannot read " + to_string(typ) + " as exprloc"); } return expr(cu, cur.get_section_offset(), size); } bool value::as_flag() const { switch (form) { case DW_FORM::flag: { cursor cur(cu->data(), offset); return cur.fixed() != 0; } case DW_FORM::flag_present: return true; default: throw value_type_mismatch("cannot read " + to_string(typ) + " as flag"); } } rangelist value::as_rangelist() const { section_offset off = as_sec_offset(); // The compilation unit may not have a base address. In this // case, the first entry in the range list must be a base // address entry, but we'll just assume 0 for the initial base // address. die cudie = cu->root(); taddr cu_low_pc = cudie.has(DW_AT::low_pc) ? at_low_pc(cudie) : 0; auto sec = cu->get_dwarf().get_section(section_type::ranges); auto cusec = cu->data(); return rangelist(sec, off, cusec->addr_size, cu_low_pc); } die value::as_reference() const { section_offset off; // XXX Would be nice if we could avoid this. The cursor is // all overhead here. cursor cur(cu->data(), offset); switch (form) { case DW_FORM::ref1: off = cur.fixed(); break; case DW_FORM::ref2: off = cur.fixed(); break; case DW_FORM::ref4: off = cur.fixed(); break; case DW_FORM::ref8: off = cur.fixed(); break; case DW_FORM::ref_udata: off = cur.uleb128(); break; case DW_FORM::ref_addr: { off = cur.offset(); // These seem to be extremely rare in practice (I // haven't been able to get gcc to produce a // ref_addr), so it's not worth caching this lookup. const compilation_unit *base_cu = nullptr; for (auto &file_cu : cu->get_dwarf().compilation_units()) { if (file_cu.get_section_offset() > off) break; base_cu = &file_cu; } die d(base_cu); d.read(off - base_cu->get_section_offset()); return d; } case DW_FORM::ref_sig8: { uint64_t sig = cur.fixed(); try { return cu->get_dwarf().get_type_unit(sig).type(); } catch (std::out_of_range &e) { throw format_error("unknown type signature 0x" + to_hex(sig)); } } default: throw value_type_mismatch("cannot read " + to_string(typ) + " as reference"); } die d(cu); d.read(off); return d; } void value::as_string(string &buf) const { size_t size; const char *p = as_cstr(&size); buf.resize(size); memmove(&buf.front(), p, size); } string value::as_string() const { size_t size; const char *s = as_cstr(&size); return string(s, size); } const char * value::as_cstr(size_t *size_out) const { cursor cur(cu->data(), offset); switch (form) { case DW_FORM::string: return cur.cstr(size_out); case DW_FORM::strp: { section_offset off = cur.offset(); cursor scur(cu->get_dwarf().get_section(section_type::str), off); return scur.cstr(size_out); } default: throw value_type_mismatch("cannot read " + to_string(typ) + " as string"); } } section_offset value::as_sec_offset() const { // Prior to DWARF 4, sec_offsets were encoded as data4 or // data8. cursor cur(cu->data(), offset); switch (form) { case DW_FORM::data4: return cur.fixed(); case DW_FORM::data8: return cur.fixed(); case DW_FORM::sec_offset: return cur.offset(); default: throw value_type_mismatch("cannot read " + to_string(typ) + " as sec_offset"); } } void value::resolve_indirect(DW_AT name) { if (form != DW_FORM::indirect) return; cursor c(cu->data(), offset); DW_FORM form; do { form = (DW_FORM)c.uleb128(); } while (form == DW_FORM::indirect); typ = attribute_spec(name, form).type; offset = c.get_section_offset(); } string to_string(const value &v) { switch (v.get_type()) { case value::type::invalid: return ""; case value::type::address: return "0x" + to_hex(v.as_address()); case value::type::block: { size_t size; const char *b = (const char*)v.as_block(&size); string res = ::to_string(size) + " byte block:"; for (size_t pos = 0; pos < size; ++pos) { res += ' '; res += to_hex(b[pos]); } return res; } case value::type::constant: return "0x" + to_hex(v.as_uconstant()); case value::type::uconstant: return ::to_string(v.as_uconstant()); case value::type::sconstant: return ::to_string(v.as_sconstant()); case value::type::exprloc: // XXX return ""; case value::type::flag: return v.as_flag() ? "true" : "false"; case value::type::line: return ""; case value::type::loclist: return ""; case value::type::mac: return ""; case value::type::rangelist: return ""; case value::type::reference: { die d = v.as_reference(); auto tu = dynamic_cast(&d.get_unit()); if (tu) return "<.debug_types+0x" + to_hex(d.get_section_offset()) + ">"; return "<0x" + to_hex(d.get_section_offset()) + ">"; } case value::type::string: return v.as_string(); } return ""; } DWARFPP_END_NAMESPACE libelfin-0.3/elf/000077500000000000000000000000001302015676000137065ustar00rootroot00000000000000libelfin-0.3/elf/.gitignore000066400000000000000000000001021302015676000156670ustar00rootroot00000000000000*.o to_string.cc libelf++.a libelf++.so libelf++.so.* libelf++.pc libelfin-0.3/elf/Makefile000066400000000000000000000042441302015676000153520ustar00rootroot00000000000000# Changed when ABI backwards compatibility is broken. # Typically uses the major version. SONAME = 0 CXXFLAGS+=-g -O2 -Werror override CXXFLAGS+=-std=c++0x -Wall -fPIC all: libelf++.a libelf++.so libelf++.so.$(SONAME) libelf++.pc SRCS := elf.cc mmap_loader.cc to_string.cc HDRS := elf++.hh data.hh common.hh to_hex.hh CLEAN := libelf++.a: $(SRCS:.cc=.o) ar rcs $@ $^ CLEAN += libelf++.a $(SRCS:.cc=.o) $(SRCS:.cc=.o): $(HDRS) to_string.cc: enum-print.py data.hh Makefile @echo "// Automatically generated by make at $$(date)" > to_string.cc @echo "// DO NOT EDIT" >> to_string.cc @echo >> to_string.cc @echo '#include "data.hh"' >> to_string.cc @echo '#include "to_hex.hh"' >> to_string.cc @echo >> to_string.cc @echo 'ELFPP_BEGIN_NAMESPACE' >> to_string.cc @echo >> to_string.cc python3 enum-print.py -u --hex --no-type --mask shf --mask pf \ -x loos -x hios -x loproc -x hiproc < data.hh >> to_string.cc @echo 'ELFPP_END_NAMESPACE' >> to_string.cc CLEAN += to_string.cc libelf++.so.$(SONAME): $(SRCS:.cc=.o) $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -Wl,-soname,$@ -o $@ $^ CLEAN += libelf++.so.* libelf++.so: ln -s $@.$(SONAME) $@ CLEAN += libelf++.so # Create pkg-config for local library and headers. This will be # transformed in to the correct global pkg-config by install. libelf++.pc: always @(VER=$$(git describe --match 'v*' | sed -e s/^v//); \ echo "libdir=$$PWD"; \ echo "includedir=$$PWD"; \ echo ""; \ echo "Name: libelf++"; \ echo "Description: C++11 ELF library"; \ echo "Version: $$VER"; \ echo "Libs: -L\$${libdir} -lelf++"; \ echo "Cflags: -I\$${includedir}") > $@ CLEAN += libelf++.pc .PHONY: always PREFIX?=/usr/local install: libelf++.a libelf++.so libelf++.so.$(SONAME) libelf++.pc install -d $(PREFIX)/lib/pkgconfig install -t $(PREFIX)/lib libelf++.a install -t $(PREFIX)/lib libelf++.so.$(SONAME) install -t $(PREFIX)/lib libelf++.so install -d $(PREFIX)/include/libelfin/elf install -t $(PREFIX)/include/libelfin/elf common.hh data.hh elf++.hh sed 's,^libdir=.*,libdir=$(PREFIX)/lib,;s,^includedir=.*,includedir=$(PREFIX)/include,' libelf++.pc \ > $(PREFIX)/lib/pkgconfig/libelf++.pc clean: rm -f $(CLEAN) .DELETE_ON_ERROR: libelfin-0.3/elf/common.hh000066400000000000000000000046441302015676000155270ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #ifndef _ELFPP_COMMON_HH_ #define _ELFPP_COMMON_HH_ #define ELFPP_BEGIN_NAMESPACE namespace elf { #define ELFPP_END_NAMESPACE } #define ELFPP_BEGIN_INTERNAL namespace internal { #define ELFPP_END_INTERNAL } #include ELFPP_BEGIN_NAMESPACE /** * A byte ordering. */ enum class byte_order { native, lsb, msb }; /** * Return either byte_order::lsb or byte_order::msb. If the argument * is byte_order::native, it will be resolved to whatever the native * byte order is. */ static inline byte_order resolve_order(byte_order o) { static const union { int i; char c[sizeof(int)]; } test = {1}; if (o == byte_order::native) return test.c[0] == 1 ? byte_order::lsb : byte_order::msb; return o; } /** * Return v converted from one byte order to another. */ template T swizzle(T v, byte_order from, byte_order to) { static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "cannot swizzle type"); from = resolve_order(from); to = resolve_order(to); if (from == to) return v; switch (sizeof(T)) { case 1: return v; case 2: { std::uint16_t x = (std::uint16_t)v; return (T)(((x&0xFF) << 8) | (x >> 8)); } case 4: return (T)__builtin_bswap32((std::uint32_t)v); case 8: return (T)__builtin_bswap64((std::uint64_t)v); } } ELFPP_BEGIN_INTERNAL /** * OrderPick selects between Native, LSB, and MSB based on ord. */ template struct OrderPick; template struct OrderPick { typedef Native T; }; template struct OrderPick { typedef LSB T; }; template struct OrderPick { typedef MSB T; }; ELFPP_END_INTERNAL ELFPP_END_NAMESPACE #endif libelfin-0.3/elf/data.hh000066400000000000000000000435761302015676000151570ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #ifndef _ELFPP_DATA_HH_ #define _ELFPP_DATA_HH_ #include "common.hh" #include #include #include ELFPP_BEGIN_NAMESPACE // Object file classes (ELF64 table 3) enum class elfclass : unsigned char { _32 = 1, // 32-bit objects _64 = 2, // 64-bit objects }; std::string to_string(elfclass v); // Common basic data types struct ElfTypes { typedef std::uint16_t Half; typedef std::uint32_t Word; typedef std::int32_t Sword; }; struct Elf32 : public ElfTypes { // ELF class static const elfclass cls = elfclass::_32; // Basic data types (ELF32 figure 1-2) typedef std::uint32_t Addr; typedef std::uint32_t Off; // Predicated types typedef Word Word32_Xword64; template struct pick { typedef t32 t; }; }; struct Elf64 : ElfTypes { // ELF class static const elfclass cls = elfclass::_64; // Basic data types (ELF64 table 1) typedef std::uint64_t Addr; typedef std::uint64_t Off; typedef std::uint64_t Xword; typedef std::int64_t Sxword; // Predicated types typedef Xword Word32_Xword64; template struct pick { typedef t64 t; }; }; // Data encodings (ELF64 table 4) enum class elfdata : unsigned char { lsb = 1, msb = 2, }; std::string to_string(elfdata v); // Operating system and ABI identifiers (ELF64 table 5) enum class elfosabi : unsigned char { sysv = 0, hpux = 1, standalone = 255, }; std::string to_string(elfosabi v); // Object file types (ELF64 table 6) enum class et : ElfTypes::Half { none = 0, // No file type rel = 1, // Relocatable object file exec = 2, // Executable file dyn = 3, // Shared object file core = 4, // Core file loos = 0xfe00, // Environment-specific use hios = 0xfeff, loproc = 0xff00, // Processor-specific use hiproc = 0xffff, }; std::string to_string(et v); // ELF header (ELF32 figure 1-3, ELF64 figure 2) template struct Ehdr { typedef E types; static const byte_order order = Order; // ELF identification unsigned char ei_magic[4]; elfclass ei_class; elfdata ei_data; unsigned char ei_version; elfosabi ei_osabi; unsigned char ei_abiversion; unsigned char ei_pad[7]; et type; // Object file type typename E::Half machine; // Machine type typename E::Word version; // Object file version typename E::Addr entry; // Entry point address typename E::Off phoff; // Program header offset typename E::Off shoff; // Section header offset typename E::Word flags; // Processor-specific flags typename E::Half ehsize; // ELF header size typename E::Half phentsize; // Size of program header entry typename E::Half phnum; // Number of program header entries typename E::Half shentsize; // Size of section header entry typename E::Half shnum; // Number of section header entries typename E::Half shstrndx; // Section name string table index template void from(const E2 &o) { std::memcpy(ei_magic, o.ei_magic, sizeof(ei_magic)); ei_class = swizzle(o.ei_class, o.order, order); ei_data = swizzle(o.ei_data, o.order, order); ei_version = swizzle(o.ei_version, o.order, order); ei_osabi = swizzle(o.ei_osabi, o.order, order); ei_abiversion = swizzle(o.ei_abiversion, o.order, order); std::memcpy(ei_pad, o.ei_pad, sizeof(ei_pad)); type = swizzle(o.type, o.order, order); machine = swizzle(o.machine, o.order, order); version = swizzle(o.version, o.order, order); entry = swizzle(o.entry, o.order, order); phoff = swizzle(o.phoff, o.order, order); shoff = swizzle(o.shoff, o.order, order); flags = swizzle(o.flags, o.order, order); ehsize = swizzle(o.ehsize, o.order, order); phentsize = swizzle(o.phentsize, o.order, order); phnum = swizzle(o.phnum, o.order, order); shentsize = swizzle(o.shentsize, o.order, order); shnum = swizzle(o.shnum, o.order, order); shstrndx = swizzle(o.shstrndx, o.order, order); } }; // Special section indices (ELF32 figure 1-7, ELF64 table 7) // // This is an integer with a few special values, so this is a regular // enum, rather than a type-safe enum. However, this is declared in a // namespace and then used to avoid polluting the elf:: namespace. namespace enums { enum shn : ElfTypes::Half // This is a Word in Shdr and Half in Sym. { undef = 0, // Undefined or meaningless loproc = 0xff00, // Processor-specific use hiproc = 0xff1f, loos = 0xff20, // Environment-specific use hios = 0xff3f, abs = 0xfff1, // Reference is an absolute value common = 0xfff2, // Symbol declared as a common block }; std::string to_string(shn v); } using enums::shn; // Section types (ELF64 table 8) enum class sht : ElfTypes::Word { null = 0, // Marks an unseen section header progbits = 1, // Contains information defined by the program symtab = 2, // Contains a linker symbol table strtab = 3, // Contains a string table rela = 4, // Contains "Rela" type relocation entries hash = 5, // Contains a symbol hash table dynamic = 6, // Contains dynamic linking tables note = 7, // Contains note information nobits = 8, // Contains uninitialized space; // does not occupy any space in the file rel = 9, // Contains "Rel" type relocation entries shlib = 10, // Reserved dynsym = 11, // Contains a dynamic loader symbol table loos = 0x60000000, // Environment-specific use hios = 0x6FFFFFFF, loproc = 0x70000000, // Processor-specific use hiproc = 0x7FFFFFFF, }; std::string to_string(sht v); // Section attributes (ELF64 table 9). Note: This is an Elf32_Word in // ELF32. We use the larger ELF64 type for the canonical // representation and switch it out for a plain Elf32_Word in the // ELF32 format. enum class shf : Elf64::Xword { write = 0x1, // Section contains writable data alloc = 0x2, // Section is allocated in memory image of program execinstr = 0x4, // Section contains executable instructions maskos = 0x0F000000, // Environment-specific use maskproc = 0xF0000000, // Processor-specific use }; std::string to_string(shf v); static inline constexpr shf operator&(shf a, shf b) { return (shf)((std::uint64_t)a & (std::uint64_t)b); } static inline constexpr shf operator|(shf a, shf b) { return (shf)((std::uint64_t)a | (std::uint64_t)b); } static inline constexpr shf operator^(shf a, shf b) { return (shf)((std::uint64_t)a ^ (std::uint64_t)b); } static inline constexpr shf operator~(shf a) { return (shf)~((std::uint64_t)a); } static inline shf& operator&=(shf &a, shf b) { a = a & b; return a; } static inline shf& operator|=(shf &a, shf b) { a = a | b; return a; } static inline shf& operator^=(shf &a, shf b) { a = a ^ b; return a; } // Section header (ELF32 figure 1-8, ELF64 figure 3) template struct Shdr { typedef E types; static const byte_order order = Order; // Section numbers are half-words in some structures and full // words in others. Here we declare a local shn type that is // elf::shn for the native byte order, but the full word for // specific encoding byte orders. typedef typename internal::OrderPick::T shn; typename E::Word name; // Section name sht type; // Section type typename E::template pick::t flags; // Section attributes typename E::Addr addr; // Virtual address in memory typename E::Off offset; // Offset in file typename E::Word32_Xword64 size; // Size of section shn link; // Link to other section typename E::Word info; // Miscellaneous information typename E::Word32_Xword64 addralign; // Address alignment boundary typename E::Word32_Xword64 entsize; // Size of entries, if section has table template void from(const E2 &o) { name = swizzle(o.name, o.order, order); type = swizzle(o.type, o.order, order); flags = (decltype(flags))swizzle(o.flags, o.order, order); addr = swizzle(o.addr, o.order, order); offset = swizzle(o.offset, o.order, order); size = swizzle(o.size, o.order, order); link = (decltype(link))swizzle((typename E::Word)o.link, o.order, order); info = swizzle(o.info, o.order, order); addralign = swizzle(o.addralign, o.order, order); entsize = swizzle(o.entsize, o.order, order); } }; // Segment types (ELF64 table 16) enum class pt : ElfTypes::Word { null = 0, // Unused entry load = 1, // Loadable segment dynamic = 2, // Dynamic linking tables interp = 3, // Program interpreter path name note = 4, // Note sections shlib = 5, // Reserved phdr = 6, // Program header table loos = 0x60000000, // Environment-specific use hios = 0x6FFFFFFF, loproc = 0x70000000, // Processor-specific use hiproc = 0x7FFFFFFF, }; std::string to_string(pt v); // Segment attributes enum class pf : ElfTypes::Word { x = 0x1, // Execute permission w = 0x2, // Write permission r = 0x4, // Read permission maskos = 0x00FF0000, // Environment-specific use maskproc = 0xFF000000, // Processor-specific use }; std::string to_string(pf v); static inline constexpr pf operator&(pf a, pf b) { return (pf)((std::uint64_t)a & (std::uint64_t)b); } static inline constexpr pf operator|(pf a, pf b) { return (pf)((std::uint64_t)a | (std::uint64_t)b); } static inline constexpr pf operator^(pf a, pf b) { return (pf)((std::uint64_t)a ^ (std::uint64_t)b); } static inline constexpr pf operator~(pf a) { return (pf)~((std::uint64_t)a); } static inline pf& operator&=(pf &a, pf b) { a = a & b; return a; } static inline pf& operator|=(pf &a, pf b) { a = a | b; return a; } static inline pf& operator^=(pf &a, pf b) { a = a ^ b; return a; } // Program header (ELF32 figure 2-1, ELF64 figure 6) template struct Phdr; template struct Phdr { typedef Elf32 types; static const byte_order order = Order; pt type; // Type of segment Elf32::Off offset; // Offset in file Elf32::Addr vaddr; // Virtual address in memory Elf32::Addr paddr; // Reserved Elf32::Word filesz; // Size of segment in file Elf32::Word memsz; // Size of segment in memory pf flags; // Segment attributes Elf32::Word align; // Alignment of segment template void from(const E2 &o) { type = swizzle(o.type, o.order, order); offset = swizzle(o.offset, o.order, order); vaddr = swizzle(o.vaddr, o.order, order); paddr = swizzle(o.paddr, o.order, order); filesz = swizzle(o.filesz, o.order, order); memsz = swizzle(o.memsz, o.order, order); flags = swizzle(o.flags, o.order, order); align = swizzle(o.align, o.order, order); } }; template struct Phdr { typedef Elf64 types; static const byte_order order = Order; pt type; // Type of segment pf flags; // Segment attributes Elf64::Off offset; // Offset in file Elf64::Addr vaddr; // Virtual address in memory Elf64::Addr paddr; // Reserved Elf64::Xword filesz; // Size of segment in file Elf64::Xword memsz; // Size of segment in memory Elf64::Xword align; // Alignment of segment template void from(const E2 &o) { type = swizzle(o.type, o.order, order); offset = swizzle(o.offset, o.order, order); vaddr = swizzle(o.vaddr, o.order, order); paddr = swizzle(o.paddr, o.order, order); filesz = swizzle(o.filesz, o.order, order); memsz = swizzle(o.memsz, o.order, order); flags = swizzle(o.flags, o.order, order); align = swizzle(o.align, o.order, order); } }; // Symbol bindings (ELF32 figure 1-16, ELF64 table 14) enum class stb : unsigned char { local = 0, // Not visible outside the object file global = 1, // Global symbol weak = 2, // Global scope, but with lower // precedence than global symbols loos = 10, // Environment-specific use hios = 12, loproc = 13, // Processor-specific use hiproc = 15, }; std::string to_string(stb v); // Symbol types (ELF32 figure 1-17, ELF64 table 15) enum class stt : unsigned char { notype = 0, // No type (e.g., absolute symbol) object = 1, // Data object func = 2, // Function entry point section = 3, // Symbol is associated with a section file = 4, // Source file associated with the // object file loos = 10, // Environment-specific use hios = 12, loproc = 13, // Processor-specific use hiproc = 15, }; std::string to_string(stt v); // Symbol table (ELF32 figure 1-15, ELF64 figure 4) template struct Sym; template struct Sym { typedef Elf32 types; static const byte_order order = Order; Elf32::Word name; // Symbol name (strtab offset) Elf32::Addr value; // Symbol value (address) Elf32::Word size; // Size of object unsigned char info; // Type and binding attributes unsigned char other; // Reserved shn shnxd; // Section table index template void from(const E2 &o) { name = swizzle(o.name, o.order, order); value = swizzle(o.value, o.order, order); size = swizzle(o.size, o.order, order); info = o.info; other = o.other; shnxd = swizzle(o.shnxd, o.order, order); } stb binding() const { return (stb)(info >> 4); } void set_binding(stb v) { info = (info & 0x0F) | ((unsigned char)v << 4); } stt type() const { return (stt)(info & 0xF); } void set_type(stt v) { info = (info & 0xF0) | (unsigned char)v; } }; template struct Sym { typedef Elf64 types; static const byte_order order = Order; Elf64::Word name; // Symbol name (strtab offset) unsigned char info; // Type and binding attributes unsigned char other; // Reserved shn shnxd; // Section table index Elf64::Addr value; // Symbol value (address) Elf64::Xword size; // Size of object template void from(const E2 &o) { name = swizzle(o.name, o.order, order); value = swizzle(o.value, o.order, order); size = swizzle(o.size, o.order, order); info = o.info; other = o.other; shnxd = swizzle(o.shnxd, o.order, order); } stb binding() const { return (stb)(info >> 4); } void set_binding(stb v) const { info = (info & 0xF) | ((unsigned char)v << 4); } stt type() const { return (stt)(info & 0xF); } void set_type(stt v) { info = (info & 0xF0) | (unsigned char)v; } }; ELFPP_END_NAMESPACE #endif libelfin-0.3/elf/elf++.hh000066400000000000000000000264331302015676000151330ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #ifndef _ELFPP_HH_ #define _ELFPP_HH_ #include "common.hh" #include "data.hh" #include #include #include #include ELFPP_BEGIN_NAMESPACE class elf; class loader; class section; class strtab; class symtab; class segment; // XXX Audit for binary compatibility // XXX Segments, other section types /** * An exception indicating malformed ELF data. */ class format_error : public std::runtime_error { public: explicit format_error(const std::string &what_arg) : std::runtime_error(what_arg) { } explicit format_error(const char *what_arg) : std::runtime_error(what_arg) { } }; /** * An ELF file. * * This class is internally reference counted and efficiently * copyable. * * Raw pointers to ELF data returned by any method of this object or * any object derived from this object point directly into loaded * section data. Hence, callers must ensure that the loader passed to * this file remains live as long as any such pointer is in use. * Keeping any object that can return such a pointer live is * sufficieint to keep the loader live. */ class elf { public: /** * Construct an ELF file that is backed by data read from the * given loader. */ explicit elf(const std::shared_ptr &l); /** * Construct an ELF file that is initially not valid. Calling * methods other than operator= and valid on this results in * undefined behavior. */ elf() = default; elf(const elf &o) = default; elf(elf &&o) = default; elf& operator=(const elf &o) = default; bool valid() const { return !!m; } /** * Return the ELF file header in canonical form (ELF64 in * native byte order). */ const Ehdr<> &get_hdr() const; /** * Return the loader used by this file. */ std::shared_ptr get_loader() const; /** * Return the segments in this file. */ const std::vector &segments() const; /** * Return the segment at the given index. If no such segment * is found, return an invalid segment. */ const segment &get_segment(unsigned index) const; /** * Return the sections in this file. */ const std::vector
§ions() const; /** * Return the section with the specified name. If no such * section is found, return an invalid section. */ const section &get_section(const std::string &name) const; /** * Return the section at the given index. If no such section * is found, return an invalid section. */ const section &get_section(unsigned index) const; private: struct impl; std::shared_ptr m; }; /** * An interface for loading sections of an ELF file. */ class loader { public: virtual ~loader() { } /** * Load the requested file section into memory and return a * pointer to the beginning of it. This memory must remain * valid and unchanged until the loader is destroyed. If the * loader cannot satisfy the full request for any reason * (including a premature EOF), it must throw an exception. */ virtual const void *load(off_t offset, size_t size) = 0; }; /** * An mmap-based loader that maps requested sections on demand. This * will close fd when done, so the caller should dup the file * descriptor if it intends to continue using it. */ std::shared_ptr create_mmap_loader(int fd); /** * An exception indicating that a section is not of the requested type. */ class section_type_mismatch : public std::logic_error { public: explicit section_type_mismatch(const std::string &what_arg) : std::logic_error(what_arg) { } explicit section_type_mismatch(const char *what_arg) : std::logic_error(what_arg) { } }; /** * An ELF segment. * * This class is internally reference counted and efficiently * copyable. */ class segment { public: /** * Construct a segment that is initially not valid. Calling * methods other than operator= and valid on this results in * undefined behavior. */ segment() { } segment(const elf &f, const void *hdr); segment(const segment &o) = default; segment(segment &&o) = default; /** * Return true if this segment is valid and corresponds to a * segment in the ELF file. */ bool valid() const { return !!m; } /** * Return the ELF section header in canonical form (ELF64 in * native byte order). */ const Phdr<> &get_hdr() const; /** * Return this segment's data. The returned buffer will * be file_size() bytes long. */ const void *data() const; /** * Return the on disk size of this segment in bytes. */ size_t file_size() const; /** * Return the in-memory size of this segment in bytes. * Bytes between file_size() and mem_size() are implicity zeroes. */ size_t mem_size() const; private: struct impl; std::shared_ptr m; }; /** * An ELF section. * * This class is internally reference counted and efficiently * copyable. */ class section { public: /** * Construct a section that is initially not valid. Calling * methods other than operator= and valid on this results in * undefined behavior. */ section() { } section(const elf &f, const void *hdr); section(const section &o) = default; section(section &&o) = default; /** * Return true if this section is valid and corresponds to a * section in the ELF file. */ bool valid() const { return !!m; } /** * Return the ELF section header in canonical form (ELF64 in * native byte order). */ const Shdr<> &get_hdr() const; /** * Return this section's name. */ const char *get_name(size_t *len_out) const; /** * Return this section's name. The returned string copies its * data, so loader liveness requirements don't apply. */ std::string get_name() const; /** * Return this section's data. If this is a NOBITS section, * return nullptr. */ const void *data() const; /** * Return the size of this section in bytes. */ size_t size() const; /** * Return this section as a strtab. Throws * section_type_mismatch if this section is not a string * table. */ strtab as_strtab() const; /** * Return this section as a symtab. Throws * section_type_mismatch if this section is not a symbol * table. */ symtab as_symtab() const; private: struct impl; std::shared_ptr m; }; /** * A string table. * * This class is internally reference counted and efficiently * copyable. */ class strtab { public: /** * Construct a strtab that is initially not valid. Calling * methods other than operator= and valid on this results in * undefined behavior. */ strtab() = default; strtab(elf f, const void *data, size_t size); bool valid() const { return !!m; } /** * Return the string at the given offset in this string table. * If the offset is out of bounds, throws std::range_error. * This is very efficient since the returned pointer points * directly into the loaded section, though this still * verifies that the returned string is NUL-terminated. */ const char *get(Elf64::Off offset, size_t *len_out) const; /** * Return the string at the given offset in this string table. */ std::string get(Elf64::Off offset) const; private: struct impl; std::shared_ptr m; }; /** * A symbol from a symbol table. */ class sym { const strtab strs; Sym<> data; public: sym(elf f, const void *data, strtab strs); /** * Return this symbol's raw data. */ const Sym<> &get_data() const { return data; } /** * Return this symbol's name. * * This returns a pointer into the string table and, as such, * is very efficient. If len_out is non-nullptr, *len_out * will be set the length of the returned string. */ const char *get_name(size_t *len_out) const; /** * Return this symbol's name as a string. */ std::string get_name() const; }; /** * A symbol table. * * This class is internally reference counted and efficiently * copyable. */ class symtab { public: /** * Construct a symtab that is initially not valid. Calling * methods other than operator= and valid on this results in * undefined behavior. */ symtab() = default; symtab(elf f, const void *data, size_t size, strtab strs); bool valid() const { return !!m; } class iterator { const elf f; const strtab strs; const char *pos; size_t stride; iterator(const symtab &tab, const char *pos); friend class symtab; public: sym operator*() const { return sym(f, pos, strs); } iterator& operator++() { return *this += 1; } iterator operator++(int) { iterator cur(*this); *this += 1; return cur; } iterator& operator+=(std::ptrdiff_t x) { pos += x * stride; return *this; } iterator& operator-=(std::ptrdiff_t x) { pos -= x * stride; return *this; } bool operator==(iterator &o) const { return pos == o.pos; } bool operator!=(iterator &o) const { return pos != o.pos; } }; /** * Return an iterator to the first symbol. */ iterator begin() const; /** * Return an iterator just past the last symbol. */ iterator end() const; private: struct impl; std::shared_ptr m; }; ELFPP_END_NAMESPACE #endif libelfin-0.3/elf/elf.cc000066400000000000000000000240421302015676000147650ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "elf++.hh" #include using namespace std; ELFPP_BEGIN_NAMESPACE template class Hdr> void canon_hdr(Hdr *out, const void *data, elfclass ei_class, elfdata ei_data) { switch (ei_class) { case elfclass::_32: switch (ei_data) { case elfdata::lsb: out->from(*(Hdr*)data); break; case elfdata::msb: out->from(*(Hdr*)data); break; } break; case elfclass::_64: switch (ei_data) { case elfdata::lsb: out->from(*(Hdr*)data); break; case elfdata::msb: out->from(*(Hdr*)data); return; } } } ////////////////////////////////////////////////////////////////// // class elf // struct elf::impl { impl(const shared_ptr &l) : l(l) { } const shared_ptr l; Ehdr<> hdr; vector
sections; vector segments; section invalid_section; segment invalid_segment; }; elf::elf(const std::shared_ptr &l) : m(make_shared(l)) { // Read the first six bytes to check the magic number, ELF // class, and byte order. struct core_hdr { char ei_magic[4]; elfclass ei_class; elfdata ei_data; unsigned char ei_version; } *core_hdr = (struct core_hdr*)l->load(0, sizeof *core_hdr); // Check basic header if (strncmp(core_hdr->ei_magic, "\x7f" "ELF", 4) != 0) throw format_error("bad ELF magic number"); if (core_hdr->ei_version != 1) throw format_error("unknown ELF version"); if (core_hdr->ei_class != elfclass::_32 && core_hdr->ei_class != elfclass::_64) throw format_error("bad ELF class"); if (core_hdr->ei_data != elfdata::lsb && core_hdr->ei_data != elfdata::msb) throw format_error("bad ELF data order"); // Read in the real header and canonicalize it size_t hdr_size = (core_hdr->ei_class == elfclass::_32 ? sizeof(Ehdr) : sizeof(Ehdr)); const void *hdr = l->load(0, hdr_size); canon_hdr(&m->hdr, hdr, core_hdr->ei_class, core_hdr->ei_data); // More checks if (m->hdr.version != 1) throw format_error("bad section ELF version"); if (m->hdr.shnum && m->hdr.shstrndx >= m->hdr.shnum) throw format_error("bad section name string table index"); // Load segments const void *seg_data = l->load(m->hdr.phoff, m->hdr.phentsize * m->hdr.phnum); for (unsigned i = 0; i < m->hdr.phnum; i++) { const void *seg = ((const char*)seg_data) + i * m->hdr.phentsize; m->segments.push_back(segment(*this, seg)); } // Load sections const void *sec_data = l->load(m->hdr.shoff, m->hdr.shentsize * m->hdr.shnum); for (unsigned i = 0; i < m->hdr.shnum; i++) { const void *sec = ((const char*)sec_data) + i * m->hdr.shentsize; // XXX Circular reference. Maybe this should be // constructed on the fly? Canonicalizing the header // isn't super-cheap. m->sections.push_back(section(*this, sec)); } } const Ehdr<> & elf::get_hdr() const { return m->hdr; } shared_ptr elf::get_loader() const { return m->l; } const std::vector
& elf::sections() const { return m->sections; } const std::vector & elf::segments() const { return m->segments; } const section & elf::get_section(const std::string &name) const { for (auto &sec : sections()) if (name == sec.get_name(nullptr)) return sec; return m->invalid_section; } const section & elf::get_section(unsigned index) const { if (index >= sections().size()) return m->invalid_section; return sections().at(index); } const segment& elf::get_segment(unsigned index) const { if (index >= segments().size()) return m->invalid_segment; return segments().at(index); } ////////////////////////////////////////////////////////////////// // class segment // struct segment::impl { impl(const elf &f) : f(f) { } const elf f; Phdr<> hdr; // const char *name; // size_t name_len; const void *data; }; segment::segment(const elf &f, const void *hdr) : m(make_shared(f)) { canon_hdr(&m->hdr, hdr, f.get_hdr().ei_class, f.get_hdr().ei_data); } const Phdr<> & segment::get_hdr() const { return m->hdr; } const void * segment::data() const { if (!m->data) m->data = m->f.get_loader()->load(m->hdr.offset, m->hdr.filesz); return m->data; } size_t segment::file_size() const { return m->hdr.filesz; } size_t segment::mem_size() const { return m->hdr.memsz; } ////////////////////////////////////////////////////////////////// // class section // std::string enums::to_string(shn v) { if (v == shn::undef) return "undef"; if (v == shn::abs) return "abs"; if (v == shn::common) return "common"; return std::to_string(v); } struct section::impl { impl(const elf &f) : f(f), name(nullptr), data(nullptr) { } const elf f; Shdr<> hdr; const char *name; size_t name_len; const void *data; }; section::section(const elf &f, const void *hdr) : m(make_shared(f)) { canon_hdr(&m->hdr, hdr, f.get_hdr().ei_class, f.get_hdr().ei_data); } const Shdr<> & section::get_hdr() const { return m->hdr; } const char * section::get_name(size_t *len_out) const { // XXX Should the section name strtab be cached? if (!m->name) m->name = m->f.get_section(m->f.get_hdr().shstrndx) .as_strtab().get(m->hdr.name, &m->name_len); if (len_out) *len_out = m->name_len; return m->name; } string section::get_name() const { return get_name(nullptr); } const void * section::data() const { if (m->hdr.type == sht::nobits) return nullptr; if (!m->data) m->data = m->f.get_loader()->load(m->hdr.offset, m->hdr.size); return m->data; } size_t section::size() const { return m->hdr.size; } strtab section::as_strtab() const { if (m->hdr.type != sht::strtab) throw section_type_mismatch("cannot use section as strtab"); return strtab(m->f, data(), size()); } symtab section::as_symtab() const { if (m->hdr.type != sht::symtab && m->hdr.type != sht::dynsym) throw section_type_mismatch("cannot use section as symtab"); return symtab(m->f, data(), size(), m->f.get_section(get_hdr().link).as_strtab()); } ////////////////////////////////////////////////////////////////// // class strtab // struct strtab::impl { impl(const elf &f, const char *data, const char *end) : f(f), data(data), end(end) { } const elf f; const char *data, *end; }; strtab::strtab(elf f, const void *data, size_t size) : m(make_shared(f, (const char*)data, (const char *)data + size)) { } const char * strtab::get(Elf64::Off offset, size_t *len_out) const { const char *start = m->data + offset; if (start >= m->end) throw range_error("string offset " + std::to_string(offset) + " exceeds section size"); // Find the null terminator const char *p = start; while (p < m->end && *p) p++; if (p == m->end) throw format_error("unterminated string"); if (len_out) *len_out = p - start; return start; } std::string strtab::get(Elf64::Off offset) const { return get(offset, nullptr); } ////////////////////////////////////////////////////////////////// // class sym // sym::sym(elf f, const void *data, strtab strs) : strs(strs) { canon_hdr(&this->data, data, f.get_hdr().ei_class, f.get_hdr().ei_data); } const char * sym::get_name(size_t *len_out) const { return strs.get(get_data().name, len_out); } std::string sym::get_name() const { return strs.get(get_data().name); } ////////////////////////////////////////////////////////////////// // class symtab // struct symtab::impl { impl(const elf &f, const char *data, const char *end, strtab strs) : f(f), data(data), end(end), strs(strs) { } const elf f; const char *data, *end; const strtab strs; }; symtab::symtab(elf f, const void *data, size_t size, strtab strs) : m(make_shared(f, (const char*)data, (const char *)data + size, strs)) { } symtab::iterator::iterator(const symtab &tab, const char *pos) : f(tab.m->f), strs(tab.m->strs), pos(pos) { if (f.get_hdr().ei_class == elfclass::_32) stride = sizeof(Sym); else stride = sizeof(Sym); } symtab::iterator symtab::begin() const { return iterator(*this, m->data); } symtab::iterator symtab::end() const { return iterator(*this, m->end); } ELFPP_END_NAMESPACE libelfin-0.3/elf/enum-print.py000066400000000000000000000124331302015676000163610ustar00rootroot00000000000000# Copyright (c) 2013 Austin T. Clements. All rights reserved. # Use of this source code is governed by an MIT license # that can be found in the LICENSE file. import sys, re from optparse import OptionParser def read_toks(): data = sys.stdin.read() while data: data = data.lstrip() if data.startswith("//") or data.startswith("#"): data = data.split("\n",1)[1] elif data.startswith("/*"): data = data.split("*/",1)[1] elif data.startswith("\"") or data.startswith("'"): c = data[0] m = re.match(r'%s([^\\%s]|\\.)*%s' % (c,c,c), data) yield m.group(0) data = data[m.end():] else: m = re.match(r"[_a-zA-Z0-9]+|[{}();]|[^_a-zA-Z0-9 \n\t\f]+", data) yield m.group(0) data = data[m.end():] enums = {} def do_top_level(toks, ns=[]): while toks: tok = toks.pop(0) if tok == "enum" and toks[0] == "class": toks.pop(0) name = toks.pop(0) # Get to the first token in the body while toks.pop(0) != "{": pass # Consume body and close brace do_enum_body("::".join(ns + [name]), toks) elif tok == "class": name = do_qname(toks) # Find the class body, if there is one while toks[0] != "{" and toks[0] != ";": toks.pop(0) # Enter the class's namespace if toks[0] == "{": toks.pop(0) do_top_level(toks, ns + [name]) elif tok == "{": # Enter an unknown namespace do_top_level(toks, ns + [None]) elif tok == "}": # Exit the namespace assert len(ns) return elif not ns and tok == "string" and toks[:2] == ["to_string", "("]: # Get the argument type and name toks.pop(0) toks.pop(0) typ = do_qname(toks) if typ not in enums: continue arg = toks.pop(0) assert toks[0] == ")" if typ in options.mask: make_to_string_mask(typ, arg) else: make_to_string(typ, arg) def fmt_value(typ, key): if options.no_type: val = key else: val = "%s%s%s" % (typ, options.separator, key) if options.strip_underscore: val = val.strip("_") return val def expr_remainder(typ, arg): if options.hex: return "\"(%s)0x\" + to_hex((int)%s)" % (typ, arg) else: return "\"(%s)\" + std::to_string((int)%s)" % (typ, arg) def make_to_string(typ, arg): print("std::string") print("to_string(%s %s)" % (typ, arg)) print("{") print(" switch (%s) {" % arg) for key in enums[typ]: if key in options.exclude: print(" case %s::%s: break;" % (typ, key)) continue print(" case %s::%s: return \"%s\";" % \ (typ, key, fmt_value(typ, key))) print(" }") print(" return %s;" % expr_remainder(typ, arg)) print("}") print() def make_to_string_mask(typ, arg): print("std::string") print("to_string(%s %s)" % (typ, arg)) print("{") print(" std::string res;") for key in enums[typ]: if key in options.exclude: continue print(" if ((%s & %s::%s) == %s::%s) { res += \"%s|\"; %s &= ~%s::%s; }" % \ (arg, typ, key, typ, key, fmt_value(typ, key), arg, typ, key)) print(" if (res.empty() || %s != (%s)0) res += %s;" % \ (arg, typ, expr_remainder(typ, arg))) print(" else res.pop_back();") print(" return res;") print("}") print() def do_enum_body(name, toks): keys = [] while True: key = toks.pop(0) if key == "}": assert toks.pop(0) == ";" enums[name] = keys return keys.append(key) if toks[0] == "=": toks.pop(0) toks.pop(0) if toks[0] == ",": toks.pop(0) else: assert toks[0] == "}" def do_qname(toks): # Get a nested-name-specifier followed by an identifier res = [] while True: res.append(toks.pop(0)) if toks[0] != "::": return "::".join(res) toks.pop(0) parser = OptionParser() parser.add_option("-x", "--exclude", dest="exclude", action="append", help="exclude FIELD", metavar="FIELD", default=[]) parser.add_option("-u", "--strip-underscore", dest="strip_underscore", action="store_true", help="strip leading and trailing underscores") parser.add_option("-s", "--separator", dest="separator", help="use SEP between type and field", metavar="SEP", default="::") parser.add_option("--hex", dest="hex", action="store_true", help="return unknown values in hex", default=False) parser.add_option("--no-type", dest="no_type", action="store_true", help="omit type") parser.add_option("--mask", dest="mask", action="append", help="treat TYPE as a bit-mask", metavar="TYPE", default=[]) (options, args) = parser.parse_args() if args: parser.error("expected 0 arguments") do_top_level(list(read_toks())) libelfin-0.3/elf/mmap_loader.cc000066400000000000000000000026621302015676000165030ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #include "elf++.hh" #include #include #include #include #include #include using namespace std; ELFPP_BEGIN_NAMESPACE class mmap_loader : public loader { void *base; size_t lim; public: mmap_loader(int fd) { off_t end = lseek(fd, 0, SEEK_END); if (end == (off_t)-1) throw system_error(errno, system_category(), "finding file length"); lim = end; base = mmap(nullptr, lim, PROT_READ, MAP_SHARED, fd, 0); if (base == MAP_FAILED) throw system_error(errno, system_category(), "mmap'ing file"); close(fd); } ~mmap_loader() { munmap(base, lim); } const void *load(off_t offset, size_t size) { if (offset + size > lim) throw range_error("offset exceeds file size"); return (const char*)base + offset; } }; std::shared_ptr create_mmap_loader(int fd) { return make_shared(fd); } ELFPP_END_NAMESPACE libelfin-0.3/elf/to_hex.hh000066400000000000000000000016241302015676000155200ustar00rootroot00000000000000// Copyright (c) 2013 Austin T. Clements. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. #ifndef _ELFPP_TO_HEX_HH_ #define _ELFPP_TO_HEX_HH_ #include #include template std::string to_hex(T v) { static_assert(std::is_integral::value, "to_hex applied to non-integral type"); if (v == 0) return std::string("0"); char buf[sizeof(T)*2 + 1]; char *pos = &buf[sizeof(buf)-1]; *pos-- = '\0'; while (v && pos >= buf) { int digit = v & 0xf; if (digit < 10) *pos = '0' + digit; else *pos = 'a' + (digit - 10); pos--; v >>= 4; } return std::string(pos + 1); } #endif // _ELFPP_TO_HEX_HH_ libelfin-0.3/examples/000077500000000000000000000000001302015676000147565ustar00rootroot00000000000000libelfin-0.3/examples/.gitignore000066400000000000000000000001141302015676000167420ustar00rootroot00000000000000*.o .*.d dump-sections dump-segments dump-syms dump-lines dump-tree find-pc libelfin-0.3/examples/Makefile000066400000000000000000000021771302015676000164250ustar00rootroot00000000000000CXXFLAGS+=-g -O2 -Werror override CXXFLAGS+=-std=c++0x -Wall CLEAN := all: dump-sections dump-segments dump-syms dump-tree dump-lines find-pc # Find libs export PKG_CONFIG_PATH=../elf:../dwarf CPPFLAGS+=$$(pkg-config --cflags libelf++ libdwarf++) # Statically link against our libs to keep the example binaries simple # and dependencies correct. LIBS=../dwarf/libdwarf++.a ../elf/libelf++.a # Dependencies CPPFLAGS+=-MD -MP -MF .$@.d -include .*.d dump-sections: dump-sections.o $(LIBS) $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ CLEAN += dump-sections dump-sections.o dump-segments: dump-segments.o $(LIBS) $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ CLEAN += dump-segments dump-segments.o dump-syms: dump-syms.o $(LIBS) $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ CLEAN += dump-syms dump-syms.o dump-tree: dump-tree.o $(LIBS) $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ CLEAN += dump-tree dump-tree.o dump-lines: dump-lines.o $(LIBS) $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ CLEAN += dump-lines dump-lines.o find-pc: find-pc.o $(LIBS) $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ CLEAN += find-pc find-pc.o clean: rm -f $(CLEAN) .*.d libelfin-0.3/examples/dump-lines.cc000066400000000000000000000021451302015676000173440ustar00rootroot00000000000000#include "elf++.hh" #include "dwarf++.hh" #include #include using namespace std; void dump_line_table(const dwarf::line_table <) { for (auto &line : lt) { if (line.end_sequence) printf("\n"); else printf("%-40s%8d%#20" PRIx64 "\n", line.file->path.c_str(), line.line, line.address); } } int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s elf-file\n", argv[0]); return 2; } int fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); return 1; } elf::elf ef(elf::create_mmap_loader(fd)); dwarf::dwarf dw(dwarf::elf::create_loader(ef)); for (auto cu : dw.compilation_units()) { printf("--- <%x>\n", (unsigned int)cu.get_section_offset()); dump_line_table(cu.get_line_table()); printf("\n"); } return 0; } libelfin-0.3/examples/dump-sections.cc000066400000000000000000000025031302015676000200570ustar00rootroot00000000000000#include "elf++.hh" #include #include #include #include int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s elf-file\n", argv[0]); return 2; } int fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); return 1; } elf::elf f(elf::create_mmap_loader(fd)); int i = 0; printf(" [Nr] %-16s %-16s %-16s %s\n", "Name", "Type", "Address", "Offset"); printf(" %-16s %-16s %-15s %5s %4s %5s\n", "Size", "EntSize", "Flags", "Link", "Info", "Align"); for (auto &sec : f.sections()) { auto &hdr = sec.get_hdr(); printf(" [%2d] %-16s %-16s %016" PRIx64 " %08" PRIx64 "\n", i++, sec.get_name().c_str(), to_string(hdr.type).c_str(), hdr.addr, hdr.offset); printf(" %016zx %016" PRIx64 " %-15s %5s %4d %5" PRIu64 "\n", sec.size(), hdr.entsize, to_string(hdr.flags).c_str(), to_string(hdr.link).c_str(), (int)hdr.info, hdr.addralign); } return 0; } libelfin-0.3/examples/dump-segments.cc000066400000000000000000000023221302015676000200540ustar00rootroot00000000000000#include "elf++.hh" #include #include #include #include int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s elf-file\n", argv[0]); return 2; } int fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); return 1; } elf::elf f(elf::create_mmap_loader(fd)); printf(" %-16s %-16s %-16s %s\n", "Type", "Offset", "VirtAddr", "PhysAddr"); printf(" %-16s %-16s %-16s %6s %5s\n", " ", "FileSiz", "MemSiz", "Flags", "Align"); for (auto &seg : f.segments()) { auto &hdr = seg.get_hdr(); printf(" %-16s 0x%016" PRIx64 " 0x%016" PRIx64 " 0x%016" PRIx64 "\n", to_string(hdr.type).c_str(), hdr.offset, hdr.vaddr, hdr.paddr); printf(" %-16s 0x%016" PRIx64 " 0x%016" PRIx64 " %-5s %-5" PRIx64 "\n", "", hdr.filesz, hdr.memsz, to_string(hdr.flags).c_str(), hdr.align); } return 0; } libelfin-0.3/examples/dump-syms.cc000066400000000000000000000026701302015676000172300ustar00rootroot00000000000000#include "elf++.hh" #include #include #include #include int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s elf-file\n", argv[0]); return 2; } int fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); return 1; } elf::elf f(elf::create_mmap_loader(fd)); for (auto &sec : f.sections()) { if (sec.get_hdr().type != elf::sht::symtab && sec.get_hdr().type != elf::sht::dynsym) continue; printf("Symbol table '%s':\n", sec.get_name().c_str()); printf("%6s: %-16s %-5s %-7s %-7s %-5s %s\n", "Num", "Value", "Size", "Type", "Binding", "Index", "Name"); int i = 0; for (auto sym : sec.as_symtab()) { auto &d = sym.get_data(); printf("%6d: %016" PRIx64 " %5" PRId64 " %-7s %-7s %5s %s\n", i++, d.value, d.size, to_string(d.type()).c_str(), to_string(d.binding()).c_str(), to_string(d.shnxd).c_str(), sym.get_name().c_str()); } } return 0; } libelfin-0.3/examples/dump-tree.cc000066400000000000000000000023101302015676000171630ustar00rootroot00000000000000#include "elf++.hh" #include "dwarf++.hh" #include #include using namespace std; void dump_tree(const dwarf::die &node, int depth = 0) { printf("%*.s<%" PRIx64 "> %s\n", depth, "", node.get_section_offset(), to_string(node.tag).c_str()); for (auto &attr : node.attributes()) printf("%*.s %s %s\n", depth, "", to_string(attr.first).c_str(), to_string(attr.second).c_str()); for (auto &child : node) dump_tree(child, depth + 1); } int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s elf-file\n", argv[0]); return 2; } int fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); return 1; } elf::elf ef(elf::create_mmap_loader(fd)); dwarf::dwarf dw(dwarf::elf::create_loader(ef)); for (auto cu : dw.compilation_units()) { printf("--- <%" PRIx64 ">\n", cu.get_section_offset()); dump_tree(cu.root()); } return 0; } libelfin-0.3/examples/find-pc.cc000066400000000000000000000065441302015676000166160ustar00rootroot00000000000000#include "elf++.hh" #include "dwarf++.hh" #include #include #include using namespace std; void usage(const char *cmd) { fprintf(stderr, "usage: %s elf-file pc\n", cmd); exit(2); } bool find_pc(const dwarf::die &d, dwarf::taddr pc, vector *stack) { using namespace dwarf; // Scan children first to find most specific DIE bool found = false; for (auto &child : d) { if ((found = find_pc(child, pc, stack))) break; } switch (d.tag) { case DW_TAG::subprogram: case DW_TAG::inlined_subroutine: try { if (found || die_pc_range(d).contains(pc)) { found = true; stack->push_back(d); } } catch (out_of_range &e) { } catch (value_type_mismatch &e) { } break; default: break; } return found; } void dump_die(const dwarf::die &node) { printf("<%" PRIx64 "> %s\n", node.get_section_offset(), to_string(node.tag).c_str()); for (auto &attr : node.attributes()) printf(" %s %s\n", to_string(attr.first).c_str(), to_string(attr.second).c_str()); } int main(int argc, char **argv) { if (argc != 3) usage(argv[0]); dwarf::taddr pc; try { pc = stoll(argv[2], nullptr, 0); } catch (invalid_argument &e) { usage(argv[0]); } catch (out_of_range &e) { usage(argv[0]); } int fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); return 1; } elf::elf ef(elf::create_mmap_loader(fd)); dwarf::dwarf dw(dwarf::elf::create_loader(ef)); // Find the CU containing pc // XXX Use .debug_aranges for (auto &cu : dw.compilation_units()) { if (die_pc_range(cu.root()).contains(pc)) { // Map PC to a line auto < = cu.get_line_table(); auto it = lt.find_address(pc); if (it == lt.end()) printf("UNKNOWN\n"); else printf("%s\n", it->get_description().c_str()); // Map PC to an object // XXX Index/helper/something for looking up PCs // XXX DW_AT_specification and DW_AT_abstract_origin vector stack; if (find_pc(cu.root(), pc, &stack)) { bool first = true; for (auto &d : stack) { if (!first) printf("\nInlined in:\n"); first = false; dump_die(d); } } break; } } return 0; } libelfin-0.3/test/000077500000000000000000000000001302015676000141175ustar00rootroot00000000000000libelfin-0.3/test/example.c000066400000000000000000000002501302015676000157130ustar00rootroot00000000000000int fib(int x) { if (x <= 1) return x; return fib(x - 1) + fib(x - 2); } int main(int argc, char **argv) { return fib(10); } libelfin-0.3/test/golden-gcc-4.9.2/000077500000000000000000000000001302015676000165715ustar00rootroot00000000000000libelfin-0.3/test/golden-gcc-4.9.2/NOTES000066400000000000000000000001761302015676000174100ustar00rootroot00000000000000Built with $ gcc -o golden-gcc-4.9.2/example -g -fdebug-prefix-map=$PWD=x example.c where gcc is version 4.9.2 from Debian. libelfin-0.3/test/golden-gcc-4.9.2/example000066400000000000000000000171001302015676000201460ustar00rootroot00000000000000ELF>@@@8@# @@@@@@@@@ ``(0 ``@@DDPtd@@<<Qtd/lib64/ld-linux-x86-64.so.2GNU GNU~Y1ѰإD  libc.so.6__libc_start_main__gmon_start__GLIBC_2.2.5ui ,```HHU Ht+H5J %L @%J h%B h1I^HHPTI@H@H@fD `UH- `HHvHt] `f]fffff. `UH `HHHH?HHtHt ] `]fD= uUHn] @`H?uHtUH]zUHSH}}EEËEH[]UHH}Hu AWAAVIAUIATL% UH- SL)1HH-HtLLDAHH9uH[]A\A]A^A_ff.HH;8,T"^|4zRx *zRx $p0FJ w?;*3$"Dn<AC Er dAC V DeBEE E(H0H8M@l8A0A(B BBB@p@ p@ @``o`@@@ 8 `0@@(@ o@oo@`@@GCC: (Debian 4.9.2-10) 4.9.2GCC: (Debian 4.8.4-1) 4.8.4,@W nx@WfibY@<YxY\intiY@Ylx`% .?: ; 'I@B: ; I$ > .?: ; 'I@B: ; I I$ > ?  example.c @gYwargccharGNU C 4.9.2 -mtune=generic -march=x86-64 -g -fdebug-prefix-map=/home/amthrax/r/libelfin/test=xmainexample.cargv.symtab.strtab.shstrtab.interp.note.ABI-tag.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.plt.init.text.fini.rodata.eh_frame_hdr.eh_frame.init_array.fini_array.jcr.dynamic.got.got.plt.data.bss.comment.debug_aranges.debug_info.debug_abbrev.debug_line.debug_str@@<@`@@@@@ (@ @@ p@ @ @@@@@`````` ` ` ` @. 0@A p@W `f` @`@```` ` @/ K  `V `]@c `  `@ @e ` @ ` @ @<  `  % p@crtstuff.c__JCR_LIST__deregister_tm_clonesregister_tm_clones__do_global_dtors_auxcompleted.6661__do_global_dtors_aux_fini_array_entryframe_dummy__frame_dummy_init_array_entryexample.c__FRAME_END____JCR_END____init_array_end_DYNAMIC__init_array_start_GLOBAL_OFFSET_TABLE___libc_csu_fini_ITM_deregisterTMCloneTabledata_start_edata_fini__libc_start_main@@GLIBC_2.2.5__data_start__gmon_start____dso_handle_IO_stdin_used__libc_csu_init_end_start__bss_startmainfib_Jv_RegisterClasses__TMC_END___ITM_registerTMCloneTable_init@#@ 1<@<$Do`@`N @HV@8^o@ko@ z(@(B@@@0 p@p@0@@ @@<@``````( `  ` 0 9I 0y #+ 1 C=0 }t H "2 P+libelfin-0.3/test/golden-gcc-4.9.2/lines000066400000000000000000000010611302015676000176240ustar00rootroot00000000000000--- <0> x/example.c 2 0x4004b6 x/example.c 3 0x4004c2 x/example.c 4 0x4004c8 x/example.c 5 0x4004cd x/example.c 6 0x4004eb x/example.c 9 0x4004f2 x/example.c 10 0x400501 x/example.c 11 0x40050b libelfin-0.3/test/golden-gcc-4.9.2/sections000066400000000000000000000117241302015676000203500ustar00rootroot00000000000000 [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] null 0000000000000000 00000000 0000000000000000 0000000000000000 (shf)0x0 undef 0 0 [ 1] .interp progbits 0000000000400200 00000200 000000000000001c 0000000000000000 alloc undef 0 1 [ 2] .note.ABI-tag note 000000000040021c 0000021c 0000000000000020 0000000000000000 alloc undef 0 4 [ 3] .note.gnu.build-id note 000000000040023c 0000023c 0000000000000024 0000000000000000 alloc undef 0 4 [ 4] .gnu.hash (sht)0x6ffffff6 0000000000400260 00000260 000000000000001c 0000000000000000 alloc 5 0 8 [ 5] .dynsym dynsym 0000000000400280 00000280 0000000000000048 0000000000000018 alloc 6 1 8 [ 6] .dynstr strtab 00000000004002c8 000002c8 0000000000000038 0000000000000000 alloc undef 0 1 [ 7] .gnu.version (sht)0x6fffffff 0000000000400300 00000300 0000000000000006 0000000000000002 alloc 5 0 2 [ 8] .gnu.version_r (sht)0x6ffffffe 0000000000400308 00000308 0000000000000020 0000000000000000 alloc 6 1 8 [ 9] .rela.dyn rela 0000000000400328 00000328 0000000000000018 0000000000000018 alloc 5 0 8 [10] .rela.plt rela 0000000000400340 00000340 0000000000000030 0000000000000018 alloc|(shf)0x40 5 12 8 [11] .init progbits 0000000000400370 00000370 000000000000001a 0000000000000000 alloc|execinstr undef 0 4 [12] .plt progbits 0000000000400390 00000390 0000000000000030 0000000000000010 alloc|execinstr undef 0 16 [13] .text progbits 00000000004003c0 000003c0 00000000000001c2 0000000000000000 alloc|execinstr undef 0 16 [14] .fini progbits 0000000000400584 00000584 0000000000000009 0000000000000000 alloc|execinstr undef 0 4 [15] .rodata progbits 0000000000400590 00000590 0000000000000004 0000000000000004 alloc|(shf)0x10 undef 0 4 [16] .eh_frame_hdr progbits 0000000000400594 00000594 000000000000003c 0000000000000000 alloc undef 0 4 [17] .eh_frame progbits 00000000004005d0 000005d0 0000000000000114 0000000000000000 alloc undef 0 8 [18] .init_array (sht)0xe 00000000006006e8 000006e8 0000000000000008 0000000000000000 write|alloc undef 0 8 [19] .fini_array (sht)0xf 00000000006006f0 000006f0 0000000000000008 0000000000000000 write|alloc undef 0 8 [20] .jcr progbits 00000000006006f8 000006f8 0000000000000008 0000000000000000 write|alloc undef 0 8 [21] .dynamic dynamic 0000000000600700 00000700 00000000000001d0 0000000000000010 write|alloc 6 0 8 [22] .got progbits 00000000006008d0 000008d0 0000000000000008 0000000000000008 write|alloc undef 0 8 [23] .got.plt progbits 00000000006008d8 000008d8 0000000000000028 0000000000000008 write|alloc undef 0 8 [24] .data progbits 0000000000600900 00000900 0000000000000010 0000000000000000 write|alloc undef 0 8 [25] .bss nobits 0000000000600910 00000910 0000000000000008 0000000000000000 write|alloc undef 0 1 [26] .comment progbits 0000000000000000 00000910 0000000000000039 0000000000000001 (shf)0x30 undef 0 1 [27] .debug_aranges progbits 0000000000000000 00000949 0000000000000030 0000000000000000 (shf)0x0 undef 0 1 [28] .debug_info progbits 0000000000000000 00000979 00000000000000b2 0000000000000000 (shf)0x0 undef 0 1 [29] .debug_abbrev progbits 0000000000000000 00000a2b 0000000000000089 0000000000000000 (shf)0x0 undef 0 1 [30] .debug_line progbits 0000000000000000 00000ab4 0000000000000043 0000000000000000 (shf)0x0 undef 0 1 [31] .debug_str progbits 0000000000000000 00000af7 000000000000007d 0000000000000001 (shf)0x30 undef 0 1 [32] .shstrtab strtab 0000000000000000 00000b74 0000000000000148 0000000000000000 (shf)0x0 undef 0 1 [33] .symtab symtab 0000000000000000 00000cc0 0000000000000690 0000000000000018 (shf)0x0 34 50 8 [34] .strtab strtab 0000000000000000 00001350 000000000000022b 0000000000000000 (shf)0x0 undef 0 1 libelfin-0.3/test/golden-gcc-4.9.2/segments000066400000000000000000000024431302015676000203440ustar00rootroot00000000000000 Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align phdr 0x0000000000000040 0x0000000000400040 0x0000000000400040 0x00000000000001c0 0x00000000000001c0 x|r 8 interp 0x0000000000000200 0x0000000000400200 0x0000000000400200 0x000000000000001c 0x000000000000001c r 1 load 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x00000000000006e4 0x00000000000006e4 x|r 200000 load 0x00000000000006e8 0x00000000006006e8 0x00000000006006e8 0x0000000000000228 0x0000000000000230 w|r 200000 dynamic 0x0000000000000700 0x0000000000600700 0x0000000000600700 0x00000000000001d0 0x00000000000001d0 w|r 8 note 0x000000000000021c 0x000000000040021c 0x000000000040021c 0x0000000000000044 0x0000000000000044 r 4 (pt)0x6474e550 0x0000000000000594 0x0000000000400594 0x0000000000400594 0x000000000000003c 0x000000000000003c r 4 (pt)0x6474e551 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 w|r 10 libelfin-0.3/test/golden-gcc-4.9.2/syms000066400000000000000000000110711302015676000175070ustar00rootroot00000000000000Symbol table '.dynsym': Num: Value Size Type Binding Index Name 0: 0000000000000000 0 notype local undef 1: 0000000000000000 0 func global undef __libc_start_main 2: 0000000000000000 0 notype weak undef __gmon_start__ Symbol table '.symtab': Num: Value Size Type Binding Index Name 0: 0000000000000000 0 notype local undef 1: 0000000000400200 0 section local 1 2: 000000000040021c 0 section local 2 3: 000000000040023c 0 section local 3 4: 0000000000400260 0 section local 4 5: 0000000000400280 0 section local 5 6: 00000000004002c8 0 section local 6 7: 0000000000400300 0 section local 7 8: 0000000000400308 0 section local 8 9: 0000000000400328 0 section local 9 10: 0000000000400340 0 section local 10 11: 0000000000400370 0 section local 11 12: 0000000000400390 0 section local 12 13: 00000000004003c0 0 section local 13 14: 0000000000400584 0 section local 14 15: 0000000000400590 0 section local 15 16: 0000000000400594 0 section local 16 17: 00000000004005d0 0 section local 17 18: 00000000006006e8 0 section local 18 19: 00000000006006f0 0 section local 19 20: 00000000006006f8 0 section local 20 21: 0000000000600700 0 section local 21 22: 00000000006008d0 0 section local 22 23: 00000000006008d8 0 section local 23 24: 0000000000600900 0 section local 24 25: 0000000000600910 0 section local 25 26: 0000000000000000 0 section local 26 27: 0000000000000000 0 section local 27 28: 0000000000000000 0 section local 28 29: 0000000000000000 0 section local 29 30: 0000000000000000 0 section local 30 31: 0000000000000000 0 section local 31 32: 0000000000000000 0 file local abs crtstuff.c 33: 00000000006006f8 0 object local 20 __JCR_LIST__ 34: 00000000004003f0 0 func local 13 deregister_tm_clones 35: 0000000000400430 0 func local 13 register_tm_clones 36: 0000000000400470 0 func local 13 __do_global_dtors_aux 37: 0000000000600910 1 object local 25 completed.6661 38: 00000000006006f0 0 object local 19 __do_global_dtors_aux_fini_array_entry 39: 0000000000400490 0 func local 13 frame_dummy 40: 00000000006006e8 0 object local 18 __frame_dummy_init_array_entry 41: 0000000000000000 0 file local abs example.c 42: 0000000000000000 0 file local abs crtstuff.c 43: 00000000004006e0 0 object local 17 __FRAME_END__ 44: 00000000006006f8 0 object local 20 __JCR_END__ 45: 0000000000000000 0 file local abs 46: 00000000006006f0 0 notype local 18 __init_array_end 47: 0000000000600700 0 object local 21 _DYNAMIC 48: 00000000006006e8 0 notype local 18 __init_array_start 49: 00000000006008d8 0 object local 23 _GLOBAL_OFFSET_TABLE_ 50: 0000000000400580 2 func global 13 __libc_csu_fini 51: 0000000000000000 0 notype weak undef _ITM_deregisterTMCloneTable 52: 0000000000600900 0 notype weak 24 data_start 53: 0000000000600910 0 notype global 24 _edata 54: 0000000000400584 0 func global 14 _fini 55: 0000000000000000 0 func global undef __libc_start_main@@GLIBC_2.2.5 56: 0000000000600900 0 notype global 24 __data_start 57: 0000000000000000 0 notype weak undef __gmon_start__ 58: 0000000000600908 0 object global 24 __dso_handle 59: 0000000000400590 4 object global 15 _IO_stdin_used 60: 0000000000400510 101 func global 13 __libc_csu_init 61: 0000000000600918 0 notype global 25 _end 62: 00000000004003c0 0 func global 13 _start 63: 0000000000600910 0 notype global 25 __bss_start 64: 00000000004004f2 27 func global 13 main 65: 00000000004004b6 60 func global 13 fib 66: 0000000000000000 0 notype weak undef _Jv_RegisterClasses 67: 0000000000600910 0 object global 24 __TMC_END__ 68: 0000000000000000 0 notype weak undef _ITM_registerTMCloneTable 69: 0000000000400370 0 func global 11 _init libelfin-0.3/test/golden-gcc-4.9.2/tree000066400000000000000000000034321302015676000174550ustar00rootroot00000000000000--- <0> DW_TAG_compile_unit DW_AT_producer GNU C 4.9.2 -mtune=generic -march=x86-64 -g -fdebug-prefix-map=/home/amthrax/r/libelfin/test=x DW_AT_language 0x1 DW_AT_name example.c DW_AT_comp_dir x DW_AT_low_pc 0x4004b6 DW_AT_high_pc 0x57 DW_AT_stmt_list <2b> DW_TAG_subprogram DW_AT_external true DW_AT_name fib DW_AT_decl_file 0x1 DW_AT_decl_line 0x1 DW_AT_prototyped true DW_AT_type <0x59> DW_AT_low_pc 0x4004b6 DW_AT_high_pc 0x3c DW_AT_frame_base (DW_AT)0x2116 true DW_AT_sibling <0x59> <4c> DW_TAG_formal_parameter DW_AT_name x DW_AT_decl_file 0x1 DW_AT_decl_line 0x1 DW_AT_type <0x59> DW_AT_location <59> DW_TAG_base_type DW_AT_byte_size 0x4 DW_AT_encoding 0x5 DW_AT_name int <60> DW_TAG_subprogram DW_AT_external true DW_AT_name main DW_AT_decl_file 0x1 DW_AT_decl_line 0x8 DW_AT_prototyped true DW_AT_type <0x59> DW_AT_low_pc 0x4004f2 DW_AT_high_pc 0x1b DW_AT_frame_base (DW_AT)0x2116 true DW_AT_sibling <0x9e> <81> DW_TAG_formal_parameter DW_AT_name argc DW_AT_decl_file 0x1 DW_AT_decl_line 0x8 DW_AT_type <0x59> DW_AT_location <8f> DW_TAG_formal_parameter DW_AT_name argv DW_AT_decl_file 0x1 DW_AT_decl_line 0x8 DW_AT_type <0x9e> DW_AT_location <9e> DW_TAG_pointer_type DW_AT_byte_size 0x8 DW_AT_type <0xa4> DW_TAG_pointer_type DW_AT_byte_size 0x8 DW_AT_type <0xaa> DW_TAG_base_type DW_AT_byte_size 0x1 DW_AT_encoding 0x6 DW_AT_name char libelfin-0.3/test/golden-gcc-6.2.1-s390x/000077500000000000000000000000001302015676000174475ustar00rootroot00000000000000libelfin-0.3/test/golden-gcc-6.2.1-s390x/NOTES000066400000000000000000000002551302015676000202640ustar00rootroot00000000000000Built with $ gcc -o golden-gcc-6.2.1-s390x/example -g -fdebug-prefix-map=$PWD=x example.c using gcc 6.2.1 from Debian on s390x. This binary uses big-endian ELF and DWARF. libelfin-0.3/test/golden-gcc-6.2.1-s390x/example000066400000000000000000000224301302015676000210260ustar00rootroot00000000000000ELF@@8 @"!@@@888 `h   HHHDDdtP,,dtQdtR/lib/ld64.so.1GNUGNU:>^Y;6r)Ћ |k ` ", H W k ' @libc.so.6__cxa_finalize__libc_start_main_ITM_deregisterTMCloneTable__gmon_start___Jv_RegisterClasses_ITM_registerTMCloneTableGLIBC_2.2 ii    @  ` ` ( 0 8 H P   o0$`$ F _ @o8$ -0 ,     A@0 P$Ap`1P  P@ A P!  0 13 ? 13   X$` ȕt   _@h$٧` t t ~ P$XPX$XX!X!չ!@믰X$P0$P) !@ 뿱8$`   qpH9*{ ᧷@o0$`$ @o;,H@zRx 48xF DD h ,Tx@F DD p 4dF DX  ` o  00X oo8oo o  `GCC: (Debian 6.2.1-5) 6.2.1 20161124,h Bxh=k@k3k~Lr~intx~8fibkhxxk~% .?: ; 'I@B: ; I$ >  I$ > .?: ; 'I@B: ; ID  example.c hY<#!GNU C11 6.2.1 20161124 -m64 -mzarch -march=z900 -gargccharmainexample.cargv8Hh 8 X 0 `      X h  H . D hSz       /"I n  Xe h)l Xy  `  d pr  h @ hx  h  `crtstuff.c__JCR_LIST__deregister_tm_clones__do_global_dtors_auxcompleted.6615__do_global_dtors_aux_fini_array_entryframe_dummy__frame_dummy_init_array_entryexample.c__FRAME_END____JCR_END____init_array_end_DYNAMIC__init_array_start__GNU_EH_FRAME_HDR_GLOBAL_OFFSET_TABLE___libc_csu_fini__cxa_finalize@@GLIBC_2.2_ITM_deregisterTMCloneTable_edata__data_start__gmon_start____dso_handle_IO_stdin_used__libc_start_main@@GLIBC_2.2__libc_csu_init__bss_startmainfib_Jv_RegisterClasses__TMC_END___ITM_registerTMCloneTable.symtab.strtab.shstrtab.interp.note.ABI-tag.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.plt.init.text.fini.rodata.eh_frame_hdr.eh_frame.init_array.fini_array.jcr.dynamic.got.data.bss.comment.debug_aranges.debug_info.debug_abbrev.debug_line.debug_str88#HH 1hh$Do$N V^o  ko88 zXXB000``@` , ,     X XX hh0h%0n(H40=Q 2 8 X?libelfin-0.3/test/golden-gcc-6.2.1-s390x/lines000066400000000000000000000010611302015676000205020ustar00rootroot00000000000000--- <0> x/example.c 2 0x768 x/example.c 3 0x77e x/example.c 4 0x78a x/example.c 5 0x792 x/example.c 6 0x7ce x/example.c 9 0x7e0 x/example.c 10 0x7fc x/example.c 11 0x80e libelfin-0.3/test/golden-gcc-6.2.1-s390x/sections000066400000000000000000000115071302015676000212250ustar00rootroot00000000000000 [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] null 0000000000000000 00000000 0000000000000000 0000000000000000 (shf)0x0 undef 0 0 [ 1] .interp progbits 0000000000000238 00000238 000000000000000f 0000000000000000 alloc undef 0 1 [ 2] .note.ABI-tag note 0000000000000248 00000248 0000000000000020 0000000000000000 alloc undef 0 4 [ 3] .note.gnu.build-id note 0000000000000268 00000268 0000000000000024 0000000000000000 alloc undef 0 4 [ 4] .gnu.hash (sht)0x6ffffff6 0000000000000290 00000290 0000000000000024 0000000000000000 alloc 5 0 8 [ 5] .dynsym dynsym 00000000000002b8 000002b8 00000000000000d8 0000000000000018 alloc 6 2 8 [ 6] .dynstr strtab 0000000000000390 00000390 000000000000008f 0000000000000000 alloc undef 0 1 [ 7] .gnu.version (sht)0x6fffffff 0000000000000420 00000420 0000000000000012 0000000000000002 alloc 5 0 2 [ 8] .gnu.version_r (sht)0x6ffffffe 0000000000000438 00000438 0000000000000020 0000000000000000 alloc 6 1 8 [ 9] .rela.dyn rela 0000000000000458 00000458 00000000000000d8 0000000000000018 alloc 5 0 8 [10] .rela.plt rela 0000000000000530 00000530 0000000000000030 0000000000000018 alloc|(shf)0x40 5 22 8 [11] .init progbits 0000000000000560 00000560 0000000000000040 0000000000000000 alloc|execinstr undef 0 4 [12] .plt progbits 00000000000005a0 000005a0 0000000000000060 0000000000000020 alloc|execinstr undef 0 4 [13] .text progbits 0000000000000600 00000600 0000000000000290 0000000000000000 alloc|execinstr undef 0 8 [14] .fini progbits 0000000000000890 00000890 000000000000002c 0000000000000000 alloc|execinstr undef 0 4 [15] .rodata progbits 00000000000008c0 000008c0 0000000000000020 0000000000000000 alloc undef 0 8 [16] .eh_frame_hdr progbits 00000000000008e0 000008e0 000000000000002c 0000000000000000 alloc undef 0 4 [17] .eh_frame progbits 0000000000000910 00000910 00000000000000d4 0000000000000000 alloc undef 0 8 [18] .init_array (sht)0xe 0000000000001e08 00000e08 0000000000000008 0000000000000008 write|alloc undef 0 8 [19] .fini_array (sht)0xf 0000000000001e10 00000e10 0000000000000008 0000000000000008 write|alloc undef 0 8 [20] .jcr progbits 0000000000001e18 00000e18 0000000000000008 0000000000000000 write|alloc undef 0 8 [21] .dynamic dynamic 0000000000001e20 00000e20 00000000000001e0 0000000000000010 write|alloc 6 0 8 [22] .got progbits 0000000000002000 00001000 0000000000000058 0000000000000008 write|alloc undef 0 8 [23] .data progbits 0000000000002058 00001058 0000000000000010 0000000000000000 write|alloc undef 0 8 [24] .bss nobits 0000000000002068 00001068 0000000000000008 0000000000000000 write|alloc undef 0 4 [25] .comment progbits 0000000000000000 00001068 0000000000000025 0000000000000001 (shf)0x30 undef 0 1 [26] .debug_aranges progbits 0000000000000000 0000108d 0000000000000030 0000000000000000 (shf)0x0 undef 0 1 [27] .debug_info progbits 0000000000000000 000010bd 00000000000000b1 0000000000000000 (shf)0x0 undef 0 1 [28] .debug_abbrev progbits 0000000000000000 0000116e 0000000000000087 0000000000000000 (shf)0x0 undef 0 1 [29] .debug_line progbits 0000000000000000 000011f5 0000000000000048 0000000000000000 (shf)0x0 undef 0 1 [30] .debug_str progbits 0000000000000000 0000123d 0000000000000051 0000000000000001 (shf)0x30 undef 0 1 [31] .symtab symtab 0000000000000000 00001290 00000000000006a8 0000000000000018 (shf)0x0 32 50 8 [32] .strtab strtab 0000000000000000 00001938 0000000000000220 0000000000000000 (shf)0x0 undef 0 1 [33] .shstrtab strtab 0000000000000000 00001b58 000000000000013f 0000000000000000 (shf)0x0 undef 0 1 libelfin-0.3/test/golden-gcc-6.2.1-s390x/segments000066400000000000000000000026641302015676000212270ustar00rootroot00000000000000 Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align phdr 0x0000000000000040 0x0000000000000040 0x0000000000000040 0x00000000000001f8 0x00000000000001f8 x|r 8 interp 0x0000000000000238 0x0000000000000238 0x0000000000000238 0x000000000000000f 0x000000000000000f r 1 load 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x00000000000009e4 0x00000000000009e4 x|r 1000 load 0x0000000000000e08 0x0000000000001e08 0x0000000000001e08 0x0000000000000260 0x0000000000000268 w|r 1000 dynamic 0x0000000000000e20 0x0000000000001e20 0x0000000000001e20 0x00000000000001e0 0x00000000000001e0 w|r 8 note 0x0000000000000248 0x0000000000000248 0x0000000000000248 0x0000000000000044 0x0000000000000044 r 4 (pt)0x6474e550 0x00000000000008e0 0x00000000000008e0 0x00000000000008e0 0x000000000000002c 0x000000000000002c r 4 (pt)0x6474e551 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 w|r 10 (pt)0x6474e552 0x0000000000000e08 0x0000000000001e08 0x0000000000001e08 0x00000000000001f8 0x00000000000001f8 r 1 libelfin-0.3/test/golden-gcc-6.2.1-s390x/syms000066400000000000000000000120651302015676000203710ustar00rootroot00000000000000Symbol table '.dynsym': Num: Value Size Type Binding Index Name 0: 0000000000000000 0 notype local undef 1: 0000000000000560 0 section local 11 2: 0000000000000000 0 func weak undef __cxa_finalize 3: 0000000000000000 0 notype weak undef _ITM_deregisterTMCloneTable 4: 0000000000000000 0 notype weak undef __gmon_start__ 5: 0000000000000000 0 func global undef __libc_start_main 6: 0000000000000000 0 notype weak undef _Jv_RegisterClasses 7: 0000000000000000 0 notype weak undef _ITM_registerTMCloneTable 8: 00000000000007e0 64 func global 13 main Symbol table '.symtab': Num: Value Size Type Binding Index Name 0: 0000000000000000 0 notype local undef 1: 0000000000000238 0 section local 1 2: 0000000000000248 0 section local 2 3: 0000000000000268 0 section local 3 4: 0000000000000290 0 section local 4 5: 00000000000002b8 0 section local 5 6: 0000000000000390 0 section local 6 7: 0000000000000420 0 section local 7 8: 0000000000000438 0 section local 8 9: 0000000000000458 0 section local 9 10: 0000000000000530 0 section local 10 11: 0000000000000560 0 section local 11 12: 00000000000005a0 0 section local 12 13: 0000000000000600 0 section local 13 14: 0000000000000890 0 section local 14 15: 00000000000008c0 0 section local 15 16: 00000000000008e0 0 section local 16 17: 0000000000000910 0 section local 17 18: 0000000000001e08 0 section local 18 19: 0000000000001e10 0 section local 19 20: 0000000000001e18 0 section local 20 21: 0000000000001e20 0 section local 21 22: 0000000000002000 0 section local 22 23: 0000000000002058 0 section local 23 24: 0000000000002068 0 section local 24 25: 0000000000000000 0 section local 25 26: 0000000000000000 0 section local 26 27: 0000000000000000 0 section local 27 28: 0000000000000000 0 section local 28 29: 0000000000000000 0 section local 29 30: 0000000000000000 0 section local 30 31: 0000000000000000 0 file local abs crtstuff.c 32: 0000000000001e18 0 object local 20 __JCR_LIST__ 33: 0000000000000648 0 func local 13 deregister_tm_clones 34: 0000000000000680 0 func local 13 register_tm_clones 35: 00000000000006c8 0 func local 13 __do_global_dtors_aux 36: 0000000000002068 1 object local 24 completed.6615 37: 0000000000001e10 0 object local 19 __do_global_dtors_aux_fini_array_entry 38: 0000000000000720 0 func local 13 frame_dummy 39: 0000000000001e08 0 object local 18 __frame_dummy_init_array_entry 40: 0000000000000000 0 file local abs example.c 41: 0000000000000000 0 file local abs crtstuff.c 42: 00000000000009e0 0 object local 17 __FRAME_END__ 43: 0000000000001e18 0 object local 20 __JCR_END__ 44: 0000000000000000 0 file local abs 45: 0000000000001e10 0 notype local 18 __init_array_end 46: 0000000000001e20 0 object local abs _DYNAMIC 47: 0000000000001e08 0 notype local 18 __init_array_start 48: 00000000000008e0 0 notype local 16 __GNU_EH_FRAME_HDR 49: 0000000000002000 0 object local abs _GLOBAL_OFFSET_TABLE_ 50: 0000000000000888 2 func global 13 __libc_csu_fini 51: 0000000000000000 0 func weak undef __cxa_finalize@@GLIBC_2.2 52: 0000000000000000 0 notype weak undef _ITM_deregisterTMCloneTable 53: 0000000000002058 0 notype weak 23 data_start 54: 0000000000002068 0 notype global 23 _edata 55: 0000000000000890 0 func global 14 _fini 56: 0000000000002058 0 notype global 23 __data_start 57: 0000000000000000 0 notype weak undef __gmon_start__ 58: 0000000000002060 0 object global 23 __dso_handle 59: 00000000000008c0 4 object global 15 _IO_stdin_used 60: 0000000000000000 0 func global undef __libc_start_main@@GLIBC_2.2 61: 0000000000000820 100 func global 13 __libc_csu_init 62: 0000000000002070 0 notype global 24 _end 63: 0000000000000600 0 func global 13 _start 64: 0000000000002068 0 notype global 24 __bss_start 65: 00000000000007e0 64 func global 13 main 66: 0000000000000768 120 func global 13 fib 67: 0000000000000000 0 notype weak undef _Jv_RegisterClasses 68: 0000000000002068 0 object global 23 __TMC_END__ 69: 0000000000000000 0 notype weak undef _ITM_registerTMCloneTable 70: 0000000000000560 0 func global 11 _init libelfin-0.3/test/golden-gcc-6.2.1-s390x/tree000066400000000000000000000033111302015676000203270ustar00rootroot00000000000000--- <0> DW_TAG_compile_unit DW_AT_producer GNU C11 6.2.1 20161124 -m64 -mzarch -march=z900 -g DW_AT_language 0xc DW_AT_name example.c DW_AT_comp_dir x DW_AT_low_pc 0x768 DW_AT_high_pc 0xb8 DW_AT_stmt_list <2b> DW_TAG_subprogram DW_AT_external true DW_AT_name main DW_AT_decl_file 0x1 DW_AT_decl_line 0x8 DW_AT_prototyped true DW_AT_type <0x6b> DW_AT_low_pc 0x7e0 DW_AT_high_pc 0x40 DW_AT_frame_base (DW_AT)0x2116 true DW_AT_sibling <0x6b> <4c> DW_TAG_formal_parameter DW_AT_name argc DW_AT_decl_file 0x1 DW_AT_decl_line 0x8 DW_AT_type <0x6b> DW_AT_location <5b> DW_TAG_formal_parameter DW_AT_name argv DW_AT_decl_file 0x1 DW_AT_decl_line 0x8 DW_AT_type <0x72> DW_AT_location <6b> DW_TAG_base_type DW_AT_byte_size 0x4 DW_AT_encoding 0x5 DW_AT_name int <72> DW_TAG_pointer_type DW_AT_byte_size 0x8 DW_AT_type <0x78> <78> DW_TAG_pointer_type DW_AT_byte_size 0x8 DW_AT_type <0x7e> <7e> DW_TAG_base_type DW_AT_byte_size 0x1 DW_AT_encoding 0x8 DW_AT_name char <85> DW_TAG_subprogram DW_AT_external true DW_AT_name fib DW_AT_decl_file 0x1 DW_AT_decl_line 0x1 DW_AT_prototyped true DW_AT_type <0x6b> DW_AT_low_pc 0x768 DW_AT_high_pc 0x78 DW_AT_frame_base (DW_AT)0x2116 true DW_TAG_formal_parameter DW_AT_name x DW_AT_decl_file 0x1 DW_AT_decl_line 0x1 DW_AT_type <0x6b> DW_AT_location libelfin-0.3/test/test.sh000077500000000000000000000032661302015676000154440ustar00rootroot00000000000000#!/bin/bash die() { echo "$@" exit 1 } (cd ../examples && make --quiet) || die "failed to build examples" dumps="sections segments lines syms tree" binaries=example compilers="gcc-4.9.2 gcc-6.2.1-s390x" if [[ $1 == --make-golden ]]; then MODE=make-golden fi output=$(mktemp --tmpdir libelfin.XXXXXXXXXX) trap "rm -f $output $output.out" EXIT FAILED=0 for dump in $dumps; do for binary in $binaries; do for compiler in $compilers; do if [[ $MODE == make-golden ]]; then ../examples/dump-$dump golden-$compiler/$binary > golden-$compiler/$dump || \ die "failed to create golden output" continue fi PASS=1 # Save stdout and stderr and redirect output to temporary file. exec 3>&1 4>&2 1>$output 2>&1 # Run the test. ../examples/dump-$dump golden-$compiler/$binary >& $output.out STATUS=$? if [[ $STATUS != 0 ]]; then PASS=0 echo "failed: exit status $STATUS" fi if ! diff -u golden-$compiler/$dump $output.out; then PASS=0 fi # Restore FDs. exec 1>&3 2>&4 3>&- 4>&- # Report results. if [[ $PASS == 0 ]]; then FAILED=$((FAILED + 1)) echo -n "FAIL " else echo -n "PASS " fi echo dump-$dump golden-$compiler/$binary if [[ $PASS == 0 ]]; then sed 's/^/\t/' $output fi done done done if [[ $FAILED != 0 ]]; then echo "$FAILED test(s) failed" exit 1 fi