#!/usr/bin/env python # Copyright 2018 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.
"""Wraps ml.exe or ml64.exe and postprocesses the output to be deterministic.
Sets timestamp in .obj file to 0, hence incompatible with link.exe /incremental.
Use by prefixing the ml(64).exe invocation with this script:
python ml.py ml.exe [args...]"""
class Struct(object): """A thin wrapper around the struct module that returns a namedtuple""" def __init__(self, name, *args): """Pass the name of the return type, and then an interleaved list of
format strings as used by the struct module and of field names."""
self.fmt = '<' + ''.join(args[0::2])
self.type = collections.namedtuple(name, args[1::2])
def Subtract(nt, **kwargs): """Subtract(nt, f=2) returns a new namedtuple with 2 subtracted from nt.f""" return nt._replace(**{k: getattr(nt, k) - v for k, v in kwargs.items()})
def MakeDeterministic(objdata): # Takes data produced by ml(64).exe (without any special flags) and # 1. Sets the timestamp to 0 # 2. Strips the .debug$S section (which contains an unwanted absolute path)
# This makes several assumptions about ml's output: # - Section data is in the same order as the corresponding section headers: # section headers preceding the .debug$S section header have their data # preceding the .debug$S section data; likewise for section headers # following the .debug$S section. # - The .debug$S section contains only the absolute path to the obj file and # nothing else, in particular there's only a single entry in the symbol # table referring to the .debug$S section. # - There are no COFF line number entries. # - There's no IMAGE_SYM_CLASS_CLR_TOKEN symbol. # These seem to hold in practice; if they stop holding this script needs to # become smarter.
objdata = array.array('b', objdata) # Writable, e.g. via struct.pack_into.
# Make sure sections in front of .debug$S have their data preceding it. for header in section_headers[:debug_section_index]: assert header.PointerToRawData < debug_offset assert header.PointerToRelocations < debug_offset assert header.PointerToLineNumbers < debug_offset
# Make sure sections after of .debug$S have their data following it. for header in section_headers[debug_section_index + 1:]: # Make sure the .debug$S data is at the very end of section data: assert header.PointerToRawData > debug_offset assert header.PointerToRelocations == 0 assert header.PointerToLineNumbers == 0
# Make sure the first non-empty section's data starts right after the section # headers. for section_header in section_headers: if section_header.PointerToRawData == 0: assert section_header.PointerToRelocations == 0 assert section_header.PointerToLineNumbers == 0 continue assert section_header.PointerToRawData == data_start break
# Make sure the symbol table (and hence, string table) appear after the last # section: assert (coff_header.PointerToSymbolTable >=
section_headers[-1].PointerToRawData + section_headers[-1].SizeOfRawData)
# The symbol table contains a symbol for the no-longer-present .debug$S # section. If we leave it there, lld-link will complain: # # lld-link: error: .debug$S should not refer to non-existent section 5 # # so we need to remove that symbol table entry as well. This shifts symbol # entries around and we need to update symbol table indices in: # - relocations # - line number records (never present) # - one aux symbol entry (IMAGE_SYM_CLASS_CLR_TOKEN; not present in ml output)
SYM = Struct('SYM', '8s', 'Name', 'I', 'Value', 'h', 'SectionNumber', # Note: Signed! 'H', 'Type',
'B', 'StorageClass', 'B', 'NumberOfAuxSymbols')
i = 0
debug_sym = -1 while i < coff_header.NumberOfSymbols:
sym_offset = coff_header.PointerToSymbolTable + i * SYM.size()
sym = SYM.unpack_from(objdata, sym_offset)
# 107 is IMAGE_SYM_CLASS_CLR_TOKEN, which has aux entry "CLR Token # Definition", which contains a symbol index. Check it's never present. assert sym.StorageClass != 107
# Note: sym.SectionNumber is 1-based, debug_section_index is 0-based. if sym.SectionNumber - 1 == debug_section_index: assert debug_sym == -1, 'more than one .debug$S symbol found'
debug_sym = i # Make sure the .debug$S symbol looks like we expect. # In particular, it should have exactly one aux symbol. assert sym.Name == b'.debug$S' assert sym.Value == 0 assert sym.Type == 0 assert sym.StorageClass == 3 assert sym.NumberOfAuxSymbols == 1 elif sym.SectionNumber > debug_section_index:
sym = Subtract(sym, SectionNumber=1)
SYM.pack_into(objdata, sym_offset, sym)
i += 1 + sym.NumberOfAuxSymbols assert debug_sym != -1, '.debug$S symbol not found'
# Note: Usually the .debug$S section is the last, but for files saying # `includelib foo.lib`, like safe_terminate_process.asm in 32-bit builds, # this isn't true: .drectve is after .debug$S.
# Update symbol table indices in relocations. # There are a few processor types that have one or two relocation types # where SymbolTableIndex has a different meaning, but not for x86.
REL = Struct('REL', 'I', 'VirtualAddress', 'I', 'SymbolTableIndex', 'H', 'Type') for header in section_headers[0:debug_section_index]: for j in range(0, header.NumberOfRelocations):
rel_offset = header.PointerToRelocations + j * REL.size()
rel = REL.unpack_from(objdata, rel_offset) assert rel.SymbolTableIndex != debug_sym if rel.SymbolTableIndex > debug_sym:
rel = Subtract(rel, SymbolTableIndex=2)
REL.pack_into(objdata, rel_offset, rel)
# Update symbol table indices in line numbers -- just check they don't exist. for header in section_headers: assert header.NumberOfLineNumbers == 0
# Now that all indices are updated, remove the symbol table entry referring to # .debug$S and its aux entry. del objdata[coff_header.PointerToSymbolTable + debug_sym * SYM.size():
coff_header.PointerToSymbolTable + (debug_sym + 2) * SYM.size()]
# Now we know that it's safe to write out the input data, with just the # timestamp overwritten to 0, the last section header cut out (and the # offsets of all other section headers decremented by the size of that # one section header), and the last section's data cut out. The symbol # table offset needs to be reduced by one section header and the size of # the missing section. # (The COFF spec only requires on-disk sections to be aligned in image files, # for obj files it's not required. If that wasn't the case, deleting slices # if data would not generally be safe.)
# Update section offsets and remove .debug$S section data. for i in range(0, debug_section_index):
header = section_headers[i] if header.SizeOfRawData:
header = Subtract(header, PointerToRawData=SECTIONHEADER.size()) if header.NumberOfRelocations:
header = Subtract(header, PointerToRelocations=SECTIONHEADER.size()) if header.NumberOfLineNumbers:
header = Subtract(header, PointerToLineNumbers=SECTIONHEADER.size())
SECTIONHEADER.pack_into(
objdata, COFFHEADER.size() + i * SECTIONHEADER.size(), header) for i in range(debug_section_index + 1, len(section_headers)):
header = section_headers[i]
shift = SECTIONHEADER.size() + debug_size if header.SizeOfRawData:
header = Subtract(header, PointerToRawData=shift) if header.NumberOfRelocations:
header = Subtract(header, PointerToRelocations=shift) if header.NumberOfLineNumbers:
header = Subtract(header, PointerToLineNumbers=shift)
SECTIONHEADER.pack_into(
objdata, COFFHEADER.size() + i * SECTIONHEADER.size(), header)
del objdata[debug_offset:debug_offset + debug_size]
objfile = None for i in range(1, len(sys.argv)): if sys.argv[i].startswith('/Fo'):
objfile = sys.argv[i][len('/Fo'):] assert objfile, 'failed to find ml output'
with open(objfile, 'rb') as f:
objdata = f.read()
objdata = MakeDeterministic(objdata) with open(objfile, 'wb') as f:
f.write(objdata)
if __name__ == '__main__':
sys.exit(main())
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.25 Sekunden
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.