Class: Rubycc::ObjFile::ArReader

Inherits:
Object
  • Object
show all
Defined in:
lib/rubycc/objfile/ar_archive.rb

Overview

Reads a GNU-format ar archive into its members and (when present) its symbol index. BSD archives are out of scope; only !<arch>\n is accepted.

The model mirrors ELFReader's load-then-query taste: read/read_file walk the whole image once into #members (in file order) and, if the archive carries one, a symbol name -> member map. That map is exactly what the linker's lazy-extraction driver queries ("which member defines this still-undefined symbol?"), so it is exposed both as the raw ordered #symbols list and the first-wins #symbol_index hash.

Three reserved member names are understood: / is the classic ranlib symbol table (big-endian uint32 count, that many big-endian uint32 member-header offsets, then NUL-terminated symbol names); // is the extended-name table holding long member names each terminated by /\n; and /N in a member's name field is a decimal byte offset into that // table. A short name is stored as name/, the trailing slash marking its end so a name may keep trailing spaces. Archives written without a symbol index (plain ar qc) are tolerated: #symbols is then empty.

Constant Summary collapse

HEADER_SIZE =
60
NAME_OFF =

Field offsets and widths within a 60-byte member header. The five ASCII fields are space-padded; only size (decimal) is needed to walk the archive, the rest are decoded for the member's metadata.

0
NAME_LEN =
16
MTIME_OFF =
16
MTIME_LEN =
12
UID_OFF =
28
UID_LEN =
6
GID_OFF =
34
GID_LEN =
6
MODE_OFF =
40
MODE_LEN =
8
SIZE_OFF =
48
SIZE_LEN =
10
MAGIC_OFF =
58
HEADER_MAGIC =
"\x60\x0a".b

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bytes) ⇒ ArReader

Returns a new instance of ArReader.



84
85
86
87
88
89
90
# File 'lib/rubycc/objfile/ar_archive.rb', line 84

def initialize(bytes)
  @data = bytes.b
  @members = []
  @symbols = []
  @name_table = nil
  @symtab_raw = nil
end

Instance Attribute Details

#membersObject (readonly)

Returns the value of attribute members.



82
83
84
# File 'lib/rubycc/objfile/ar_archive.rb', line 82

def members
  @members
end

#name_tableObject (readonly)

Returns the value of attribute name_table.



82
83
84
# File 'lib/rubycc/objfile/ar_archive.rb', line 82

def name_table
  @name_table
end

#symbolsObject (readonly)

Returns the value of attribute symbols.



82
83
84
# File 'lib/rubycc/objfile/ar_archive.rb', line 82

def symbols
  @symbols
end

Class Method Details

.read(bytes) ⇒ Object

Parses an in-memory archive (an ASCII-8BIT String) into a ready-to-query reader.



72
73
74
# File 'lib/rubycc/objfile/ar_archive.rb', line 72

def read(bytes)
  new(bytes).tap(&:parse!)
end

.read_file(path) ⇒ Object

Reads an archive file from disk and parses it.



77
78
79
# File 'lib/rubycc/objfile/ar_archive.rb', line 77

def read_file(path)
  read(File.binread(path))
end

Instance Method Details

#member(name) ⇒ Object

The member with the given name, or nil. Regular members only — the / and // metadata members never carry a user-facing name.



110
111
112
# File 'lib/rubycc/objfile/ar_archive.rb', line 110

def member(name)
  @members.find { |m| !m.special? && m.name == name }
end

#member_defining(symbol_name) ⇒ Object

The member that defines symbol_name, or nil — the query the lazy linker asks for each still-undefined reference.



124
125
126
# File 'lib/rubycc/objfile/ar_archive.rb', line 124

def member_defining(symbol_name)
  symbol_index[symbol_name]
end

#parse!Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/rubycc/objfile/ar_archive.rb', line 92

def parse!
  unless @data.byteslice(0, AR_MAGIC.bytesize) == AR_MAGIC
    raise ArFormatError, "not an ar archive (bad global magic)"
  end

  pos = AR_MAGIC.bytesize
  # This loop always terminates: parse_member advances pos by at least the
  # 60-byte header plus a non-negative member size (a size field that is not
  # a non-negative decimal is rejected outright), so pos strictly increases
  # by >= 60 each turn and reaches @data.bytesize in a bounded number of
  # steps — a malformed archive cannot make it stall or loop.
  pos = parse_member(pos) while pos < @data.bytesize
  resolve_symbol_index
  self
end

#symbol_indexObject

symbol name -> defining member, first definition winning when a symbol is exported by more than one member (the order the linker resolves in).



116
117
118
119
120
# File 'lib/rubycc/objfile/ar_archive.rb', line 116

def symbol_index
  @symbol_index ||= @symbols.each_with_object({}) do |entry, map|
    map[entry[:name]] ||= entry[:member]
  end
end