Class: ElfUtils::Section::DebugInfo::Die

Inherits:
Object
  • Object
show all
Defined in:
lib/elf_utils/section/debug_info/die.rb

Defined Under Namespace

Classes: Base

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cu:, offset:, tag:, children:, abbrev:, attrs: nil, buf: nil) ⇒ Die

Returns a new instance of Die.



5
6
7
8
9
10
11
12
13
# File 'lib/elf_utils/section/debug_info/die.rb', line 5

def initialize(cu:, offset:, tag:, children:, abbrev:, attrs: nil, buf: nil)
  @cu = cu
  @offset = offset
  @tag = tag
  @children = children ? [] : false
  @abbrev = abbrev
  @unpacked_attrs = attrs
  @buf = buf
end

Instance Attribute Details

#abbrevObject (readonly)

Returns the value of attribute abbrev.



14
15
16
# File 'lib/elf_utils/section/debug_info/die.rb', line 14

def abbrev
  @abbrev
end

#cuObject (readonly)

Returns the value of attribute cu.



14
15
16
# File 'lib/elf_utils/section/debug_info/die.rb', line 14

def cu
  @cu
end

#offsetObject (readonly)

Returns the value of attribute offset.



14
15
16
# File 'lib/elf_utils/section/debug_info/die.rb', line 14

def offset
  @offset
end

#tagObject (readonly)

Returns the value of attribute tag.



14
15
16
# File 'lib/elf_utils/section/debug_info/die.rb', line 14

def tag
  @tag
end

Instance Method Details

#[](name) ⇒ Object



89
90
91
92
# File 'lib/elf_utils/section/debug_info/die.rb', line 89

def [](name)
  @attrs ||= parse_attrs
  @attrs[name]
end

#[]=(name, value) ⇒ Object



94
95
96
97
# File 'lib/elf_utils/section/debug_info/die.rb', line 94

def []=(name, value)
  @attrs ||= parse_attrs
  @attrs[name] = value
end

#add_child(child) ⇒ Object

Raises:



65
66
67
68
69
# File 'lib/elf_utils/section/debug_info/die.rb', line 65

def add_child(child)
  raise Error, "This DIE cannot have children" unless @children
  @children << child
  self
end

#addr_rangesObject



112
113
114
115
116
117
118
119
120
121
# File 'lib/elf_utils/section/debug_info/die.rb', line 112

def addr_ranges
  @attrs ||= parse_attrs
  @addr_ranges ||=
    if (low = @attrs[:low_pc]) && (high = @attrs[:high_pc])
      [low..(high - 1)]
    elsif @attrs[:ranges]
      @cu.file.section(".debug_ranges")
        .get(offset: @attrs[:ranges], base: @attrs[:low] || 0)
    end
end

#attrsObject



99
100
101
102
# File 'lib/elf_utils/section/debug_info/die.rb', line 99

def attrs
  @attrs ||= parse_attrs
  @attrs.keys
end

#childrenObject



57
58
59
# File 'lib/elf_utils/section/debug_info/die.rb', line 57

def children
  @children || []
end

#children?Boolean

Returns:

  • (Boolean)


53
54
55
# File 'lib/elf_utils/section/debug_info/die.rb', line 53

def children?
  @children != false
end

#ctypeObject



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/elf_utils/section/debug_info/die.rb', line 226

def ctype
  return unless type?
  return @ctype if @ctype

  @attrs ||= parse_attrs

  @ctype = case tag
  when :base_type
    signed = !@attrs[:encoding].to_s.include?("unsigned")
    int_type(size: @attrs[:byte_size], signed:)
  when :typedef
    @attrs[:type].ctype
  when :const_type, :volatile_type
    # const void has no equivalent ctype... maybe empty struct?
    @attrs[:type]&.ctype
  when :pointer_type, :subroutine_type
    @cu.addr_type
  when :array_type
    # subrange_len may return nil for variable-length arrays
    sizes = children
      .select { |c| c.tag == :subrange_type }
      .map { |c| c.subrange_len }

    # if we have a signed char array, we'll use a string to hold it
    inner_type = @attrs[:type].ctype
    if inner_type.is_a?(CTypes::Int) &&
        inner_type.size == 1 &&
        inner_type.signed?
      inner_type = CTypes::String.new(size: sizes.pop)
    end

    sizes.reverse_each do |size|
      inner_type = CTypes::Array.new(type: inner_type, size:)
    end

    inner_type
  when :structure_type
    offset = 0
    struct = CTypes::Struct.builder
      .name(self[:name])
      .endian(@endian ||= @cu.file.endian)
    bitfield = nil

    (@children || []).each do |child|
      next unless child.tag == :member

      name = child[:name]&.to_sym
      type = child[:type].ctype
      loc = child[:data_member_location] || 0
      if loc.is_a?(Types::Dwarf::Expression)
        child[:data_member_location] = loc =
          loc.evaluate(addr_type: @cu.addr_type)[0]
      end
      delta = loc - offset

      # add any bitfield if we're moving past it
      if bitfield && delta >= 0
        struct.attribute bitfield.build
        bitfield = nil
      end

      # handle any padding we need to do
      struct.pad delta if delta > 0

      # check for v2, v3 bitfield
      if (bits = child[:bit_size]) && (bit_offset = child[:bit_offset])
        bitfield ||= CTypes::Bitfield.builder.endian(@endian)

        # convert the left-hand offset to a right-hand offset when we're
        # on a little-endian system
        if CTypes.host_endian == :little
          bit_offset = child[:byte_size] * 8 - bit_offset - bits
        end

        bitfield.field(name,
          offset: bit_offset,
          bits: bits,
          signed: type.signed?)

      # v4, v5 bitfield
      elsif (bits = child[:bit_size]) &&
          (bit_offset = child[:data_bit_offset])

        bitfield ||= CTypes::Bitfield.builder.endian(@endian)

        # If the endian of the type doesn't match the host-endian, we
        # need to flip the offset
        if @endian != CTypes.host_endian
          bit_offset = self[:byte_size] * 8 - bit_offset - bits
        end

        bitfield.field(name,
          offset: bit_offset,
          bits: bits,
          signed: type.signed?)

      # not a bitfield
      elsif name.nil?
        # handle unnamed field
        struct.attribute type

        # must be a named field
      else
        struct.attribute name, type
      end
      offset = loc + type.size
    end

    # if there's tail padding to fit a specific alignment, add it here
    tail = self[:byte_size] - offset
    struct.pad(tail) if tail > 0

    # ensure we include the bitfield if it was the last element
    struct.attribute(bitfield.build) if bitfield

    struct.build
  when :union_type
    union = CTypes::Union.builder
      .name(self[:name])
      .endian(@endian ||= @cu.file.endian)
    (@children || []).each do |child|
      next unless child.tag == :member

      name = child[:name]
      type = child[:type].ctype
      if name
        union.member name.to_sym, type
      else
        union.member type
      end
    end

    union.build
  when :enumeration_type
    type = int_type(size: @attrs[:byte_size], signed: false)

    values = {}
    @children.each do |child|
      values[child[:name].downcase.to_sym] = child[:const_value]
    end

    CTypes::Enum.new(type.with_endian(@cu.file.endian), values)
  when :subrange_type
    return
  else
    raise Error, "unsupported DIE: %p" % [self]
  end
end

#has_addr?(addr) ⇒ Boolean

Returns:

  • (Boolean)


123
124
125
126
127
128
129
130
131
132
133
# File 'lib/elf_utils/section/debug_info/die.rb', line 123

def has_addr?(addr)
  case tag
  when :subprogram
    addr_ranges&.any? { |r| r.include?(addr) }
  when :variable
    type = self[:type].ctype
    [location...location + type.size]
  else
    false
  end
end

#has_attr?(attr) ⇒ Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/elf_utils/section/debug_info/die.rb', line 61

def has_attr?(attr)
  @attrs ? @attrs.has_key?(attr) : @abbrev.attrs.has_key?(attr)
end

#locationObject

return the value of DW_AT_location



166
167
168
169
170
171
172
173
# File 'lib/elf_utils/section/debug_info/die.rb', line 166

def location
  value = self[:location]

  if value.is_a?(Types::Dwarf::Expression)
    value = value.evaluate(addr_type: cu.addr_type, stack: [])[-1]
  end
  value
end

#pretty_print(q) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/elf_utils/section/debug_info/die.rb', line 16

def pretty_print(q)
  q.group(4,
    "<%p 0x%08x: DW_TAG_%s (%d children)" %
      [self.class, @offset, @tag, @children ? @children.size : 0],
    ">") do
    q.text("\n" + " " * q.indent)
    q.group_sub do
      attrs = if @attrs
        @attrs.map do |name, value|
          ["DW_AT_%-15s  " % [name], value]
        end
      elsif @unpacked_attrs
        @unpacked_attrs.map do |name, form, value|
          ["DW_AT_%-15s  [DW_FORM_%s] " % [name, form], value]
        end
      end

      if attrs
        q.seplist(attrs) do |label, value|
          q.group(4, label) do
            case value
            when self.class
              q.text("<%p 0x%08x: DW_TAG_%s ...>" %
                     [self.class, value.offset, value.tag])
            else
              q.pp(value)
            end
          end
        end
      else
        q.group(1, "abbrev=") { q.pp(@abbrev) }
      end
    end
  end
end

#raw_attr(key) ⇒ Object

get the unprocessed value for an attribute

Raises:



105
106
107
108
109
110
# File 'lib/elf_utils/section/debug_info/die.rb', line 105

def raw_attr(key)
  unpacked_attrs.each do |name, _, value|
    return value if name == key
  end
  raise Error, "no attribute %p found in DIE: %p" % [key, self]
end

#subrange_lenObject

return the number of elements in the subrange



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/elf_utils/section/debug_info/die.rb', line 136

def subrange_len
  @attrs ||= parse_attrs

  raise "not a subrange DIE: %p" % [self] unless @tag == :subrange_type
  count = @attrs[:count]
  case count
  when Integer
    return count
  when Section::DebugInfo::Die
    # return a size of nil for variable-length array
    return nil if count.tag == :variable
    raise ElfUtils::Error, "unexpected DIE type in subrange[:count]: %p" %
      [count] 
  when nil
    # fall through when there is no count attribute
  else
    raise ElfUtils::Error, "unexpected value in subrange[:count]: %p" %
      [count]
  end

  # upper bound may be DW_TAG_variable, so special handling
  if (upper_bound = @attrs[:upper_bound])
    return @attrs[:upper_bound] + 1 if upper_bound.is_a?(Integer)
  end

  # XXX we'll need to do some work to support flexible array members
  0
end

#type?Boolean

Returns:

  • (Boolean)


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/elf_utils/section/debug_info/die.rb', line 71

def type?
  @is_a_type ||= case @tag
  when :base_type,
        :typedef,
        :volatile_type,
        :const_type,
        :pointer_type,
        :array_type,
        :union_type,
        :enumeration_type,
        :structure_type,
        :subroutine_type
    true
  else
    false
  end
end

#type_nameObject



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/elf_utils/section/debug_info/die.rb', line 175

def type_name
  return nil unless type?

  @type_name ||= begin
    @attrs ||= parse_attrs

    case @tag
    when :base_type, :typedef
      @attrs[:name]
    when :volatile_type
      "volatile #{@attrs[:type]&.type_name || "void"}"
    when :const_type
      "const #{@attrs[:type]&.type_name || "void"}"
    when :array_type
      buf = "#{@attrs[:type]&.type_name}"
      sizes = children.each do |child|
          next unless child.tag == :subrange_type

          # subrange_len may return nil for variable-length arrays
          len = child.subrange_len
          if len
            buf << "[%d]" % len
          else
            buf << "[]"
          end
        end
      buf
    when :pointer_type
      inner = @attrs[:type]&.type_name || "void"
      if inner.end_with?("*")
        "#{inner}*"
      else
        "#{inner} *"
      end
    when :structure_type
      name = @attrs[:name] || ("anon_%x" % @offset)
      "struct #{name}"
    when :union_type
      name = @attrs[:name] || ("anon_%x" % @offset)
      "union #{name}"
    when :enumeration_type
      name = @attrs[:name] || ("anon_%x" % @offset)
      "enum #{name}"
    when :subrange_type, :subroutine_type
      return
    else
      raise Error, "unsupported DIE: %p" % [self]
    end
  end
end