diff --git a/compiler/src/main/pascal/blaise.linker.elf.pas b/compiler/src/main/pascal/blaise.linker.elf.pas index 48e4898..7d76ddb 100644 --- a/compiler/src/main/pascal/blaise.linker.elf.pas +++ b/compiler/src/main/pascal/blaise.linker.elf.pas @@ -1669,9 +1669,10 @@ begin raise ELinker.Create('undefined reference to `' + Sym.Name + ''''); end; -{ Patch the merged section bytes for every relocation. In static - mode (Phase B) only PC-relative forms are supported; in dynamic mode - (Phase C) absolute, GOT-relative and TLS relocations are handled. } +{ Patch the merged section bytes for every relocation. Static mode + (Phase B) supports PC-relative and absolute forms (every symbol has a + 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; var Oi, Ri: Integer; @@ -1744,11 +1745,29 @@ begin R_X86_64_32, R_X86_64_32S: begin - if not FDynamic then - raise ELinker.Create(Ctx - + ': absolute 32-bit relocation unsupported in static mode'); S := Self.ResolveSymbolAddr(Obj, Rel.SymIndex, Ctx); 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); end; diff --git a/compiler/src/test/pascal/cp.test.linker.pas b/compiler/src/test/pascal/cp.test.linker.pas index 4926d13..ea7086f 100644 --- a/compiler/src/test/pascal/cp.test.linker.pas +++ b/compiler/src/test/pascal/cp.test.linker.pas @@ -59,6 +59,7 @@ type { Static relocations } procedure TestReloc_PC32CrossObjectCall; procedure TestReloc_Quad64_ResolvesToAbsoluteAddr; + procedure TestReloc_Abs32_ResolvesInStaticMode; { Executable structure } procedure TestExe_ElfHeaderIsExec; procedure TestExe_EntryPointMatchesSymbol; @@ -840,6 +841,49 @@ begin 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; var Lk: TLinker;