diff --git a/compiler/src/main/pascal/Blaise.pas b/compiler/src/main/pascal/Blaise.pas index 5f610d8..0c0dc21 100644 --- a/compiler/src/main/pascal/Blaise.pas +++ b/compiler/src/main/pascal/Blaise.pas @@ -483,8 +483,6 @@ begin try Args.Add('-o'); Args.Add(AOutputFile); - if (AOPDFAsmFile <> '') or AOpdfDebug then - Args.Add('-no-pie'); { OPDF addresses are absolute; PIE relocation breaks them } Args.Add(AsmFile); if (AOPDFAsmFile <> '') and FileExists(AOPDFAsmFile) then Args.Add(AOPDFAsmFile); @@ -532,8 +530,6 @@ begin try Args.Add('-o'); Args.Add(AOutputFile); - if (AOPDFAsmFile <> '') or AOpdfDebug then - Args.Add('-no-pie'); { OPDF addresses are absolute; PIE relocation breaks them } Args.Add(AAsmFile); if (AOPDFAsmFile <> '') and FileExists(AOPDFAsmFile) then Args.Add(AOPDFAsmFile); diff --git a/docs/future-improvements.adoc b/docs/future-improvements.adoc index 81dcd6d..2618f41 100644 --- a/docs/future-improvements.adoc +++ b/docs/future-improvements.adoc @@ -29,11 +29,6 @@ prioritise them. == Language -=== Require `()` on all zero-argument function/procedure/method calls - -*Status:* Implemented. See `docs/language-rationale.adoc` §_Mandatory -parentheses on zero-argument calls_ for the design rationale. - === Procedural types - reference to function/procedure (anonymous methods) @@ -232,44 +227,6 @@ replacements. `Extended` maps to `Double` with a precision-loss warning. *Effort.* Medium — primarily RTL/stdlib work once operator overloading is in place. No compiler changes needed. -== Debugger - -=== PIE (ASLR) support in PDR - -*What.* When a Blaise binary is compiled with `--debug-opdf`, the compiler -currently passes `-no-pie` to gcc to ensure OPDF `.quad symbol` entries -contain absolute link-time addresses. The PDR debugger should instead read -the binary's actual load base at runtime and adjust all OPDF addresses by the -ASLR slide, allowing debug-opdf builds to remain PIE. - -*Why it matters.* PIE (Position Independent Executable) is the default on all -modern Linux distributions and is required for ASLR protection. Disabling it -in debug builds is a pragmatic workaround that is acceptable for local -development but is not appropriate for any scenario where the binary is -distributed or run in a production-adjacent environment. Lifting the -restriction makes `--debug-opdf` safe to enable in CI and staging environments. - -*Implementation notes.* - -* On Linux, `/proc//maps` lists each memory mapping with its base - address and the backing file. At debugger attach time, read this file, - find the entry whose backing path matches the target binary, and record - its base address as `LoadBase`. -* On FreeBSD, PIE and ASLR are supported by default from FreeBSD 12 onwards. - FreeBSD's procfs is not mounted by default, so `/proc//map` is not - reliable. The correct approach is `sysctl(KERN_PROC_VMMAP, pid)`, which - returns the full VM map for a process; find the entry matching the binary - path and extract its start address as `LoadBase`. -* On macOS, the equivalent is `task_info(TASK_DYLD_INFO)` or parsing the - Mach-O load commands; `DYLD_IMAGE_BASE` is available via the dyld shared - cache APIs. -* At symbol-resolution time on all platforms: `RuntimeAddr = OPDFAddr + LoadBase`. -* Once PIE-aware address resolution is in place, remove the `-no-pie` guard - in `Blaise.pas` (`CompileToNative`). - -*Effort.* Small on Linux and FreeBSD (one sysctl/procfs read at attach time); -moderate on macOS (dyld integration). - == macOS — Debugging and Code-Signing === Debugger entitlement requirements @@ -1058,60 +1015,3 @@ to distinguish this from simple enums so existing code is unaffected. is acceptable provided it is a new keyword. -== ARC: elide retain/release for const value parameters (IMPLEMENTED) - -A `const` parameter promises the caller keeps the argument alive for the -duration of the call, so the callee-side `_StringAddRef`/`_StringRelease` -(and `_ClassAddRef`/`_ClassRelease`) pair on entry/exit is, in principle, -redundant. Eliding it removes two RTL calls per const string/class/interface -parameter — a measurable saving on hot paths that thread strings through many -`const` parameters (the parser and semantic analyser do this heavily). - -Status: shipped. The elision is in place (the four entry/exit ARC loops skip -`IsConstParam`), paired with a *caller-side* retain for transient string -arguments — `EnsureConstStringRef`/`ReleaseConstStringArgs` wrap a value-mode -argument to a const-string parameter with `_StringAddRef` before the call and -`_StringRelease` after, which is a no-op for immortal literals and nets to zero -for owned strings, so the cost lands only on the `+0` transients that need it. -The remainder of this section is retained as the design record. - -The first attempt (commit `5a5b5d4`) elided the pair *without* the caller-side -retain and was reverted after it introduced a use-after-free. The reason it was -unsound as stated: - -* The "caller keeps it alive" premise holds for a *named* argument the caller - owns for the whole call, but *not* for a *temporary* bound to the const - parameter — e.g. `Use(A + ' ' + B)`, where the concatenation result is a - freshly-allocated string whose only reference is the argument slot itself. - With the callee-side retain elided, that temporary's refcount reaches zero - at the call boundary and the string is freed *before* the callee reads it. - -* This bites the RTL especially hard: routines such as `_StringCopy` and - `StrHead` take `const string` parameters and are frequently called with - built-at-runtime temporaries. A miscompiled RTL is self-reproducing under - self-hosting — the defect only manifests at the *second* generation (the - compiler that emits the broken RTL is itself fine), so a single fixpoint - step (stage-2 == stage-3) does not necessarily expose it, and the compiler's - own sources may not trigger the exact aliasing case. The original change - passed its valgrind test only because that test passed a string *literal* - (immortal), not a temporary. - -To re-attempt the optimisation safely, the elision must be conditioned on the -*argument*, not merely the parameter: - -* Elide the callee-side retain/release only when the caller can prove the - argument outlives the call without the callee's reference — i.e. a named - local/global/parameter the caller already retains — and *keep* the - retain/release when the argument is a temporary (function/property result, - concatenation, cast, or any `ExprOwnsRef` expression). - -* Equivalently, move the responsibility to the *call site*: for a const - parameter bound to a temporary, the caller retains the temporary across the - call and releases it afterwards (caller-side balancing), leaving the callee - free of ARC traffic. This keeps the optimisation's benefit for the common - named-argument case while remaining correct for temporaries. - -* Mandatory regression coverage: a valgrind e2e test that passes a - *concatenation result* (not a literal) as a `const string` parameter and - reads it in the callee — see `TestRun_ConstStringTemp_StaysAlive_Valgrind` - in `cp.test.e2e.arc.pas`. Add the class- and interface-typed analogues.