|
1
|
1 |
|
from plugin.core.configuration import Configuration |
|
2
|
1 |
|
from plugin.core.libraries.helpers.android import AndroidHelper |
|
3
|
1 |
|
from plugin.core.libraries.helpers.arm import ArmHelper |
|
4
|
|
|
|
|
5
|
1 |
|
from elftools.elf.attributes import AttributesSection |
|
6
|
1 |
|
from elftools.elf.elffile import ELFFile |
|
7
|
1 |
|
import logging |
|
8
|
1 |
|
import os |
|
9
|
1 |
|
import platform |
|
10
|
1 |
|
import re |
|
11
|
1 |
|
import sys |
|
12
|
|
|
|
|
13
|
1 |
|
log = logging.getLogger(__name__) |
|
14
|
|
|
|
|
15
|
1 |
|
_distributor_id_file_re = re.compile("(?:DISTRIB_ID\s*=)\s*(.*)", re.I) |
|
|
|
|
|
|
16
|
1 |
|
_release_file_re = re.compile("(?:DISTRIB_RELEASE\s*=)\s*(.*)", re.I) |
|
|
|
|
|
|
17
|
1 |
|
_codename_file_re = re.compile("(?:DISTRIB_CODENAME\s*=)\s*(.*)", re.I) |
|
|
|
|
|
|
18
|
|
|
|
|
19
|
1 |
|
BITS_MAP = { |
|
20
|
|
|
'32bit': 'i386', |
|
21
|
|
|
'64bit': 'x86_64' |
|
22
|
|
|
} |
|
23
|
|
|
|
|
24
|
1 |
|
MACHINE_MAP = { |
|
25
|
|
|
('32bit', 'i686'): 'i686', |
|
26
|
|
|
('32bit', 'ppc' ): 'PowerPC' |
|
|
|
|
|
|
27
|
|
|
} |
|
28
|
|
|
|
|
29
|
1 |
|
MSVCR_MAP = { |
|
30
|
|
|
'msvcr110.dll': 'vc11', |
|
31
|
|
|
'msvcr120.dll': 'vc12', |
|
32
|
|
|
'msvcr130.dll': 'vc14' |
|
33
|
|
|
} |
|
34
|
|
|
|
|
35
|
1 |
|
NAME_MAP = { |
|
36
|
|
|
'Darwin': 'MacOSX' |
|
37
|
|
|
} |
|
38
|
|
|
|
|
39
|
1 |
|
FALLBACK_EXECUTABLE = '/bin/ls' |
|
40
|
|
|
|
|
41
|
|
|
|
|
42
|
1 |
|
class SystemHelper(object): |
|
43
|
1 |
|
@classmethod |
|
44
|
|
|
def name(cls): |
|
45
|
|
|
"""Retrieve system name (Windows, Linux, FreeBSD, MacOSX)""" |
|
46
|
|
|
|
|
47
|
1 |
|
system = platform.system() |
|
48
|
|
|
|
|
49
|
|
|
# Check for android platform |
|
50
|
1 |
|
if system == 'Linux' and AndroidHelper.is_android(): |
|
51
|
|
|
system = 'Android' |
|
52
|
|
|
|
|
53
|
|
|
# Apply system name map |
|
54
|
1 |
|
if system in NAME_MAP: |
|
55
|
|
|
system = NAME_MAP[system] |
|
56
|
|
|
|
|
57
|
1 |
|
return system |
|
58
|
|
|
|
|
59
|
1 |
|
@classmethod |
|
60
|
|
|
def attributes(cls): |
|
61
|
|
|
# Retrieve platform attributes |
|
62
|
|
|
system = cls.name() |
|
63
|
|
|
|
|
64
|
|
|
release = platform.release() |
|
65
|
|
|
version = platform.version() |
|
66
|
|
|
|
|
67
|
|
|
# Build attributes dictionary |
|
68
|
|
|
result = { |
|
69
|
|
|
'cpu.architecture': cls.architecture(), |
|
70
|
|
|
'cpu.name': cls.cpu_name(), |
|
71
|
|
|
|
|
72
|
|
|
'os.system': system, |
|
73
|
|
|
|
|
74
|
|
|
'os.name': system, |
|
75
|
|
|
'os.release': release, |
|
76
|
|
|
'os.version': version |
|
77
|
|
|
} |
|
78
|
|
|
|
|
79
|
|
|
if system == 'Linux': |
|
80
|
|
|
# Update with linux distribution attributes |
|
81
|
|
|
result.update(cls.attributes_linux( |
|
82
|
|
|
release, version |
|
83
|
|
|
)) |
|
84
|
|
|
|
|
85
|
|
|
return result |
|
86
|
|
|
|
|
87
|
1 |
|
@classmethod |
|
88
|
1 |
|
def attributes_linux(cls, release=None, version=None): |
|
89
|
|
|
d_name, d_version, d_id = cls.distribution() |
|
90
|
|
|
|
|
91
|
|
|
# Build linux attributes dictionary |
|
92
|
|
|
result = { |
|
93
|
|
|
'os.name': None, |
|
94
|
|
|
'os.release': None, |
|
95
|
|
|
'os.version': None, |
|
96
|
|
|
|
|
97
|
|
|
'linux.release': release, |
|
98
|
|
|
'linux.version': version |
|
99
|
|
|
} |
|
100
|
|
|
|
|
|
|
|
|
|
101
|
|
|
if d_name: |
|
102
|
|
|
result['os.name'] = d_name |
|
103
|
|
|
|
|
104
|
|
|
if d_version: |
|
105
|
|
|
result['os.version'] = d_version |
|
106
|
|
|
|
|
107
|
|
|
if d_id: |
|
108
|
|
|
result['os.release'] = d_id |
|
109
|
|
|
|
|
110
|
|
|
return result |
|
111
|
|
|
|
|
112
|
1 |
|
@classmethod |
|
113
|
|
|
def architecture(cls): |
|
114
|
|
|
"""Retrieve system architecture (i386, i686, x86_64)""" |
|
115
|
|
|
|
|
116
|
|
|
# Use `cpu_architecture` value from advanced configuration (if defined) |
|
117
|
1 |
|
cpu_architecture = Configuration.advanced['libraries'].get('cpu_architecture') |
|
118
|
|
|
|
|
119
|
1 |
|
if cpu_architecture: |
|
120
|
|
|
log.info('Using CPU Architecture from advanced configuration: %r', cpu_architecture) |
|
121
|
|
|
return cpu_architecture |
|
122
|
|
|
|
|
123
|
|
|
# Determine architecture from platform attributes |
|
124
|
1 |
|
bits, _ = platform.architecture() |
|
125
|
1 |
|
machine = platform.machine() |
|
126
|
|
|
|
|
127
|
|
|
# Check for ARM machine |
|
128
|
1 |
|
if (bits == '32bit' and machine.startswith('armv')) or machine.startswith('aarch64'): |
|
129
|
|
|
return cls.arm(machine) |
|
130
|
|
|
|
|
131
|
|
|
# Check (bits, machine) map |
|
132
|
1 |
|
machine_key = (bits, machine) |
|
133
|
|
|
|
|
134
|
1 |
|
if machine_key in MACHINE_MAP: |
|
135
|
|
|
return MACHINE_MAP[machine_key] |
|
136
|
|
|
|
|
137
|
|
|
# Check (bits) map |
|
138
|
1 |
|
if bits in BITS_MAP: |
|
139
|
1 |
|
return BITS_MAP[bits] |
|
140
|
|
|
|
|
141
|
|
|
log.error('Unable to determine system architecture - bits: %r, machine: %r', bits, machine) |
|
142
|
|
|
return None |
|
143
|
|
|
|
|
144
|
1 |
|
@classmethod |
|
145
|
1 |
|
def arm(cls, machine, float_type=None): |
|
146
|
|
|
# Determine ARM version |
|
147
|
1 |
|
floats, architecture = cls.arm_architecture(machine) |
|
148
|
|
|
|
|
149
|
1 |
|
if architecture is None: |
|
150
|
|
|
log.warn('Unable to use ARM libraries, unsupported ARM architecture (%r)?' % machine) |
|
|
|
|
|
|
151
|
|
|
return None |
|
152
|
|
|
|
|
153
|
1 |
|
if not floats: |
|
154
|
1 |
|
return architecture |
|
155
|
|
|
|
|
156
|
|
|
# Determine floating-point type |
|
157
|
1 |
|
float_type = float_type or cls.arm_float_type() |
|
158
|
|
|
|
|
159
|
1 |
|
if float_type is None: |
|
160
|
|
|
log.warn('Unable to use ARM libraries, unsupported floating-point type?') |
|
161
|
|
|
return None |
|
162
|
|
|
|
|
163
|
1 |
|
return '%s_%s' % (architecture, float_type) |
|
164
|
|
|
|
|
165
|
1 |
|
@classmethod |
|
166
|
1 |
|
def arm_architecture(cls, machine=None): |
|
167
|
|
|
# Read `machine` name if not provided |
|
168
|
1 |
|
if machine is None: |
|
169
|
|
|
machine = platform.machine() |
|
170
|
|
|
|
|
171
|
|
|
# Ensure `machine` is valid |
|
172
|
1 |
|
if not machine: |
|
173
|
|
|
return False, None |
|
174
|
|
|
|
|
175
|
|
|
# ARMv5 |
|
176
|
1 |
|
if machine.startswith('armv5'): |
|
177
|
1 |
|
return True, 'armv5' |
|
178
|
|
|
|
|
179
|
|
|
# ARMv6 |
|
180
|
1 |
|
if machine.startswith('armv6'): |
|
181
|
1 |
|
return True, 'armv6' |
|
182
|
|
|
|
|
183
|
|
|
# ARMv7 |
|
184
|
1 |
|
if machine.startswith('armv7'): |
|
185
|
1 |
|
return True, 'armv7' |
|
186
|
|
|
|
|
187
|
|
|
# ARMv8 / AArch64 |
|
188
|
1 |
|
if machine.startswith('armv8') or machine.startswith('aarch64'): |
|
189
|
1 |
|
return False, 'aarch64' |
|
190
|
|
|
|
|
191
|
|
|
return False, None |
|
192
|
|
|
|
|
193
|
1 |
|
@classmethod |
|
194
|
1 |
|
def arm_float_type(cls, executable_path=sys.executable): |
|
195
|
|
|
# Use `arm_float_type` value from advanced configuration (if defined) |
|
196
|
|
|
arm_float_type = Configuration.advanced['libraries'].get('arm_float_type') |
|
197
|
|
|
|
|
198
|
|
|
if arm_float_type: |
|
199
|
|
|
log.info('Using ARM Float Type from advanced configuration: %r', arm_float_type) |
|
200
|
|
|
return arm_float_type |
|
201
|
|
|
|
|
202
|
|
|
# Try determine float-type from "/lib" directories |
|
203
|
|
|
if os.path.exists('/lib/arm-linux-gnueabihf'): |
|
204
|
|
|
return 'hf' |
|
205
|
|
|
|
|
206
|
|
|
if os.path.exists('/lib/arm-linux-gnueabi'): |
|
207
|
|
|
return 'sf' |
|
208
|
|
|
|
|
209
|
|
|
# Determine system float-type from python executable |
|
210
|
|
|
section, attributes = cls.elf_attributes(executable_path) |
|
211
|
|
|
|
|
212
|
|
|
if not section or not attributes: |
|
213
|
|
|
return None |
|
214
|
|
|
|
|
215
|
|
|
if section.name != 'aeabi': |
|
216
|
|
|
log.warn('Unknown attributes section name: %r', section.name) |
|
217
|
|
|
return None |
|
218
|
|
|
|
|
219
|
|
|
# Assume hard-float if "tag_abi_vfp_args" is present |
|
220
|
|
|
if attributes.get('abi_vfp_args'): |
|
221
|
|
|
return 'hf' |
|
222
|
|
|
|
|
223
|
|
|
return 'sf' |
|
224
|
|
|
|
|
225
|
1 |
|
@classmethod |
|
226
|
1 |
|
def cpu_name(cls, executable_path=sys.executable): |
|
227
|
1 |
|
if cls.name() == 'Windows': |
|
228
|
|
|
return None |
|
229
|
|
|
|
|
230
|
|
|
# Retrieve CPU name from ELF |
|
231
|
1 |
|
section, attributes = cls.elf_attributes(executable_path) |
|
232
|
|
|
|
|
233
|
1 |
|
if not section or not attributes: |
|
234
|
1 |
|
return None |
|
235
|
|
|
|
|
236
|
|
|
name = attributes.get('cpu_name') |
|
237
|
|
|
|
|
238
|
|
|
if not name: |
|
239
|
|
|
return None |
|
240
|
|
|
|
|
241
|
|
|
return name.lower() |
|
242
|
|
|
|
|
243
|
1 |
|
@classmethod |
|
244
|
1 |
|
def cpu_type(cls, executable_path=sys.executable): |
|
245
|
|
|
# Use `cpu_type` value from advanced configuration (if defined) |
|
246
|
1 |
|
cpu_type = Configuration.advanced['libraries'].get('cpu_type') |
|
247
|
|
|
|
|
248
|
1 |
|
if cpu_type: |
|
249
|
|
|
log.info('Using CPU Type from advanced configuration: %r', cpu_type) |
|
250
|
|
|
return cpu_type |
|
251
|
|
|
|
|
252
|
|
|
# Try retrieve cpu type via "/proc/cpuinfo" |
|
253
|
1 |
|
try: |
|
254
|
1 |
|
_, _, cpu_type = ArmHelper.identifier() |
|
255
|
|
|
|
|
256
|
1 |
|
if cpu_type: |
|
257
|
|
|
return cpu_type |
|
258
|
|
|
except Exception as ex: |
|
|
|
|
|
|
259
|
|
|
log.warn('Unable to retrieve cpu type from "/proc/cpuinfo": %s', ex, exc_info=True) |
|
260
|
|
|
|
|
261
|
|
|
# Fallback to using the ELF cpu name |
|
262
|
1 |
|
return cls.cpu_name(executable_path) |
|
263
|
|
|
|
|
264
|
1 |
|
@classmethod |
|
265
|
1 |
|
def distribution(cls, distname='', version='', id='', |
|
|
|
|
|
|
266
|
|
|
supported_dists=platform._supported_dists, |
|
|
|
|
|
|
267
|
|
|
full_distribution_name=1): |
|
268
|
|
|
|
|
269
|
|
|
# check for the Debian/Ubuntu /etc/lsb-release file first, needed so |
|
270
|
|
|
# that the distribution doesn't get identified as Debian. |
|
271
|
|
|
try: |
|
272
|
|
|
_distname = None |
|
273
|
|
|
_version = None |
|
274
|
|
|
_id = None |
|
275
|
|
|
|
|
276
|
|
|
with open("/etc/lsb-release", "rU") as fp: |
|
277
|
|
|
for line in fp: |
|
278
|
|
|
# Distribution Name |
|
279
|
|
|
m = _distributor_id_file_re.search(line) |
|
280
|
|
|
|
|
281
|
|
|
if m: |
|
282
|
|
|
_distname = m.group(1).strip() |
|
283
|
|
|
|
|
284
|
|
|
# Release |
|
285
|
|
|
m = _release_file_re.search(line) |
|
286
|
|
|
|
|
287
|
|
|
if m: |
|
288
|
|
|
_version = m.group(1).strip() |
|
289
|
|
|
|
|
290
|
|
|
# ID |
|
291
|
|
|
m = _codename_file_re.search(line) |
|
292
|
|
|
|
|
293
|
|
|
if m: |
|
294
|
|
|
_id = m.group(1).strip() |
|
295
|
|
|
|
|
296
|
|
|
if _distname and _version: |
|
297
|
|
|
return _distname, _version, _id |
|
298
|
|
|
|
|
299
|
|
|
except (EnvironmentError, UnboundLocalError): |
|
|
|
|
|
|
300
|
|
|
pass |
|
301
|
|
|
|
|
302
|
|
|
# Fallback to using the "platform" module |
|
303
|
|
|
return platform.linux_distribution( |
|
304
|
|
|
distname, version, id, |
|
305
|
|
|
supported_dists=supported_dists, |
|
306
|
|
|
full_distribution_name=full_distribution_name |
|
307
|
|
|
) |
|
308
|
|
|
|
|
309
|
1 |
|
@classmethod |
|
310
|
1 |
|
def elf_attributes(cls, executable_path=sys.executable): |
|
311
|
1 |
|
if cls.name() == 'MacOSX': |
|
312
|
|
|
log.info('Unable to retrieve ELF attributes on Mac OSX (not supported)') |
|
313
|
|
|
return None, None |
|
314
|
|
|
|
|
315
|
|
|
# Read attributes from "/bin/ls" if `executable_path` doesn't exist |
|
316
|
1 |
|
if not executable_path or not os.path.exists(executable_path): |
|
317
|
|
|
log.info('Executable at %r doesn\'t exist, using %r instead', executable_path, FALLBACK_EXECUTABLE) |
|
318
|
|
|
executable_path = FALLBACK_EXECUTABLE |
|
319
|
|
|
|
|
320
|
1 |
|
try: |
|
321
|
|
|
# Open executable stream |
|
322
|
1 |
|
stream = open(executable_path, 'rb') |
|
323
|
|
|
|
|
324
|
|
|
# Retrieve magic number (header) |
|
325
|
1 |
|
magic = stream.read(4) |
|
326
|
|
|
|
|
327
|
1 |
|
if magic != b'\x7fELF': |
|
328
|
|
|
log.warn('Unknown ELF format for %r (magic: %r)', executable_path, magic) |
|
329
|
|
|
return None, None |
|
330
|
|
|
|
|
331
|
1 |
|
stream.seek(0) |
|
332
|
|
|
|
|
333
|
|
|
# Parse ELF |
|
334
|
1 |
|
elf = ELFFile(stream) |
|
335
|
|
|
|
|
336
|
|
|
# Find attributes section |
|
337
|
1 |
|
section = cls._find_elf_section(elf, AttributesSection) |
|
338
|
|
|
|
|
339
|
1 |
|
if section is None: |
|
340
|
1 |
|
log.info('Unable to find attributes section in ELF: %r', executable_path) |
|
341
|
1 |
|
return None, None |
|
342
|
|
|
|
|
343
|
|
|
# Build dictionary of attributes |
|
344
|
|
|
attributes = dict([ |
|
345
|
|
|
(attr.tag.lower(), attr.value) |
|
346
|
|
|
for attr in section.iter_attributes() |
|
347
|
|
|
]) |
|
348
|
|
|
|
|
349
|
|
|
return section, attributes |
|
350
|
|
|
except Exception as ex: |
|
|
|
|
|
|
351
|
|
|
log.warn('Unable to retrieve attributes from ELF %r: %s', executable_path, ex, exc_info=True) |
|
352
|
|
|
|
|
353
|
|
|
return None, None |
|
354
|
|
|
|
|
355
|
1 |
|
@classmethod |
|
356
|
|
|
def page_size(cls): |
|
357
|
1 |
|
try: |
|
358
|
1 |
|
import resource |
|
359
|
1 |
|
page_size = resource.getpagesize() |
|
360
|
|
|
|
|
361
|
1 |
|
if not page_size: |
|
362
|
|
|
return None |
|
363
|
|
|
|
|
364
|
1 |
|
return '%dk' % (page_size / 1024) |
|
365
|
|
|
except Exception as ex: |
|
|
|
|
|
|
366
|
|
|
log.warn('Unable to retrieve memory page size: %s', ex, exc_info=True) |
|
367
|
|
|
return None |
|
368
|
|
|
|
|
369
|
1 |
|
@staticmethod |
|
370
|
|
|
def _find_elf_section(elf, cls): |
|
371
|
1 |
|
for section in elf.iter_sections(): |
|
372
|
1 |
|
if isinstance(section, cls): |
|
373
|
|
|
return section |
|
374
|
|
|
|
|
375
|
1 |
|
return None |
|
376
|
|
|
|
|
377
|
1 |
|
@classmethod |
|
378
|
|
|
def vcr_version(cls): |
|
379
|
|
|
try: |
|
380
|
|
|
import ctypes.util |
|
381
|
|
|
|
|
382
|
|
|
# Retrieve linked msvcr dll |
|
383
|
|
|
name = ctypes.util.find_msvcrt() |
|
384
|
|
|
|
|
385
|
|
|
# Return VC++ version from map |
|
386
|
|
|
if name not in MSVCR_MAP: |
|
387
|
|
|
log.error('Unknown VC++ runtime: %r', name) |
|
388
|
|
|
return None |
|
389
|
|
|
|
|
390
|
|
|
return MSVCR_MAP[name] |
|
391
|
|
|
except Exception: |
|
|
|
|
|
|
392
|
|
|
log.error('Unable to retrieve VC++ runtime version', exc_info=True) |
|
393
|
|
|
return None |
|
394
|
|
|
|
Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with
rorRare they interpreted as regular expressions.The escape sequence that was used indicates that you might have intended to write a regular expression.
Learn more about the available escape sequences. in the Python documentation.