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 |
