2026-06-25 02:19:58 +03:00
|
|
|
{
|
|
|
|
|
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
|
2026-06-25 02:19:58 +03:00
|
|
|
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();
|
2026-06-25 02:19:58 +03:00
|
|
|
|
|
|
|
|
end.
|