elf_utils 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.standard.yml +3 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/CONTRIBUTING.md +55 -0
- data/Gemfile +23 -0
- data/LICENSE.txt +21 -0
- data/MAINTAINERS.md +3 -0
- data/README.md +126 -0
- data/Rakefile +76 -0
- data/SECURITY.md +57 -0
- data/elf_utils.gemspec +41 -0
- data/ext/elf_utils/elf_utils.c +53 -0
- data/ext/elf_utils/extconf.rb +3 -0
- data/lib/elf_utils/elf_file.rb +312 -0
- data/lib/elf_utils/section/base.rb +77 -0
- data/lib/elf_utils/section/debug_abbrev/abbreviation.rb +171 -0
- data/lib/elf_utils/section/debug_abbrev/abbreviation_table.rb +27 -0
- data/lib/elf_utils/section/debug_abbrev.rb +15 -0
- data/lib/elf_utils/section/debug_addr.rb +9 -0
- data/lib/elf_utils/section/debug_arange.rb +54 -0
- data/lib/elf_utils/section/debug_info/compilation_unit.rb +189 -0
- data/lib/elf_utils/section/debug_info/debug_str_offsets_ref.rb +15 -0
- data/lib/elf_utils/section/debug_info/debug_str_ref.rb +17 -0
- data/lib/elf_utils/section/debug_info/die/base.rb +130 -0
- data/lib/elf_utils/section/debug_info/die.rb +470 -0
- data/lib/elf_utils/section/debug_info/die_ref.rb +22 -0
- data/lib/elf_utils/section/debug_info/header.rb +26 -0
- data/lib/elf_utils/section/debug_info.rb +93 -0
- data/lib/elf_utils/section/debug_line/line_number_program/header.rb +48 -0
- data/lib/elf_utils/section/debug_line/line_number_program/state_machine.rb +206 -0
- data/lib/elf_utils/section/debug_line/line_number_program.rb +134 -0
- data/lib/elf_utils/section/debug_line.rb +35 -0
- data/lib/elf_utils/section/debug_ranges.rb +22 -0
- data/lib/elf_utils/section/debug_str_offsets.rb +16 -0
- data/lib/elf_utils/section/dynsym.rb +14 -0
- data/lib/elf_utils/section/strtab.rb +9 -0
- data/lib/elf_utils/section/symtab.rb +11 -0
- data/lib/elf_utils/section.rb +50 -0
- data/lib/elf_utils/segment/base.rb +72 -0
- data/lib/elf_utils/segment.rb +9 -0
- data/lib/elf_utils/string_pread.rb +18 -0
- data/lib/elf_utils/symbol.rb +144 -0
- data/lib/elf_utils/types/dwarf/expression.rb +34 -0
- data/lib/elf_utils/types/dwarf.rb +639 -0
- data/lib/elf_utils/types/dwarf32/v2.rb +44 -0
- data/lib/elf_utils/types/dwarf32/v3.rb +40 -0
- data/lib/elf_utils/types/dwarf32/v4.rb +41 -0
- data/lib/elf_utils/types/dwarf32/v5.rb +44 -0
- data/lib/elf_utils/types/dwarf32.rb +12 -0
- data/lib/elf_utils/types/dwarf64/v3.rb +42 -0
- data/lib/elf_utils/types/dwarf64/v4.rb +43 -0
- data/lib/elf_utils/types/dwarf64/v5.rb +46 -0
- data/lib/elf_utils/types/dwarf64.rb +8 -0
- data/lib/elf_utils/types/sleb128.rb +66 -0
- data/lib/elf_utils/types/uleb128.rb +56 -0
- data/lib/elf_utils/types/unit_length.rb +51 -0
- data/lib/elf_utils/types.rb +328 -0
- data/lib/elf_utils/version.rb +5 -0
- data/lib/elf_utils.rb +83 -0
- data/sig/elf_utils.rbs +4 -0
- metadata +120 -0
| @@ -0,0 +1,470 @@ | |
| 1 | 
            +
            require "forwardable"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ElfUtils
         | 
| 4 | 
            +
              class Section::DebugInfo::Die
         | 
| 5 | 
            +
                def initialize(cu:, offset:, tag:, children:, abbrev:, attrs: nil, buf: nil)
         | 
| 6 | 
            +
                  @cu = cu
         | 
| 7 | 
            +
                  @offset = offset
         | 
| 8 | 
            +
                  @tag = tag
         | 
| 9 | 
            +
                  @children = children ? [] : false
         | 
| 10 | 
            +
                  @abbrev = abbrev
         | 
| 11 | 
            +
                  @unpacked_attrs = attrs
         | 
| 12 | 
            +
                  @buf = buf
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
                attr_reader :tag, :offset, :abbrev, :cu
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def pretty_print(q)
         | 
| 17 | 
            +
                  q.group(4,
         | 
| 18 | 
            +
                    "<%p 0x%08x: DW_TAG_%s (%d children)" %
         | 
| 19 | 
            +
                      [self.class, @offset, @tag, @children ? @children.size : 0],
         | 
| 20 | 
            +
                    ">") do
         | 
| 21 | 
            +
                    q.text("\n" + " " * q.indent)
         | 
| 22 | 
            +
                    q.group_sub do
         | 
| 23 | 
            +
                      attrs = if @attrs
         | 
| 24 | 
            +
                        @attrs.map do |name, value|
         | 
| 25 | 
            +
                          ["DW_AT_%-15s  " % [name], value]
         | 
| 26 | 
            +
                        end
         | 
| 27 | 
            +
                      elsif @unpacked_attrs
         | 
| 28 | 
            +
                        @unpacked_attrs.map do |name, form, value|
         | 
| 29 | 
            +
                          ["DW_AT_%-15s  [DW_FORM_%s] " % [name, form], value]
         | 
| 30 | 
            +
                        end
         | 
| 31 | 
            +
                      end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                      if attrs
         | 
| 34 | 
            +
                        q.seplist(attrs) do |label, value|
         | 
| 35 | 
            +
                          q.group(4, label) do
         | 
| 36 | 
            +
                            case value
         | 
| 37 | 
            +
                            when self.class
         | 
| 38 | 
            +
                              q.text("<%p 0x%08x: DW_TAG_%s ...>" %
         | 
| 39 | 
            +
                                     [self.class, value.offset, value.tag])
         | 
| 40 | 
            +
                            else
         | 
| 41 | 
            +
                              q.pp(value)
         | 
| 42 | 
            +
                            end
         | 
| 43 | 
            +
                          end
         | 
| 44 | 
            +
                        end
         | 
| 45 | 
            +
                      else
         | 
| 46 | 
            +
                        q.group(1, "abbrev=") { q.pp(@abbrev) }
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
                alias_method :inspect, :pretty_inspect
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def children?
         | 
| 54 | 
            +
                  @children != false
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def children
         | 
| 58 | 
            +
                  @children || []
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                def has_attr?(attr)
         | 
| 62 | 
            +
                  @attrs ? @attrs.has_key?(attr) : @abbrev.attrs.has_key?(attr)
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                def add_child(child)
         | 
| 66 | 
            +
                  raise Error, "This DIE cannot have children" unless @children
         | 
| 67 | 
            +
                  @children << child
         | 
| 68 | 
            +
                  self
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def type?
         | 
| 72 | 
            +
                  @is_a_type ||= case @tag
         | 
| 73 | 
            +
                  when :base_type,
         | 
| 74 | 
            +
                        :typedef,
         | 
| 75 | 
            +
                        :volatile_type,
         | 
| 76 | 
            +
                        :const_type,
         | 
| 77 | 
            +
                        :pointer_type,
         | 
| 78 | 
            +
                        :array_type,
         | 
| 79 | 
            +
                        :union_type,
         | 
| 80 | 
            +
                        :enumeration_type,
         | 
| 81 | 
            +
                        :structure_type,
         | 
| 82 | 
            +
                        :subroutine_type
         | 
| 83 | 
            +
                    true
         | 
| 84 | 
            +
                  else
         | 
| 85 | 
            +
                    false
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                def [](name)
         | 
| 90 | 
            +
                  @attrs ||= parse_attrs
         | 
| 91 | 
            +
                  @attrs[name]
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                def []=(name, value)
         | 
| 95 | 
            +
                  @attrs ||= parse_attrs
         | 
| 96 | 
            +
                  @attrs[name] = value
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                def attrs
         | 
| 100 | 
            +
                  @attrs ||= parse_attrs
         | 
| 101 | 
            +
                  @attrs.keys
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                # get the unprocessed value for an attribute
         | 
| 105 | 
            +
                def raw_attr(key)
         | 
| 106 | 
            +
                  unpacked_attrs.each do |name, _, value|
         | 
| 107 | 
            +
                    return value if name == key
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
                  raise Error, "no attribute %p found in DIE: %p" % [key, self]
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                def addr_ranges
         | 
| 113 | 
            +
                  @attrs ||= parse_attrs
         | 
| 114 | 
            +
                  @addr_ranges ||=
         | 
| 115 | 
            +
                    if (low = @attrs[:low_pc]) && (high = @attrs[:high_pc])
         | 
| 116 | 
            +
                      [low..(high - 1)]
         | 
| 117 | 
            +
                    elsif @attrs[:ranges]
         | 
| 118 | 
            +
                      @cu.file.section(".debug_ranges")
         | 
| 119 | 
            +
                        .get(offset: @attrs[:ranges], base: @attrs[:low] || 0)
         | 
| 120 | 
            +
                    end
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                def has_addr?(addr)
         | 
| 124 | 
            +
                  case tag
         | 
| 125 | 
            +
                  when :subprogram
         | 
| 126 | 
            +
                    addr_ranges&.any? { |r| r.include?(addr) }
         | 
| 127 | 
            +
                  when :variable
         | 
| 128 | 
            +
                    type = self[:type].ctype
         | 
| 129 | 
            +
                    [location...location + type.size]
         | 
| 130 | 
            +
                  else
         | 
| 131 | 
            +
                    false
         | 
| 132 | 
            +
                  end
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                # return the number of elements in the subrange
         | 
| 136 | 
            +
                def subrange_len
         | 
| 137 | 
            +
                  @attrs ||= parse_attrs
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                  raise "not a subrange DIE: %p" % [self] unless @tag == :subrange_type
         | 
| 140 | 
            +
                  return @attrs[:count] if @attrs.has_key?(:count)
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                  # upper bound may be DW_TAG_variable, so special handling
         | 
| 143 | 
            +
                  if (upper_bound = @attrs[:upper_bound])
         | 
| 144 | 
            +
                    return @attrs[:upper_bound] + 1 if upper_bound.is_a?(Integer)
         | 
| 145 | 
            +
                  end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                  # XXX we'll need to do some work to support flexible array members, or
         | 
| 148 | 
            +
                  # arrays with an upper bound defined by DW_TAG_variable
         | 
| 149 | 
            +
                  0
         | 
| 150 | 
            +
                end
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                # return the value of DW_AT_location
         | 
| 153 | 
            +
                def location
         | 
| 154 | 
            +
                  value = self[:location]
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                  if value.is_a?(Types::Dwarf::Expression)
         | 
| 157 | 
            +
                    value = value.evaluate(addr_type: cu.addr_type, stack: [])[-1]
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
                  value
         | 
| 160 | 
            +
                end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                def type_name
         | 
| 163 | 
            +
                  return nil unless type?
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                  @type_name ||= begin
         | 
| 166 | 
            +
                    @attrs ||= parse_attrs
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                    case @tag
         | 
| 169 | 
            +
                    when :base_type, :typedef
         | 
| 170 | 
            +
                      @attrs[:name]
         | 
| 171 | 
            +
                    when :volatile_type
         | 
| 172 | 
            +
                      "volatile #{@attrs[:type]&.type_name || "void"}"
         | 
| 173 | 
            +
                    when :const_type
         | 
| 174 | 
            +
                      "const #{@attrs[:type]&.type_name || "void"}"
         | 
| 175 | 
            +
                    when :array_type
         | 
| 176 | 
            +
                      sizes = children
         | 
| 177 | 
            +
                        .inject("") do |buf, child|
         | 
| 178 | 
            +
                          buf << "[%d]" % child.subrange_len if child.tag == :subrange_type
         | 
| 179 | 
            +
                          buf
         | 
| 180 | 
            +
                        end
         | 
| 181 | 
            +
                      "#{@attrs[:type].type_name}#{sizes}"
         | 
| 182 | 
            +
                    when :pointer_type
         | 
| 183 | 
            +
                      inner = @attrs[:type]&.type_name || "void"
         | 
| 184 | 
            +
                      if inner.end_with?("*")
         | 
| 185 | 
            +
                        "#{inner}*"
         | 
| 186 | 
            +
                      else
         | 
| 187 | 
            +
                        "#{inner} *"
         | 
| 188 | 
            +
                      end
         | 
| 189 | 
            +
                    when :structure_type
         | 
| 190 | 
            +
                      name = @attrs[:name] || ("anon_%x" % @offset)
         | 
| 191 | 
            +
                      "struct #{name}"
         | 
| 192 | 
            +
                    when :union_type
         | 
| 193 | 
            +
                      name = @attrs[:name] || ("anon_%x" % @offset)
         | 
| 194 | 
            +
                      "union #{name}"
         | 
| 195 | 
            +
                    when :enumeration_type
         | 
| 196 | 
            +
                      name = @attrs[:name] || ("anon_%x" % @offset)
         | 
| 197 | 
            +
                      "enum #{name}"
         | 
| 198 | 
            +
                    when :subrange_type, :subroutine_type
         | 
| 199 | 
            +
                      return
         | 
| 200 | 
            +
                    else
         | 
| 201 | 
            +
                      raise Error, "unsupported DIE: %p" % [self]
         | 
| 202 | 
            +
                    end
         | 
| 203 | 
            +
                  end
         | 
| 204 | 
            +
                end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                def ctype
         | 
| 207 | 
            +
                  return unless type?
         | 
| 208 | 
            +
                  return @ctype if @ctype
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                  @attrs ||= parse_attrs
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                  @ctype = case tag
         | 
| 213 | 
            +
                  when :base_type
         | 
| 214 | 
            +
                    signed = !@attrs[:encoding].to_s.include?("unsigned")
         | 
| 215 | 
            +
                    int_type(size: @attrs[:byte_size], signed:)
         | 
| 216 | 
            +
                  when :typedef
         | 
| 217 | 
            +
                    @attrs[:type].ctype
         | 
| 218 | 
            +
                  when :const_type, :volatile_type
         | 
| 219 | 
            +
                    # const void has no equivalent ctype... maybe empty struct?
         | 
| 220 | 
            +
                    @attrs[:type]&.ctype
         | 
| 221 | 
            +
                  when :pointer_type, :subroutine_type
         | 
| 222 | 
            +
                    @cu.addr_type
         | 
| 223 | 
            +
                  when :array_type
         | 
| 224 | 
            +
                    sizes = children
         | 
| 225 | 
            +
                      .filter_map { |c| c.subrange_len if c.tag == :subrange_type }
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                    # if we have a signed char array, we'll use a string to hold it
         | 
| 228 | 
            +
                    inner_type = @attrs[:type].ctype
         | 
| 229 | 
            +
                    if inner_type.is_a?(CTypes::Int) &&
         | 
| 230 | 
            +
                        inner_type.size == 1 &&
         | 
| 231 | 
            +
                        inner_type.signed?
         | 
| 232 | 
            +
                      inner_type = CTypes::String.new(size: sizes.pop)
         | 
| 233 | 
            +
                    end
         | 
| 234 | 
            +
             | 
| 235 | 
            +
                    while (size = sizes.pop)
         | 
| 236 | 
            +
                      inner_type = CTypes::Array.new(type: inner_type, size:)
         | 
| 237 | 
            +
                    end
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                    inner_type
         | 
| 240 | 
            +
                  when :structure_type
         | 
| 241 | 
            +
                    offset = 0
         | 
| 242 | 
            +
                    struct = CTypes::Struct.builder
         | 
| 243 | 
            +
                      .name(self[:name])
         | 
| 244 | 
            +
                      .endian(@endian ||= @cu.file.endian)
         | 
| 245 | 
            +
                    bitfield = nil
         | 
| 246 | 
            +
             | 
| 247 | 
            +
                    (@children || []).each do |child|
         | 
| 248 | 
            +
                      next unless child.tag == :member
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                      name = child[:name]&.to_sym
         | 
| 251 | 
            +
                      type = child[:type].ctype
         | 
| 252 | 
            +
                      loc = child[:data_member_location] || 0
         | 
| 253 | 
            +
                      if loc.is_a?(Types::Dwarf::Expression)
         | 
| 254 | 
            +
                        child[:data_member_location] = loc =
         | 
| 255 | 
            +
                          loc.evaluate(addr_type: @cu.addr_type)[0]
         | 
| 256 | 
            +
                      end
         | 
| 257 | 
            +
                      delta = loc - offset
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                      # add any bitfield if we're moving past it
         | 
| 260 | 
            +
                      if bitfield && delta >= 0
         | 
| 261 | 
            +
                        struct.attribute bitfield.build
         | 
| 262 | 
            +
                        bitfield = nil
         | 
| 263 | 
            +
                      end
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                      # handle any padding we need to do
         | 
| 266 | 
            +
                      struct.pad delta if delta > 0
         | 
| 267 | 
            +
             | 
| 268 | 
            +
                      # check for v2, v3 bitfield
         | 
| 269 | 
            +
                      if (bits = child[:bit_size]) && (bit_offset = child[:bit_offset])
         | 
| 270 | 
            +
                        bitfield ||= CTypes::Bitfield.builder.endian(@endian)
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                        # convert the left-hand offset to a right-hand offset when we're
         | 
| 273 | 
            +
                        # on a little-endian system
         | 
| 274 | 
            +
                        if CTypes.host_endian == :little
         | 
| 275 | 
            +
                          bit_offset = child[:byte_size] * 8 - bit_offset - bits
         | 
| 276 | 
            +
                        end
         | 
| 277 | 
            +
             | 
| 278 | 
            +
                        bitfield.field(name,
         | 
| 279 | 
            +
                          offset: bit_offset,
         | 
| 280 | 
            +
                          bits: bits,
         | 
| 281 | 
            +
                          signed: type.signed?)
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                      # v4, v5 bitfield
         | 
| 284 | 
            +
                      elsif (bits = child[:bit_size]) &&
         | 
| 285 | 
            +
                          (bit_offset = child[:data_bit_offset])
         | 
| 286 | 
            +
             | 
| 287 | 
            +
                        bitfield ||= CTypes::Bitfield.builder.endian(@endian)
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                        # If the endian of the type doesn't match the host-endian, we
         | 
| 290 | 
            +
                        # need to flip the offset
         | 
| 291 | 
            +
                        if @endian != CTypes.host_endian
         | 
| 292 | 
            +
                          bit_offset = self[:byte_size] * 8 - bit_offset - bits
         | 
| 293 | 
            +
                        end
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                        bitfield.field(name,
         | 
| 296 | 
            +
                          offset: bit_offset,
         | 
| 297 | 
            +
                          bits: bits,
         | 
| 298 | 
            +
                          signed: type.signed?)
         | 
| 299 | 
            +
             | 
| 300 | 
            +
                      # not a bitfield
         | 
| 301 | 
            +
                      elsif name.nil?
         | 
| 302 | 
            +
                        # handle unnamed field
         | 
| 303 | 
            +
                        struct.attribute type
         | 
| 304 | 
            +
             | 
| 305 | 
            +
                        # must be a named field
         | 
| 306 | 
            +
                      else
         | 
| 307 | 
            +
                        struct.attribute name, type
         | 
| 308 | 
            +
                      end
         | 
| 309 | 
            +
                      offset = loc + type.size
         | 
| 310 | 
            +
                    end
         | 
| 311 | 
            +
             | 
| 312 | 
            +
                    # ensure we include the bitfield if it was the last element
         | 
| 313 | 
            +
                    struct.attribute(bitfield.build) if bitfield
         | 
| 314 | 
            +
             | 
| 315 | 
            +
                    struct.build
         | 
| 316 | 
            +
                  when :union_type
         | 
| 317 | 
            +
                    union = CTypes::Union.builder
         | 
| 318 | 
            +
                      .name(self[:name])
         | 
| 319 | 
            +
                      .endian(@endian ||= @cu.file.endian)
         | 
| 320 | 
            +
                    (@children || []).each do |child|
         | 
| 321 | 
            +
                      next unless child.tag == :member
         | 
| 322 | 
            +
             | 
| 323 | 
            +
                      name = child[:name]
         | 
| 324 | 
            +
                      type = child[:type].ctype
         | 
| 325 | 
            +
                      if name
         | 
| 326 | 
            +
                        union.member name.to_sym, type
         | 
| 327 | 
            +
                      else
         | 
| 328 | 
            +
                        union.member type
         | 
| 329 | 
            +
                      end
         | 
| 330 | 
            +
                    end
         | 
| 331 | 
            +
             | 
| 332 | 
            +
                    union.build
         | 
| 333 | 
            +
                  when :enumeration_type
         | 
| 334 | 
            +
                    type = int_type(size: @attrs[:byte_size], signed: false)
         | 
| 335 | 
            +
             | 
| 336 | 
            +
                    values = {}
         | 
| 337 | 
            +
                    @children.each do |child|
         | 
| 338 | 
            +
                      values[child[:name].downcase.to_sym] = child[:const_value]
         | 
| 339 | 
            +
                    end
         | 
| 340 | 
            +
             | 
| 341 | 
            +
                    CTypes::Enum.new(type.with_endian(@cu.file.endian), values)
         | 
| 342 | 
            +
                  when :subrange_type
         | 
| 343 | 
            +
                    return
         | 
| 344 | 
            +
                  else
         | 
| 345 | 
            +
                    raise Error, "unsupported DIE: %p" % [self]
         | 
| 346 | 
            +
                  end
         | 
| 347 | 
            +
                end
         | 
| 348 | 
            +
             | 
| 349 | 
            +
                private
         | 
| 350 | 
            +
             | 
| 351 | 
            +
                def unpacked_attrs
         | 
| 352 | 
            +
                  @unpacked_attrs ||= begin
         | 
| 353 | 
            +
                    o, _ = @abbrev.unpack_die(buf: @buf,
         | 
| 354 | 
            +
                      endian: @cu.file.endian,
         | 
| 355 | 
            +
                      offset_type: @cu.offset_type,
         | 
| 356 | 
            +
                      addr_type: @cu.addr_type)
         | 
| 357 | 
            +
                    o
         | 
| 358 | 
            +
                  end
         | 
| 359 | 
            +
                end
         | 
| 360 | 
            +
             | 
| 361 | 
            +
                def parse_attrs
         | 
| 362 | 
            +
                  attrs = {}
         | 
| 363 | 
            +
                  unpacked_attrs.each do |name, form, value|
         | 
| 364 | 
            +
                    case name
         | 
| 365 | 
            +
                    when :encoding
         | 
| 366 | 
            +
                      value = Types::Dwarf::Encoding[value]
         | 
| 367 | 
            +
                    when :data_member_location
         | 
| 368 | 
            +
                      value = Types::Dwarf::Expression.new(@cu, value) if
         | 
| 369 | 
            +
                        value.is_a?(String)
         | 
| 370 | 
            +
                    when :location
         | 
| 371 | 
            +
                      value = Types::Dwarf::Expression.new(@cu, value) if
         | 
| 372 | 
            +
                        value.is_a?(String)
         | 
| 373 | 
            +
                    when :high_pc
         | 
| 374 | 
            +
                      value += attrs[:low_pc] if form == :data4
         | 
| 375 | 
            +
                    end
         | 
| 376 | 
            +
             | 
| 377 | 
            +
                    case form
         | 
| 378 | 
            +
                    when :ref1, :ref2, :ref4, :ref8
         | 
| 379 | 
            +
                      value = @cu.die(value + @cu.offset)
         | 
| 380 | 
            +
                    when :strp
         | 
| 381 | 
            +
                      value = @cu.file.section(".debug_str")[value]
         | 
| 382 | 
            +
                    when :addrx
         | 
| 383 | 
            +
                      value = @cu.file.section(".debug_addr")
         | 
| 384 | 
            +
                        .get(base: @cu.addr_base, index: value)
         | 
| 385 | 
            +
                    when :strx1
         | 
| 386 | 
            +
                      base = @cu.str_offsets_base
         | 
| 387 | 
            +
                      offset = @cu.file.section(".debug_str_offsets")
         | 
| 388 | 
            +
                        .get(base:, offset: value, format: @cu.format)
         | 
| 389 | 
            +
                      value = @cu.file.section(".debug_str")[offset]
         | 
| 390 | 
            +
                    end
         | 
| 391 | 
            +
             | 
| 392 | 
            +
                    attrs[name] = value
         | 
| 393 | 
            +
                  end
         | 
| 394 | 
            +
                  attrs
         | 
| 395 | 
            +
                end
         | 
| 396 | 
            +
             | 
| 397 | 
            +
                def attr_value(name, form, value)
         | 
| 398 | 
            +
                  case name
         | 
| 399 | 
            +
                  when :encoding
         | 
| 400 | 
            +
                    return Types::Dwarf::Encoding[value]
         | 
| 401 | 
            +
                  when :data_member_location
         | 
| 402 | 
            +
                    if value.is_a?(String)
         | 
| 403 | 
            +
                      return Types::Dwarf::Expression.new(@cu, value)
         | 
| 404 | 
            +
                    else
         | 
| 405 | 
            +
                      return value
         | 
| 406 | 
            +
                    end
         | 
| 407 | 
            +
                  when :location
         | 
| 408 | 
            +
                    if value.is_a?(String)
         | 
| 409 | 
            +
                      return Types::Dwarf::Expression.new(@cu, value)
         | 
| 410 | 
            +
                    else
         | 
| 411 | 
            +
                      return value
         | 
| 412 | 
            +
                    end
         | 
| 413 | 
            +
                  end
         | 
| 414 | 
            +
             | 
| 415 | 
            +
                  case form
         | 
| 416 | 
            +
                  when :addrx
         | 
| 417 | 
            +
                    return @cu.file.section(".debug_addr")[@cu.addr_base + value]
         | 
| 418 | 
            +
                  when :ref1, :ref2, :ref4, :ref8
         | 
| 419 | 
            +
                    return @cu.die(value + @cu.offset)
         | 
| 420 | 
            +
                  when :strp
         | 
| 421 | 
            +
                    return @cu.file.section(".debug_str")[value]
         | 
| 422 | 
            +
                  when :strx1
         | 
| 423 | 
            +
                    # if we're working on the compile unit, we need the unparsed
         | 
| 424 | 
            +
                    # :str_offsets_base in this DIE to resolve str offset values.  For any
         | 
| 425 | 
            +
                    # other DIE, we can use the parsed compile unit to get the offset.
         | 
| 426 | 
            +
                    str_offsets, base = if @tag == :compile_unit
         | 
| 427 | 
            +
                      @str_offsets_base = @unpacked_attrs.find do |name, form, value|
         | 
| 428 | 
            +
                        next unless name == :str_offsets_base
         | 
| 429 | 
            +
                        break value
         | 
| 430 | 
            +
                      end or raise Error, ":str_offsets_base not found in %p" % [self]
         | 
| 431 | 
            +
                      [@cu.file.section(".debug_str_offsets"), @str_offsets_base]
         | 
| 432 | 
            +
                    else
         | 
| 433 | 
            +
                      @cu.debug_str_offsets
         | 
| 434 | 
            +
                    end
         | 
| 435 | 
            +
                    offset = str_offsets.get(base:, offset: value, format: @cu.format)
         | 
| 436 | 
            +
                    return @cu.file.section(".debug_str")[offset]
         | 
| 437 | 
            +
                  end
         | 
| 438 | 
            +
             | 
| 439 | 
            +
                  value
         | 
| 440 | 
            +
                end
         | 
| 441 | 
            +
             | 
| 442 | 
            +
                def int_type(size:, signed:)
         | 
| 443 | 
            +
                  @endian ||= @cu.endian
         | 
| 444 | 
            +
             | 
| 445 | 
            +
                  case size
         | 
| 446 | 
            +
                  when 0
         | 
| 447 | 
            +
                    CTypes::String.new(size: 0)
         | 
| 448 | 
            +
                  when 1
         | 
| 449 | 
            +
                    signed ? CTypes::Int8 : CTypes::UInt8
         | 
| 450 | 
            +
                  when 2
         | 
| 451 | 
            +
                    signed ? CTypes::Int16 : CTypes::UInt16
         | 
| 452 | 
            +
                  when 4
         | 
| 453 | 
            +
                    signed ? CTypes::Int32 : CTypes::UInt32
         | 
| 454 | 
            +
                  when 8
         | 
| 455 | 
            +
                    signed ? CTypes::Int64 : CTypes::UInt64
         | 
| 456 | 
            +
                  when 16
         | 
| 457 | 
            +
                    CTypes::Array.new(type: CTypes::UInt64, size: 2)
         | 
| 458 | 
            +
                  when 32
         | 
| 459 | 
            +
                    CTypes::Array.new(type: CTypes::UInt64, size: 4)
         | 
| 460 | 
            +
                  else
         | 
| 461 | 
            +
                    raise "unsupported base_type size %d in: %p" % [size, self]
         | 
| 462 | 
            +
                  end.with_endian(@endian)
         | 
| 463 | 
            +
                end
         | 
| 464 | 
            +
              end
         | 
| 465 | 
            +
            end
         | 
| 466 | 
            +
             | 
| 467 | 
            +
            require_relative "die/base"
         | 
| 468 | 
            +
            require_relative "die_ref"
         | 
| 469 | 
            +
            require_relative "debug_str_ref"
         | 
| 470 | 
            +
            require_relative "debug_str_offsets_ref"
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            module ElfUtils
         | 
| 2 | 
            +
              class Section::DebugInfo::DieRef
         | 
| 3 | 
            +
                def initialize(cu, offset)
         | 
| 4 | 
            +
                  @cu = cu
         | 
| 5 | 
            +
                  @offset = offset
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def deref
         | 
| 9 | 
            +
                  @die ||= @cu.die(@offset + @cu.offset)
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def inspect
         | 
| 13 | 
            +
                  if (die = deref)
         | 
| 14 | 
            +
                    "<%p cu=0x%x, offset=0x%08x, die=(0x%08x %s)>" %
         | 
| 15 | 
            +
                      [self.class, @cu.offset, @offset, die.offset, die.tag]
         | 
| 16 | 
            +
                  else
         | 
| 17 | 
            +
                    "<%p cu=0x%x, offset=0x%x, die=:invalid>" %
         | 
| 18 | 
            +
                      [self.class, @cu.offset, @offset]
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            module ElfUtils
         | 
| 2 | 
            +
              class Section::DebugInfo::Header < CTypes::Union
         | 
| 3 | 
            +
                extend Forwardable
         | 
| 4 | 
            +
                include CTypes::Helpers
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                layout do
         | 
| 7 | 
            +
                  member :unit, Types::Dwarf::UnitHeader
         | 
| 8 | 
            +
                  member :dwarf32_v2, Types::Dwarf32::V2::CU_Header
         | 
| 9 | 
            +
                  member :dwarf32_v3, Types::Dwarf32::V3::CU_Header
         | 
| 10 | 
            +
                  member :dwarf32_v4, Types::Dwarf32::V4::CU_Header
         | 
| 11 | 
            +
                  member :dwarf32_v5, Types::Dwarf32::V5::CU_Header
         | 
| 12 | 
            +
                  member :dwarf64_v3, Types::Dwarf64::V3::CU_Header
         | 
| 13 | 
            +
                  member :dwarf64_v4, Types::Dwarf64::V4::CU_Header
         | 
| 14 | 
            +
                  member :dwarf64_v5, Types::Dwarf64::V5::CU_Header
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  size { |header| header[:unit].unit_size }
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
                def_delegators :unit, :format, :version
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def inner
         | 
| 21 | 
            +
                  # PERF: we're caching this here because the Union type will do a bunch of
         | 
| 22 | 
            +
                  # extra work if we keep accessing the union via different members.
         | 
| 23 | 
            +
                  @inner ||= send(:"#{format}_v#{version}").freeze
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
| @@ -0,0 +1,93 @@ | |
| 1 | 
            +
            module ElfUtils
         | 
| 2 | 
            +
              class Section::DebugInfo < Section::Base
         | 
| 3 | 
            +
                def compilation_units
         | 
| 4 | 
            +
                  @cus ||= begin
         | 
| 5 | 
            +
                    offset = 0
         | 
| 6 | 
            +
                    Header.with_endian(@file.endian).unpack_all(bytes).map do |header|
         | 
| 7 | 
            +
                      # PERF: we freeze the header to prevent CTypes::Union from
         | 
| 8 | 
            +
                      # re-encoding the header when accessing the inner value.  This is
         | 
| 9 | 
            +
                      # the common performance work-around for union change tracking.
         | 
| 10 | 
            +
                      #
         | 
| 11 | 
            +
                      # The proper fix is probably to break up the convenience Header union
         | 
| 12 | 
            +
                      # and just parse the unit header directly, then parse the proper
         | 
| 13 | 
            +
                      # header version.
         | 
| 14 | 
            +
                      header.freeze
         | 
| 15 | 
            +
                      cu = CompilationUnit.new(@file, offset, header.inner)
         | 
| 16 | 
            +
                      offset += (cu.format == :dwarf32) ? 4 : 12
         | 
| 17 | 
            +
                      offset += cu.size
         | 
| 18 | 
            +
                      cu
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
                alias_method :cus, :compilation_units
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                # get a compilation unit
         | 
| 25 | 
            +
                def compilation_unit(arg)
         | 
| 26 | 
            +
                  # XXX do this in a way where we don't need to load all the CUs
         | 
| 27 | 
            +
                  case arg
         | 
| 28 | 
            +
                  when String
         | 
| 29 | 
            +
                    cus.find { |cu| cu.root_die[:name] == arg }
         | 
| 30 | 
            +
                  when Integer
         | 
| 31 | 
            +
                    cus.find { |cu| cu.offset == arg }
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
                alias_method :cu, :compilation_unit
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                # return the CompilationUnit that contains the provided offset
         | 
| 37 | 
            +
                def compilation_unit_for_offset(offset)
         | 
| 38 | 
            +
                  cus.find { |cu| cu.die_range.include?(offset) }
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
                alias_method :cu_for_offset, :compilation_unit_for_offset
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                # get a enumerator to iterate through the {Die}s present in all
         | 
| 43 | 
            +
                # {CompilationUnit}s
         | 
| 44 | 
            +
                #
         | 
| 45 | 
            +
                # By default, the Enumerator returned by this method will yield every
         | 
| 46 | 
            +
                # {Die} in every {CompilationUnit} in the order they were declared in.  If
         | 
| 47 | 
            +
                # `top` is set to true, the Enumerator will instead only yield those {Die}s
         | 
| 48 | 
            +
                # that are children of the root {Die} in each {CompilationUnit}.
         | 
| 49 | 
            +
                #
         | 
| 50 | 
            +
                # @param top [Boolean] set to true to only return the children of the root
         | 
| 51 | 
            +
                #   die
         | 
| 52 | 
            +
                # @param root [Boolean] set to true to yield the root DIE of each
         | 
| 53 | 
            +
                #   CompilationUnit prior to its children
         | 
| 54 | 
            +
                def dies(top: false, root: false)
         | 
| 55 | 
            +
                  Enumerator.new do |yielder|
         | 
| 56 | 
            +
                    cus.each do |cu|
         | 
| 57 | 
            +
                      if top
         | 
| 58 | 
            +
                        yielder << cu.root_die if root
         | 
| 59 | 
            +
                        cu.root_die.children.each { |die| yielder << die }
         | 
| 60 | 
            +
                      else
         | 
| 61 | 
            +
                        cu.dies.each { |die| yielder << die }
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                def die(offset)
         | 
| 68 | 
            +
                  cu = compilation_unit_for_offset(offset) or
         | 
| 69 | 
            +
                    raise Error, "No CU found containing offset %x" % offset
         | 
| 70 | 
            +
                  cu.die(offset)
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                def type(name)
         | 
| 74 | 
            +
                  compilation_units.each do |cu|
         | 
| 75 | 
            +
                    type = cu.type(name)
         | 
| 76 | 
            +
                    return type if type
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                  nil
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def type_die(name)
         | 
| 82 | 
            +
                  compilation_units.each do |cu|
         | 
| 83 | 
            +
                    cu.dies.each do |die|
         | 
| 84 | 
            +
                      return die if die.type_name == name
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                  nil
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
              end
         | 
| 90 | 
            +
            end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            require_relative "debug_info/header"
         | 
| 93 | 
            +
            require_relative "debug_info/compilation_unit"
         | 
| @@ -0,0 +1,48 @@ | |
| 1 | 
            +
            module ElfUtils
         | 
| 2 | 
            +
              class Section::DebugLine::LineNumberProgram::Header < CTypes::Union
         | 
| 3 | 
            +
                extend Forwardable
         | 
| 4 | 
            +
                include CTypes::Helpers
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                # struct used for `directory_entry_format` and `file_name_entry_format` in
         | 
| 7 | 
            +
                # DWARF v5.
         | 
| 8 | 
            +
                EntryFormat = CTypes::Helpers.struct(
         | 
| 9 | 
            +
                  type: Types::Dwarf::EntryFormatType,
         | 
| 10 | 
            +
                  form: Types::Dwarf::Form
         | 
| 11 | 
            +
                )
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                # entry format for `include_directories` prior to DWARF v5
         | 
| 14 | 
            +
                INCLUDE_DIRECTORIES_FORMAT = [EntryFormat.new(type: :path, form: :string)]
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                # entry format for `file_names` prior for DWARF v5
         | 
| 17 | 
            +
                FILE_NAMES_FORMAT = [
         | 
| 18 | 
            +
                  EntryFormat.new(type: :path, form: :string),
         | 
| 19 | 
            +
                  EntryFormat.new(type: :directory_index, form: :udata),
         | 
| 20 | 
            +
                  EntryFormat.new(type: :timestamp, form: :udata),
         | 
| 21 | 
            +
                  EntryFormat.new(type: :size, form: :udata)
         | 
| 22 | 
            +
                ]
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                layout do
         | 
| 25 | 
            +
                  member :unit, Types::Dwarf::UnitHeader
         | 
| 26 | 
            +
                  member :dwarf32_v2, Types::Dwarf32::V2::LineNumberProgramHeader
         | 
| 27 | 
            +
                  member :dwarf32_v3, Types::Dwarf32::V3::LineNumberProgramHeader
         | 
| 28 | 
            +
                  member :dwarf32_v4, Types::Dwarf32::V4::LineNumberProgramHeader
         | 
| 29 | 
            +
                  member :dwarf32_v5, Types::Dwarf32::V5::LineNumberProgramHeader
         | 
| 30 | 
            +
                  member :dwarf64_v3, Types::Dwarf64::V3::LineNumberProgramHeader
         | 
| 31 | 
            +
                  member :dwarf64_v4, Types::Dwarf64::V4::LineNumberProgramHeader
         | 
| 32 | 
            +
                  member :dwarf64_v5, Types::Dwarf64::V5::LineNumberProgramHeader
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  size do |union|
         | 
| 35 | 
            +
                    type = union[:unit].type
         | 
| 36 | 
            +
                    header = union[type]
         | 
| 37 | 
            +
                    header.class.offsetof(:minimum_instruction_length) +
         | 
| 38 | 
            +
                      header[:header_length]
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
                def_delegators :inner, :version, :type
         | 
| 42 | 
            +
                def_delegators :unit, :format, :addr_type
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def inner
         | 
| 45 | 
            +
                  @inner = send(unit.type)
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
            end
         |