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