2 # Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 # Tool for reading archive (.a) files
7 # For information about the archive file format, see:
8 # http://en.wikipedia.org/wiki/Ar_(Unix)
14 # See above link to wiki entry on archive format.
15 AR_MAGIC = '!<arch>\n'
16 # Thin archives are like normal archives except that there are only
17 # indirect references to each member (the data is not embedded).
18 # See manpage for a description of this.
19 THIN_MAGIC = '!<thin>\n'
21 # filetype.IsArchive calls this IsArchive. Top-level tools should prefer
22 # filetype.IsArchive, both for consistency (i.e., all checks for file type come
23 # from that library), and because its results are cached.
24 def IsArchive(filename):
25 fp = driver_log.DriverOpen(filename, "rb")
26 magic = fp.read(len(AR_MAGIC))
28 return magic in [AR_MAGIC, THIN_MAGIC]
31 def GetMemberFilename(member, strtab_data):
32 """ Get the real filename of the archive member. """
33 if not member.is_long_name:
34 return member.name.strip()
36 # GNU style long filenames are /[index]
37 # where index is a position within the strtab_data.
38 name_index = int(member.name[1:].strip())
39 name_data = strtab_data[name_index:]
40 name_data = name_data.split('\n', 2)[0]
41 assert (name_data.endswith('/'))
45 def GetThinArchiveData(archive_filename, member, strtab_data):
46 # Get member's filename (relative to the archive) and open the member
47 # ourselves to check the data.
48 member_filename = GetMemberFilename(member, strtab_data)
49 member_filename = pathtools.join(
50 pathtools.dirname(pathtools.abspath(archive_filename)),
52 member_fp = driver_log.DriverOpen(member_filename, 'rb')
53 data = member_fp.read(member.size)
58 def GetArchiveType(filename):
59 fp = driver_log.DriverOpen(filename, "rb")
61 # Read the archive magic header
62 magic = fp.read(len(AR_MAGIC))
63 assert(magic in [AR_MAGIC, THIN_MAGIC])
65 # Find a regular file or symbol table
70 member = MemberHeader(fp)
71 if member.error == 'EOF':
74 driver_log.Log.Fatal("%s: %s", filename, member.error)
78 if member.is_regular_file:
79 if not magic == THIN_MAGIC:
80 data = fp.read(member.size)
82 # For thin archives, do not read the data section.
83 # We instead must get at the member indirectly.
84 data = GetThinArchiveData(filename, member, strtab_data)
86 if data.startswith('BC'):
87 found_type = 'archive-bc'
89 elf_header = elftools.DecodeELFHeader(data, filename)
91 found_type = 'archive-%s' % elf_header.arch
92 elif member.is_strtab:
93 # We need the strtab data to get long filenames.
94 data = fp.read(member.size)
98 # Other symbol tables we can just skip ahead.
99 data = fp.read(member.size)
103 # Empty archives are treated as bitcode ones.
104 found_type = 'archive-bc'
106 driver_log.Log.Fatal("%s: Unable to determine archive type", filename)
112 class MemberHeader(object):
113 def __init__(self, fp):
120 if len(header) != 60:
121 self.error = 'Short count reading archive member header'
124 self.name = header[0:16]
125 self.size = header[48:48 + 10]
126 self.fmag = header[58:60]
128 if self.fmag != '`\n':
129 self.error = 'Invalid archive member header magic string %s' % header
132 self.size = int(self.size)
134 self.is_svr4_symtab = (self.name == '/ ')
135 self.is_llvm_symtab = (self.name == '#_LLVM_SYM_TAB_#')
136 self.is_bsd4_symtab = (self.name == '__.SYMDEF SORTED')
137 self.is_strtab = (self.name == '// ')
138 self.is_regular_file = not (self.is_svr4_symtab or
139 self.is_llvm_symtab or
140 self.is_bsd4_symtab or
143 # BSD style long names (not supported)
144 if self.name.startswith('#1/'):
145 self.error = "BSD-style long file names not supported"
148 # If it's a GNU long filename, note this. We use this for thin archives.
149 self.is_long_name = (self.is_regular_file and self.name.startswith('/'))
151 if self.is_regular_file and not self.is_long_name:
152 # Filenames end with '/' and are padded with spaces up to 16 bytes
153 self.name = self.name.strip()[:-1]