fix(linker): resolve absolute 32-bit relocations in static mode
A static FreeBSD link with --debug-opdf failed with 'absolute 32-bit relocation unsupported in static mode': the OPDF emitter writes a 32-bit section-offset reference (.long label, R_X86_64_32) into the .opdf section, and the internal linker's static mode rejected all abs32 forms. The guard was over-conservative - in a non-PIE ET_EXEC linked at base 0x400000 every mapped address has a final link-time value that fits in 32 bits, so the relocation is directly resolvable (this is what GNU ld does, erroring only on overflow). Static mode now patches S+A with GNU-ld-style range checks: R_X86_64_32 must fit zero-extended, R_X86_64_32S sign-extended. Dynamic/PIE behaviour is unchanged (the patched value remains the unslid link-time address; the OPDF reader applies the run-time slide). Linux never hit this because Linux binaries link in PIE/dynamic mode; the bug only surfaced on the first FreeBSD-hosted pdr smoke test. Guard: TestReloc_Abs32_ResolvesInStaticMode links a .long target data reference statically and asserts the slot is patched with the symbol's absolute address. Verified: all four fixpoints green (FIXPOINT_OK, NATIVE_FIXPOINT_OK, NATIVE_INTERNAL_OK, WARMCACHE_FIXPOINT_OK); full suite OK (4035) under both the QBE-fixpoint-built and native-built test runners; cross- compiled freebsd-x86_64 --debug-opdf hello links and carries a correctly patched .opdf section.
This commit is contained in:
parent
5e9ec4eeec
commit
8d1130852f
|
|
@ -1669,9 +1669,10 @@ begin
|
||||||
raise ELinker.Create('undefined reference to `' + Sym.Name + '''');
|
raise ELinker.Create('undefined reference to `' + Sym.Name + '''');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Patch the merged section bytes for every relocation. In static
|
{ Patch the merged section bytes for every relocation. Static mode
|
||||||
mode (Phase B) only PC-relative forms are supported; in dynamic mode
|
(Phase B) supports PC-relative and absolute forms (every symbol has a
|
||||||
(Phase C) absolute, GOT-relative and TLS relocations are handled. }
|
final link-time address in a non-PIE ET_EXEC); dynamic mode (Phase C)
|
||||||
|
additionally handles GOT-relative and emits RELATIVE fixups. }
|
||||||
procedure TLinker.ApplyRelocations;
|
procedure TLinker.ApplyRelocations;
|
||||||
var
|
var
|
||||||
Oi, Ri: Integer;
|
Oi, Ri: Integer;
|
||||||
|
|
@ -1744,11 +1745,29 @@ begin
|
||||||
|
|
||||||
R_X86_64_32, R_X86_64_32S:
|
R_X86_64_32, R_X86_64_32S:
|
||||||
begin
|
begin
|
||||||
if not FDynamic then
|
|
||||||
raise ELinker.Create(Ctx
|
|
||||||
+ ': absolute 32-bit relocation unsupported in static mode');
|
|
||||||
S := Self.ResolveSymbolAddr(Obj, Rel.SymIndex, Ctx);
|
S := Self.ResolveSymbolAddr(Obj, Rel.SymIndex, Ctx);
|
||||||
Val := S + Rel.Addend;
|
Val := S + Rel.Addend;
|
||||||
|
if M.ShType = SHT_NOBITS then
|
||||||
|
raise ELinker.Create(Ctx
|
||||||
|
+ ': relocation into a NOBITS section');
|
||||||
|
{ Resolvable in BOTH modes: static ET_EXEC links at a fixed base
|
||||||
|
(0x400000) where every mapped address fits in 32 bits, and in
|
||||||
|
PIE mode the patched value is the unslid link-time address
|
||||||
|
(the OPDF reader applies the run-time slide itself; code never
|
||||||
|
uses abs32 in PIE objects). Range-check like GNU ld does:
|
||||||
|
R_X86_64_32 must fit zero-extended, R_X86_64_32S sign-extended. }
|
||||||
|
if Rel.RelocType = R_X86_64_32 then
|
||||||
|
begin
|
||||||
|
if (Val < 0) or (Val > $FFFFFFFF) then
|
||||||
|
raise ELinker.Create(Ctx
|
||||||
|
+ ': R_X86_64_32 overflow against ' + Sym.Name);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
if (Val < -2147483648) or (Val > 2147483647) then
|
||||||
|
raise ELinker.Create(Ctx
|
||||||
|
+ ': R_X86_64_32S overflow against ' + Sym.Name);
|
||||||
|
end;
|
||||||
LkPatch32(M.Data, PFileOff, Val and $FFFFFFFF);
|
LkPatch32(M.Data, PFileOff, Val and $FFFFFFFF);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ type
|
||||||
{ Static relocations }
|
{ Static relocations }
|
||||||
procedure TestReloc_PC32CrossObjectCall;
|
procedure TestReloc_PC32CrossObjectCall;
|
||||||
procedure TestReloc_Quad64_ResolvesToAbsoluteAddr;
|
procedure TestReloc_Quad64_ResolvesToAbsoluteAddr;
|
||||||
|
procedure TestReloc_Abs32_ResolvesInStaticMode;
|
||||||
{ Executable structure }
|
{ Executable structure }
|
||||||
procedure TestExe_ElfHeaderIsExec;
|
procedure TestExe_ElfHeaderIsExec;
|
||||||
procedure TestExe_EntryPointMatchesSymbol;
|
procedure TestExe_EntryPointMatchesSymbol;
|
||||||
|
|
@ -840,6 +841,49 @@ begin
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TLinkerTests.TestReloc_Abs32_ResolvesInStaticMode;
|
||||||
|
var
|
||||||
|
Lk: TLinker;
|
||||||
|
Bytes: string;
|
||||||
|
Addr, Got: Int64;
|
||||||
|
I, Slot: Integer;
|
||||||
|
begin
|
||||||
|
{ An absolute 32-bit data reference (.long target, R_X86_64_32) is
|
||||||
|
resolvable at link time in a non-PIE ET_EXEC: the image is linked at a
|
||||||
|
fixed base (0x400000) and every mapped address fits in 32 bits. The
|
||||||
|
OPDF emitter uses exactly this form (.long label) inside the .opdf
|
||||||
|
section, so a static FreeBSD link with debug info must accept it.
|
||||||
|
A marker word (0xDEADBEEF as decimal) precedes the slot so the test can
|
||||||
|
locate the patched bytes in the flat image. }
|
||||||
|
Lk := LinkObjs(
|
||||||
|
['.data' + LineEnding +
|
||||||
|
'marker:' + LineEnding + '.long 3735928559' + LineEnding +
|
||||||
|
'ptr32:' + LineEnding + '.long target' + LineEnding +
|
||||||
|
'.text' + LineEnding + '.globl target' + LineEnding + 'target:' +
|
||||||
|
LineEnding + 'ret' + LineEnding +
|
||||||
|
'.globl _start' + LineEnding + '_start:' + LineEnding + 'ret' + LineEnding],
|
||||||
|
'_start', Bytes);
|
||||||
|
try
|
||||||
|
Addr := Lk.AddrOfSymbol('target');
|
||||||
|
AssertTrue('target resolved above base', Addr > $400000);
|
||||||
|
{ Find the marker; the patched abs32 slot follows it. }
|
||||||
|
Slot := -1;
|
||||||
|
for I := 0 to Length(Bytes) - 9 do
|
||||||
|
if (OrdAt(Bytes, I) = $EF) and (OrdAt(Bytes, I + 1) = $BE) and
|
||||||
|
(OrdAt(Bytes, I + 2) = $AD) and (OrdAt(Bytes, I + 3) = $DE) then
|
||||||
|
begin
|
||||||
|
Slot := I + 4;
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
AssertTrue('marker found in image', Slot >= 0);
|
||||||
|
Got := OrdAt(Bytes, Slot) or (OrdAt(Bytes, Slot + 1) shl 8) or
|
||||||
|
(OrdAt(Bytes, Slot + 2) shl 16) or (OrdAt(Bytes, Slot + 3) shl 24);
|
||||||
|
AssertEquals('abs32 slot patched with target address', Addr, Got);
|
||||||
|
finally
|
||||||
|
Lk.Free();
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TLinkerTests.TestExe_ElfHeaderIsExec;
|
procedure TLinkerTests.TestExe_ElfHeaderIsExec;
|
||||||
var
|
var
|
||||||
Lk: TLinker;
|
Lk: TLinker;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue