feat(rtl): complete --static self-host support (chmod, strtod-free, TLS align)

Closes the gaps that blocked a --static (libc-free) build of the compiler itself:

- runtime.syscall.linux: add the chmod(2) syscall stub (SYS_chmod=90) defining
  the bare 'chmod' the internal linker's MakeFileExecutable imports.
- uSemantic: route RawStrToDouble through the RTL's pure-Pascal _StrToDouble
  (runtime.float) instead of libc's strtod, dropping the last libc dependency in
  the compiler's float-literal constant folding.  Mirrors the existing
  _DoubleToStr routing and is correct for both static and dynamic builds.
- blaise.linker.elf: align FTlsSize to FTlsAlign in both the static and dynamic
  TLS layout so TPOFF32 (sym - FTlsAddr - FTlsSize) matches the thread pointer
  the freestanding _start places at AlignUp(memsz, align).  Previously these
  diverged when the raw .tdata+.tbss sum was not a multiple of the TLS alignment,
  shifting every threadvar read by the padding (latent: today's 104-byte/8-align
  TLS happens to coincide).

With these, `blaise --static` can link the compiler itself; the resulting
binary is a fully static, libc-free ET_EXEC.
This commit is contained in:
Graeme Geldenhuys 2026-06-26 19:15:25 +01:00
parent a20a82c417
commit 75628f3e20
3 changed files with 29 additions and 9 deletions

View file

@ -898,6 +898,12 @@ begin
FTlsSize := FTlsSize + M.Size;
if M.Align > FTlsAlign then FTlsAlign := M.Align;
end;
{ Variant-II TLS: the thread pointer sits at the ALIGNED end of the block, and
TPOFF32 is computed as (sym - FTlsAddr - FTlsSize). The freestanding _start
places TP at AlignUp(memsz, align); align FTlsSize to match so both agree even
when the raw section sum is not a multiple of FTlsAlign (else every threadvar
read is off by the alignment padding). }
FTlsSize := LkAlignUp(FTlsSize, FTlsAlign);
end;
function TLinker.FindSymbol(const AName: string): TLinkSymbol;
@ -1565,6 +1571,9 @@ begin
FTlsSize := FTlsSize + M.Size;
if M.Align > FTlsAlign then FTlsAlign := M.Align;
end;
{ TP sits at the aligned end of the TLS block (variant II); align FTlsSize so
TPOFF32 (sym - FTlsAddr - FTlsSize) matches the aligned thread pointer. }
FTlsSize := LkAlignUp(FTlsSize, FTlsAlign);
{ Build .dynamic section. 'libc.so.6' is at offset 1 in .dynstr. }
FDynamicData := '';

View file

@ -54,6 +54,7 @@ function rmdir(Path: PChar): Integer;
function unlink(Path: PChar): Integer;
function rename(OldPath, NewPath: PChar): Integer;
function chdir(Path: PChar): Integer;
function chmod(Path: PChar; Mode: Integer): Integer;
{ Process. }
function getpid: Integer;
@ -128,6 +129,7 @@ const
SYS_getpid = 39;
SYS_rename = 82;
SYS_mkdir = 83;
SYS_chmod = 90;
SYS_rmdir = 84;
SYS_unlink = 87;
SYS_chdir = 80;
@ -213,6 +215,14 @@ asm
ret
end;
function chmod(Path: PChar; Mode: Integer): Integer;
assembler; nostackframe;
asm
movq $90, %rax { SYS_chmod }
syscall
ret
end;
function rmdir(Path: PChar): Integer;
assembler; nostackframe;
asm

View file

@ -535,14 +535,15 @@ type
implementation
{ Double->string conversion uses the RTL's pure-Pascal _DoubleToStr, NOT libc's
snprintf. snprintf is a genuinely variadic function; declaring it with a fixed
Double parameter violates the SysV x86-64 variadic ABI (the %al vector-register
count is left unset), so glibc may read the double from the wrong place and emit
a wrong string. The symptom is environment-dependent miscompilation of folded
float constants (passes locally, fails in CI). _DoubleToStr is FormatFloat(.,15),
i.e. %.15g, and is ABI-safe. strtod is NOT variadic, so it is kept as-is. }
function _strtod(S: PChar; EndPtr: Pointer): Double; external name 'strtod';
{ Float<->string conversion uses the RTL's pure-Pascal _DoubleToStr / _StrToDouble,
NOT libc's snprintf / strtod. snprintf is a genuinely variadic function;
declaring it with a fixed Double parameter violates the SysV x86-64 variadic ABI
(the %al vector-register count is left unset), so glibc may read the double from
the wrong place and emit a wrong string (environment-dependent miscompilation of
folded float constants passes locally, fails in CI). Routing both directions
through the RTL also drops the last libc dependency in this path, so a --static
(libc-free) build of the compiler links without strtod. }
function _StrToDouble(S: Pointer): Double; external name '_StrToDouble';
function _DoubleToStr(V: Double): string; external name '_DoubleToStr';
function RawDoubleToStr(V: Double): string;
@ -552,7 +553,7 @@ end;
function RawStrToDouble(const S: string): Double;
begin
Result := _strtod(PChar(S), nil);
Result := _StrToDouble(PChar(S));
end;
function TSemanticAnalyser.GetSymbolTable: TSymbolTable;