# Copyright 2017 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. from __future__ import division import array import difflib import filecmp import io import operator import os import posixpath import re import shutil import struct import subprocess import sys import tempfile import uuid from functools import reduce 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] == b'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] == b'\x08\x00\x3e\x00\x00\x00' assert re.match( br'Created by MIDL version 8\.\d\d\.\d{4} ' br'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] == \ b'\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] == b'\x13\x00' # Replace "Created by" string with fixed string, and fixed MIDL version with # 8.1.622 always. contents = ( contents[0:custom_off + 6] + b'Created by MIDL version 8.xx.xxxx at a redacted point in time\n' + # uint32 (0x13) val 0x7fffffff, WW, uint32 (0x13), val 0x0801026e, WW b'\x13\x00\xff\xff\xff\x7f\x57\x57\x13\x00\x6e\x02\x01\x08\x57\x57' + contents[custom_off + 0x54:]) else: contents = re.sub( br'File created by MIDL compiler version 8\.\d\d\.\d{4} \*/\r\n' br'/\* at ... Jan 1. ..:..:.. 2038', br'File created by MIDL compiler version 8.xx.xxxx */\r\n' br'/* at a redacted point in time', contents) contents = re.sub( br' Oicf, W1, Zp8, env=(.....) \(32b run\), ' br'target_arch=(AMD64|X86) 8\.\d\d\.\d{4}', br' 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(b'#endif !_MIDL_USE_GUIDDEF_', b'#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(b'0x801026c, /* MIDL Version 8.1.620 */', b'0x801026e, /* MIDL Version 8.1.622 */') open(filename, 'wb').write(contents) def get_tlb_contents(tlb_file): # See ZapTimestamp() for a short overview of the .tlb format. contents = open(tlb_file, 'rb').read() assert contents[0:8] == b'MSFT\x02\x00\x01\x00' ntypes, = struct.unpack_from('