blaise/compiler/src/main/pascal/rtl.platform.layout.linux.pas

137 lines
4.8 KiB
ObjectPascal
Raw Normal View History

{
Blaise - An Object Pascal Compiler
Copyright (c) 2026 Graeme Geldenhuys
SPDX-License-Identifier: Apache-2.0 WITH Swift-exception
Licensed under the Apache License v2.0 with Runtime Library Exception.
See LICENSE file in the project root for full license terms.
}
unit rtl.platform.layout.linux;
// Linux x86_64 struct layouts and OS constants — the concrete TPlatformLayout
// adapter for Linux (docs/native-target-architecture.adoc, Step 0b).
//
// These are the values that previously lived as `const` declarations and the
// TStatBuf record inside rtl.platform.posix.pas. Moving them behind the
// TPlatformLayout port keeps the POSIX method bodies (which are shared with
// FreeBSD) free of any Linux-specific layout assumption; the FreeBSD adapter
// supplies its own offsets and flag values without conditional compilation.
//
// struct stat offsets below match the Linux x86_64 `struct stat`
// (sys/stat.h): st_mode at byte 24, st_size at byte 48, st_mtime at byte 88.
// The buffer is sized at 144 bytes (sizeof(struct stat) on Linux x86_64).
interface
uses
rtl.platform;
type
TPlatformLayoutLinuxX86_64 = class(TPlatformLayout)
public
function O_RDONLY: Integer; override;
function O_WRONLY: Integer; override;
function O_RDWR: Integer; override;
function O_CREAT: Integer; override;
function O_TRUNC: Integer; override;
function O_APPEND: Integer; override;
function S_IFMT: Integer; override;
function S_IFDIR: Integer; override;
function SEEK_SET: Integer; override;
function SEEK_CUR: Integer; override;
function SEEK_END: Integer; override;
function CLOCK_REALTIME: Integer; override;
function WNOHANG: Integer; override;
function StatBufSize: Integer; override;
function StatSize(Buf: Pointer): Int64; override;
function StatMtime(Buf: Pointer): Int64; override;
function StatMode(Buf: Pointer): Integer; override;
end;
implementation
const
{ Linux x86_64 struct stat field offsets (bytes). }
STAT_OFF_MODE = 24; { st_mode (Integer) }
STAT_OFF_SIZE = 48; { st_size (Int64) }
STAT_OFF_MTIME = 88; { st_mtim.tv_sec (Int64) }
STAT_SIZE = 144; { sizeof(struct stat) }
function TPlatformLayoutLinuxX86_64.O_RDONLY: Integer; begin Result := 0; end;
function TPlatformLayoutLinuxX86_64.O_WRONLY: Integer; begin Result := 1; end;
function TPlatformLayoutLinuxX86_64.O_RDWR: Integer; begin Result := 2; end;
function TPlatformLayoutLinuxX86_64.O_CREAT: Integer; begin Result := $40; end;
function TPlatformLayoutLinuxX86_64.O_TRUNC: Integer; begin Result := $200; end;
function TPlatformLayoutLinuxX86_64.O_APPEND: Integer; begin Result := $400; end;
function TPlatformLayoutLinuxX86_64.S_IFMT: Integer; begin Result := $F000; end;
function TPlatformLayoutLinuxX86_64.S_IFDIR: Integer; begin Result := $4000; end;
function TPlatformLayoutLinuxX86_64.SEEK_SET: Integer; begin Result := 0; end;
function TPlatformLayoutLinuxX86_64.SEEK_CUR: Integer; begin Result := 1; end;
function TPlatformLayoutLinuxX86_64.SEEK_END: Integer; begin Result := 2; end;
function TPlatformLayoutLinuxX86_64.CLOCK_REALTIME: Integer; begin Result := 0; end;
function TPlatformLayoutLinuxX86_64.WNOHANG: Integer; begin Result := 1; end;
function TPlatformLayoutLinuxX86_64.StatBufSize: Integer;
begin
Result := STAT_SIZE;
end;
function TPlatformLayoutLinuxX86_64.StatSize(Buf: Pointer): Int64;
var
P: ^Int64;
begin
P := Pointer(PChar(Buf) + STAT_OFF_SIZE);
Result := P^;
end;
function TPlatformLayoutLinuxX86_64.StatMtime(Buf: Pointer): Int64;
var
P: ^Int64;
begin
P := Pointer(PChar(Buf) + STAT_OFF_MTIME);
Result := P^;
end;
function TPlatformLayoutLinuxX86_64.StatMode(Buf: Pointer): Integer;
var
P: ^Integer;
begin
P := Pointer(PChar(Buf) + STAT_OFF_MODE);
Result := P^;
end;
feat(freebsd): drive static/freestanding link + per-target layout by --target (Step 6) Complete the end-to-end FreeBSD cross-compile: `blaise --target freebsd-x86_64` now emits a static, freestanding FreeBSD ET_EXEC on a Linux host with no external tools. Target selection through the linker: * TargetIsFreestanding(target) — FreeBSD (Strategy B: direct syscalls, no libc) is always a static ET_EXEC. LinkViaInternalLinker sets dynamic mode to `not (AOpts.Static or freestanding)`, and EnsureRTLObjects selects the freestanding kernel-leaf unit list the same way — so a FreeBSD target links the syscall/libc-shim/thread leaves and no libc, regardless of --static. Per-target platform layout (dependency injection off --target): * The concrete TPlatformLayout adapter (rtl.platform.layout.<os>) is selected and linked by the driver (BuildRTLUnitList), and the compiler injects a direct call to that unit's initialiser — PlatformLayoutInitSym(target) = rtl.platform.layout.<os>_init — into main, first, for the compile-time --target. rtl.platform.posix no longer `uses` a concrete layout, so the shared POSIX code is OS-agnostic and a FreeBSD build carries no Linux layout. * Bootstrap fallback: each layout unit also defines a WEAK _BlaisePlatformInit trampoline that _SetArgs calls, so a binary built by an older codegen (which does not emit main's by-name call) still initialises GPlatformLayout. Weak binding lets both layout units coexist in one link (e.g. the TestRunner, which imports the FreeBSD layout via cp.test.platformlayout.freebsd) without a duplicate-symbol error — the linker keeps the first-seen (host) copy. FreeBSD libc-shaped leaf: * New runtime.libc.freebsd (sibling of runtime.libc.linux): getcwd/getenv/ time/waitpid/execvp/sysconf on the FreeBSD syscall leaf. time() reads CLOCK_REALTIME (no SYS_time on FreeBSD); sysconf(CPU count) uses sysctl(hw.ncpu) — new SYS___sysctl=202 leaf, MIB {CTL_HW=6, HW_NCPU=3}. Tests: * TTargetToolkitTests.TestTargetIsFreestanding_{FreeBSD_True,Linux_False}. * TInternalLinkerE2ETests.TestCompile_FreeBSDTarget_EmitsStaticFreeBSDExe — a full compiler-CLI cross-compile asserting EI_OSABI=9, ET_EXEC, no PT_INTERP (the binary cannot run on the Linux host, so shape only). Verified: all four fixpoints green; full suite passes under QBE-built and native-built test runner (4029 tests); the previously latent TestRunner GPlatformLayout crash is resolved.
2026-07-01 15:53:54 +03:00
{ Assign GPlatformLayout to this target's layout, once. Called both from this
unit's initialization (rtl.platform.layout.linux_init, which main invokes
by-name for a Linux --target) and from the weak _BlaisePlatformInit trampoline
below. }
procedure AssignLayoutLinux;
begin
if GPlatformLayout = nil then
GPlatformLayout := TPlatformLayoutLinuxX86_64.Create();
feat(freebsd): drive static/freestanding link + per-target layout by --target (Step 6) Complete the end-to-end FreeBSD cross-compile: `blaise --target freebsd-x86_64` now emits a static, freestanding FreeBSD ET_EXEC on a Linux host with no external tools. Target selection through the linker: * TargetIsFreestanding(target) — FreeBSD (Strategy B: direct syscalls, no libc) is always a static ET_EXEC. LinkViaInternalLinker sets dynamic mode to `not (AOpts.Static or freestanding)`, and EnsureRTLObjects selects the freestanding kernel-leaf unit list the same way — so a FreeBSD target links the syscall/libc-shim/thread leaves and no libc, regardless of --static. Per-target platform layout (dependency injection off --target): * The concrete TPlatformLayout adapter (rtl.platform.layout.<os>) is selected and linked by the driver (BuildRTLUnitList), and the compiler injects a direct call to that unit's initialiser — PlatformLayoutInitSym(target) = rtl.platform.layout.<os>_init — into main, first, for the compile-time --target. rtl.platform.posix no longer `uses` a concrete layout, so the shared POSIX code is OS-agnostic and a FreeBSD build carries no Linux layout. * Bootstrap fallback: each layout unit also defines a WEAK _BlaisePlatformInit trampoline that _SetArgs calls, so a binary built by an older codegen (which does not emit main's by-name call) still initialises GPlatformLayout. Weak binding lets both layout units coexist in one link (e.g. the TestRunner, which imports the FreeBSD layout via cp.test.platformlayout.freebsd) without a duplicate-symbol error — the linker keeps the first-seen (host) copy. FreeBSD libc-shaped leaf: * New runtime.libc.freebsd (sibling of runtime.libc.linux): getcwd/getenv/ time/waitpid/execvp/sysconf on the FreeBSD syscall leaf. time() reads CLOCK_REALTIME (no SYS_time on FreeBSD); sysconf(CPU count) uses sysctl(hw.ncpu) — new SYS___sysctl=202 leaf, MIB {CTL_HW=6, HW_NCPU=3}. Tests: * TTargetToolkitTests.TestTargetIsFreestanding_{FreeBSD_True,Linux_False}. * TInternalLinkerE2ETests.TestCompile_FreeBSDTarget_EmitsStaticFreeBSDExe — a full compiler-CLI cross-compile asserting EI_OSABI=9, ET_EXEC, no PT_INTERP (the binary cannot run on the Linux host, so shape only). Verified: all four fixpoints green; full suite passes under QBE-built and native-built test runner (4029 tests); the previously latent TestRunner GPlatformLayout crash is resolved.
2026-07-01 15:53:54 +03:00
end;
{ Bootstrap fallback: a binary built by a codegen that does not yet emit main's
direct, strong, by-name call to rtl.platform.layout.<os>_init reaches the
layout through _SetArgs -> _BlaisePlatformInit instead. Emitted WEAK so that
every concrete layout unit can define it without a duplicate-symbol link error
when more than one is linked (e.g. a test that imports a non-host layout): the
linker keeps the first-seen weak, which is the host layout BuildRTLUnitList
links first. A tiny asm trampoline that tail-calls AssignLayout. }
procedure _BlaisePlatformInit; assembler; nostackframe;
asm
.weak _BlaisePlatformInit
jmp AssignLayoutLinux
end;
initialization
AssignLayoutLinux();
end.