From: Roland McGrath Date: Thu, 29 Jan 2009 06:14:29 +0000 (-0800) Subject: C++ reader interface for line info X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0dd600a5cf6841a1f0a35dab43a983491a596099;p=thirdparty%2Felfutils.git C++ reader interface for line info --- diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 06ff0290f..7b1d37e9d 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,5 +1,11 @@ 2009-01-28 Roland McGrath + * c++/dwarf: Add line table support. + * c++/values.cc: New file. + * c++/line_info.cc: New file. + * c++/subr.hh: New file. + * Makefile.am (pkginclude_HEADERS): Add it. + * libdwP.h (struct Dwarf_Line_s): Move out of struct Dwarf_Lines_s defn so C++ doesn't scope the name to not match the Dwarf_Line typedef. diff --git a/libdw/Makefile.am b/libdw/Makefile.am index daeeead1e..3f5032dc6 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -48,6 +48,7 @@ endif include_HEADERS = dwarf.h pkginclude_HEADERS = libdw.h \ + c++/subr.hh \ c++/dwarf c++/dwarf_edit libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ diff --git a/libdw/c++/dwarf b/libdw/c++/dwarf index 8fa911553..e8fb2214b 100644 --- a/libdw/c++/dwarf +++ b/libdw/c++/dwarf @@ -52,8 +52,8 @@ #include "libdw.h" #include "dwarf.h" +#include "subr.hh" #include -#include #include #include @@ -375,6 +375,11 @@ namespace elfutils class location_attr; class range_list; class ranges; + class line_info_table; + class directory_table; + class file_table; + class line_table; + class line_entry; class debug_info_entry { @@ -888,6 +893,23 @@ namespace elfutils inline compile_unit (const debug_info_entry &die) : debug_info_entry (die) {} + // Fetch the CU's DW_AT_stmt_list. + const line_info_table line_info () const; + + // Convenience methods for line_info_table sub-containers. + inline const directory_table include_directories () const + { + return line_info ().include_directories (); + } + const file_table files () const + { + return line_info ().files (); + } + const line_table lines () const + { + return line_info ().lines (); + } + /* containers/iterators: @@ -931,6 +953,8 @@ namespace elfutils class source_file { friend class attr_value; + friend class file_table; + friend class line_entry; private: ::Dwarf_Attribute _m_attr; inline ::Dwarf_Attribute *thisattr () const @@ -1029,7 +1053,8 @@ namespace elfutils return range_list (*this); } - // XXX lineptr + const line_info_table line_info () const; + // XXX macptr template @@ -1050,7 +1075,9 @@ namespace elfutils case VS_rangelistptr: return ranges () == other.ranges (); - case VS_lineptr: // XXX punt for now, treat as constant + case VS_lineptr: + return line_info () == other.line_info (); + case VS_macptr: // XXX punt for now, treat as constant /*FALLTHRU*/ case VS_constant: @@ -1244,6 +1271,310 @@ namespace elfutils } }; + // This describes a CU's directory table, a simple array of strings. + class directory_table + { + private: + ::Dwarf_Files *_m_files; + + template + struct name_equal + : public std::binary_function + { + bool operator () (const char *me, const string &you) + { + return you == me; + } + }; + template + inline bool table_equal (const table &other) const + { + /* We ignore the first element, the compilation directory. + This is not encoded in the .debug_line table, but in + the DW_AT_comp_dir attribute of the referring CU. + The directory table itself matches regardless. */ + const_iterator i = begin (); + typename table::const_iterator j = other.begin (); + return std::equal (++i, end (), ++j, + name_equal ()); + } + + public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef const char *value_type; + + inline directory_table (::Dwarf_Files *const files) + : _m_files (files) {} + inline directory_table (const directory_table &t) + : _m_files (t._m_files) {} + + typedef const char *const *const_iterator; + + inline bool empty () const + { + return size () == 0; + } + + size_t size () const; + const_iterator begin () const; + const_iterator end () const; + + template + inline bool operator== (const table &other) const + { + return table_equal (other); + } + template + inline bool operator!= (const table &other) const + { + return !(*this == other); + } + // Short-circuit for comparing to self. + inline bool operator== (const directory_table &other) const + { + return _m_files == other._m_files || table_equal (other); + } + }; + + /* This describes a CU's file table. It works like a read-only + std::vector, and also supports lookup by name. */ + class file_table + { + private: + ::Dwarf_Files *_m_files; + + public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef source_file value_type; + + inline file_table (::Dwarf_Files *const files) + : _m_files (files) {} + inline file_table (const file_table &t) + : _m_files (t._m_files) {} + + inline file_table &operator= (const file_table &t) + { + _m_files = t._m_files; + return *this; + } + + typedef subr::indexed_iterator const_iterator; + + inline bool empty () const + { + return size () == 0; + } + + size_t size () const; + + inline const_iterator begin () const + { + return const_iterator (*this, 0); + } + inline const_iterator end () const + { + return const_iterator (*this, size ()); + } + + const source_file at (size_t idx) const; + const source_file operator[] (size_t idx) const + { + return at (idx); + } + + // Look up by matching file name. + const_iterator find (const source_file &) const; + const_iterator find (const char *filename) const + { + const_iterator i = begin (); + while (i != end () && strcmp ((*i).name (), filename) != 0) + ++i; + return i; + } + template + const_iterator find (const string &filename) const + { + const_iterator i = begin (); + while (i != end () && filename != (*i).name ()) + ++i; + return i; + } + }; + + class line_entry + { + private: + ::Dwarf_Line *_m_line; + + public: + line_entry (::Dwarf_Line *entry) : _m_line (entry) {} + line_entry (const line_entry &entry) : _m_line (entry._m_line) {} + + // XXX reloc, dwfl + ::Dwarf_Addr address () const; + + bool statement () const; + bool basic_block () const; + bool end_sequence () const; + bool prologue_end () const; + bool epilogue_begin () const; + + const source_file file () const; + unsigned int line () const; + unsigned int column () const; + + template + bool operator< (const entry &other) const + { + return address () < other.address (); + } + template + bool operator> (const entry &other) const + { + return address () > other.address (); + } + template + bool operator<= (const entry &other) const + { + return address () <= other.address (); + } + template + bool operator>= (const entry &other) const + { + return address () >= other.address (); + } + + template + inline bool operator== (const entry &other) const + { + return (address () == other.address () + && line () == other.line () + && column () == other.column () + && statement () == other.statement () + && basic_block () == other.basic_block () + && end_sequence () == other.end_sequence () + && prologue_end () == other.prologue_end () + && epilogue_begin () == other.epilogue_begin () + && file () == other.file ()); + } + template + inline bool operator!= (const entry &other) const + { + return !(*this == other); + } + // Short-circuit for our own type. + bool operator== (const line_entry &other) const; + }; + + class line_table + { + private: + ::Dwarf_Lines *_m_lines; + + public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef line_entry value_type; + + inline line_table (::Dwarf_Lines *const lines) + : _m_lines (lines) {} + inline line_table (const line_table &t) + : _m_lines (t._m_lines) {} + + inline line_table &operator= (const line_table &t) + { + _m_lines = t._m_lines; + return *this; + } + + typedef subr::indexed_iterator const_iterator; + + inline bool empty () const + { + return size () == 0; + } + + size_t size () const; + + inline const_iterator begin () const + { + return const_iterator (*this, 0); + } + inline const_iterator end () const + { + return const_iterator (*this, size ()); + } + + const line_entry at (size_t idx) const; + const line_entry operator[] (size_t idx) const + { + return at (idx); + } + + template + inline bool operator== (const table &other) const + { + return std::equal (begin (), end (), other.begin ()); + } + template + inline bool operator!= (const table &other) const + { + return !(*this == other); + } + // Short-circuit for comparing to self. + inline bool operator== (const line_table &other) const + { + return (_m_lines == other._m_lines + || std::equal (begin (), end (), other.begin ())); + } + + // Look up by matching address. + const_iterator find (::Dwarf_Addr) const; + }; + + // The DW_AT_stmt_list attribute yields a line info table. + class line_info_table + { + private: + ::Dwarf_Files *_m_files; + + public: + inline line_info_table (::Dwarf_Files *const t) + : _m_files (t) {} + inline line_info_table (const line_info_table &t) + : _m_files (t._m_files) {} + + inline line_info_table &operator= (const line_info_table &t) + { + _m_files = t._m_files; + return *this; + } + + const directory_table include_directories () const + { + return directory_table (_m_files); + } + const file_table files () const + { + return file_table (_m_files); + } + const line_table lines () const; + + template + inline bool operator== (const table &other) const + { + return (include_directories () == other.include_directories () + && lines () == other.lines ()); + } + template + inline bool operator!= (const table &other) const + { + return !(*this == other); + } + }; + // This describes one attribute, equivalent to pair. class attribute { @@ -1679,6 +2010,17 @@ namespace elfutils } }; + // Explicit specialization used inside dwarf::directory_table::operator==. + template<> + struct dwarf::directory_table::name_equal + : public std::binary_function + { + bool operator () (const char *me, const char *you) + { + return !strcmp (me, you); + } + }; + inline class dwarf::debug_info_entry::raw_children dwarf::debug_info_entry::raw_children () const { diff --git a/libdw/c++/line_info.cc b/libdw/c++/line_info.cc new file mode 100644 index 000000000..d86b724ad --- /dev/null +++ b/libdw/c++/line_info.cc @@ -0,0 +1,338 @@ +#include +#include +#include "dwarf" + +extern "C" +{ +#include "libdwP.h" +} + +using namespace elfutils; +using namespace std; + + +// dwarf::line_info_table + +const dwarf::line_info_table +dwarf::attr_value::line_info () const +{ + assert (dwarf_whatattr (thisattr ()) == DW_AT_stmt_list); + + CUDIE (cudie, _m_attr.cu); + Dwarf_Lines *lines; + size_t n; + xif (dwarf_getsrclines (&cudie, &lines, &n) < 0); + + return line_info_table (_m_attr.cu->files); +} + +const dwarf::line_table +dwarf::line_info_table::lines () const +{ + return line_table (_m_files->cu->lines); +} + +// dwarf::source_file + +const class dwarf::source_file +dwarf::attr_value::source_file () const +{ + switch (what_space ()) + { + case VS_string: + case VS_source_file: + break; + default: + throw std::runtime_error ("XXX not a file name"); + } + return source_file::source_file (_m_attr); +} + +static bool +stringform (Dwarf_Attribute *attr) +{ + if (attr->valp != NULL) + switch (dwarf_whatform (attr)) + { + case DW_FORM_string: + case DW_FORM_strp: + return true; + } + return false; +} + +/* Mock up a dummy attribute with a special kludge that get_files groks. + We use these for source_file objects consed directly from an index + rather than from a real attribute. */ +static inline const Dwarf_Attribute +dummy_source_file (Dwarf_CU *cu, unsigned int idx) +{ + const Dwarf_Attribute dummy = { idx, DW_FORM_indirect, NULL, cu }; + return dummy; +} + +static bool +get_files (const Dwarf_Attribute *attr, Dwarf_Files **files, Dwarf_Word *idx) +{ + if (attr->valp == NULL) + { + // Dummy hack created by dummy_source_file, above. + assert (attr->form == DW_FORM_indirect); + *files = attr->cu->files; + *idx = attr->code; + return false; + } + + CUDIE (cudie, attr->cu); + return (dwarf_formudata (const_cast (attr), idx) < 0 + || dwarf_getsrcfiles (&cudie, files, NULL) < 0); +} + +Dwarf_Word +dwarf::source_file::mtime () const +{ + if (stringform (thisattr ())) + return 0; + + Dwarf_Files *files; + Dwarf_Word idx; + xif (get_files (thisattr (), &files, &idx)); + + Dwarf_Word result; + xif (dwarf_filesrc (files, idx, &result, NULL) == NULL); + return result; +} + +Dwarf_Word +dwarf::source_file::size () const +{ + if (stringform (thisattr ())) + return 0; + + Dwarf_Files *files; + Dwarf_Word idx; + xif (get_files (thisattr (), &files, &idx)); + + Dwarf_Word result; + xif (dwarf_filesrc (files, idx, NULL, &result) == NULL); + return result; +} + +const char * +dwarf::source_file::name () const +{ + if (stringform (thisattr ())) + return dwarf_formstring (thisattr ()); + + Dwarf_Files *files; + size_t idx; + xif (get_files (thisattr (), &files, &idx)); + + const char *result = dwarf_filesrc (files, idx, NULL, NULL); + xif (result == NULL); + return result; +} + +static inline string +plain_string (const char *filename) +{ + return string ("\"") + filename + "\""; +} + +string +dwarf::source_file::to_string () const +{ + if (stringform (thisattr ())) + return plain_string (dwarf_formstring (thisattr ())); + + Dwarf_Files *files; + size_t idx; + xif (get_files (thisattr (), &files, &idx)); + + Dwarf_Word file_mtime; + Dwarf_Word file_size; + const char *result = dwarf_filesrc (files, idx, &file_mtime, &file_size); + xif (result == NULL); + + if (likely (file_mtime == 0) && likely (file_size == 0)) + return plain_string (result); + + std::ostringstream os; + os << "{\"" << result << "," << file_mtime << "," << file_size << "}"; + return os.str (); +} + +// dwarf::directory_table + +size_t +dwarf::directory_table::size () const +{ + return _m_files->ndirs; +} + +static inline dwarf::directory_table::const_iterator +directory_table_array (Dwarf_Files *files) +{ + // See dwarf_getsrcdirs. + return reinterpret_cast (&files->info[files->nfiles]); +} + +dwarf::directory_table::const_iterator +dwarf::directory_table::begin () const +{ + return directory_table_array (_m_files); +} + +dwarf::directory_table::const_iterator +dwarf::directory_table::end () const +{ + return directory_table_array (_m_files) + _m_files->ndirs; +} + +// dwarf::file_table + +size_t +dwarf::file_table::size () const +{ + return _m_files->nfiles; +} + +const dwarf::source_file +dwarf::file_table::at (size_t idx) const +{ + if (unlikely (idx >= _m_files->nfiles)) + throw std::out_of_range ("XXX fileidx"); + + return dwarf::source_file (dummy_source_file (_m_files->cu, idx)); +} + +dwarf::file_table::const_iterator +dwarf::file_table::find (const source_file &src) const +{ + if (src._m_attr.cu->files == _m_files) + { + // Same table, just cons an iterator using its index. + Dwarf_Files *files; + Dwarf_Word idx; + xif (get_files (&src._m_attr, &files, &idx)); + return const_iterator (*this, idx); + } + + // Not from this table, just match on file name. + return find (src.name ()); +} + +// dwarf::line_table + +size_t +dwarf::line_table::size () const +{ + return _m_lines->nlines; +} + +const dwarf::line_entry +dwarf::line_table::at (size_t idx) const +{ + if (unlikely (idx >= _m_lines->nlines)) + throw std::out_of_range ("XXX line table index"); + + return line_entry (reinterpret_cast (&_m_lines->info[idx])); +} + +dwarf::line_table::const_iterator +dwarf::line_table::find (Dwarf_Addr address) const +{ + size_t idx = _m_lines->nlines; // end () + if (likely (idx > 0)) + { + CUDIE (cudie, _m_lines->info[0].files->cu); + Dwarf_Line *line = dwarf_getsrc_die (&cudie, address); + if (line != NULL) + idx = line - &_m_lines->info[0]; + } + return const_iterator (*this, idx); +} + +// dwarf::line_entry + +const dwarf::source_file +dwarf::line_entry::file () const +{ + return dwarf::source_file (dummy_source_file (_m_line->files->cu, + _m_line->file)); +} + +#define LINEFIELD(type, method, field) \ + type \ + dwarf::line_entry::method () const \ + { \ + return _m_line->field; \ + } + +LINEFIELD (Dwarf_Addr, address, addr) // XXX dwfl? +LINEFIELD (unsigned int, line, line) +LINEFIELD (unsigned int, column, column) +LINEFIELD (bool, statement, is_stmt) +LINEFIELD (bool, basic_block, basic_block) +LINEFIELD (bool, end_sequence, end_sequence) +LINEFIELD (bool, prologue_end, prologue_end) +LINEFIELD (bool, epilogue_begin, epilogue_begin) + +#undef LINEFIELD + +bool +dwarf::line_entry::operator== (const dwarf::line_entry &other) const +{ + Dwarf_Line *const a = _m_line; + Dwarf_Line *const b = other._m_line; + + if (a == b) + return true; + + if (a->addr != b->addr + || a->line != b->line + || a->column != b->column + || a->is_stmt != b->is_stmt + || a->basic_block != b->basic_block + || a->end_sequence != b->end_sequence + || a->prologue_end != b->prologue_end + || a->epilogue_begin != b->epilogue_begin) + return false; + + // Everything else matches, now have to try the file. + if (a->files == b->files) + // Same table, just compare indices. + return a->file == b->file; + + Dwarf_Word atime; + Dwarf_Word asize; + const char *aname = dwarf_linesrc (a, &atime, &asize); + xif (aname == NULL); + Dwarf_Word btime; + Dwarf_Word bsize; + const char *bname = dwarf_linesrc (b, &btime, &bsize); + xif (bname == NULL); + + /* The mtime and size only count when encoded as nonzero. + If either side is zero, we don't consider the field. */ + + if (atime != btime && atime != 0 && btime != 0) + return false; + + if (asize != bsize && asize != 0 && bsize != 0) + return false; + + return !strcmp (aname, bname); +} + +// dwarf::compile_unit convenience functions. + +const dwarf::line_info_table +dwarf::compile_unit::line_info () const +{ + Dwarf_Lines *l; + size_t n; + xif (dwarf_getsrclines (thisdie (), &l, &n) < 0); + + return line_info_table (thisdie ()->cu->files); +} diff --git a/libdw/c++/subr.hh b/libdw/c++/subr.hh new file mode 100644 index 000000000..070b3cd1f --- /dev/null +++ b/libdw/c++/subr.hh @@ -0,0 +1,125 @@ +/* Private helper classes for elfutils -*- C++ -*- interfaces. + + */ + +#ifndef _ELFUTILS_SUBR_HH +#define _ELFUTILS_SUBR_HH 1 + +#include + +namespace elfutils +{ + namespace subr + { + + template + class indexed_iterator + : public std::iterator + { + private: + typedef typename array::size_type index_type; + + array _m_contents; + index_type _m_idx; + + public: + indexed_iterator (array contents, index_type idx) + : _m_contents (contents), _m_idx (idx) {} + indexed_iterator (const indexed_iterator &i) + : _m_contents (i._m_contents), _m_idx (i._m_idx) {} + + inline element operator* () const + { + return _m_contents[_m_idx]; + } + template + inline elt operator* () const + { + return _m_contents[_m_idx]; + } + template + inline elt *operator-> () const + { + return &_m_contents[_m_idx]; + } + template + inline elt operator[] (const index_type &n) const + { + return _m_contents[_m_idx + n]; + } + + inline indexed_iterator operator+ (const indexed_iterator &i) + { + return indexed_iterator (_m_contents, _m_idx + i._m_idx); + } + inline indexed_iterator operator- (const indexed_iterator &i) + { + return indexed_iterator (_m_contents, _m_idx - i._m_idx); + } + + inline bool operator== (const indexed_iterator &i) + { + return _m_idx == i._m_idx; + } + inline bool operator!= (const indexed_iterator &i) + { + return _m_idx != i._m_idx; + } + inline bool operator< (const indexed_iterator &i) + { + return _m_idx < i._m_idx; + } + inline bool operator> (const indexed_iterator &i) + { + return _m_idx > i._m_idx; + } + inline bool operator<= (const indexed_iterator &i) + { + return _m_idx <= i._m_idx; + } + inline bool operator>= (const indexed_iterator &i) + { + return _m_idx >= i._m_idx; + } + + inline indexed_iterator &operator= (const indexed_iterator &i) + { + _m_idx = i._m_idx; + return *this; + } + inline indexed_iterator &operator+= (const index_type &n) + { + _m_idx += n; + return *this; + } + inline indexed_iterator &operator-= (const index_type &n) + { + _m_idx -= n; + return *this; + } + + inline indexed_iterator &operator++ () // prefix + { + ++_m_idx; + return *this; + } + inline indexed_iterator operator++ (int) // postfix + { + return indexed_iterator (_m_contents, _m_idx++); + } + inline indexed_iterator &operator-- () // prefix + { + --_m_idx; + return *this; + } + inline indexed_iterator operator-- (int) // postfix + { + return indexed_iterator (_m_contents, _m_idx--); + } + }; + }; +}; + +#endif // diff --git a/libdw/c++/values.cc b/libdw/c++/values.cc index 3f858cf35..45f2ce30c 100644 --- a/libdw/c++/values.cc +++ b/libdw/c++/values.cc @@ -48,6 +48,7 @@ . */ #include +#include #include "dwarf" extern "C" @@ -296,110 +297,6 @@ dwarf::attr_value::constant_block () const return const_vector (begin, end); } -// dwarf::source_file - -const class dwarf::source_file -dwarf::attr_value::source_file () const -{ - switch (what_space ()) - { - case VS_string: - case VS_source_file: - break; - default: - throw std::runtime_error ("XXX not a file name"); - } - return source_file::source_file (_m_attr); -} - -static bool -stringform (Dwarf_Attribute *attr) -{ - switch (dwarf_whatform (attr)) - { - case DW_FORM_string: - case DW_FORM_strp: - return true; - } - return false; -} - -static bool -get_files (Dwarf_Attribute *attr, Dwarf_Files **files, Dwarf_Word *idx) -{ - CUDIE (cudie, attr->cu); - return (dwarf_formudata (attr, idx) < 0 - || dwarf_getsrcfiles (&cudie, files, NULL) < 0); -} - -Dwarf_Word -dwarf::source_file::mtime () const -{ - if (stringform (thisattr ())) - return 0; - - Dwarf_Files *files; - Dwarf_Word idx; - xif (get_files (thisattr (), &files, &idx)); - - Dwarf_Word result; - xif (dwarf_filesrc (files, idx, &result, NULL) == NULL); - return result; -} - -Dwarf_Word -dwarf::source_file::size () const -{ - if (stringform (thisattr ())) - return 0; - - Dwarf_Files *files; - Dwarf_Word idx; - xif (get_files (thisattr (), &files, &idx)); - - Dwarf_Word result; - xif (dwarf_filesrc (files, idx, NULL, &result) == NULL); - return result; -} - -const char * -dwarf::source_file::name () const -{ - if (stringform (thisattr ())) - return dwarf_formstring (thisattr ()); - - Dwarf_Files *files; - size_t idx; - xif (get_files (thisattr (), &files, &idx)); - - const char *result = dwarf_filesrc (files, idx, NULL, NULL); - xif (result == NULL); - return result; -} - -string -dwarf::source_file::to_string () const -{ - if (stringform (thisattr ())) - return plain_string (dwarf_formstring (thisattr ())); - - Dwarf_Files *files; - size_t idx; - xif (get_files (thisattr (), &files, &idx)); - - Dwarf_Word file_mtime; - Dwarf_Word file_size; - const char *result = dwarf_filesrc (files, idx, &file_mtime, &file_size); - xif (result == NULL); - - if (likely (file_mtime == 0) && likely (file_size == 0)) - return plain_string (result); - - std::ostringstream os; - os << "{\"" << result << "," << file_mtime << "," << file_size << "}"; - return os.str (); -} - // dwarf::location_attr const dwarf::location_attr diff --git a/src/Makefile.am b/src/Makefile.am index be8989e36..ffb58cb87 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,9 +81,12 @@ ld_SOURCES = ld.c ldgeneric.c ldlex.l ldscript.y symbolhash.c sectionhash.c \ libar_a_SOURCES = arlib.c arlib2.c -dwarfcmp_SOURCES = dwarfcmp.cc # XXX need to figure out C++ dso crapola -dwarfcmp_SOURCES += ../libdw/c++/values.cc ../libdw/c++/known.cc +libdwplusplus_SOURCES = ../libdw/c++/values.cc ../libdw/c++/known.cc \ + ../libdw/c++/line_info.cc + +dwarfcmp_SOURCES = dwarfcmp.cc +dwarfcmp_SOURCES += $(libdwplusplus_SOURCES) noinst_HEADERS = ld.h symbolhash.h sectionhash.h versionhash.h \ ldscript.h xelf.h unaligned.h