feat(rtl): FreeBSD Tier-2 libc leaves + mremap/getrandom (Step 4b)
The Step-4b RTL leaves for a static FreeBSD build. memcpy/memset/memcmp/ strlen are already target-invariant (runtime.cstub, pure Pascal — shared), and the x86-64 jmp_buf is ABI-invariant (runtime.setjmp — shared), so this step is small: * runtime.syscall.freebsd gains getrandom (FreeBSD SYS 563, not Linux's 318) and an mremap stub. FreeBSD has no in-place-remap syscall, so mremap is a plain function returning MAP_FAILED (-1); runtime.mem's large-realloc path already treats an mremap failure as "grow the slow way" (alloc a fresh region, memcpy, free the old), so exporting a failing mremap gives the allocator its no-mremap fallback with zero allocator changes. * runtime.libc2.freebsd — the FreeBSD sibling of runtime.libc2.linux: the atexit registry + `exit`, __cxa_atexit, abort, freestanding UTC civil-calendar gmtime_r/localtime_r/timegm, mkstemp, and system. The calendar math, atexit registry, and struct tm layout are target-invariant (tm is identical on Linux/FreeBSD amd64), so the only differences from the Linux unit are the syscall leaf it builds on (runtime.syscall.freebsd) and the FreeBSD O_* flag values (O_CREAT=$0200, O_EXCL=$0800 — not Linux's $40/$80). Both units are standalone (nothing in the Linux build graph uses them), so the Linux self-host is untouched — confirmed by the clean fixpoints/suite. Test TestLink_FreeBSDSyscallLeaf_GetrandomAndMremapStub asserts getrandom uses 563 (and NOT Linux's 318) and the mremap stub returns -1. Threads (thr_new/_umtx_op) are Step 4c.
This commit is contained in:
parent
2662535579
commit
bded5294d5
296
compiler/src/main/pascal/runtime.libc2.freebsd.pas
Normal file
296
compiler/src/main/pascal/runtime.libc2.freebsd.pas
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
{
|
||||
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 runtime.libc2.freebsd;
|
||||
|
||||
// FreeBSD sibling of runtime.libc2.linux — the Tier 2 libc leaves that need real
|
||||
// logic, not a raw syscall:
|
||||
// * the atexit registry + `exit` (runs handlers then _exit)
|
||||
// * __cxa_atexit (register a handler)
|
||||
// * abort (raise SIGABRT, then _exit 134)
|
||||
// * gmtime_r / localtime_r / timegm (freestanding civil-calendar math, UTC)
|
||||
// * mkstemp (random-named O_CREAT|O_EXCL file)
|
||||
// * system (fork + execve /bin/sh -c + wait4)
|
||||
//
|
||||
// The civil-calendar math, the atexit registry, and the tm layout are all
|
||||
// target-invariant (struct tm is identical on Linux/FreeBSD amd64), so the only
|
||||
// differences from the Linux unit are the syscall leaf it builds on
|
||||
// (runtime.syscall.freebsd) and the FreeBSD O_* flag values. Linked only in a
|
||||
// --static (libc-free) FreeBSD build (docs/freebsd-x86_64-backend-design.adoc,
|
||||
// Step 4b).
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
runtime.syscall.freebsd;
|
||||
|
||||
{ __cxa_atexit(func, arg, dso_handle): register func(arg) to run at exit, LIFO.
|
||||
Returns 0 on success. dso_handle is ignored (single program, no DSO unload). }
|
||||
function __cxa_atexit(Func, Arg, DsoHandle: Pointer): Integer;
|
||||
|
||||
{ exit(code): run the registered handlers (LIFO), then _exit(code). This is the
|
||||
bare `exit` main's epilogue calls; it is a reserved word in Pascal, so the
|
||||
Pascal proc is _libc2_exit and the asm body publishes `exit`. }
|
||||
procedure _RunAtExitAndExit(Code: Integer);
|
||||
|
||||
{ abort(): raise SIGABRT to self; if the signal is caught/ignored, force-exit
|
||||
with status 134 (128 + SIGABRT). }
|
||||
procedure abort;
|
||||
|
||||
{ struct tm split/join (UTC). T points at a time_t (Int64 epoch seconds). }
|
||||
function gmtime_r(T: Pointer; Tm: Pointer): Pointer;
|
||||
function localtime_r(T: Pointer; Tm: Pointer): Pointer;
|
||||
function timegm(Tm: Pointer): Int64;
|
||||
|
||||
{ mkstemp(template): template ends with "XXXXXX"; replace with random chars and
|
||||
open O_CREAT|O_EXCL|O_RDWR mode 0600. Returns the fd or -1. }
|
||||
function mkstemp(Template: PChar): Integer;
|
||||
|
||||
{ system(cmd): fork, child execve "/bin/sh -c <cmd>", parent wait4; returns the
|
||||
child's raw wait status (the RTL extracts the exit code). }
|
||||
function system(Cmd: PChar): Integer;
|
||||
|
||||
implementation
|
||||
|
||||
type
|
||||
PInt64 = ^Int64;
|
||||
PInteger = ^Integer;
|
||||
|
||||
const
|
||||
SIGABRT = 6;
|
||||
O_RDWR = 2;
|
||||
{ FreeBSD open(2) flag bits — differ from Linux ($40 / $80). See
|
||||
rtl.platform.layout.freebsd (O_CREAT=$0200, O_TRUNC=$0400, O_APPEND=$0008);
|
||||
O_EXCL is $0800 (sys/fcntl.h). }
|
||||
O_CREAT = $0200;
|
||||
O_EXCL = $0800;
|
||||
MAX_ATEXIT = 64;
|
||||
|
||||
{ struct tm field byte offsets (SysV layout — identical on Linux and FreeBSD
|
||||
amd64; the RTL's TTm mirrors it):
|
||||
Sec@0 Min@4 Hour@8 MDay@12 Mon@16 Year@20 WDay@24 YDay@28 IsDST@32 (all int). }
|
||||
const
|
||||
TM_SEC=0; TM_MIN=4; TM_HOUR=8; TM_MDAY=12; TM_MON=16; TM_YEAR=20;
|
||||
TM_WDAY=24; TM_YDAY=28; TM_ISDST=32;
|
||||
|
||||
{ --- atexit registry --- }
|
||||
|
||||
var
|
||||
GAtExitFns: array[0..MAX_ATEXIT - 1] of Pointer;
|
||||
GAtExitArgs: array[0..MAX_ATEXIT - 1] of Pointer;
|
||||
GAtExitCount: Integer;
|
||||
|
||||
function __cxa_atexit(Func, Arg, DsoHandle: Pointer): Integer;
|
||||
begin
|
||||
if GAtExitCount >= MAX_ATEXIT then
|
||||
begin
|
||||
Result := -1;
|
||||
Exit;
|
||||
end;
|
||||
GAtExitFns[GAtExitCount] := Func;
|
||||
GAtExitArgs[GAtExitCount] := Arg;
|
||||
GAtExitCount := GAtExitCount + 1;
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
{ Call a registered handler: void (*func)(void* arg). The handler is invoked
|
||||
with its arg in %rdi (a 0-arg Pascal handler simply ignores it). }
|
||||
procedure CallHandler(Func, Arg: Pointer); assembler; nostackframe;
|
||||
asm
|
||||
movq %rsi, %rax { Arg -> scratch; Func is in %rdi }
|
||||
movq %rdi, %r11 { Func }
|
||||
movq %rax, %rdi { Arg -> first C arg }
|
||||
jmp *%r11
|
||||
end;
|
||||
|
||||
procedure _RunAtExitAndExit(Code: Integer);
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
{ LIFO. }
|
||||
I := GAtExitCount - 1;
|
||||
while I >= 0 do
|
||||
begin
|
||||
if GAtExitFns[I] <> nil then
|
||||
CallHandler(GAtExitFns[I], GAtExitArgs[I]);
|
||||
I := I - 1;
|
||||
end;
|
||||
_exit(Code);
|
||||
end;
|
||||
|
||||
{ Publish the bare `exit` symbol as a tail-call into _RunAtExitAndExit (Code is
|
||||
already in %edi). }
|
||||
procedure _libc2_exit(Code: Integer); assembler; nostackframe;
|
||||
asm
|
||||
.globl exit
|
||||
exit:
|
||||
jmp _RunAtExitAndExit
|
||||
end;
|
||||
|
||||
procedure abort;
|
||||
begin
|
||||
kill(getpid(), SIGABRT);
|
||||
_exit(134);
|
||||
end;
|
||||
|
||||
{ --- civil calendar (UTC), based on Howard Hinnant's days_from_civil --- }
|
||||
|
||||
{ Days since 1970-01-01 for a y/m/d (proleptic Gregorian). }
|
||||
function DaysFromCivil(Y, M, D: Int64): Int64;
|
||||
var
|
||||
Era, Yoe, Doy, Doe: Int64;
|
||||
YY: Int64;
|
||||
begin
|
||||
if M <= 2 then YY := Y - 1 else YY := Y;
|
||||
if YY >= 0 then Era := YY div 400 else Era := (YY - 399) div 400;
|
||||
Yoe := YY - Era * 400;
|
||||
if M > 2 then Doy := (153 * (M - 3) + 2) div 5 + D - 1
|
||||
else Doy := (153 * (M + 9) + 2) div 5 + D - 1;
|
||||
Doe := Yoe * 365 + Yoe div 4 - Yoe div 100 + Doy;
|
||||
Result := Era * 146097 + Doe - 719468;
|
||||
end;
|
||||
|
||||
{ Inverse: y/m/d from days-since-epoch. }
|
||||
procedure CivilFromDays(Z: Int64; var Y, M, D: Int64);
|
||||
var
|
||||
Era, Doe, Yoe, Doy, Mp: Int64;
|
||||
begin
|
||||
Z := Z + 719468;
|
||||
if Z >= 0 then Era := Z div 146097 else Era := (Z - 146096) div 146097;
|
||||
Doe := Z - Era * 146097;
|
||||
Yoe := (Doe - Doe div 1460 + Doe div 36524 - Doe div 146096) div 365;
|
||||
Y := Yoe + Era * 400;
|
||||
Doy := Doe - (365 * Yoe + Yoe div 4 - Yoe div 100);
|
||||
Mp := (5 * Doy + 2) div 153;
|
||||
D := Doy - (153 * Mp + 2) div 5 + 1;
|
||||
if Mp < 10 then M := Mp + 3 else M := Mp - 9;
|
||||
if M <= 2 then Y := Y + 1;
|
||||
end;
|
||||
|
||||
procedure FillTm(T: Int64; Tm: Pointer);
|
||||
var
|
||||
Days, Secs, Y, M, D: Int64;
|
||||
P: PInteger;
|
||||
begin
|
||||
Days := T div 86400;
|
||||
Secs := T - Days * 86400;
|
||||
if Secs < 0 then
|
||||
begin
|
||||
Secs := Secs + 86400;
|
||||
Days := Days - 1;
|
||||
end;
|
||||
CivilFromDays(Days, Y, M, D);
|
||||
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_SEC)); P^ := Integer(Secs mod 60);
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_MIN)); P^ := Integer((Secs div 60) mod 60);
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_HOUR)); P^ := Integer(Secs div 3600);
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_MDAY)); P^ := Integer(D);
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_MON)); P^ := Integer(M - 1); { 0-based }
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_YEAR)); P^ := Integer(Y - 1900); { years since 1900 }
|
||||
{ WDay: 1970-01-01 was a Thursday (4). }
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_WDAY));
|
||||
P^ := Integer(((Days mod 7) + 4 + 7) mod 7);
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_YDAY));
|
||||
P^ := Integer(Days - DaysFromCivil(Y, 1, 1));
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_ISDST)); P^ := 0;
|
||||
end;
|
||||
|
||||
function gmtime_r(T: Pointer; Tm: Pointer): Pointer;
|
||||
begin
|
||||
FillTm(PInt64(T)^, Tm);
|
||||
Result := Tm;
|
||||
end;
|
||||
|
||||
function localtime_r(T: Pointer; Tm: Pointer): Pointer;
|
||||
begin
|
||||
{ No timezone database in the freestanding RTL - treat local as UTC. }
|
||||
Result := gmtime_r(T, Tm);
|
||||
end;
|
||||
|
||||
function timegm(Tm: Pointer): Int64;
|
||||
var
|
||||
Y, M, D, Hh, Mm, Ss: Int64;
|
||||
P: PInteger;
|
||||
begin
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_SEC)); Ss := P^;
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_MIN)); Mm := P^;
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_HOUR)); Hh := P^;
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_MDAY)); D := P^;
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_MON)); M := P^ + 1;
|
||||
P := PInteger(Pointer(PChar(Tm) + TM_YEAR)); Y := P^ + 1900;
|
||||
Result := DaysFromCivil(Y, M, D) * 86400 + Hh * 3600 + Mm * 60 + Ss;
|
||||
end;
|
||||
|
||||
{ --- mkstemp --- }
|
||||
|
||||
function mkstemp(Template: PChar): Integer;
|
||||
const
|
||||
ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
var
|
||||
Len, I, Tries, Got: Integer;
|
||||
N: Int64;
|
||||
RandBuf: array[0..5] of Byte;
|
||||
Fd: Integer;
|
||||
begin
|
||||
Len := 0;
|
||||
while (Template[Len] and $FF) <> 0 do Len := Len + 1;
|
||||
if Len < 6 then Exit(-1);
|
||||
Tries := 0;
|
||||
while Tries < 256 do
|
||||
begin
|
||||
{ Fill all 6 bytes; getrandom may short-read, so loop until satisfied rather
|
||||
than leave stale/zero bytes that would weaken the random suffix. }
|
||||
Got := 0;
|
||||
while Got < 6 do
|
||||
begin
|
||||
N := getrandom(@RandBuf[Got], 6 - Got, 0);
|
||||
if N <= 0 then Continue; { EINTR / transient: retry }
|
||||
Got := Got + Integer(N);
|
||||
end;
|
||||
for I := 0 to 5 do
|
||||
Template[Len - 6 + I] := Ord(ALPHABET[(RandBuf[I] mod 62)]);
|
||||
Fd := open(Template, O_RDWR or O_CREAT or O_EXCL, 384); { 0600 }
|
||||
if Fd >= 0 then Exit(Fd);
|
||||
Tries := Tries + 1;
|
||||
end;
|
||||
Result := -1;
|
||||
end;
|
||||
|
||||
{ --- system --- }
|
||||
|
||||
function system(Cmd: PChar): Integer;
|
||||
var
|
||||
Pid, Status: Integer;
|
||||
Argv: array[0..3] of Pointer;
|
||||
ShPath, DashC: array[0..15] of Byte;
|
||||
begin
|
||||
{ Build argv = /bin/sh, -c, cmd, NULL. The /bin/sh path and -c flag are short
|
||||
literals copied into local byte buffers so they have stable addresses. }
|
||||
ShPath[0]:=Ord('/'); ShPath[1]:=Ord('b'); ShPath[2]:=Ord('i'); ShPath[3]:=Ord('n');
|
||||
ShPath[4]:=Ord('/'); ShPath[5]:=Ord('s'); ShPath[6]:=Ord('h'); ShPath[7]:=0;
|
||||
DashC[0]:=Ord('-'); DashC[1]:=Ord('c'); DashC[2]:=0;
|
||||
Argv[0] := @ShPath[0];
|
||||
Argv[1] := @DashC[0];
|
||||
Argv[2] := Pointer(Cmd);
|
||||
Argv[3] := nil;
|
||||
|
||||
Pid := fork();
|
||||
if Pid = 0 then
|
||||
begin
|
||||
{ Child: replace image; on failure exit 127 like the shell. Forward the
|
||||
process environment so the spawned shell sees PATH/HOME/etc. }
|
||||
execve(PChar(@ShPath[0]), @Argv[0], environ);
|
||||
_exit(127);
|
||||
end;
|
||||
if Pid < 0 then Exit(-1);
|
||||
Status := 0;
|
||||
wait4(Pid, @Status, 0, nil);
|
||||
Result := Status;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -83,6 +83,17 @@ function mmap(Addr: Pointer; Length: Int64; Prot, Flags, Fd: Integer;
|
|||
Offset: Int64): Pointer;
|
||||
function munmap(Addr: Pointer; Length: Int64): Integer;
|
||||
|
||||
{ mremap(old, oldsz, newsz, flags): FreeBSD has NO in-place remap syscall, so
|
||||
this is a stub that always reports failure (MAP_FAILED = -1). runtime.mem's
|
||||
large-realloc path already treats an mremap failure as "grow the slow way":
|
||||
it allocates a fresh region, memcpy's the old contents across, and frees the
|
||||
old one (see the MapFailed fall-through in _BlaiseReallocMem). Exporting the
|
||||
`mremap` symbol as a failing stub therefore gives the allocator its no-mremap
|
||||
grow fallback with no allocator changes. Signature matches the Linux leaf so
|
||||
runtime.mem's `external name 'mremap'` binds on both targets. }
|
||||
function mremap(OldAddr: Pointer; OldSize, NewSize: Int64; Flags: Integer;
|
||||
NewAddr: Pointer): Pointer;
|
||||
|
||||
{ Time. }
|
||||
function nanosleep(Req, Rem: Pointer): Integer;
|
||||
function clock_gettime(ClockId: Integer; Ts: Pointer): Integer;
|
||||
|
|
@ -93,6 +104,9 @@ function execve(Path: PChar; Argv, Envp: Pointer): Integer;
|
|||
function wait4(Pid: Integer; Status: Pointer; Options: Integer;
|
||||
Rusage: Pointer): Integer;
|
||||
function kill(Pid, Sig: Integer): Integer;
|
||||
{ getrandom(buf, count, flags): fill buf with count random bytes; returns the
|
||||
count written or -errno. Used by mkstemp's random suffix. }
|
||||
function getrandom(Buf: Pointer; Count: Int64; Flags: Integer): Int64;
|
||||
{ Raw __getcwd(2): fills Buf (up to Size); returns 0 on success or -errno. The
|
||||
wrapper in the posix layer adapts that to libc's buffer-pointer contract. }
|
||||
function sys_getcwd(Buf: PChar; Size: Int64): Int64;
|
||||
|
|
@ -134,6 +148,7 @@ const
|
|||
SYS_mkdir = 136;
|
||||
SYS_rmdir = 137;
|
||||
SYS_getcwd = 326; { __getcwd }
|
||||
SYS_getrandom = 563;
|
||||
SYS_clock_gettime = 232;
|
||||
SYS_nanosleep = 240;
|
||||
SYS_mmap = 477; { ino64 (legacy freebsd6 mmap = 197) }
|
||||
|
|
@ -360,6 +375,14 @@ asm
|
|||
ret
|
||||
end;
|
||||
|
||||
{ mremap: no FreeBSD in-place-remap syscall. Always return MAP_FAILED (-1) so
|
||||
the allocator takes its alloc-new-copy-free fallback (see the interface note). }
|
||||
function mremap(OldAddr: Pointer; OldSize, NewSize: Int64; Flags: Integer;
|
||||
NewAddr: Pointer): Pointer;
|
||||
begin
|
||||
Result := Pointer(-1);
|
||||
end;
|
||||
|
||||
function nanosleep(Req, Rem: Pointer): Integer;
|
||||
assembler; nostackframe;
|
||||
asm
|
||||
|
|
@ -429,6 +452,17 @@ asm
|
|||
ret
|
||||
end;
|
||||
|
||||
function getrandom(Buf: Pointer; Count: Int64; Flags: Integer): Int64;
|
||||
assembler; nostackframe;
|
||||
asm
|
||||
movq $563, %rax { SYS_getrandom }
|
||||
syscall
|
||||
jae .Lok_getrandom
|
||||
negq %rax
|
||||
.Lok_getrandom:
|
||||
ret
|
||||
end;
|
||||
|
||||
function sys_getcwd(Buf: PChar; Size: Int64): Int64;
|
||||
assembler; nostackframe;
|
||||
asm
|
||||
|
|
|
|||
|
|
@ -92,6 +92,11 @@ type
|
|||
CF-error-translation idiom (jae + negq). Standalone — no FreeBSD
|
||||
emulation, header/byte shape only. }
|
||||
procedure TestLink_FreeBSDSyscallLeaf_SymbolsAndNumbers;
|
||||
{ Step 4b: the FreeBSD leaf additions — getrandom uses the FreeBSD number
|
||||
(563, not Linux's 318) and the mremap stub is a plain function that returns
|
||||
MAP_FAILED (-1) so runtime.mem takes its alloc-copy-free grow fallback (no
|
||||
in-place remap syscall on FreeBSD). Standalone byte/symbol shape. }
|
||||
procedure TestLink_FreeBSDSyscallLeaf_GetrandomAndMremapStub;
|
||||
end;
|
||||
|
||||
TDynLinkerTests = class(TTestCase)
|
||||
|
|
@ -1209,6 +1214,62 @@ begin
|
|||
Pos(Chr($49) + Chr($89) + Chr($CA), Bytes) >= 0);
|
||||
end;
|
||||
|
||||
procedure TLinkerE2ETests.TestLink_FreeBSDSyscallLeaf_GetrandomAndMremapStub;
|
||||
const
|
||||
{ getrandom is a raw syscall with the FreeBSD number 563 (Linux's is 318).
|
||||
mremap has no FreeBSD syscall, so the leaf's stub just returns MAP_FAILED
|
||||
(-1) — a mov of -1 into %rax then ret; runtime.mem's realloc path treats
|
||||
that as "grow the slow way". A leading _start keeps the entry resolvable. }
|
||||
FixtureAsm =
|
||||
'.text' + LineEnding +
|
||||
'.globl _start' + LineEnding +
|
||||
'_start:' + LineEnding +
|
||||
' callq getrandom' + LineEnding +
|
||||
' callq mremap' + LineEnding +
|
||||
' xorl %edi, %edi' + LineEnding +
|
||||
' movq $1, %rax' + LineEnding + { SYS_exit }
|
||||
' syscall' + LineEnding +
|
||||
' hlt' + LineEnding +
|
||||
'.globl getrandom' + LineEnding +
|
||||
'getrandom:' + LineEnding +
|
||||
' movq $563, %rax' + LineEnding + { SYS_getrandom (FreeBSD) }
|
||||
' syscall' + LineEnding +
|
||||
' jae .Lok_gr' + LineEnding +
|
||||
' negq %rax' + LineEnding +
|
||||
'.Lok_gr:' + LineEnding +
|
||||
' ret' + LineEnding +
|
||||
'.globl mremap' + LineEnding +
|
||||
'mremap:' + LineEnding +
|
||||
' movq $-1, %rax' + LineEnding + { MAP_FAILED — no FreeBSD mremap }
|
||||
' ret' + LineEnding;
|
||||
function MovRaxImm4b(AImm: Integer): string;
|
||||
begin
|
||||
Result := Chr($48) + Chr($C7) + Chr($C0) +
|
||||
Chr(AImm and $FF) + Chr((AImm shr 8) and $FF) +
|
||||
Chr((AImm shr 16) and $FF) + Chr((AImm shr 24) and $FF);
|
||||
end;
|
||||
var
|
||||
Lk: TLinker;
|
||||
Obj: TElfObjectFile;
|
||||
Bytes: string;
|
||||
begin
|
||||
Lk := TLinker.Create(FreeBSDX86_64Target());
|
||||
try
|
||||
Obj := ParseElfObject(AssembleToBytes(FixtureAsm), 'fbsys2.o');
|
||||
Lk.AddOwnedObject(Obj);
|
||||
Bytes := Lk.LinkToBytes('_start');
|
||||
AssertTrue('getrandom defined', Lk.AddrOfSymbol('getrandom') > 0);
|
||||
AssertTrue('mremap defined', Lk.AddrOfSymbol('mremap') > 0);
|
||||
finally
|
||||
Lk.Free();
|
||||
end;
|
||||
{ getrandom uses the FreeBSD number 563 — NOT Linux's 318. }
|
||||
AssertTrue('SYS_getrandom=563 movq imm present', Pos(MovRaxImm4b(563), Bytes) >= 0);
|
||||
AssertTrue('Linux SYS_getrandom=318 NOT present', Pos(MovRaxImm4b(318), Bytes) < 0);
|
||||
{ mremap stub loads -1 (movq $-1,%rax = 48 C7 C0 FF FF FF FF). }
|
||||
AssertTrue('mremap stub returns -1 (MAP_FAILED)', Pos(MovRaxImm4b(-1), Bytes) >= 0);
|
||||
end;
|
||||
|
||||
{ ---- TDynLinkerTests ---- }
|
||||
|
||||
function TDynLinkerTests.ProjectRoot: string;
|
||||
|
|
|
|||
Loading…
Reference in a new issue