# Copyright 2017 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import array import difflib import distutils.dir_util import filecmp import operator import os import re import shutil import struct import subprocess import sys import tempfile import uuid def ZapTimestamp(filename): contents = open(filename, 'rb').read() # midl.exe writes timestamp 2147483647 (2^31 - 1) as creation date into its # outputs, but using the local timezone. To make the output timezone- # independent, replace that date with a fixed string of the same length. # Also blank out the minor version number. if filename.endswith('.tlb'): # See https://chromium-review.googlesource.com/c/chromium/src/+/693223 for # a fairly complete description of the .tlb binary format. # TLB files start with a 54 byte header. Offset 0x20 stores how many types # are defined in the file, and the header is followed by that many uint32s. # After that, 15 section headers appear. Each section header is 16 bytes, # starting with offset and length uint32s. # Section 12 in the file contains custom() data. custom() data has a type # (int, string, etc). Each custom data chunk starts with a uint16_t # describing its type. Type 8 is string data, consisting of a uint32_t # len, followed by that many data bytes, followed by 'W' bytes to pad to a # 4 byte boundary. Type 0x13 is uint32 data, followed by 4 data bytes, # followed by two 'W' to pad to a 4 byte boundary. # The custom block always starts with one string containing "Created by # MIDL version 8...", followed by one uint32 containing 0x7fffffff, # followed by another uint32 containing the MIDL compiler version (e.g. # 0x0801026e for v8.1.622 -- 0x26e == 622). These 3 fields take 0x54 bytes. # There might be more custom data after that, but these 3 blocks are always # there for file-level metadata. # All data is little-endian in the file. assert contents[0:8] == 'MSFT\x02\x00\x01\x00' ntypes, = struct.unpack_from('= 0x54 # First: Type string (0x8), followed by 0x3e characters. assert contents[custom_off:custom_off+6] == '\x08\x00\x3e\x00\x00\x00' assert re.match( 'Created by MIDL version 8\.\d\d\.\d{4} at ... Jan 1. ..:..:.. 2038\n', contents[custom_off+6:custom_off+6+0x3e]) # Second: Type uint32 (0x13) storing 0x7fffffff (followed by WW / 0x57 pad) assert contents[custom_off+6+0x3e:custom_off+6+0x3e+8] == \ '\x13\x00\xff\xff\xff\x7f\x57\x57' # Third: Type uint32 (0x13) storing MIDL compiler version. assert contents[custom_off+6+0x3e+8:custom_off+6+0x3e+8+2] == '\x13\x00' # Replace "Created by" string with fixed string, and fixed MIDL version with # 8.1.622 always. contents = (contents[0:custom_off+6] + 'Created by MIDL version 8.xx.xxxx at a redacted point in time\n' + # uint32 (0x13) val 0x7fffffff, WW, uint32 (0x13), val 0x0801026e, WW '\x13\x00\xff\xff\xff\x7f\x57\x57\x13\x00\x6e\x02\x01\x08\x57\x57' + contents[custom_off + 0x54:]) else: contents = re.sub( 'File created by MIDL compiler version 8\.\d\d\.\d{4} \*/\r\n' '/\* at ... Jan 1. ..:..:.. 2038', 'File created by MIDL compiler version 8.xx.xxxx */\r\n' '/* at a redacted point in time', contents) contents = re.sub( ' Oicf, W1, Zp8, env=(.....) \(32b run\), ' 'target_arch=(AMD64|X86) 8\.\d\d\.\d{4}', ' Oicf, W1, Zp8, env=\\1 (32b run), target_arch=\\2 8.xx.xxxx', contents) # TODO(thakis): If we need more hacks than these, try to verify checked-in # outputs when we're using the hermetic toolchain. # midl.exe older than 8.1.622 omit '//' after #endif, fix that: contents = contents.replace('#endif !_MIDL_USE_GUIDDEF_', '#endif // !_MIDL_USE_GUIDDEF_') # midl.exe puts the midl version into code in one place. To have # predictable output, lie about the midl version if it's not 8.1.622. # This is unfortunate, but remember that there's beauty too in imperfection. contents = contents.replace('0x801026c, /* MIDL Version 8.1.620 */', '0x801026e, /* MIDL Version 8.1.622 */') open(filename, 'wb').write(contents) def overwrite_cls_guid_h(h_file, dynamic_guid): contents = open(h_file, 'rb').read() contents = re.sub('class DECLSPEC_UUID\("[^"]*"\)', 'class DECLSPEC_UUID("%s")' % str(dynamic_guid), contents) open(h_file, 'wb').write(contents) def overwrite_cls_guid_iid(iid_file, dynamic_guid): contents = open(iid_file, 'rb').read() hexuuid = '0x%08x,0x%04x,0x%04x,' % dynamic_guid.fields[0:3] hexuuid += ','.join('0x%02x' % ord(b) for b in dynamic_guid.bytes[8:]) contents = re.sub(r'MIDL_DEFINE_GUID\(CLSID, ([^,]*),[^)]*\)', r'MIDL_DEFINE_GUID(CLSID, \1,%s)' % hexuuid, contents) open(iid_file, 'wb').write(contents) def overwrite_cls_guid_tlb(tlb_file, dynamic_guid): # See ZapTimestamp() for a short overview of the .tlb format. The 1st # section contains type descriptions, and the first type should be our # coclass. It points to the type's GUID in section 6, the GUID section. contents = open(tlb_file, 'rb').read() assert contents[0:8] == 'MSFT\x02\x00\x01\x00' ntypes, = struct.unpack_from('