| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- #!/usr/bin/env python3
- """check_comimage.py -- the image layout is described in exactly one file.
- Any check that reads a linked .COM has to know where its parts are: the entry
- jump is three bytes, the header is sixteen, initmem's first bytes sit at ENT_SZ
- and the header is findable by its own signature. Before tests/comimage.py
- existed that knowledge lived in three places - comtest.py had a find_header,
- the independent checker inside run_com_tests.sh had a second copy of it, and
- check_framedisp.py had a third answer of an entirely different kind: a literal
- RT_SZ that had drifted from the runtime it claimed to describe.
- Three copies are three chances to drift, and drift is SILENT here, because each
- copy is self-consistent on its own terms: every one of them reports confidently
- and only the world disagrees. So the layout lives in one file, and this asserts
- that it stays that way:
- A. comimage.py defines each part exactly once, and imports. Counting rather
- than merely searching is the point: a rule that matches nothing looks
- exactly like a rule that passes (the lesson in tests/rt_exec.py's
- "matched nothing" case), so a deleted definition would otherwise be a green
- report about a subject nobody examined.
- B. no other test source defines any of those names.
- C. every test source that CALLS find_header takes it from comimage rather
- than from some other consumer: `from comtest import find_header' would be
- one helper reached through two of them, and the second of those two is a
- copy waiting to drift. Definitions quoted inside a mutation string are
- not calls, so `def find_header(d):' written as a case's target does not
- count - nonvacuity.sh would otherwise fail this audit for naming the very
- thing it is breaking.
- Scope, stated so this is not read as more than it is
- ----------------------------------------------------
- This cannot see a check that HAND-ROLLS the layout from literals - check_8086
- used to write `hdr = 3 + rt_size', which defines none of these names and would
- have passed here untouched. Numbers written inline are caught a different way:
- check_8086.program_code_region and check_framedisp.main both state the layout
- twice, from two independent sources (the probe-measured runtime size against
- the header's own equation; the entry jump against the header's end), and
- require the two to agree. A disagreement is reported instead of swept, and
- tests/nonvacuity.sh turns each of those red on purpose.
- Usage: check_comimage.py (from shell/)
- """
- import os
- import re
- import sys
- HERE = os.path.dirname(os.path.abspath(__file__))
- COMIMAGE = os.path.join(HERE, "comimage.py")
- # The names that make up the layout of a linked image.
- LAYOUT = ["ENT_SZ", "HDR_SZ", "LOAD_BIAS", "HEAD", "HDR_DS_WORD",
- "HDR_HEAP_WORD"]
- # rt_exec.py states a load bias of its own, and deliberately so: the address
- # space it works in is the boot image IT builds (see build_image, and
- # exec/rtdrv.s, which carries a .set of the same value for the same reason).
- # Those two are independent statements checked against each other by the cases
- # failing if they disagree - and rtdrv.s cannot import a Python module. It is a
- # different artefact's bias, not a copy of this one.
- EXEMPT = {("rt_exec.py", "LOAD_BIAS")}
- DEF_FN = re.compile(r"^\s*def\s+find_header\b")
- IMPORTS = re.compile(r"^\s*(?:import\s+comimage\b|from\s+comimage\s+import\b)")
- # A CALL, not a definition. The lookbehind is what lets nonvacuity.sh quote
- # `def find_header(d):' as its mutation target without this audit reporting the
- # harness itself as a second copy of the helper.
- MENTIONS = re.compile(r"(?<!def )\bfind_header\s*\(")
- def sources():
- """every test source this audit reads, as (relative path, lines)"""
- out = []
- for root, dirs, files in os.walk(HERE):
- dirs[:] = sorted(d for d in dirs if not d.startswith("."))
- for f in sorted(files):
- if f == "comimage.py" or not (f.endswith(".py")
- or f.endswith(".sh")):
- continue
- path = os.path.join(root, f)
- with open(path, "r") as fh:
- out.append((os.path.relpath(path, HERE),
- fh.read().splitlines()))
- return out
- def main():
- problems = []
- # A: the helper exists, is complete, and loads. The import runs
- # comimage's own module-level assertion (initmem's displacements against
- # the header word offsets), so a broken helper is caught here too.
- if not os.path.exists(COMIMAGE):
- print("FAIL: comimage.py does not exist")
- return 1
- try:
- sys.path.insert(0, HERE)
- import comimage # noqa: F401
- except Exception as exc: # noqa: BLE001
- print("FAIL: comimage.py does not import: %s" % exc)
- return 1
- # Each name is matched on its own. A single alternation would count one
- # line for every name in it - six definitions each, from six lines total -
- # which reports the layout as six times duplicated while it is defined once.
- defs = dict((name, re.compile(r"^\s*%s\s*=" % re.escape(name)))
- for name in LAYOUT)
- with open(COMIMAGE) as fh:
- comimage_lines = fh.read().splitlines()
- for name in LAYOUT:
- n = sum(1 for ln in comimage_lines if defs[name].match(ln))
- if n != 1:
- problems.append("comimage.py defines %s %d times, want exactly 1"
- % (name, n))
- nfn = sum(1 for ln in comimage_lines if DEF_FN.match(ln))
- if nfn != 1:
- problems.append("comimage.py defines find_header %d times, want "
- "exactly 1" % nfn)
- # B and C: every other test source.
- for rel, lines in sources():
- for ln in lines:
- if DEF_FN.match(ln):
- problems.append("%s defines its own find_header" % rel)
- for name in LAYOUT:
- if (rel, name) in EXEMPT:
- continue
- if defs[name].match(ln):
- problems.append("%s defines its own %s" % (rel, name))
- mentions = [ln for ln in lines if MENTIONS.search(ln)]
- if mentions and not any(IMPORTS.match(ln) for ln in lines):
- problems.append("%s calls find_header without importing it from "
- "comimage" % rel)
- if problems:
- print("FAIL: the image layout is not described in exactly one file")
- for p in problems:
- print(" - %s" % p)
- return 1
- print("image layout: defined once in comimage.py and imported by every "
- "reader (%d names, %d sources scanned)"
- % (len(LAYOUT) + 1, len(sources()) + 1))
- return 0
- if __name__ == "__main__":
- sys.exit(main())
|