| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  | """Target-specific code generation, parsing, and processing.""" | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  | import asyncio | 
					
						
							|  |  |  | import dataclasses | 
					
						
							|  |  |  | import hashlib | 
					
						
							|  |  |  | import json | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import pathlib | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import tempfile | 
					
						
							|  |  |  | import typing | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import _llvm | 
					
						
							|  |  |  | import _schema | 
					
						
							|  |  |  | import _stencils | 
					
						
							|  |  |  | import _writer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if sys.version_info < (3, 11): | 
					
						
							|  |  |  |     raise RuntimeError("Building the JIT compiler requires Python 3.11 or newer!") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TOOLS_JIT_BUILD = pathlib.Path(__file__).resolve() | 
					
						
							|  |  |  | TOOLS_JIT = TOOLS_JIT_BUILD.parent | 
					
						
							|  |  |  | TOOLS = TOOLS_JIT.parent | 
					
						
							|  |  |  | CPYTHON = TOOLS.parent | 
					
						
							|  |  |  | PYTHON_EXECUTOR_CASES_C_H = CPYTHON / "Python" / "executor_cases.c.h" | 
					
						
							|  |  |  | TOOLS_JIT_TEMPLATE_C = TOOLS_JIT / "template.c" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _S = typing.TypeVar("_S", _schema.COFFSection, _schema.ELFSection, _schema.MachOSection) | 
					
						
							|  |  |  | _R = typing.TypeVar( | 
					
						
							|  |  |  |     "_R", _schema.COFFRelocation, _schema.ELFRelocation, _schema.MachORelocation | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @dataclasses.dataclass | 
					
						
							|  |  |  | class _Target(typing.Generic[_S, _R]): | 
					
						
							|  |  |  |     triple: str | 
					
						
							|  |  |  |     _: dataclasses.KW_ONLY | 
					
						
							|  |  |  |     alignment: int = 1 | 
					
						
							| 
									
										
										
										
											2024-02-26 08:32:44 -08:00
										 |  |  |     args: typing.Sequence[str] = () | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |     prefix: str = "" | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |     stable: bool = False | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |     debug: bool = False | 
					
						
							|  |  |  |     verbose: bool = False | 
					
						
							| 
									
										
										
										
											2024-10-02 20:07:20 +01:00
										 |  |  |     known_symbols: dict[str, int] = dataclasses.field(default_factory=dict) | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _compute_digest(self, out: pathlib.Path) -> str: | 
					
						
							|  |  |  |         hasher = hashlib.sha256() | 
					
						
							|  |  |  |         hasher.update(self.triple.encode()) | 
					
						
							| 
									
										
										
										
											2024-02-22 10:22:23 -08:00
										 |  |  |         hasher.update(self.debug.to_bytes()) | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |         # These dependencies are also reflected in _JITSources in regen.targets: | 
					
						
							|  |  |  |         hasher.update(PYTHON_EXECUTOR_CASES_C_H.read_bytes()) | 
					
						
							|  |  |  |         hasher.update((out / "pyconfig.h").read_bytes()) | 
					
						
							|  |  |  |         for dirpath, _, filenames in sorted(os.walk(TOOLS_JIT)): | 
					
						
							|  |  |  |             for filename in filenames: | 
					
						
							|  |  |  |                 hasher.update(pathlib.Path(dirpath, filename).read_bytes()) | 
					
						
							|  |  |  |         return hasher.hexdigest() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async def _parse(self, path: pathlib.Path) -> _stencils.StencilGroup: | 
					
						
							|  |  |  |         group = _stencils.StencilGroup() | 
					
						
							|  |  |  |         args = ["--disassemble", "--reloc", f"{path}"] | 
					
						
							|  |  |  |         output = await _llvm.maybe_run("llvm-objdump", args, echo=self.verbose) | 
					
						
							|  |  |  |         if output is not None: | 
					
						
							| 
									
										
										
										
											2024-11-24 14:42:50 -08:00
										 |  |  |             # Make sure that full paths don't leak out (for reproducibility): | 
					
						
							|  |  |  |             long, short = str(path), str(path.name) | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             group.code.disassembly.extend( | 
					
						
							| 
									
										
										
										
											2024-11-24 14:42:50 -08:00
										 |  |  |                 line.expandtabs().strip().replace(long, short) | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |                 for line in output.splitlines() | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         args = [ | 
					
						
							|  |  |  |             "--elf-output-style=JSON", | 
					
						
							|  |  |  |             "--expand-relocs", | 
					
						
							|  |  |  |             # "--pretty-print", | 
					
						
							|  |  |  |             "--section-data", | 
					
						
							|  |  |  |             "--section-relocations", | 
					
						
							|  |  |  |             "--section-symbols", | 
					
						
							|  |  |  |             "--sections", | 
					
						
							|  |  |  |             f"{path}", | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         output = await _llvm.run("llvm-readobj", args, echo=self.verbose) | 
					
						
							|  |  |  |         # --elf-output-style=JSON is only *slightly* broken on Mach-O... | 
					
						
							|  |  |  |         output = output.replace("PrivateExtern\n", "\n") | 
					
						
							|  |  |  |         output = output.replace("Extern\n", "\n") | 
					
						
							|  |  |  |         # ...and also COFF: | 
					
						
							|  |  |  |         output = output[output.index("[", 1, None) :] | 
					
						
							|  |  |  |         output = output[: output.rindex("]", None, -1) + 1] | 
					
						
							|  |  |  |         sections: list[dict[typing.Literal["Section"], _S]] = json.loads(output) | 
					
						
							|  |  |  |         for wrapped_section in sections: | 
					
						
							|  |  |  |             self._handle_section(wrapped_section["Section"], group) | 
					
						
							| 
									
										
										
										
											2024-10-30 12:03:31 -07:00
										 |  |  |         assert group.symbols["_JIT_ENTRY"] == (_stencils.HoleValue.CODE, 0) | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |         if group.data.body: | 
					
						
							|  |  |  |             line = f"0: {str(bytes(group.data.body)).removeprefix('b')}" | 
					
						
							|  |  |  |             group.data.disassembly.append(line) | 
					
						
							|  |  |  |         return group | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _handle_section(self, section: _S, group: _stencils.StencilGroup) -> None: | 
					
						
							|  |  |  |         raise NotImplementedError(type(self)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _handle_relocation( | 
					
						
							| 
									
										
										
										
											2025-02-17 10:55:30 +03:00
										 |  |  |         self, base: int, relocation: _R, raw: bytes | bytearray | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |     ) -> _stencils.Hole: | 
					
						
							|  |  |  |         raise NotImplementedError(type(self)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async def _compile( | 
					
						
							|  |  |  |         self, opname: str, c: pathlib.Path, tempdir: pathlib.Path | 
					
						
							|  |  |  |     ) -> _stencils.StencilGroup: | 
					
						
							|  |  |  |         o = tempdir / f"{opname}.o" | 
					
						
							|  |  |  |         args = [ | 
					
						
							|  |  |  |             f"--target={self.triple}", | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |             "-DPy_BUILD_CORE_MODULE", | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             "-D_DEBUG" if self.debug else "-DNDEBUG", | 
					
						
							|  |  |  |             f"-D_JIT_OPCODE={opname}", | 
					
						
							|  |  |  |             "-D_PyJIT_ACTIVE", | 
					
						
							|  |  |  |             "-D_Py_JIT", | 
					
						
							|  |  |  |             "-I.", | 
					
						
							|  |  |  |             f"-I{CPYTHON / 'Include'}", | 
					
						
							|  |  |  |             f"-I{CPYTHON / 'Include' / 'internal'}", | 
					
						
							|  |  |  |             f"-I{CPYTHON / 'Include' / 'internal' / 'mimalloc'}", | 
					
						
							|  |  |  |             f"-I{CPYTHON / 'Python'}", | 
					
						
							| 
									
										
										
										
											2024-10-30 12:03:31 -07:00
										 |  |  |             f"-I{CPYTHON / 'Tools' / 'jit'}", | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             "-O3", | 
					
						
							|  |  |  |             "-c", | 
					
						
							| 
									
										
										
										
											2024-11-24 14:42:50 -08:00
										 |  |  |             # Shorten full absolute file paths in the generated code (like the | 
					
						
							|  |  |  |             # __FILE__ macro and assert failure messages) for reproducibility: | 
					
						
							|  |  |  |             f"-ffile-prefix-map={CPYTHON}=.", | 
					
						
							|  |  |  |             f"-ffile-prefix-map={tempdir}=.", | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |             # This debug info isn't necessary, and bloats out the JIT'ed code. | 
					
						
							|  |  |  |             # We *may* be able to re-enable this, process it, and JIT it for a | 
					
						
							|  |  |  |             # nicer debugging experience... but that needs a lot more research: | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             "-fno-asynchronous-unwind-tables", | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |             # Don't call built-in functions that we can't find or patch: | 
					
						
							| 
									
										
										
										
											2024-02-22 10:22:23 -08:00
										 |  |  |             "-fno-builtin", | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |             # Emit relaxable 64-bit calls/jumps, so we don't have to worry about | 
					
						
							|  |  |  |             # about emitting in-range trampolines for out-of-range targets. | 
					
						
							|  |  |  |             # We can probably remove this and emit trampolines in the future: | 
					
						
							| 
									
										
										
										
											2024-02-26 08:32:44 -08:00
										 |  |  |             "-fno-plt", | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |             # Don't call stack-smashing canaries that we can't find or patch: | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             "-fno-stack-protector", | 
					
						
							|  |  |  |             "-std=c11", | 
					
						
							| 
									
										
										
										
											2024-10-30 12:03:31 -07:00
										 |  |  |             "-o", | 
					
						
							|  |  |  |             f"{o}", | 
					
						
							|  |  |  |             f"{c}", | 
					
						
							| 
									
										
										
										
											2024-02-26 08:32:44 -08:00
										 |  |  |             *self.args, | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |         ] | 
					
						
							| 
									
										
										
										
											2024-10-30 12:03:31 -07:00
										 |  |  |         await _llvm.run("clang", args, echo=self.verbose) | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |         return await self._parse(o) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async def _build_stencils(self) -> dict[str, _stencils.StencilGroup]: | 
					
						
							|  |  |  |         generated_cases = PYTHON_EXECUTOR_CASES_C_H.read_text() | 
					
						
							| 
									
										
										
										
											2024-08-14 07:53:46 -07:00
										 |  |  |         cases_and_opnames = sorted( | 
					
						
							|  |  |  |             re.findall( | 
					
						
							|  |  |  |                 r"\n {8}(case (\w+): \{\n.*?\n {8}\})", generated_cases, flags=re.DOTALL | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |         tasks = [] | 
					
						
							|  |  |  |         with tempfile.TemporaryDirectory() as tempdir: | 
					
						
							|  |  |  |             work = pathlib.Path(tempdir).resolve() | 
					
						
							|  |  |  |             async with asyncio.TaskGroup() as group: | 
					
						
							| 
									
										
										
										
											2024-11-05 15:26:46 -08:00
										 |  |  |                 coro = self._compile("shim", TOOLS_JIT / "shim.c", work) | 
					
						
							|  |  |  |                 tasks.append(group.create_task(coro, name="shim")) | 
					
						
							| 
									
										
										
										
											2024-08-14 07:53:46 -07:00
										 |  |  |                 template = TOOLS_JIT_TEMPLATE_C.read_text() | 
					
						
							|  |  |  |                 for case, opname in cases_and_opnames: | 
					
						
							|  |  |  |                     # Write out a copy of the template with *only* this case | 
					
						
							|  |  |  |                     # inserted. This is about twice as fast as #include'ing all | 
					
						
							|  |  |  |                     # of executor_cases.c.h each time we compile (since the C | 
					
						
							|  |  |  |                     # compiler wastes a bunch of time parsing the dead code for | 
					
						
							|  |  |  |                     # all of the other cases): | 
					
						
							|  |  |  |                     c = work / f"{opname}.c" | 
					
						
							|  |  |  |                     c.write_text(template.replace("CASE", case)) | 
					
						
							|  |  |  |                     coro = self._compile(opname, c, work) | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |                     tasks.append(group.create_task(coro, name=opname)) | 
					
						
							| 
									
										
										
										
											2024-11-24 14:42:50 -08:00
										 |  |  |         stencil_groups = {task.get_name(): task.result() for task in tasks} | 
					
						
							|  |  |  |         for stencil_group in stencil_groups.values(): | 
					
						
							|  |  |  |             stencil_group.process_relocations( | 
					
						
							|  |  |  |                 known_symbols=self.known_symbols, alignment=self.alignment | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         return stencil_groups | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |     def build( | 
					
						
							|  |  |  |         self, out: pathlib.Path, *, comment: str = "", force: bool = False | 
					
						
							|  |  |  |     ) -> None: | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |         """Build jit_stencils.h in the given directory.""" | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |         if not self.stable: | 
					
						
							|  |  |  |             warning = f"JIT support for {self.triple} is still experimental!" | 
					
						
							|  |  |  |             request = "Please report any issues you encounter.".center(len(warning)) | 
					
						
							|  |  |  |             outline = "=" * len(warning) | 
					
						
							|  |  |  |             print("\n".join(["", outline, warning, request, outline, ""])) | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |         digest = f"// {self._compute_digest(out)}\n" | 
					
						
							|  |  |  |         jit_stencils = out / "jit_stencils.h" | 
					
						
							|  |  |  |         if ( | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |             not force | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             and jit_stencils.exists() | 
					
						
							|  |  |  |             and jit_stencils.read_text().startswith(digest) | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         stencil_groups = asyncio.run(self._build_stencils()) | 
					
						
							| 
									
										
										
										
											2024-05-16 12:11:42 -04:00
										 |  |  |         jit_stencils_new = out / "jit_stencils.h.new" | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             with jit_stencils_new.open("w") as file: | 
					
						
							|  |  |  |                 file.write(digest) | 
					
						
							|  |  |  |                 if comment: | 
					
						
							|  |  |  |                     file.write(f"// {comment}\n") | 
					
						
							|  |  |  |                 file.write("\n") | 
					
						
							| 
									
										
										
										
											2024-10-02 20:07:20 +01:00
										 |  |  |                 for line in _writer.dump(stencil_groups, self.known_symbols): | 
					
						
							| 
									
										
										
										
											2024-05-16 12:11:42 -04:00
										 |  |  |                     file.write(f"{line}\n") | 
					
						
							| 
									
										
										
										
											2024-08-06 01:10:40 +02:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 jit_stencils_new.replace(jit_stencils) | 
					
						
							|  |  |  |             except FileNotFoundError: | 
					
						
							|  |  |  |                 # another process probably already moved the file | 
					
						
							|  |  |  |                 if not jit_stencils.is_file(): | 
					
						
							|  |  |  |                     raise | 
					
						
							| 
									
										
										
										
											2024-05-16 12:11:42 -04:00
										 |  |  |         finally: | 
					
						
							|  |  |  |             jit_stencils_new.unlink(missing_ok=True) | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _COFF( | 
					
						
							|  |  |  |     _Target[_schema.COFFSection, _schema.COFFRelocation] | 
					
						
							|  |  |  | ):  # pylint: disable = too-few-public-methods | 
					
						
							|  |  |  |     def _handle_section( | 
					
						
							|  |  |  |         self, section: _schema.COFFSection, group: _stencils.StencilGroup | 
					
						
							|  |  |  |     ) -> None: | 
					
						
							|  |  |  |         flags = {flag["Name"] for flag in section["Characteristics"]["Flags"]} | 
					
						
							|  |  |  |         if "SectionData" in section: | 
					
						
							|  |  |  |             section_data_bytes = section["SectionData"]["Bytes"] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # Zeroed BSS data, seen with printf debugging calls: | 
					
						
							|  |  |  |             section_data_bytes = [0] * section["RawDataSize"] | 
					
						
							|  |  |  |         if "IMAGE_SCN_MEM_EXECUTE" in flags: | 
					
						
							|  |  |  |             value = _stencils.HoleValue.CODE | 
					
						
							|  |  |  |             stencil = group.code | 
					
						
							|  |  |  |         elif "IMAGE_SCN_MEM_READ" in flags: | 
					
						
							|  |  |  |             value = _stencils.HoleValue.DATA | 
					
						
							|  |  |  |             stencil = group.data | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         base = len(stencil.body) | 
					
						
							|  |  |  |         group.symbols[section["Number"]] = value, base | 
					
						
							|  |  |  |         stencil.body.extend(section_data_bytes) | 
					
						
							|  |  |  |         for wrapped_symbol in section["Symbols"]: | 
					
						
							|  |  |  |             symbol = wrapped_symbol["Symbol"] | 
					
						
							|  |  |  |             offset = base + symbol["Value"] | 
					
						
							|  |  |  |             name = symbol["Name"] | 
					
						
							|  |  |  |             name = name.removeprefix(self.prefix) | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |             if name not in group.symbols: | 
					
						
							|  |  |  |                 group.symbols[name] = value, offset | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |         for wrapped_relocation in section["Relocations"]: | 
					
						
							|  |  |  |             relocation = wrapped_relocation["Relocation"] | 
					
						
							|  |  |  |             hole = self._handle_relocation(base, relocation, stencil.body) | 
					
						
							|  |  |  |             stencil.holes.append(hole) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |     def _unwrap_dllimport(self, name: str) -> tuple[_stencils.HoleValue, str | None]: | 
					
						
							|  |  |  |         if name.startswith("__imp_"): | 
					
						
							|  |  |  |             name = name.removeprefix("__imp_") | 
					
						
							|  |  |  |             name = name.removeprefix(self.prefix) | 
					
						
							|  |  |  |             return _stencils.HoleValue.GOT, name | 
					
						
							|  |  |  |         name = name.removeprefix(self.prefix) | 
					
						
							|  |  |  |         return _stencils.symbol_to_value(name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |     def _handle_relocation( | 
					
						
							| 
									
										
										
										
											2025-02-17 10:55:30 +03:00
										 |  |  |         self, | 
					
						
							|  |  |  |         base: int, | 
					
						
							|  |  |  |         relocation: _schema.COFFRelocation, | 
					
						
							|  |  |  |         raw: bytes | bytearray, | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |     ) -> _stencils.Hole: | 
					
						
							|  |  |  |         match relocation: | 
					
						
							|  |  |  |             case { | 
					
						
							|  |  |  |                 "Offset": offset, | 
					
						
							|  |  |  |                 "Symbol": s, | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                 "Type": {"Name": "IMAGE_REL_I386_DIR32" as kind}, | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             }: | 
					
						
							|  |  |  |                 offset += base | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |                 value, symbol = self._unwrap_dllimport(s) | 
					
						
							|  |  |  |                 addend = int.from_bytes(raw[offset : offset + 4], "little") | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             case { | 
					
						
							|  |  |  |                 "Offset": offset, | 
					
						
							|  |  |  |                 "Symbol": s, | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |                 "Type": { | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                     "Name": "IMAGE_REL_AMD64_REL32" | "IMAGE_REL_I386_REL32" as kind | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |                 }, | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             }: | 
					
						
							|  |  |  |                 offset += base | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |                 value, symbol = self._unwrap_dllimport(s) | 
					
						
							|  |  |  |                 addend = ( | 
					
						
							|  |  |  |                     int.from_bytes(raw[offset : offset + 4], "little", signed=True) - 4 | 
					
						
							|  |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2024-03-04 10:16:56 -08:00
										 |  |  |             case { | 
					
						
							|  |  |  |                 "Offset": offset, | 
					
						
							|  |  |  |                 "Symbol": s, | 
					
						
							|  |  |  |                 "Type": { | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                     "Name": "IMAGE_REL_ARM64_BRANCH26" | 
					
						
							| 
									
										
										
										
											2024-03-04 10:16:56 -08:00
										 |  |  |                     | "IMAGE_REL_ARM64_PAGEBASE_REL21" | 
					
						
							|  |  |  |                     | "IMAGE_REL_ARM64_PAGEOFFSET_12A" | 
					
						
							|  |  |  |                     | "IMAGE_REL_ARM64_PAGEOFFSET_12L" as kind | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |             }: | 
					
						
							|  |  |  |                 offset += base | 
					
						
							|  |  |  |                 value, symbol = self._unwrap_dllimport(s) | 
					
						
							|  |  |  |                 addend = 0 | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             case _: | 
					
						
							|  |  |  |                 raise NotImplementedError(relocation) | 
					
						
							|  |  |  |         return _stencils.Hole(offset, kind, value, symbol, addend) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _ELF( | 
					
						
							|  |  |  |     _Target[_schema.ELFSection, _schema.ELFRelocation] | 
					
						
							|  |  |  | ):  # pylint: disable = too-few-public-methods | 
					
						
							|  |  |  |     def _handle_section( | 
					
						
							|  |  |  |         self, section: _schema.ELFSection, group: _stencils.StencilGroup | 
					
						
							|  |  |  |     ) -> None: | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |         section_type = section["Type"]["Name"] | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |         flags = {flag["Name"] for flag in section["Flags"]["Flags"]} | 
					
						
							|  |  |  |         if section_type == "SHT_RELA": | 
					
						
							|  |  |  |             assert "SHF_INFO_LINK" in flags, flags | 
					
						
							|  |  |  |             assert not section["Symbols"] | 
					
						
							|  |  |  |             value, base = group.symbols[section["Info"]] | 
					
						
							|  |  |  |             if value is _stencils.HoleValue.CODE: | 
					
						
							|  |  |  |                 stencil = group.code | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 assert value is _stencils.HoleValue.DATA | 
					
						
							|  |  |  |                 stencil = group.data | 
					
						
							|  |  |  |             for wrapped_relocation in section["Relocations"]: | 
					
						
							|  |  |  |                 relocation = wrapped_relocation["Relocation"] | 
					
						
							|  |  |  |                 hole = self._handle_relocation(base, relocation, stencil.body) | 
					
						
							|  |  |  |                 stencil.holes.append(hole) | 
					
						
							|  |  |  |         elif section_type == "SHT_PROGBITS": | 
					
						
							|  |  |  |             if "SHF_ALLOC" not in flags: | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             if "SHF_EXECINSTR" in flags: | 
					
						
							|  |  |  |                 value = _stencils.HoleValue.CODE | 
					
						
							|  |  |  |                 stencil = group.code | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 value = _stencils.HoleValue.DATA | 
					
						
							|  |  |  |                 stencil = group.data | 
					
						
							|  |  |  |             group.symbols[section["Index"]] = value, len(stencil.body) | 
					
						
							|  |  |  |             for wrapped_symbol in section["Symbols"]: | 
					
						
							|  |  |  |                 symbol = wrapped_symbol["Symbol"] | 
					
						
							|  |  |  |                 offset = len(stencil.body) + symbol["Value"] | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                 name = symbol["Name"]["Name"] | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |                 name = name.removeprefix(self.prefix) | 
					
						
							|  |  |  |                 group.symbols[name] = value, offset | 
					
						
							|  |  |  |             stencil.body.extend(section["SectionData"]["Bytes"]) | 
					
						
							|  |  |  |             assert not section["Relocations"] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             assert section_type in { | 
					
						
							|  |  |  |                 "SHT_GROUP", | 
					
						
							|  |  |  |                 "SHT_LLVM_ADDRSIG", | 
					
						
							| 
									
										
										
										
											2024-05-13 23:37:02 +02:00
										 |  |  |                 "SHT_NOTE", | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |                 "SHT_NULL", | 
					
						
							|  |  |  |                 "SHT_STRTAB", | 
					
						
							|  |  |  |                 "SHT_SYMTAB", | 
					
						
							|  |  |  |             }, section_type | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _handle_relocation( | 
					
						
							| 
									
										
										
										
											2025-02-17 10:55:30 +03:00
										 |  |  |         self, | 
					
						
							|  |  |  |         base: int, | 
					
						
							|  |  |  |         relocation: _schema.ELFRelocation, | 
					
						
							|  |  |  |         raw: bytes | bytearray, | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |     ) -> _stencils.Hole: | 
					
						
							| 
									
										
										
										
											2024-02-26 08:32:44 -08:00
										 |  |  |         symbol: str | None | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |         match relocation: | 
					
						
							| 
									
										
										
										
											2024-02-26 08:32:44 -08:00
										 |  |  |             case { | 
					
						
							|  |  |  |                 "Addend": addend, | 
					
						
							|  |  |  |                 "Offset": offset, | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                 "Symbol": {"Name": s}, | 
					
						
							| 
									
										
										
										
											2024-02-26 08:32:44 -08:00
										 |  |  |                 "Type": { | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                     "Name": "R_AARCH64_ADR_GOT_PAGE" | 
					
						
							| 
									
										
										
										
											2024-02-26 08:32:44 -08:00
										 |  |  |                     | "R_AARCH64_LD64_GOT_LO12_NC" | 
					
						
							|  |  |  |                     | "R_X86_64_GOTPCREL" | 
					
						
							|  |  |  |                     | "R_X86_64_GOTPCRELX" | 
					
						
							|  |  |  |                     | "R_X86_64_REX_GOTPCRELX" as kind | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |             }: | 
					
						
							|  |  |  |                 offset += base | 
					
						
							|  |  |  |                 s = s.removeprefix(self.prefix) | 
					
						
							|  |  |  |                 value, symbol = _stencils.HoleValue.GOT, s | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             case { | 
					
						
							|  |  |  |                 "Addend": addend, | 
					
						
							|  |  |  |                 "Offset": offset, | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                 "Symbol": {"Name": s}, | 
					
						
							|  |  |  |                 "Type": {"Name": kind}, | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             }: | 
					
						
							|  |  |  |                 offset += base | 
					
						
							|  |  |  |                 s = s.removeprefix(self.prefix) | 
					
						
							|  |  |  |                 value, symbol = _stencils.symbol_to_value(s) | 
					
						
							|  |  |  |             case _: | 
					
						
							|  |  |  |                 raise NotImplementedError(relocation) | 
					
						
							|  |  |  |         return _stencils.Hole(offset, kind, value, symbol, addend) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _MachO( | 
					
						
							|  |  |  |     _Target[_schema.MachOSection, _schema.MachORelocation] | 
					
						
							|  |  |  | ):  # pylint: disable = too-few-public-methods | 
					
						
							|  |  |  |     def _handle_section( | 
					
						
							|  |  |  |         self, section: _schema.MachOSection, group: _stencils.StencilGroup | 
					
						
							|  |  |  |     ) -> None: | 
					
						
							|  |  |  |         assert section["Address"] >= len(group.code.body) | 
					
						
							|  |  |  |         assert "SectionData" in section | 
					
						
							|  |  |  |         flags = {flag["Name"] for flag in section["Attributes"]["Flags"]} | 
					
						
							|  |  |  |         name = section["Name"]["Value"] | 
					
						
							|  |  |  |         name = name.removeprefix(self.prefix) | 
					
						
							| 
									
										
										
										
											2024-02-22 10:22:23 -08:00
										 |  |  |         if "Debug" in flags: | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |         if "SomeInstructions" in flags: | 
					
						
							|  |  |  |             value = _stencils.HoleValue.CODE | 
					
						
							|  |  |  |             stencil = group.code | 
					
						
							|  |  |  |             start_address = 0 | 
					
						
							|  |  |  |             group.symbols[name] = value, section["Address"] - start_address | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             value = _stencils.HoleValue.DATA | 
					
						
							|  |  |  |             stencil = group.data | 
					
						
							|  |  |  |             start_address = len(group.code.body) | 
					
						
							|  |  |  |             group.symbols[name] = value, len(group.code.body) | 
					
						
							|  |  |  |         base = section["Address"] - start_address | 
					
						
							|  |  |  |         group.symbols[section["Index"]] = value, base | 
					
						
							|  |  |  |         stencil.body.extend( | 
					
						
							|  |  |  |             [0] * (section["Address"] - len(group.code.body) - len(group.data.body)) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         stencil.body.extend(section["SectionData"]["Bytes"]) | 
					
						
							|  |  |  |         assert "Symbols" in section | 
					
						
							|  |  |  |         for wrapped_symbol in section["Symbols"]: | 
					
						
							|  |  |  |             symbol = wrapped_symbol["Symbol"] | 
					
						
							|  |  |  |             offset = symbol["Value"] - start_address | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |             name = symbol["Name"]["Name"] | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             name = name.removeprefix(self.prefix) | 
					
						
							|  |  |  |             group.symbols[name] = value, offset | 
					
						
							|  |  |  |         assert "Relocations" in section | 
					
						
							|  |  |  |         for wrapped_relocation in section["Relocations"]: | 
					
						
							|  |  |  |             relocation = wrapped_relocation["Relocation"] | 
					
						
							|  |  |  |             hole = self._handle_relocation(base, relocation, stencil.body) | 
					
						
							|  |  |  |             stencil.holes.append(hole) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _handle_relocation( | 
					
						
							| 
									
										
										
										
											2025-02-17 10:55:30 +03:00
										 |  |  |         self, | 
					
						
							|  |  |  |         base: int, | 
					
						
							|  |  |  |         relocation: _schema.MachORelocation, | 
					
						
							|  |  |  |         raw: bytes | bytearray, | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |     ) -> _stencils.Hole: | 
					
						
							|  |  |  |         symbol: str | None | 
					
						
							|  |  |  |         match relocation: | 
					
						
							|  |  |  |             case { | 
					
						
							|  |  |  |                 "Offset": offset, | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                 "Symbol": {"Name": s}, | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |                 "Type": { | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                     "Name": "ARM64_RELOC_GOT_LOAD_PAGE21" | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |                     | "ARM64_RELOC_GOT_LOAD_PAGEOFF12" as kind | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |             }: | 
					
						
							|  |  |  |                 offset += base | 
					
						
							|  |  |  |                 s = s.removeprefix(self.prefix) | 
					
						
							|  |  |  |                 value, symbol = _stencils.HoleValue.GOT, s | 
					
						
							|  |  |  |                 addend = 0 | 
					
						
							| 
									
										
										
										
											2024-02-26 08:32:44 -08:00
										 |  |  |             case { | 
					
						
							|  |  |  |                 "Offset": offset, | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                 "Symbol": {"Name": s}, | 
					
						
							|  |  |  |                 "Type": {"Name": "X86_64_RELOC_GOT" | "X86_64_RELOC_GOT_LOAD" as kind}, | 
					
						
							| 
									
										
										
										
											2024-02-26 08:32:44 -08:00
										 |  |  |             }: | 
					
						
							|  |  |  |                 offset += base | 
					
						
							|  |  |  |                 s = s.removeprefix(self.prefix) | 
					
						
							|  |  |  |                 value, symbol = _stencils.HoleValue.GOT, s | 
					
						
							|  |  |  |                 addend = ( | 
					
						
							|  |  |  |                     int.from_bytes(raw[offset : offset + 4], "little", signed=True) - 4 | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             case { | 
					
						
							|  |  |  |                 "Offset": offset, | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                 "Section": {"Name": s}, | 
					
						
							|  |  |  |                 "Type": {"Name": "X86_64_RELOC_SIGNED" as kind}, | 
					
						
							| 
									
										
										
										
											2024-02-26 08:32:44 -08:00
										 |  |  |             } | { | 
					
						
							|  |  |  |                 "Offset": offset, | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                 "Symbol": {"Name": s}, | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |                 "Type": {"Name": "X86_64_RELOC_BRANCH" | "X86_64_RELOC_SIGNED" as kind}, | 
					
						
							| 
									
										
										
										
											2024-02-26 08:32:44 -08:00
										 |  |  |             }: | 
					
						
							|  |  |  |                 offset += base | 
					
						
							|  |  |  |                 s = s.removeprefix(self.prefix) | 
					
						
							|  |  |  |                 value, symbol = _stencils.symbol_to_value(s) | 
					
						
							|  |  |  |                 addend = ( | 
					
						
							|  |  |  |                     int.from_bytes(raw[offset : offset + 4], "little", signed=True) - 4 | 
					
						
							|  |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             case { | 
					
						
							|  |  |  |                 "Offset": offset, | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                 "Section": {"Name": s}, | 
					
						
							|  |  |  |                 "Type": {"Name": kind}, | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             } | { | 
					
						
							|  |  |  |                 "Offset": offset, | 
					
						
							| 
									
										
										
										
											2024-04-29 14:09:16 -07:00
										 |  |  |                 "Symbol": {"Name": s}, | 
					
						
							|  |  |  |                 "Type": {"Name": kind}, | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |             }: | 
					
						
							|  |  |  |                 offset += base | 
					
						
							|  |  |  |                 s = s.removeprefix(self.prefix) | 
					
						
							|  |  |  |                 value, symbol = _stencils.symbol_to_value(s) | 
					
						
							|  |  |  |                 addend = 0 | 
					
						
							|  |  |  |             case _: | 
					
						
							|  |  |  |                 raise NotImplementedError(relocation) | 
					
						
							|  |  |  |         return _stencils.Hole(offset, kind, value, symbol, addend) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_target(host: str) -> _COFF | _ELF | _MachO: | 
					
						
							|  |  |  |     """Build a _Target for the given host "triple" and options.""" | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |     target: _COFF | _ELF | _MachO | 
					
						
							| 
									
										
										
										
											2024-01-28 18:48:48 -08:00
										 |  |  |     if re.fullmatch(r"aarch64-apple-darwin.*", host): | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |         target = _MachO(host, alignment=8, prefix="_") | 
					
						
							|  |  |  |     elif re.fullmatch(r"aarch64-pc-windows-msvc", host): | 
					
						
							| 
									
										
										
										
											2024-03-04 10:16:56 -08:00
										 |  |  |         args = ["-fms-runtime-lib=dll"] | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |         target = _COFF(host, alignment=8, args=args) | 
					
						
							|  |  |  |     elif re.fullmatch(r"aarch64-.*-linux-gnu", host): | 
					
						
							| 
									
										
										
										
											2024-10-02 02:40:59 -07:00
										 |  |  |         args = [ | 
					
						
							|  |  |  |             "-fpic", | 
					
						
							|  |  |  |             # On aarch64 Linux, intrinsics were being emitted and this flag | 
					
						
							|  |  |  |             # was required to disable them. | 
					
						
							|  |  |  |             "-mno-outline-atomics", | 
					
						
							|  |  |  |         ] | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |         target = _ELF(host, alignment=8, args=args) | 
					
						
							|  |  |  |     elif re.fullmatch(r"i686-pc-windows-msvc", host): | 
					
						
							| 
									
										
										
										
											2024-10-30 12:03:31 -07:00
										 |  |  |         args = [ | 
					
						
							|  |  |  |             "-DPy_NO_ENABLE_SHARED", | 
					
						
							|  |  |  |             # __attribute__((preserve_none)) is not supported | 
					
						
							|  |  |  |             "-Wno-ignored-attributes", | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         target = _COFF(host, args=args, prefix="_") | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |     elif re.fullmatch(r"x86_64-apple-darwin.*", host): | 
					
						
							| 
									
										
										
										
											2024-10-30 12:03:31 -07:00
										 |  |  |         target = _MachO(host, prefix="_") | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |     elif re.fullmatch(r"x86_64-pc-windows-msvc", host): | 
					
						
							| 
									
										
										
										
											2024-02-29 08:11:28 -08:00
										 |  |  |         args = ["-fms-runtime-lib=dll"] | 
					
						
							| 
									
										
										
										
											2024-10-30 12:03:31 -07:00
										 |  |  |         target = _COFF(host, args=args) | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |     elif re.fullmatch(r"x86_64-.*-linux-gnu", host): | 
					
						
							| 
									
										
										
										
											2025-03-05 11:54:33 -08:00
										 |  |  |         args = ["-fno-pic", "-mcmodel=medium", "-mlarge-data-threshold=0"] | 
					
						
							| 
									
										
										
										
											2024-10-30 12:03:31 -07:00
										 |  |  |         target = _ELF(host, args=args) | 
					
						
							| 
									
										
										
										
											2024-05-01 14:35:49 -07:00
										 |  |  |     else: | 
					
						
							|  |  |  |         raise ValueError(host) | 
					
						
							|  |  |  |     return target |