fix(linker,bootstrap): error on missing RTL; carry RTL with correct name
The internal linker silently emitted a runtime-less binary when blaise_rtl.a could not be found beside the compiler (RTLPath empty): every RTL symbol became an undefined dynamic import, so the binary linked but died at runtime with `undefined symbol: _SetArgs`. scripts/rolling-bootstrap.sh hit exactly this: it carried each step's binary forward as _carry_blaise with its RTL as _carry_rtl.a, but FindRTLArchive looks for `blaise_rtl.a` beside the binary (CompilerBinDir) — a name it never used — so the next step linked the compiler with no runtime archive and SMOKE_FAILED. - Carry the binary + RTL into a dedicated _carry/ dir as blaise + blaise_rtl.a, so FindRTLArchive resolves it. Rolling bootstrap reaches HEAD again. - Make an empty RTLPath a hard error on both link paths (internal and external cc) instead of producing a broken binary. - Regression test TInternalLinkerE2ETests.TestLink_MissingRTL_FailsLoudly: runs a symlinked compiler from an RTL-free dir and asserts a non-zero exit, a message naming blaise_rtl.a, and no output binary. The four fixpoint scripts missed this because fixpoint-native.sh links the .s via gcc, never exercising the internal-linker + FindRTL path on a carried binary layout.
This commit is contained in:
parent
6c38672be3
commit
48569f36d1
|
|
@ -343,6 +343,15 @@ var
|
|||
begin
|
||||
Result := '';
|
||||
TC := ResolveToolchain(AOpts.Target);
|
||||
{ Every Blaise program needs blaise_rtl.a; an empty RTLPath means it was not
|
||||
found beside the compiler binary or via $BLAISE_RTL. Linking without it
|
||||
yields a flood of undefined-reference errors from the external linker (or,
|
||||
on the internal linker, a silently broken binary) — fail with a clear
|
||||
message instead. }
|
||||
if TC.RTLPath = '' then
|
||||
Exit('runtime archive blaise_rtl.a not found '
|
||||
+ '(looked beside the compiler binary and in $BLAISE_RTL); '
|
||||
+ 'cannot link a Blaise program without the RTL');
|
||||
Args := TStringList.Create();
|
||||
try
|
||||
Args.Add('-o');
|
||||
|
|
@ -359,8 +368,7 @@ begin
|
|||
if AExtraObjects <> nil then
|
||||
for I := 0 to AExtraObjects.Count - 1 do
|
||||
Args.Add(AExtraObjects.Strings[I]);
|
||||
if TC.RTLPath <> '' then
|
||||
Args.Add(TC.RTLPath);
|
||||
Args.Add(TC.RTLPath);
|
||||
Args.Add('-lm'); { math functions (sqrt, sin, cos, etc.) }
|
||||
Args.Add('-lpthread'); { POSIX threads (blaise_thread unit) }
|
||||
ExitCode := RunProcess(Self.ToolPath('linker', AOpts.Target), Args, Msg);
|
||||
|
|
|
|||
|
|
@ -197,6 +197,18 @@ begin
|
|||
|
||||
TC := ResolveToolchain(AOpts.Target);
|
||||
|
||||
{ Every Blaise program links blaise_rtl.a — the program entry emits
|
||||
_SetArgs and the runtime supplies ARC, strings, exceptions, the platform
|
||||
layer, etc. An empty RTLPath means FindRTLArchive could not locate
|
||||
blaise_rtl.a beside the compiler binary (or via BLAISE_RTL). Linking
|
||||
anyway is never correct: every RTL symbol becomes an undefined dynamic
|
||||
import that silently links but dies at run time (e.g. `undefined symbol:
|
||||
_SetArgs`). Fail loudly instead of emitting a broken executable. }
|
||||
if TC.RTLPath = '' then
|
||||
Exit('internal linker: runtime archive blaise_rtl.a not found '
|
||||
+ '(looked beside the compiler binary and in $BLAISE_RTL); '
|
||||
+ 'cannot link a Blaise program without the RTL');
|
||||
|
||||
Lk := TLinker.Create();
|
||||
try
|
||||
try
|
||||
|
|
@ -215,8 +227,7 @@ begin
|
|||
Lk.AddOwnedObject(Obj);
|
||||
end;
|
||||
|
||||
if TC.RTLPath <> '' then
|
||||
Lk.AddArchive(TC.RTLPath);
|
||||
Lk.AddArchive(TC.RTLPath);
|
||||
|
||||
Lk.AddCrtObject(CrtEndS);
|
||||
Lk.AddCrtObject(Crtn);
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ type
|
|||
procedure TestRun_StringOps;
|
||||
procedure TestRun_ExceptionHandling;
|
||||
procedure TestRun_ClassAndVirtual;
|
||||
procedure TestLink_MissingRTL_FailsLoudly;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
|
@ -1145,6 +1146,12 @@ end;
|
|||
|
||||
{ ---- TInternalLinkerE2ETests ---- }
|
||||
|
||||
{ symlink(2): point the isolated-dir compiler name at the real binary so
|
||||
CompilerBinDir() resolves to the isolated dir (no blaise_rtl.a beside it),
|
||||
without copying ~3 MB. Returns 0 on success. }
|
||||
function _test_symlink(ATarget, ALinkPath: PChar): Integer;
|
||||
external name 'symlink';
|
||||
|
||||
var
|
||||
GIntLinkSkipNoted: Boolean = False;
|
||||
|
||||
|
|
@ -1387,6 +1394,66 @@ begin
|
|||
AssertEquals('stdout', 'child' + LineEnding, Out_);
|
||||
end;
|
||||
|
||||
{ Regression: when blaise_rtl.a cannot be found beside the compiler binary
|
||||
(and BLAISE_RTL is unset), the compiler must REFUSE to link rather than
|
||||
silently emit a binary with every RTL symbol left as an undefined dynamic
|
||||
import — that broken-but-runnable binary dies at run time with
|
||||
`undefined symbol: _SetArgs`. A mismatched carried-RTL name in
|
||||
rolling-bootstrap surfaced exactly this. We reproduce by running a COPY of
|
||||
the compiler from a scratch dir that has no blaise_rtl.a beside it. }
|
||||
procedure TInternalLinkerE2ETests.TestLink_MissingRTL_FailsLoudly;
|
||||
var
|
||||
IsoDir, IsoCompiler, SrcFile, OutFile, CompOut: string;
|
||||
Rc: Integer;
|
||||
begin
|
||||
if not Self.CompilerAvailable() then
|
||||
begin
|
||||
Ignore('<toolchain-missing>');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
FCounter := FCounter + 1;
|
||||
IsoDir := FScratch + 'no_rtl_' + IntToStr(FCounter) + '/';
|
||||
ForceDirectories(IsoDir);
|
||||
IsoCompiler := IsoDir + 'blaise';
|
||||
{ Symlink the real compiler into the isolated dir — argv[0]'s dirname is the
|
||||
isolated dir, which has NO blaise_rtl.a beside it, so FindRTLArchive
|
||||
returns ''. (A symlink avoids copying the multi-MB binary and the
|
||||
byte-exactness pitfalls of a string round-trip.) }
|
||||
if FileExists(IsoCompiler) then DeleteFile(IsoCompiler);
|
||||
if _test_symlink(PChar(FCompiler), PChar(IsoCompiler)) <> 0 then
|
||||
begin
|
||||
Ignore('<symlink-unavailable>');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
SrcFile := IsoDir + 'prog.pas';
|
||||
OutFile := IsoDir + 'prog';
|
||||
WriteFile(SrcFile,
|
||||
'program test_no_rtl;' + LineEnding +
|
||||
'begin' + LineEnding +
|
||||
' WriteLn(6 * 7)' + LineEnding +
|
||||
'end.');
|
||||
|
||||
{ BLAISE_RTL is normally unset in the test environment; the isolated dir has
|
||||
no blaise_rtl.a, so FindRTLArchive returns '' and the guard must fire. }
|
||||
Rc := Self.RunProc(IsoCompiler, [
|
||||
'--source', SrcFile,
|
||||
'--unit-path', FRTLPath,
|
||||
'--unit-path', FStdlibPath,
|
||||
'--output', OutFile,
|
||||
'--backend', 'native',
|
||||
'--assembler', 'internal',
|
||||
'--linker', 'internal'
|
||||
], CompOut);
|
||||
|
||||
AssertTrue('compiler must FAIL when RTL is unreachable (got rc=0)', Rc <> 0);
|
||||
AssertTrue('error must name the missing RTL archive: ' + CompOut,
|
||||
Pos('blaise_rtl.a', CompOut) >= 0);
|
||||
AssertFalse('no output binary should be produced on RTL-missing failure',
|
||||
FileExists(OutFile));
|
||||
end;
|
||||
|
||||
initialization
|
||||
RegisterTest(TElfReaderTests);
|
||||
RegisterTest(TSectionMergerTests);
|
||||
|
|
|
|||
|
|
@ -264,10 +264,16 @@ for sha in "${COMMITS[@]}"; do
|
|||
fi
|
||||
|
||||
# Carry this step's artifacts forward as the compiler for the next commit.
|
||||
cp "$WT/_boot/blaise" "$WT/_carry_blaise"
|
||||
cp "$WT/_boot/blaise_rtl.a" "$WT/_carry_rtl.a"
|
||||
CUR_BIN="$WT/_carry_blaise"
|
||||
CUR_RTL="$WT/_carry_rtl.a"
|
||||
# FindRTLArchive (uToolchain.pas) looks for `blaise_rtl.a` BESIDE the compiler
|
||||
# binary, so the carried RTL must sit next to the carried binary under that
|
||||
# exact name — keep both in a dedicated _carry/ dir. (A mismatched name left
|
||||
# RTLPath empty, so the next step linked the compiler with NO runtime archive
|
||||
# and silently produced a binary missing _SetArgs et al. — SMOKE_FAILED.)
|
||||
mkdir -p "$WT/_carry"
|
||||
cp "$WT/_boot/blaise" "$WT/_carry/blaise"
|
||||
cp "$WT/_boot/blaise_rtl.a" "$WT/_carry/blaise_rtl.a"
|
||||
CUR_BIN="$WT/_carry/blaise"
|
||||
CUR_RTL="$WT/_carry/blaise_rtl.a"
|
||||
done
|
||||
|
||||
echo
|
||||
|
|
|
|||
Loading…
Reference in a new issue