Mercurial > repos > guerler > hhblits
comparison lib/python3.8/site-packages/wheel/macosx_libfile.py @ 1:64071f2a4cf0 draft default tip
Deleted selected files
author | guerler |
---|---|
date | Mon, 27 Jul 2020 03:55:49 -0400 |
parents | 9e54283cc701 |
children |
comparison
equal
deleted
inserted
replaced
0:9e54283cc701 | 1:64071f2a4cf0 |
---|---|
1 """ | |
2 This module contains function to analyse dynamic library | |
3 headers to extract system information | |
4 | |
5 Currently only for MacOSX | |
6 | |
7 Library file on macosx system starts with Mach-O or Fat field. | |
8 This can be distinguish by first 32 bites and it is called magic number. | |
9 Proper value of magic number is with suffix _MAGIC. Suffix _CIGAM means | |
10 reversed bytes order. | |
11 Both fields can occur in two types: 32 and 64 bytes. | |
12 | |
13 FAT field inform that this library contains few version of library | |
14 (typically for different types version). It contains | |
15 information where Mach-O headers starts. | |
16 | |
17 Each section started with Mach-O header contains one library | |
18 (So if file starts with this field it contains only one version). | |
19 | |
20 After filed Mach-O there are section fields. | |
21 Each of them starts with two fields: | |
22 cmd - magic number for this command | |
23 cmdsize - total size occupied by this section information. | |
24 | |
25 In this case only sections LC_VERSION_MIN_MACOSX (for macosx 10.13 and earlier) | |
26 and LC_BUILD_VERSION (for macosx 10.14 and newer) are interesting, | |
27 because them contains information about minimal system version. | |
28 | |
29 Important remarks: | |
30 - For fat files this implementation looks for maximum number version. | |
31 It not check if it is 32 or 64 and do not compare it with currently builded package. | |
32 So it is possible to false report higher version that needed. | |
33 - All structures signatures are taken form macosx header files. | |
34 - I think that binary format will be more stable than `otool` output. | |
35 and if apple introduce some changes both implementation will need to be updated. | |
36 """ | |
37 | |
38 import ctypes | |
39 import sys | |
40 | |
41 """here the needed const and struct from mach-o header files""" | |
42 | |
43 FAT_MAGIC = 0xcafebabe | |
44 FAT_CIGAM = 0xbebafeca | |
45 FAT_MAGIC_64 = 0xcafebabf | |
46 FAT_CIGAM_64 = 0xbfbafeca | |
47 MH_MAGIC = 0xfeedface | |
48 MH_CIGAM = 0xcefaedfe | |
49 MH_MAGIC_64 = 0xfeedfacf | |
50 MH_CIGAM_64 = 0xcffaedfe | |
51 | |
52 LC_VERSION_MIN_MACOSX = 0x24 | |
53 LC_BUILD_VERSION = 0x32 | |
54 | |
55 | |
56 mach_header_fields = [ | |
57 ("magic", ctypes.c_uint32), ("cputype", ctypes.c_int), | |
58 ("cpusubtype", ctypes.c_int), ("filetype", ctypes.c_uint32), | |
59 ("ncmds", ctypes.c_uint32), ("sizeofcmds", ctypes.c_uint32), | |
60 ("flags", ctypes.c_uint32) | |
61 ] | |
62 """ | |
63 struct mach_header { | |
64 uint32_t magic; /* mach magic number identifier */ | |
65 cpu_type_t cputype; /* cpu specifier */ | |
66 cpu_subtype_t cpusubtype; /* machine specifier */ | |
67 uint32_t filetype; /* type of file */ | |
68 uint32_t ncmds; /* number of load commands */ | |
69 uint32_t sizeofcmds; /* the size of all the load commands */ | |
70 uint32_t flags; /* flags */ | |
71 }; | |
72 typedef integer_t cpu_type_t; | |
73 typedef integer_t cpu_subtype_t; | |
74 """ | |
75 | |
76 mach_header_fields_64 = mach_header_fields + [("reserved", ctypes.c_uint32)] | |
77 """ | |
78 struct mach_header_64 { | |
79 uint32_t magic; /* mach magic number identifier */ | |
80 cpu_type_t cputype; /* cpu specifier */ | |
81 cpu_subtype_t cpusubtype; /* machine specifier */ | |
82 uint32_t filetype; /* type of file */ | |
83 uint32_t ncmds; /* number of load commands */ | |
84 uint32_t sizeofcmds; /* the size of all the load commands */ | |
85 uint32_t flags; /* flags */ | |
86 uint32_t reserved; /* reserved */ | |
87 }; | |
88 """ | |
89 | |
90 fat_header_fields = [("magic", ctypes.c_uint32), ("nfat_arch", ctypes.c_uint32)] | |
91 """ | |
92 struct fat_header { | |
93 uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */ | |
94 uint32_t nfat_arch; /* number of structs that follow */ | |
95 }; | |
96 """ | |
97 | |
98 fat_arch_fields = [ | |
99 ("cputype", ctypes.c_int), ("cpusubtype", ctypes.c_int), | |
100 ("offset", ctypes.c_uint32), ("size", ctypes.c_uint32), | |
101 ("align", ctypes.c_uint32) | |
102 ] | |
103 """ | |
104 struct fat_arch { | |
105 cpu_type_t cputype; /* cpu specifier (int) */ | |
106 cpu_subtype_t cpusubtype; /* machine specifier (int) */ | |
107 uint32_t offset; /* file offset to this object file */ | |
108 uint32_t size; /* size of this object file */ | |
109 uint32_t align; /* alignment as a power of 2 */ | |
110 }; | |
111 """ | |
112 | |
113 fat_arch_64_fields = [ | |
114 ("cputype", ctypes.c_int), ("cpusubtype", ctypes.c_int), | |
115 ("offset", ctypes.c_uint64), ("size", ctypes.c_uint64), | |
116 ("align", ctypes.c_uint32), ("reserved", ctypes.c_uint32) | |
117 ] | |
118 """ | |
119 struct fat_arch_64 { | |
120 cpu_type_t cputype; /* cpu specifier (int) */ | |
121 cpu_subtype_t cpusubtype; /* machine specifier (int) */ | |
122 uint64_t offset; /* file offset to this object file */ | |
123 uint64_t size; /* size of this object file */ | |
124 uint32_t align; /* alignment as a power of 2 */ | |
125 uint32_t reserved; /* reserved */ | |
126 }; | |
127 """ | |
128 | |
129 segment_base_fields = [("cmd", ctypes.c_uint32), ("cmdsize", ctypes.c_uint32)] | |
130 """base for reading segment info""" | |
131 | |
132 segment_command_fields = [ | |
133 ("cmd", ctypes.c_uint32), ("cmdsize", ctypes.c_uint32), | |
134 ("segname", ctypes.c_char * 16), ("vmaddr", ctypes.c_uint32), | |
135 ("vmsize", ctypes.c_uint32), ("fileoff", ctypes.c_uint32), | |
136 ("filesize", ctypes.c_uint32), ("maxprot", ctypes.c_int), | |
137 ("initprot", ctypes.c_int), ("nsects", ctypes.c_uint32), | |
138 ("flags", ctypes.c_uint32), | |
139 ] | |
140 """ | |
141 struct segment_command { /* for 32-bit architectures */ | |
142 uint32_t cmd; /* LC_SEGMENT */ | |
143 uint32_t cmdsize; /* includes sizeof section structs */ | |
144 char segname[16]; /* segment name */ | |
145 uint32_t vmaddr; /* memory address of this segment */ | |
146 uint32_t vmsize; /* memory size of this segment */ | |
147 uint32_t fileoff; /* file offset of this segment */ | |
148 uint32_t filesize; /* amount to map from the file */ | |
149 vm_prot_t maxprot; /* maximum VM protection */ | |
150 vm_prot_t initprot; /* initial VM protection */ | |
151 uint32_t nsects; /* number of sections in segment */ | |
152 uint32_t flags; /* flags */ | |
153 }; | |
154 typedef int vm_prot_t; | |
155 """ | |
156 | |
157 segment_command_fields_64 = [ | |
158 ("cmd", ctypes.c_uint32), ("cmdsize", ctypes.c_uint32), | |
159 ("segname", ctypes.c_char * 16), ("vmaddr", ctypes.c_uint64), | |
160 ("vmsize", ctypes.c_uint64), ("fileoff", ctypes.c_uint64), | |
161 ("filesize", ctypes.c_uint64), ("maxprot", ctypes.c_int), | |
162 ("initprot", ctypes.c_int), ("nsects", ctypes.c_uint32), | |
163 ("flags", ctypes.c_uint32), | |
164 ] | |
165 """ | |
166 struct segment_command_64 { /* for 64-bit architectures */ | |
167 uint32_t cmd; /* LC_SEGMENT_64 */ | |
168 uint32_t cmdsize; /* includes sizeof section_64 structs */ | |
169 char segname[16]; /* segment name */ | |
170 uint64_t vmaddr; /* memory address of this segment */ | |
171 uint64_t vmsize; /* memory size of this segment */ | |
172 uint64_t fileoff; /* file offset of this segment */ | |
173 uint64_t filesize; /* amount to map from the file */ | |
174 vm_prot_t maxprot; /* maximum VM protection */ | |
175 vm_prot_t initprot; /* initial VM protection */ | |
176 uint32_t nsects; /* number of sections in segment */ | |
177 uint32_t flags; /* flags */ | |
178 }; | |
179 """ | |
180 | |
181 version_min_command_fields = segment_base_fields + \ | |
182 [("version", ctypes.c_uint32), ("sdk", ctypes.c_uint32)] | |
183 """ | |
184 struct version_min_command { | |
185 uint32_t cmd; /* LC_VERSION_MIN_MACOSX or | |
186 LC_VERSION_MIN_IPHONEOS or | |
187 LC_VERSION_MIN_WATCHOS or | |
188 LC_VERSION_MIN_TVOS */ | |
189 uint32_t cmdsize; /* sizeof(struct min_version_command) */ | |
190 uint32_t version; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ | |
191 uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ | |
192 }; | |
193 """ | |
194 | |
195 build_version_command_fields = segment_base_fields + \ | |
196 [("platform", ctypes.c_uint32), ("minos", ctypes.c_uint32), | |
197 ("sdk", ctypes.c_uint32), ("ntools", ctypes.c_uint32)] | |
198 """ | |
199 struct build_version_command { | |
200 uint32_t cmd; /* LC_BUILD_VERSION */ | |
201 uint32_t cmdsize; /* sizeof(struct build_version_command) plus */ | |
202 /* ntools * sizeof(struct build_tool_version) */ | |
203 uint32_t platform; /* platform */ | |
204 uint32_t minos; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ | |
205 uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ | |
206 uint32_t ntools; /* number of tool entries following this */ | |
207 }; | |
208 """ | |
209 | |
210 | |
211 def swap32(x): | |
212 return (((x << 24) & 0xFF000000) | | |
213 ((x << 8) & 0x00FF0000) | | |
214 ((x >> 8) & 0x0000FF00) | | |
215 ((x >> 24) & 0x000000FF)) | |
216 | |
217 | |
218 def get_base_class_and_magic_number(lib_file, seek=None): | |
219 if seek is None: | |
220 seek = lib_file.tell() | |
221 else: | |
222 lib_file.seek(seek) | |
223 magic_number = ctypes.c_uint32.from_buffer_copy( | |
224 lib_file.read(ctypes.sizeof(ctypes.c_uint32))).value | |
225 | |
226 # Handle wrong byte order | |
227 if magic_number in [FAT_CIGAM, FAT_CIGAM_64, MH_CIGAM, MH_CIGAM_64]: | |
228 if sys.byteorder == "little": | |
229 BaseClass = ctypes.BigEndianStructure | |
230 else: | |
231 BaseClass = ctypes.LittleEndianStructure | |
232 | |
233 magic_number = swap32(magic_number) | |
234 else: | |
235 BaseClass = ctypes.Structure | |
236 | |
237 lib_file.seek(seek) | |
238 return BaseClass, magic_number | |
239 | |
240 | |
241 def read_data(struct_class, lib_file): | |
242 return struct_class.from_buffer_copy(lib_file.read( | |
243 ctypes.sizeof(struct_class))) | |
244 | |
245 | |
246 def extract_macosx_min_system_version(path_to_lib): | |
247 with open(path_to_lib, "rb") as lib_file: | |
248 BaseClass, magic_number = get_base_class_and_magic_number(lib_file, 0) | |
249 if magic_number not in [FAT_MAGIC, FAT_MAGIC_64, MH_MAGIC, MH_MAGIC_64]: | |
250 return | |
251 | |
252 if magic_number in [FAT_MAGIC, FAT_CIGAM_64]: | |
253 class FatHeader(BaseClass): | |
254 _fields_ = fat_header_fields | |
255 | |
256 fat_header = read_data(FatHeader, lib_file) | |
257 if magic_number == FAT_MAGIC: | |
258 | |
259 class FatArch(BaseClass): | |
260 _fields_ = fat_arch_fields | |
261 else: | |
262 | |
263 class FatArch(BaseClass): | |
264 _fields_ = fat_arch_64_fields | |
265 | |
266 fat_arch_list = [read_data(FatArch, lib_file) for _ in range(fat_header.nfat_arch)] | |
267 | |
268 versions_list = [] | |
269 for el in fat_arch_list: | |
270 try: | |
271 version = read_mach_header(lib_file, el.offset) | |
272 if version is not None: | |
273 versions_list.append(version) | |
274 except ValueError: | |
275 pass | |
276 | |
277 if len(versions_list) > 0: | |
278 return max(versions_list) | |
279 else: | |
280 return None | |
281 | |
282 else: | |
283 try: | |
284 return read_mach_header(lib_file, 0) | |
285 except ValueError: | |
286 """when some error during read library files""" | |
287 return None | |
288 | |
289 | |
290 def read_mach_header(lib_file, seek=None): | |
291 """ | |
292 This funcition parse mach-O header and extract | |
293 information about minimal system version | |
294 | |
295 :param lib_file: reference to opened library file with pointer | |
296 """ | |
297 if seek is not None: | |
298 lib_file.seek(seek) | |
299 base_class, magic_number = get_base_class_and_magic_number(lib_file) | |
300 arch = "32" if magic_number == MH_MAGIC else "64" | |
301 | |
302 class SegmentBase(base_class): | |
303 _fields_ = segment_base_fields | |
304 | |
305 if arch == "32": | |
306 | |
307 class MachHeader(base_class): | |
308 _fields_ = mach_header_fields | |
309 | |
310 else: | |
311 | |
312 class MachHeader(base_class): | |
313 _fields_ = mach_header_fields_64 | |
314 | |
315 mach_header = read_data(MachHeader, lib_file) | |
316 for _i in range(mach_header.ncmds): | |
317 pos = lib_file.tell() | |
318 segment_base = read_data(SegmentBase, lib_file) | |
319 lib_file.seek(pos) | |
320 if segment_base.cmd == LC_VERSION_MIN_MACOSX: | |
321 class VersionMinCommand(base_class): | |
322 _fields_ = version_min_command_fields | |
323 | |
324 version_info = read_data(VersionMinCommand, lib_file) | |
325 return parse_version(version_info.version) | |
326 elif segment_base.cmd == LC_BUILD_VERSION: | |
327 class VersionBuild(base_class): | |
328 _fields_ = build_version_command_fields | |
329 | |
330 version_info = read_data(VersionBuild, lib_file) | |
331 return parse_version(version_info.minos) | |
332 else: | |
333 lib_file.seek(pos + segment_base.cmdsize) | |
334 continue | |
335 | |
336 | |
337 def parse_version(version): | |
338 x = (version & 0xffff0000) >> 16 | |
339 y = (version & 0x0000ff00) >> 8 | |
340 z = (version & 0x000000ff) | |
341 return x, y, z |