feat(asm): strip @PLT from symbol names; probe stale fallback compiler in e2e tests
This commit is contained in:
parent
821ce90caf
commit
7781242696
|
|
@ -543,12 +543,14 @@ begin
|
|||
end
|
||||
else
|
||||
begin
|
||||
while (P < Length(S)) and (IsAlnum(S[P]) or (S[P]= Ord('.'))
|
||||
or (S[P]= Ord('_')) or (S[P]= Ord('$')) or (S[P]= Ord('@'))) do
|
||||
begin
|
||||
SymName := SymName + Chr(S[P]);
|
||||
{ Any other @SUFFIX (e.g. @PLT, @GOTPCREL) is a relocation qualifier,
|
||||
NOT part of the symbol name — consume and discard it. @PLT just
|
||||
selects the PLT32 relocation, which call/jmp already emit by default.
|
||||
Appending it to the name would create a bogus symbol like
|
||||
`__libc_start_main@PLT` that no loader resolves. }
|
||||
P := P + 1;
|
||||
while (P < Length(S)) and (IsAlnum(S[P]) or (S[P]= Ord('_'))) do
|
||||
P := P + 1;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ type
|
|||
procedure TestJmpIndirectMem_FFSlash4;
|
||||
procedure TestEndbr64AndHlt;
|
||||
procedure TestSyscall_0F05;
|
||||
procedure TestCallPltStripsSuffix;
|
||||
end;
|
||||
|
||||
{ ---- ELF writer unit tests ---- }
|
||||
|
|
@ -126,6 +127,14 @@ implementation
|
|||
var
|
||||
GInternalAsmSkipNoted: Boolean = False;
|
||||
|
||||
{ Validity-probe cache for the fallback compiler — see the matching note in
|
||||
cp.test.cli.pas. The /tmp/fp_blaise2 fallback is a transient fixpoint
|
||||
artifact that is often stale; a stale binary turns these e2e tests into a
|
||||
cascade of cryptic failures. Probe once and skip (not fail) on a bad probe.
|
||||
0 = not probed, 1 = good, 2 = bad. }
|
||||
var
|
||||
GInternalAsmProbeState: Integer = 0;
|
||||
|
||||
{ ---- TAsmEncodingTests ---- }
|
||||
|
||||
function TAsmEncodingTests.ContainsBytes(const ABuf, APat: string): Boolean;
|
||||
|
|
@ -222,6 +231,20 @@ begin
|
|||
AssertTrue('0f 05 missing', ContainsBytes(Obj, Chr($0F) + Chr($05)));
|
||||
end;
|
||||
|
||||
procedure TAsmEncodingTests.TestCallPltStripsSuffix;
|
||||
var
|
||||
Obj: string;
|
||||
begin
|
||||
{ call foo@PLT must reference symbol 'foo' (with a PLT32 reloc), NOT a bogus
|
||||
symbol literally named 'foo@PLT' that no loader resolves. The @PLT suffix
|
||||
is a relocation qualifier, not part of the name. Regression for the
|
||||
_start migration (call __libc_start_main@PLT). }
|
||||
Obj := AssembleToBytes('call foo@PLT' + LineEnding);
|
||||
AssertTrue('symbol foo missing', ContainsBytes(Obj, 'foo'));
|
||||
AssertTrue('bogus foo@PLT symbol present',
|
||||
not ContainsBytes(Obj, 'foo@PLT'));
|
||||
end;
|
||||
|
||||
procedure TAsmEncodingTests.TestQuadSymbol_EmitsReloc;
|
||||
var
|
||||
Obj: string;
|
||||
|
|
@ -672,6 +695,9 @@ begin
|
|||
end;
|
||||
|
||||
function TInternalAsmE2ETests.CompilerAvailable: Boolean;
|
||||
var
|
||||
SrcFile, OutFile, CompOut, RunOut: string;
|
||||
Rc: Integer;
|
||||
begin
|
||||
Result := FileExists(FCompiler) and FileExists(FRTL);
|
||||
if (not Result) and (not GInternalAsmSkipNoted) then
|
||||
|
|
@ -680,7 +706,34 @@ begin
|
|||
WriteLn(StdErr, 'note: TInternalAsmE2ETests skipped — compiler binary "',
|
||||
FCompiler, '" or RTL "', FRTL, '" not found ',
|
||||
'(set BLAISE_QBE_COMPILER to a QBE-backend blaise binary to run them)');
|
||||
Exit;
|
||||
end;
|
||||
if not Result then Exit;
|
||||
|
||||
{ Validity probe: compile+run a trivial program with --assembler internal.
|
||||
A stale fallback binary compiles fine but produces a SIGILL/wrong-output
|
||||
executable; detect that here and skip rather than emit a cryptic cascade. }
|
||||
if GInternalAsmProbeState = 0 then
|
||||
begin
|
||||
SrcFile := FScratch + 'probe.pas';
|
||||
OutFile := FScratch + 'probe';
|
||||
WriteFile(SrcFile, 'program p; begin WriteLn(42); end.');
|
||||
Rc := Self.RunProc(FCompiler, [
|
||||
'--source', SrcFile, '--unit-path', FRTLPath, '--unit-path', FStdlibPath,
|
||||
'--output', OutFile, '--backend', 'native', '--assembler', 'internal'
|
||||
], CompOut);
|
||||
if (Rc = 0) and (Self.RunProcNoArgs(OutFile, RunOut) = 0)
|
||||
and (Pos('42', RunOut) >= 0) then
|
||||
GInternalAsmProbeState := 1
|
||||
else
|
||||
begin
|
||||
GInternalAsmProbeState := 2;
|
||||
WriteLn(StdErr, 'note: TInternalAsmE2ETests skipped — compiler binary "',
|
||||
FCompiler, '" is stale/broken (probe program did not run); ',
|
||||
'rebuild it or set BLAISE_QBE_COMPILER to a current binary.');
|
||||
end;
|
||||
end;
|
||||
Result := GInternalAsmProbeState = 1;
|
||||
end;
|
||||
|
||||
function TInternalAsmE2ETests.RunProc(const AExe: string;
|
||||
|
|
|
|||
|
|
@ -94,6 +94,17 @@ implementation
|
|||
var
|
||||
GCLISkipNoted: Boolean = False;
|
||||
|
||||
{ Validity-probe cache for the fallback compiler. The fallback path
|
||||
(/tmp/fp_blaise2) is a transient fixpoint artifact that is frequently STALE —
|
||||
built by an earlier, possibly-broken compiler. Running the contract tests
|
||||
against a stale binary produces a cascade of cryptic failures (e.g. SIGILL
|
||||
from a since-fixed mis-encoding) that look like regressions but are not.
|
||||
We probe the binary once (compile+run a trivial program) and, if it does not
|
||||
behave, skip the suite with an actionable message instead of failing.
|
||||
0 = not probed, 1 = good, 2 = bad. }
|
||||
var
|
||||
GCLIProbeState: Integer = 0;
|
||||
|
||||
{ ---- helpers ---- }
|
||||
|
||||
function TCLIContractTests.ProjectRoot: string;
|
||||
|
|
@ -140,6 +151,9 @@ begin
|
|||
end;
|
||||
|
||||
function TCLIContractTests.CompilerAvailable: Boolean;
|
||||
var
|
||||
Out_: string;
|
||||
EC: Integer;
|
||||
begin
|
||||
Result := FileExists(FCompiler) and FileExists(FRTL);
|
||||
if (not Result) and (not GCLISkipNoted) then
|
||||
|
|
@ -148,7 +162,27 @@ begin
|
|||
WriteLn(StdErr, 'note: TCLIContractTests skipped — compiler binary "',
|
||||
FCompiler, '" or RTL "', FRTL, '" not found ',
|
||||
'(set BLAISE_QBE_COMPILER to a QBE-backend blaise binary to run them)');
|
||||
Exit;
|
||||
end;
|
||||
if not Result then Exit;
|
||||
|
||||
{ Validity probe: a stale fallback binary would otherwise turn into a cascade
|
||||
of cryptic failures. Probe once; on a bad probe, treat as unavailable. }
|
||||
if GCLIProbeState = 0 then
|
||||
begin
|
||||
if CompileRunFull('program p; begin WriteLn(42); end.', 'native', Out_, EC)
|
||||
and (EC = 0) and (Pos('42', Out_) >= 0) then
|
||||
GCLIProbeState := 1
|
||||
else
|
||||
begin
|
||||
GCLIProbeState := 2;
|
||||
WriteLn(StdErr, 'note: TCLIContractTests skipped — compiler binary "',
|
||||
FCompiler, '" is stale/broken (probe program did not run); ',
|
||||
'rebuild it or set BLAISE_QBE_COMPILER to a current QBE-backend ',
|
||||
'blaise binary.');
|
||||
end;
|
||||
end;
|
||||
Result := GCLIProbeState = 1;
|
||||
end;
|
||||
|
||||
function TCLIContractTests.RunCompiler(const AArgs: array of string;
|
||||
|
|
|
|||
Loading…
Reference in a new issue