Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / native_client / pnacl / driver / artools.py
1 #!/usr/bin/python
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.
5
6 # Tool for reading archive (.a) files
7 # For information about the archive file format, see:
8 #   http://en.wikipedia.org/wiki/Ar_(Unix)
9
10 import driver_log
11 import elftools
12 import pathtools
13
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'
20
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))
27   fp.close()
28   return magic in [AR_MAGIC, THIN_MAGIC]
29
30
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()
35   else:
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('/'))
42     return name_data[:-1]
43
44
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)),
51       member_filename)
52   member_fp = driver_log.DriverOpen(member_filename, 'rb')
53   data = member_fp.read(member.size)
54   member_fp.close()
55   return data
56
57
58 def GetArchiveType(filename):
59   fp = driver_log.DriverOpen(filename, "rb")
60
61   # Read the archive magic header
62   magic = fp.read(len(AR_MAGIC))
63   assert(magic in [AR_MAGIC, THIN_MAGIC])
64
65   # Find a regular file or symbol table
66   empty_file = True
67   found_type = ''
68   strtab_data = ''
69   while not found_type:
70     member = MemberHeader(fp)
71     if member.error == 'EOF':
72       break
73     elif member.error:
74       driver_log.Log.Fatal("%s: %s", filename, member.error)
75
76     empty_file = False
77
78     if member.is_regular_file:
79       if not magic == THIN_MAGIC:
80         data = fp.read(member.size)
81       else:
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)
85
86       if data.startswith('BC'):
87         found_type = 'archive-bc'
88       else:
89         elf_header = elftools.DecodeELFHeader(data, filename)
90         if elf_header:
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)
95       strtab_data = data
96       continue
97     else:
98       # Other symbol tables we can just skip ahead.
99       data = fp.read(member.size)
100       continue
101
102   if empty_file:
103     # Empty archives are treated as bitcode ones.
104     found_type = 'archive-bc'
105   elif not found_type:
106     driver_log.Log.Fatal("%s: Unable to determine archive type", filename)
107
108   fp.close()
109   return found_type
110
111
112 class MemberHeader(object):
113   def __init__(self, fp):
114     self.error = ''
115     header = fp.read(60)
116     if len(header) == 0:
117       self.error = "EOF"
118       return
119
120     if len(header) != 60:
121       self.error = 'Short count reading archive member header'
122       return
123
124     self.name = header[0:16]
125     self.size = header[48:48 + 10]
126     self.fmag = header[58:60]
127
128     if self.fmag != '`\n':
129       self.error = 'Invalid archive member header magic string %s' % header
130       return
131
132     self.size = int(self.size)
133
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
141                                 self.is_strtab)
142
143     # BSD style long names (not supported)
144     if self.name.startswith('#1/'):
145       self.error = "BSD-style long file names not supported"
146       return
147
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('/'))
150
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]