632
|
1 |
#
|
|
2 |
# Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
|
3 |
# All rights reserved.
|
|
4 |
# This component and the accompanying materials are made available
|
|
5 |
# under the terms of "Eclipse Public License v1.0"
|
|
6 |
# which accompanies this distribution, and is available
|
|
7 |
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
|
8 |
#
|
|
9 |
# Initial Contributors:
|
|
10 |
# Nokia Corporation - initial contribution.
|
|
11 |
#
|
|
12 |
# Contributors:
|
|
13 |
#
|
|
14 |
# Description:
|
|
15 |
# Generate checksums for different types of files
|
|
16 |
#
|
|
17 |
|
|
18 |
import sys
|
|
19 |
import os
|
|
20 |
import re
|
|
21 |
import struct
|
|
22 |
import subprocess
|
|
23 |
import platform
|
|
24 |
try:
|
|
25 |
from hashlib import md5
|
|
26 |
except ImportError:
|
|
27 |
import md5
|
|
28 |
|
|
29 |
import SymbianUtils
|
|
30 |
|
|
31 |
class Evalid(object):
|
|
32 |
'''
|
|
33 |
Provide some of the functionality found in epoc32/tools/EvalidCompare.pm
|
|
34 |
|
|
35 |
generateSignature() - use appropriate method to calculate checksum for a file
|
|
36 |
getUid() - extract Uid from file
|
|
37 |
'''
|
|
38 |
|
|
39 |
class ExternalError(SymbianUtils.SymbianUtilsError):
|
|
40 |
""" An external utility exited with an error """
|
|
41 |
|
|
42 |
ext = ".exe" if platform.system() == "Windows" else ""
|
|
43 |
binPath = os.path.normpath(os.path.join(os.path.dirname(__file__), "bin"))
|
|
44 |
NM = os.path.join(binPath, "nm" + ext)
|
|
45 |
ELFDUMP = os.path.join(binPath, "elfdump" + ext)
|
|
46 |
ELF2E32 = os.path.join(binPath, "elf2e32" + ext)
|
|
47 |
PE_DUMP = os.path.join(binPath, "pe_dump" + ext)
|
|
48 |
|
|
49 |
ELF_DLL_NAME = re.compile(r"#<DLL>(\S+\.\S+)#<\\DLL>")
|
|
50 |
ELF_DEBUG = re.compile(r"^\.(rel\.)?debug_")
|
|
51 |
ELF_P_HEAD = re.compile(r"^\tProgram header offset.*$")
|
|
52 |
ELF_S_HEAD = re.compile(r"^\tSection header offset.*$")
|
|
53 |
PRECOMP_IGNORE = re.compile(r'^# \d+ ".*"( \d)?$')
|
|
54 |
E32_EMPTY = re.compile("Time Stamp:|E32ImageFile|Header CRC:")
|
|
55 |
E32_LOWER = re.compile("imports from")
|
|
56 |
INTEL_OBJECTPATH_WIN = re.compile(r"\.\.\\[^(]*\\")
|
|
57 |
INTEL_OBJECTPATH_NIX = re.compile("\.\.\/[^(]*\/")
|
|
58 |
INTEL_DLLTOOL = re.compile("^(.+ (_head|_))\w+_(EPOC32_\w+(_LIB|_iname))$", re.I)
|
|
59 |
|
|
60 |
@classmethod
|
|
61 |
def typeLookup(cls, type):
|
|
62 |
'''
|
|
63 |
Return the internally used identifier string for the type
|
|
64 |
@todo: Warning
|
|
65 |
@param type: The type
|
|
66 |
@type type: String
|
|
67 |
@return: Internally used type identifier
|
|
68 |
@rtype: String
|
|
69 |
'''
|
|
70 |
if type in ("e32", "default", "elf", "preprocessed_text", "intel", "intel_pe"):
|
|
71 |
return type
|
|
72 |
elif type in ("file", "symbol"):
|
|
73 |
return "default"
|
|
74 |
elif type in ("staticlib", "dso"):
|
|
75 |
return "elf"
|
|
76 |
elif type in ("exe", "plugin", "dll"):
|
|
77 |
return "e32"
|
|
78 |
else:
|
|
79 |
#sys.stderr.write("warning - unknown hashtype %s.\n"%type)
|
|
80 |
return "default"
|
|
81 |
|
|
82 |
@classmethod
|
|
83 |
def generateSignature(cls, path, fileType):
|
|
84 |
'''
|
|
85 |
Generic dispatcher method for file types. Use the appropriate method for
|
|
86 |
I{type} to generate the signature for file at I{path}.
|
|
87 |
|
|
88 |
@param path: The path where the file is located
|
|
89 |
@type path: String
|
|
90 |
@return: checksum
|
|
91 |
@rtype: String
|
|
92 |
'''
|
|
93 |
if not isinstance(path, basestring):
|
|
94 |
raise TypeError, "path must be a string"
|
|
95 |
if not path:
|
|
96 |
raise ValueError, "path must not be zero length"
|
|
97 |
path = os.path.normpath(path)
|
|
98 |
fileType = cls.typeLookup(fileType)
|
|
99 |
methodName = "sig_" + fileType
|
|
100 |
if hasattr(cls, methodName):
|
|
101 |
method = getattr(cls, methodName)
|
|
102 |
return method(path)
|
|
103 |
else:
|
|
104 |
raise NotImplementedError("No signature generator for type %s" % fileType)
|
|
105 |
|
|
106 |
@staticmethod
|
|
107 |
def getUid(index, file):
|
|
108 |
'''Get UID of file
|
|
109 |
|
|
110 |
@param index: Which UID
|
|
111 |
@param file: Absolute path
|
|
112 |
@return: UID
|
|
113 |
@rtype: String
|
|
114 |
'''
|
|
115 |
if index not in (1, 2, 3):
|
|
116 |
raise ValueError("Index can only be one of 1, 2 or 3")
|
|
117 |
if os.path.getsize(file) < 12:
|
|
118 |
return None
|
|
119 |
start = (index-1) * 4
|
|
120 |
finish = start + 4
|
|
121 |
f = open(file, "rb")
|
|
122 |
head = f.read(12)
|
|
123 |
f.close()
|
|
124 |
return struct.unpack("<l", head[start:finish])[0]
|
|
125 |
|
|
126 |
@staticmethod
|
|
127 |
def getMd5():
|
|
128 |
'''A convenicence method to use appropriate library regardless of Python
|
|
129 |
version. Maintain compatibility while using hashlib whenever possible.
|
|
130 |
|
|
131 |
@return: md5 object
|
|
132 |
@rtype: md5
|
|
133 |
'''
|
|
134 |
if hasattr(md5, "new"):
|
|
135 |
return md5.new()
|
|
136 |
else:
|
|
137 |
return md5()
|
|
138 |
|
|
139 |
# Signatures for various formats
|
|
140 |
|
|
141 |
@classmethod
|
|
142 |
def sig_e32(cls, path):
|
|
143 |
'''
|
|
144 |
Return the checksum of significant parts using elf2e32
|
|
145 |
|
|
146 |
@param path: The absolute path
|
|
147 |
@type path: String
|
|
148 |
@return: checksum
|
|
149 |
@rtype: String
|
|
150 |
'''
|
|
151 |
bin = cls.ELF2E32 + " --dump --e32input="
|
|
152 |
m = cls.getMd5()
|
|
153 |
fo = os.popen(bin+path, "r", -1)
|
|
154 |
for line in fo:
|
|
155 |
if cls.E32_EMPTY.search(line):
|
|
156 |
line = ""
|
|
157 |
if cls.E32_LOWER.search(line):
|
|
158 |
line = line.lower()
|
|
159 |
m.update(line)
|
|
160 |
if fo.close():
|
|
161 |
raise cls.ExternalError("elf2e32 failed at %s" % path)
|
|
162 |
return m.hexdigest()
|
|
163 |
|
|
164 |
@classmethod
|
|
165 |
def sig_default(cls, path):
|
|
166 |
'''
|
|
167 |
Calculate the checksum of the file without filtering.
|
|
168 |
|
|
169 |
@param path: The absolute path
|
|
170 |
@type path: String
|
|
171 |
@return: checksum
|
|
172 |
@rtype: String
|
|
173 |
'''
|
|
174 |
m = cls.getMd5()
|
|
175 |
f = open(path, "rb")
|
|
176 |
while True:
|
|
177 |
buf = f.read(32*1024)
|
|
178 |
if not buf:
|
|
179 |
break
|
|
180 |
m.update(buf)
|
|
181 |
f.close()
|
|
182 |
return m.hexdigest()
|
|
183 |
|
|
184 |
@classmethod
|
|
185 |
def sig_elf(cls, path):
|
|
186 |
'''
|
|
187 |
Return the checksum of significant parts using elfdump
|
|
188 |
|
|
189 |
@param path: The absolute path
|
|
190 |
@type path: String
|
|
191 |
@return: checksum
|
|
192 |
@rtype: String
|
|
193 |
'''
|
|
194 |
bin = cls.ELFDUMP + " -i "
|
|
195 |
def firstGroupToLc(match):
|
|
196 |
return ("#<DLL>" + match.group(1).lower() + "#<\\DLL>")
|
|
197 |
m = cls.getMd5()
|
|
198 |
fo = os.popen(bin+path, "r", -1)
|
|
199 |
for line in fo:
|
|
200 |
if cls.ELF_P_HEAD.match(line):
|
|
201 |
line = "Program header offset\n"
|
|
202 |
if cls.ELF_S_HEAD.match(line):
|
|
203 |
line = "Section header offset\n"
|
|
204 |
line = cls.ELF_DLL_NAME.sub(firstGroupToLc, line)
|
|
205 |
if cls.ELF_DEBUG.match(line):
|
|
206 |
line = ""
|
|
207 |
#sys.stderr.write(line)
|
|
208 |
m.update(line)
|
|
209 |
if fo.close():
|
|
210 |
raise cls.ExternalError("elfdump failed at %s" % path)
|
|
211 |
return m.hexdigest()
|
|
212 |
|
|
213 |
@classmethod
|
|
214 |
def sig_preprocessed_text(cls, path):
|
|
215 |
'''
|
|
216 |
Return the checksum of significant parts of preprocessed text
|
|
217 |
|
|
218 |
@param path: The absolute path
|
|
219 |
@type path: String
|
|
220 |
@return: checksum
|
|
221 |
@rtype: String
|
|
222 |
'''
|
|
223 |
m = cls.getMd5()
|
|
224 |
f = open(path, "rb")
|
|
225 |
for line in f:
|
|
226 |
line = line.replace("\r\n", "\n")
|
|
227 |
if cls.PRECOMP_IGNORE.search(line):
|
|
228 |
line = "\n"
|
|
229 |
m.update(line)
|
|
230 |
f.close()
|
|
231 |
return m.hexdigest()
|
|
232 |
|
|
233 |
@classmethod
|
|
234 |
def sig_intel_pe(cls, path):
|
|
235 |
'''
|
|
236 |
Return the checksum of significant parts of pe_dump output
|
|
237 |
|
|
238 |
@param path: The absolute path
|
|
239 |
@type path: String
|
|
240 |
@return: checksum
|
|
241 |
@rtype: String
|
|
242 |
'''
|
|
243 |
m = cls.getMd5()
|
|
244 |
fo = os.popen("%s %s" % (cls.PE_DUMP, path), "r", -1)
|
|
245 |
for line in fo:
|
|
246 |
m.update(line)
|
|
247 |
if fo.close():
|
|
248 |
raise cls.ExternalError("pe_dump failed at %s" % path)
|
|
249 |
return m.hexdigest()
|
|
250 |
|
|
251 |
|
|
252 |
@classmethod
|
|
253 |
def sig_intel(cls, path):
|
|
254 |
'''
|
|
255 |
Return the checksum of significant parts using nm
|
|
256 |
|
|
257 |
@param path: The absolute path
|
|
258 |
@type path: String
|
|
259 |
@return: checksum
|
|
260 |
@rtype: String
|
|
261 |
'''
|
|
262 |
m = cls.getMd5()
|
|
263 |
try:
|
|
264 |
s = subprocess.Popen([cls.NM, "--no-sort", path], env=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
265 |
out, err = s.communicate()
|
|
266 |
except OSError, e:
|
|
267 |
raise cls.ExternalError, "nm failed at %s: %s" % (path, str(e))
|
|
268 |
if s.returncode != 0:
|
|
269 |
raise cls.ExternalError, "nm failed at %s: %s" % (path, err)
|
|
270 |
for line in out.splitlines():
|
|
271 |
# no need for regexps here
|
|
272 |
if line.endswith(":\n") \
|
|
273 |
or line.startswith("BFD: ") \
|
|
274 |
or cls.INTEL_OBJECTPATH_WIN.search(line) \
|
|
275 |
or cls.INTEL_OBJECTPATH_NIX.search(line):
|
|
276 |
line = "\n"
|
|
277 |
match = cls.INTEL_DLLTOOL.search(line)
|
|
278 |
if match:
|
|
279 |
line = "%s_..._%s" % (match.groups()[0], match.groups()[2])
|
|
280 |
line = line.upper()
|
|
281 |
m.update(line)
|
|
282 |
|
|
283 |
if s.returncode != 0:
|
|
284 |
raise cls.ExternalError("nm failed at %s" % path)
|
|
285 |
return m.hexdigest()
|
|
286 |
|
|
287 |
def main():
|
|
288 |
path = sys.argv[1]
|
|
289 |
ftype = sys.argv[2]
|
|
290 |
print Evalid.generateSignature(path, ftype)
|
|
291 |
|
|
292 |
if __name__ == "__main__":
|
|
293 |
main() |