Placed.lean
Declarations on this page (54)
- def nopWord
- def tlsdescAdrPage21Word
- def tlsdescLd64Lo12Word
- def tprelHi12Word
- def movwSignedWord
- def movwNcWord
- def TlsUInt32InRange
- def TprelHi12InRange
- def TprelLo12InRange
- def TprelG1InRange
- def TprelG0InRange
- def CodeRelocInRange
- def patchRelocWord
- def addSignedAddr
- def isAdrpWord
- def isAddImm64Word
- def isBWord
- def adrTemplate
- structure CodeBlock
- structure PlacedLib
- structure Placement
- def resolve
- def relocAt?
- def target
- def wordAt
- def relaxableAt
- def gotPairAt
- def tlsieRelaxableAt
- def patchGotOrTlsIeRelaxed
- def b2bNext?
- def relocTarget
- def undefWeakBranch
- def relaxedAt
- def patchedWord
- def patched
- def relocInRange
- structure Placeable
-
def PlacedLib.
patchDataReloc - def dataRelocWidth
-
def PlacedLib.
program - structure MultiPlacedLib
-
def MultiPlacedLib.
elfSym - structure MultiPlacement
- def lookupBlockSymAux
- def lookupBlockSym
-
def MultiPlacedLib.
resolve -
def MultiPlacedLib.
blockPlacement -
def MultiPlacedLib.
blockLib -
def MultiPlacedLib.
patchDataReloc -
def MultiPlacedLib.
patchedData - def RangesDisjoint
-
structure MultiPlacedLib.
Placeable -
def MultiPlacedLib.
blockProgram -
def MultiPlacedLib.
fnAddr
Placed Libraries: Layout-Generic Relocatable Code
Models a library as relocatable object code and data before linking (Obj., Obj., Obj.), byte-slice memory mapping (Obj.), and the output of a static linker (ld.) placing code at base and resolving external and data symbols through ρ (Obj., Obj., Obj., Obj., Obj.). Covers AArch64 ADRP+ADD to NOP+ADR, GOT, and TLS linker relaxations (Placement.), branch-to-branch retargeting (Placement.), and undefined weak references (Placement.).
Model
A PlacedLib is one rigid text block (CodeBlock: the concatenated .text* input sections of the library's objects, in link order, with alignment padding) plus data objects (SymDataInfo: one per .rodata/.data/.bss input section or merged-section piece). A Placement chooses:
-
base: the text block's address (no page alignment is assumed); -
ρ: the address of every symbol not defined in the block (data objects, extern functions); -
relax: for each relaxableADRP+ADDpair (byADRPbyte offset), whether the linker relaxed it; -
b2b: for each branch whose target is a blockB, whether the linker retargeted it to thatB's target; -
undefWeak: for each weak reference (CodeBlock.), whether the linker left it undefined (direct branches to it then branch to the next instruction). Every code relocation (including branches inside the block) is applied byweakRefs CodeBlock.with targetspatched addSignedAddr(resolve sym) addend.Placeablecollects the conditions under which a linker applies the relocations without overflow; a contract overPlacedLib.assumingprogram Placeableholds for every such link. Whether some real binary is such a link is a separate, checkable fact (Obj.).PlacedLib. InElf
Auditing
Soundness of a library theorem about a real binary rests only on PlacedLib., PlacedLib., PlacedLib. and PlacedLib. (bytes and permissions in the ELF). The relocation, relaxation and branch-to-branch model (patchedWord, Placeable) only affects completeness: if it disagrees with what the linker emitted, InElf fails on the real bytes (or the theorem is harder to prove), but it cannot make a false claim about those bytes.
@[expose] public sectionnamespace Arm64.ObjRelocation word patching
def nopWord
The NOP instruction word (0xd503201f).
def nopWord : BitVec 32 := 0xd503201f#32def tlsdescAdrPage21Word
R_AARCH64_ relaxed to Local-Exec (TLSLE): movz x0, #((target >> 16) & 0xffff), lsl #16.
def tlsdescAdrPage21Word (target : BitVec 64) : BitVec 32 :=
0xd2a00000#32 ||| (((target >>> 16).setWidth 32 &&& 0xffff#32) <<< 5)def tlsdescLd64Lo12Word
R_AARCH64_TLSDESC_LD64_LO12 relaxed to Local-Exec (TLSLE): movk x0, #(target & 0xffff).
def tlsdescLd64Lo12Word (target : BitVec 64) : BitVec 32 :=
0xf2800000#32 ||| ((target.setWidth 32 &&& 0xffff#32) <<< 5)def tprelHi12Word
Patch 32-bit ADD Xd, Xn, #imm12, lsl #12 template w for R_AARCH64_.
def tprelHi12Word (w : BitVec 32) (target : BitVec 64) : BitVec 32 :=
(w &&& 0xffc003ff#32) ||| (((target >>> 12).setWidth 32 &&& 0xfff#32) <<< 10)def movwSignedWord
Patch 32-bit MOVZ/MOVN template w with signed target shifted right by shift bits (R_AARCH64_ at shift = 16, R_AARCH64_ at shift = 0): sets bit 30 (MOVZ) when 0 ≤ target., or clears bit 30 (MOVN) and encodes ~~~target when target..
def movwSignedWord (shift : Nat) (w : BitVec 32) (target : BitVec 64) : BitVec 32 :=
if 0 ≤ target.toInt then
let imm16 : BitVec 32 := ((target >>> shift).setWidth 32) &&& 0xffff#32
((w &&& 0xbfe0001f#32) ||| 0x40000000#32) ||| (imm16 <<< 5)
else
let imm16 : BitVec 32 := (((~~~target) >>> shift).setWidth 32) &&& 0xffff#32
(w &&& 0xbfe0001f#32) ||| (imm16 <<< 5)def movwNcWord
Patch 32-bit MOVK template w with unchecked 16-bit slice target >>> shift (R_AARCH64_ at shift = 16, R_AARCH64_ at shift = 0).
def movwNcWord (shift : Nat) (w : BitVec 32) (target : BitVec 64) : BitVec 32 :=
let imm16 : BitVec 32 := ((target >>> shift).setWidth 32) &&& 0xffff#32
(w &&& 0xffe0001f#32) ||| (imm16 <<< 5)def TlsUInt32InRange
Propositional range condition ([0, 2^32)) for TLSDESC → TLSLE relaxation.
def TlsUInt32InRange (target : BitVec 64) : Prop :=
0 ≤ target.toInt ∧ target.toInt < (2 ^ 32 : Int)def TprelHi12InRange
Propositional range condition ([0, 2^24)) for R_AARCH64_.
def TprelHi12InRange (target : BitVec 64) : Prop :=
0 ≤ target.toInt ∧ target.toInt < (2 ^ 24 : Int)def TprelLo12InRange
Propositional range condition ([0, 2^12)) for R_AARCH64_.
def TprelLo12InRange (target : BitVec 64) : Prop :=
0 ≤ target.toInt ∧ target.toInt < (2 ^ 12 : Int)def TprelG1InRange
Propositional signed range condition ([-2^32, 2^32)) for R_AARCH64_.
def TprelG1InRange (target : BitVec 64) : Prop :=
-(2 ^ 32 : Int) ≤ target.toInt ∧ target.toInt < (2 ^ 32 : Int)def TprelG0InRange
Propositional signed range condition ([-2^16, 2^16)) for R_AARCH64_.
def TprelG0InRange (target : BitVec 64) : Prop :=
-(2 ^ 16 : Int) ≤ target.toInt ∧ target.toInt < (2 ^ 16 : Int)1 declaration not shown (lines 113–117): theorems, which Lean checks, or definitions that no headline statement uses
Arm64.
def CodeRelocInRange
Propositional range/alignment condition for a general code relocation kind at place targeting target.
def CodeRelocInRange (kind : RelocKind) (place target : BitVec 64) : Prop :=
match kind with
| .call26 | .jump26 => BranchInRange place target
| .adrpHi21 | .adrGotPage | .tlsieAdrGottprelPage21 => AdrpInRange place target
| .adrpHi21Nc => True
| .addLo12 | .ldst8Lo12 => True
| .ldst16Lo12 => Lo12Aligned 1 target
| .ldst32Lo12 => Lo12Aligned 2 target
| .ldst64Lo12 | .ld64GotLo12Nc | .tlsieLd64GottprelLo12Nc => Lo12Aligned 3 target
| .ldst128Lo12 => Lo12Aligned 4 target
| .condbr19 | .ldPrelLo19 => Imm19InRange place target
| .tstbr14 => TstBr14InRange place target
| .adrPrelLo21 => AdrInRange place target
| .prel32 => Prel32InRange place target
| .abs64 | .abs32 => False
| .tlsdescAdrPage21 | .tlsdescLd64Lo12 | .tlsdescAddLo12 | .tlsdescCall => TlsUInt32InRange target
| .tlsleAddTprelHi12 => TprelHi12InRange target
| .tlsleAddTprelLo12 => TprelLo12InRange target
| .tlsleAddTprelLo12Nc | .tlsleLdst8TprelLo12Nc => True
| .tlsleLdst16TprelLo12Nc => Lo12Aligned 1 target
| .tlsleLdst32TprelLo12Nc => Lo12Aligned 2 target
| .tlsleLdst64TprelLo12Nc => Lo12Aligned 3 target
| .tlsleLdst128TprelLo12Nc => Lo12Aligned 4 target
| .tlsleMovwTprelG1 => TprelG1InRange target
| .tlsleMovwTprelG1Nc => True
| .tlsleMovwTprelG0 => TprelG0InRange target
| .tlsleMovwTprelG0Nc => Truedef patchRelocWord
Patch a 32-bit instruction template w at address place for relocation kind targeting address target.
def patchRelocWord (kind : RelocKind) (w : BitVec 32) (place target : BitVec 64) : BitVec 32 :=
match kind with
| .call26 | .jump26 => branchWord w place target
| .adrpHi21 | .adrpHi21Nc
| .adrGotPage | .tlsieAdrGottprelPage21 => adrpWord w place target
| .addLo12 | .ldst8Lo12 => lo12Word 0 w target
| .ldst16Lo12 => lo12Word 1 w target
| .ldst32Lo12 => lo12Word 2 w target
| .ldst64Lo12 | .ld64GotLo12Nc | .tlsieLd64GottprelLo12Nc => lo12Word 3 w target
| .ldst128Lo12 => lo12Word 4 w target
| .condbr19 | .ldPrelLo19 => imm19Word w place target
| .tstbr14 => tstbr14Word w place target
| .adrPrelLo21 => adrWord w place target
| .prel32 => (target - place).setWidth 32
| .abs64 | .abs32 => w
| .tlsdescAdrPage21 => tlsdescAdrPage21Word target
| .tlsdescLd64Lo12 => tlsdescLd64Lo12Word target
| .tlsdescAddLo12 | .tlsdescCall => nopWord
| .tlsleAddTprelHi12 => tprelHi12Word w target
| .tlsleAddTprelLo12 | .tlsleAddTprelLo12Nc | .tlsleLdst8TprelLo12Nc => lo12Word 0 w target
| .tlsleLdst16TprelLo12Nc => lo12Word 1 w target
| .tlsleLdst32TprelLo12Nc => lo12Word 2 w target
| .tlsleLdst64TprelLo12Nc => lo12Word 3 w target
| .tlsleLdst128TprelLo12Nc => lo12Word 4 w target
| .tlsleMovwTprelG1 => movwSignedWord 16 w target
| .tlsleMovwTprelG1Nc => movwNcWord 16 w target
| .tlsleMovwTprelG0 => movwSignedWord 0 w target
| .tlsleMovwTprelG0Nc => movwNcWord 0 w targetdef addSignedAddr
Compute symAddr + addend in 64-bit two's-complement arithmetic (symAddr when addend = 0).
def addSignedAddr (symAddr : BitVec 64) (addend : Int) : BitVec 64 :=
if addend == 0 then symAddr else symAddr + BitVec.ofInt 64 addenddef isAdrpWord
w is an ADRP Xd, #imm (1 immlo 10000 immhi Rd).
def isAdrpWord (w : BitVec 32) : Bool := (w &&& 0x9f000000#32) == 0x90000000#32def isAddImm64Word
w is a 64-bit ADD Xd, Xn, #imm12 with no shift (1 0 0 100010 0 imm12 Rn Rd).
def isAddImm64Word (w : BitVec 32) : Bool := (w &&& 0xffc00000#32) == 0x91000000#32def isBWord
w is an unconditional branch B #imm (000101 imm26).
def isBWord (w : BitVec 32) : Bool := (w &&& 0xfc000000#32) == 0x14000000#32def adrTemplate
The ADR Xd, #0 template with destination register rd (0 00 10000 0...0 Rd).
def adrTemplate (rd : BitVec 32) : BitVec 32 := 0x10000000#32 ||| (rd &&& 0x1f#32)Code blocks
structure CodeBlock
One library's text, as a rigid block of relocatable code:
-
code: the bytes of the library's.text*input sections, concatenated in link order with alignment padding, before relocation (relocated fields hold the assembler's values); -
align: the alignment the linker gives the block (its largest input-section alignment); -
symbols: the functions it defines and their byte offsets incode; -
relocs: its code relocations (RelocEntry.is a byte offset inoff code); a relocation whose symbol is insymbolstargetsbase + off, any other symbol resolves throughρ; -
externs: functions it calls but does not define; -
weakRefs: the subset ofexternsthat the objects reference as undefinedSTB_WEAKsymbols (a linker may leave these undefined, seePlacement.).undefWeak
structure CodeBlock where
code : List (BitVec 8)
align : Nat := 4
symbols : List (String × Nat) := []
relocs : List RelocEntry := []
externs : List String := []
weakRefs : List String := []
deriving Inhabitedstructure PlacedLib
A library: its text block, its data objects, and the functions it exports (whose .symtab entries a linked binary must keep).
structure PlacedLib where
name : String
text : CodeBlock
data : List SymDataInfo := []
exports : List String := []
deriving Inhabitedstructure Placement
Where a linker placed a library: text at base, every other symbol at ρ sym, which relaxable pairs/sites (by byte offset in the block) it relaxed, and which branches (by byte offset) it retargeted past a block B (branch-to-branch).
The symbol map ρ sym gives:
-
the symbol's virtual address for direct code/data relocations, relaxed GOT (
GotToNopAdr/GotToAdrpAdd) sites, data objects (PlacedLib.), and extern stubs;data -
the GOT-slot virtual address for unrelaxed GOT (
adrGotPage/ld64GotLo12Nc) and unrelaxed initial-exec TLS (tlsieAdrGottprelPage21/tlsieLd64GottprelLo12Nc) sites; and -
the thread-pointer (
TPIDR_EL0) offset for relaxed initial-exec TLS (TLSIE→MOVZ/MOVK),TLSDESC→TLSLE, and direct local-exec TLS (TLSLE) sites.undefWeak symsays the linker left the weak referencesym(one of the block'sweakRefs) undefined: a directcall26/jump26to it is then patched to branch to the next instruction (AAELF64 §5.7.11.2, asld.does), while its GOT sites still load the GOT slot atlld -static ρ sym(which then holds0). (Mixed relaxed+unrelaxed GOT accesses to the same symbol, GOT-loading aPlacedLib.symbol, or a direct branch to a defined weak symbol that is also GOT-loaded unrelaxed, would require a separate GOT-slot map if ever needed.)data
structure Placement where
base : BitVec 64
ρ : String → BitVec 64
relax : Nat → Bool := fun _ => false
b2b : Nat → Bool := fun _ => false
undefWeak : String → Bool := fun _ => falsenamespace CodeBlockdef resolve
Address of sym under placement pl: base + off for a block symbol, else pl.ρ sym.
def relocAt?
The relocation at byte offset off, if any (the first one; Placeable requires uniqueness).
def relocAt? (blk : CodeBlock) (off : Nat) : Option RelocEntry :=
blk.relocs.find? (·.off == off)def target
Relocation target sym + addend under pl.
def target (blk : CodeBlock) (pl : Placement) (r : RelocEntry) : BitVec 64 :=
addSignedAddr (blk.resolve pl r.sym) r.addenddef wordAt
The unrelocated 32-bit word at byte offset off.
def wordAt (blk : CodeBlock) (off : Nat) : BitVec 32 := wordOfBytesLE blk.code offdef relaxableAt
The words at off and off + 4 form an ADRP Xd, sym + ADD Xd, Xd, :lo12:sym pair that a linker may relax to NOP + ADR Xd, sym (R_AARCH64_ADR_PREL_PG_HI21 and R_AARCH64_ADD_ABS_LO12_NC with the same symbol and addend, 64-bit unshifted ADD, and the same general-purpose register Xd != 31 as ADRP destination, ADD source and ADD destination, since register 31 encodes SP in ADD but XZR in ADR).
def relaxableAt (blk : CodeBlock) (off : Nat) : Bool :=
match blk.relocAt? off, blk.relocAt? (off + 4) with
| some r1, some r2 =>
r1.kind == .adrpHi21 && r2.kind == .addLo12 && r1.sym == r2.sym && r1.addend == r2.addend &&
isAdrpWord (blk.wordAt off) && isAddImm64Word (blk.wordAt (off + 4)) &&
(blk.wordAt off &&& 0x1f#32) == (blk.wordAt (off + 4) &&& 0x1f#32) &&
((blk.wordAt (off + 4) >>> 5) &&& 0x1f#32) == (blk.wordAt off &&& 0x1f#32) &&
(blk.wordAt off &&& 0x1f#32) != 0x1f#32
| _, _ => falsedef gotPairAt
The words at off and off + 4 form a GOT-load ADRP Xd, :got:sym + LDR Xd, [Xd, :got_lo12:sym] pair (R_AARCH64_ADR_GOT_PAGE and R_AARCH64_LD64_GOT_LO12_NC with the same symbol, addend 0, 64-bit unsigned LDR, and the same general-purpose register Xd != 31 as ADRP destination, LDR base and LDR destination, matching lld/ tryRelaxAdrpLdr). A linker may relax the pair to NOP + ADR Xd, sym (pl.), to ADRP Xd, sym + ADD Xd, Xd, :lo12:sym (pl.), or leave it as an unrelaxed GOT load (pl.).
def gotPairAt (blk : CodeBlock) (off : Nat) : Bool :=
match blk.relocAt? off, blk.relocAt? (off + 4) with
| some r1, some r2 =>
r1.kind == .adrGotPage && r2.kind == .ld64GotLo12Nc &&
r1.sym == r2.sym && r1.addend == 0 && r2.addend == 0 &&
isAdrpWord (blk.wordAt off) && isLdr64UnsignedWord (blk.wordAt (off + 4)) &&
(blk.wordAt off &&& 0x1f#32) == (blk.wordAt (off + 4) &&& 0x1f#32) &&
((blk.wordAt (off + 4) >>> 5) &&& 0x1f#32) == (blk.wordAt off &&& 0x1f#32) &&
(blk.wordAt off &&& 0x1f#32) != 0x1f#32
| _, _ => falsedef tlsieRelaxableAt
The word at off has an initial-exec TLS relocation (R_AARCH64_ on ADRP Xd or R_AARCH64_ on LDR Xd, [Xd, ...]) that a static executable linker may relax to local-exec MOVZ Xd, #:tprel_g1:sym, LSL #16 or MOVK Xd, #:tprel_g0_nc:sym when pl.. Requires Rn == Rt on the LDR (matching standard compiler codegen; lld/ relaxTlsIeToLe relaxes regardless of Rn, so Rn != Rt safely stays unrelaxed; Rd = 31 is XZR in ADRP, LDR, MOVZ, and MOVK).
def tlsieRelaxableAt (blk : CodeBlock) (off : Nat) : Bool :=
match blk.relocAt? off with
| some r =>
let w := blk.wordAt off
match r.kind with
| .tlsieAdrGottprelPage21 => isAdrpWord w
| .tlsieLd64GottprelLo12Nc =>
isLdr64UnsignedWord w && ((w >>> 5) &&& 0x1f#32) == (w &&& 0x1f#32)
| _ => false
| none => falsedef patchGotOrTlsIeRelaxed
Relocated word for a relaxed GOT or initial-exec TLS relocation site.
def patchGotOrTlsIeRelaxed (w : BitVec 32) (place target : BitVec 64)
(kind : RelocKind) : BitVec 32 :=
match kind with
| .adrGotPage => nopWord
| .ld64GotLo12Nc => lo12Word 0 (ldrToAddWord w) target
| .tlsieAdrGottprelPage21 => tlsieMovzWord w target
| .tlsieLd64GottprelLo12Nc => tlsieMovkWord w target
| _ => patchRelocWord kind w place targetdef b2bNext?
Branch-to-branch candidate: branch relocation r (call26/jump26) targets a block word that is an unconditional B with a jump26 relocation r2; a linker may then retarget the branch to r2's target (BL f where f: B g becomes BL g, same behaviour).
def b2bNext? (blk : CodeBlock) (r : RelocEntry) : Option RelocEntry :=
if r.kind == .call26 || r.kind == .jump26 then
match lookup r.sym blk.symbols with
| some symOff =>
let t : Int := (symOff : Int) + r.addend
if 0 ≤ t then
match blk.relocAt? t.toNat with
| some r2 => if r2.kind == .jump26 && isBWord (blk.wordAt t.toNat) then some r2 else none
| none => none
else none
| none => none
else nonedef relocTarget
Target of relocation r at byte offset off under pl: r2's target if pl. and r is a branch-to-branch candidate (b2bNext? r = some r2), else blk..
def undefWeakBranch
Relocation r is a direct branch (call26/jump26) to a weak reference of the block (weakRefs) that pl leaves undefined (pl.); the linker then patches it to branch to the next instruction instead of to relocTarget (AAELF64 §5.7.11.2).
def relaxedAt
The pair at off is relaxed under pl.
def relaxedAt (blk : CodeBlock) (pl : Placement) (off : Nat) : Bool :=
blk.relaxableAt off && pl.relax offdef patchedWord
The relocated word at byte offset off under placement pl:
-
the
ADRPof a relaxedADRP+ADDpair becomesNOP, itsADDbecomesADR Xd, sym + addend; -
a GOT pair (
gotPairAt) or initial-exec TLS relocation (tlsieRelaxableAt) is relaxed according topl.(relax GotToNopAdr,GotToAdrpAdd, orTlsIeToLe); -
any other relocated word is patched by
patchRelocWordatplace = base + off, targetingplace + 4for a direct branch to an undefined weak reference (undefWeakBranch) andrelocTarget(which applies branch-to-branch) otherwise; -
unrelocated words are unchanged.
def patchedWord (blk : CodeBlock) (pl : Placement) (off : Nat) : BitVec 32 :=
let w := blk.wordAt off
match blk.relocAt? off with
| none => w
| some r =>
let place := pl.base + BitVec.ofNat 64 off
if blk.relaxedAt pl off then nopWord
else if 4 ≤ off && blk.relaxedAt pl (off - 4) then
adrWord (adrTemplate w) place (blk.target pl r)
else if r.kind.isGotOrTlsIe && 4 ≤ off &&
(blk.gotPairAt (off - 4) && pl.relax (off - 4)) then
adrWord (adrTemplate w) place (blk.target pl r)
else if r.kind.isGotOrTlsIe &&
(blk.gotPairAt off || blk.tlsieRelaxableAt off ||
(4 ≤ off && blk.gotPairAt (off - 4))) && pl.relax off then
patchGotOrTlsIeRelaxed w place (blk.target pl r) r.kind
else patchRelocWord r.kind w place
(if blk.undefWeakBranch pl r then place + 4 else blk.relocTarget pl off r)def patched
The relocated bytes of the block under placement pl.
def patched (blk : CodeBlock) (pl : Placement) : List (BitVec 8) :=
(List.range (blk.code.length / 4)).flatMap fun j =>
bytesOfWordLE (blk.patchedWord pl (4 * j))def relocInRange
Range and alignment condition for relocation kind at byte offset off (place = base + off) targeting target in blk under placement pl:
-
for .
adrGotPage:Truewhen relaxed toNOP + ADR(gotPairAtoff && pl.relax off), orAdrpInRangeplacetargetotherwise; -
for .
ld64GotLo12Nc:AdrInRangeplacetargetwhen relaxed toNOP + ADR(4 ≤ off &&gotPairAt(off - 4) && pl.relax (off - 4)),Truewhen relaxed toADRP + ADD(4 ≤ off &&gotPairAt(off - 4) && pl.relax off), orLo12Aligned3targetwhen unrelaxed; -
for .
tlsieAdrGottprelPage21/ .tlsieLd64GottprelLo12Nc:TlsIeInRangetargetwhen relaxed to local-exec (tlsieRelaxableAtoff && pl.relax off), orCodeRelocInRangekind (AdrpInRange/Lo12Aligned3) when unrelaxed; -
for all other relocation kinds:
CodeRelocInRangekind placetarget.
def relocInRange (blk : CodeBlock) (pl : Placement) (kind : RelocKind) (off : Nat)
(place target : BitVec 64) : Prop :=
match kind with
| .adrGotPage =>
if blk.gotPairAt off && pl.relax off then True
else AdrpInRange place target
| .ld64GotLo12Nc =>
if 4 ≤ off && blk.gotPairAt (off - 4) && pl.relax (off - 4) then
AdrInRange place target
else if 4 ≤ off && blk.gotPairAt (off - 4) && pl.relax off then True
else Lo12Aligned 3 target
| .tlsieAdrGottprelPage21 =>
if blk.tlsieRelaxableAt off && pl.relax off then TlsIeInRange target
else AdrpInRange place target
| .tlsieLd64GottprelLo12Nc =>
if blk.tlsieRelaxableAt off && pl.relax off then TlsIeInRange target
else Lo12Aligned 3 target
| _ => CodeRelocInRange kind place targetstructure Placeable
A linker placing the block under pl applies every relocation without overflow:
-
baseis aligned toalign(a positive multiple of 4), the block does not wrap, and its length is a multiple of 4; -
relocations are at distinct, 4-aligned, in-bounds offsets and are code relocations (not
abs64/abs32) whose targets (relocTarget) satisfyrelocInRange(also for anundefWeakBranchsite, which is conservative: the linker does not range-check it); -
each relaxed pair's
ADR(atbase + off + 4) reaches its target (AdrInRange); -
block symbols are 4-aligned and in bounds, distinct from externs, and every extern resolves outside the block.
structure Placeable (blk : CodeBlock) (pl : Placement) : Prop where
align_pos : 0 < blk.align ∧ blk.align % 4 = 0
base_aligned : pl.base.toNat % blk.align = 0
no_wrap : pl.base.toNat + blk.code.length ≤ 2 ^ 64
len_aligned : blk.code.length % 4 = 0
relocs_nodup : (blk.relocs.map (·.off)).Nodup
relocs_ok : ∀ r ∈ blk.relocs,
r.off % 4 = 0 ∧ r.off + 4 ≤ blk.code.length ∧
blk.relocInRange pl r.kind r.off (pl.base + BitVec.ofNat 64 r.off) (blk.relocTarget pl r.off r)
relaxed_ok : ∀ off r, blk.relaxedAt pl off = true → blk.relocAt? (off + 4) = some r →
AdrInRange (pl.base + BitVec.ofNat 64 (off + 4)) (blk.target pl r)
symbols_ok : ∀ s ∈ blk.symbols, s.2 % 4 = 0 ∧ s.2 ≤ blk.code.length
externs_ok : ∀ e ∈ blk.externs, lookup e blk.symbols = none ∧
blk.code.length ≤ (pl.ρ e - pl.base).toNatend CodeBlockData objects
def PlacedLib.
Apply data relocation r (abs64, abs32, prel32; others leave bytes unchanged) to the bytes bytes of a data object placed at addr.
def PlacedLib.patchDataReloc (lib : PlacedLib) (pl : Placement) (addr : BitVec 64)
(bytes : List (BitVec 8)) (r : RelocEntry) : List (BitVec 8) :=
let t := addSignedAddr (lib.text.resolve pl r.sym) r.addend
let put (n : Nat) (v : BitVec 64) := bytes.take r.off ++ bytesLE n v ++ bytes.drop (r.off + n)
match r.kind with
| .abs64 => put 8 t
| .abs32 => put 4 t
| .prel32 => put 4 (t - (addr + BitVec.ofNat 64 r.off))
| _ => bytes1 declaration not shown (lines 469–473): theorems, which Lean checks, or definitions that no headline statement uses
Arm64.
def dataRelocWidth
Width in bytes of a data relocation (0 for non-data kinds).
1 declaration not shown (lines 481–495): theorems, which Lean checks, or definitions that no headline statement uses
Arm64., Arm64., Arm64., Arm64., Arm64.
The placed program
def PlacedLib.
The library placed under pl as a Program: the patched text at pl., its symbols, a hook entry point for each extern e at pl.ρ e (consulted via externs), and override hooks overrides for its own functions.
def PlacedLib.program (lib : PlacedLib) (pl : Placement)
(externs : String → Option (State → State) := fun _ => none)
(overrides : String → Option (State → State) := fun _ => none)
(svc : State → State := Syscall.standard) : Program :=
{ base := pl.base
code := lib.text.patched pl
symbols := lib.text.symbols
stubs := lib.text.externs.map fun e => (e, (pl.ρ e - pl.base).toNat)
externs := externs
overrides := overrides
svc := svc }1 declaration not shown (lines 514–516): theorems, which Lean checks, or definitions that no headline statement uses
Arm64.
Multi-block placed libraries
structure MultiPlacedLib
A library as several independently placed code blocks, its data objects, and the symbols it exports to callers. exportSyms maps an export (named as in blocks' symbols, e.g. a demangled Rust path) to its raw .symtab name in the objects (e.g. the mangled symbol) when the two differ (elfSym).
structure MultiPlacedLib where
name : String
blocks : List CodeBlock
data : List SymDataInfo := []
exports : List String := []
exportSyms : List (String × String) := []
deriving Inhabited
def MultiPlacedLib.
The raw .symtab name of export e of lib: its exportSyms entry, or e itself.
def MultiPlacedLib.elfSym (lib : MultiPlacedLib) (e : String) : String :=
(lookup e lib.exportSyms).getD estructure MultiPlacement
Linker-chosen parameters for placing a MultiPlacedLib:
-
base i: runtime start address of blocki(blocks[i]); -
ρ sym: runtime address of any symbolsymoutsideblocks(data objects and externs); -
relax i off: whether theADRP+ADDpair at byteoffof blockiwas relaxed toNOP+ADR; -
b2b i off: whether the unconditional branch (b26/call26) at byteoffof blocki, targeting an unconditionalB(b26) in blocki, was retargeted to thatB's target; -
undefWeak sym: whether the weak referencesymwas left undefined (Placement.).undefWeak
structure MultiPlacement where
base : Nat → BitVec 64
ρ : String → BitVec 64
relax : Nat → Nat → Bool := fun _ _ => false
b2b : Nat → Nat → Bool := fun _ _ => false
undefWeak : String → Bool := fun _ => falsedef lookupBlockSymAux
First (blockIdx, off) pair for symbol name across blocks, searching from startIdx.
def lookupBlockSymAux (name : String) : Nat → List CodeBlock → Option (Nat × Nat)
| _, [] => none
| idx, blk :: rest =>
match lookup name blk.symbols with
| some off => some (idx, off)
| none => lookupBlockSymAux name (idx + 1) restdef lookupBlockSym
First (blockIdx, off) pair for symbol name across blocks.
def lookupBlockSym (name : String) (blocks : List CodeBlock) : Option (Nat × Nat) :=
lookupBlockSymAux name 0 blocks
def MultiPlacedLib.
Runtime address of symbol sym under multi-block placement mpl: mpl. if sym is at offset off in block i, otherwise mpl.ρ sym.
def MultiPlacedLib.resolve (lib : MultiPlacedLib) (mpl : MultiPlacement) (sym : String) :
BitVec 64 :=
match lookupBlockSym sym lib.blocks with
| some (i, off) => mpl.base i + BitVec.ofNat 64 off
| none => mpl.ρ sym
def MultiPlacedLib.
Projected single-block Placement for block i of lib under mpl: placed at mpl., with all other blocks' symbols and external/data symbols resolved through lib..
def MultiPlacedLib.
Block i of lib viewed as a single-block PlacedLib sharing lib. and lib..
def MultiPlacedLib.
Apply data relocation r (abs64, abs32, prel32; others leave bytes unchanged) to the bytes bytes of a data object placed at addr under mpl.
def MultiPlacedLib.patchDataReloc (lib : MultiPlacedLib) (mpl : MultiPlacement) (addr : BitVec 64)
(bytes : List (BitVec 8)) (r : RelocEntry) : List (BitVec 8) :=
let t := addSignedAddr (lib.resolve mpl r.sym) r.addend
let put (n : Nat) (v : BitVec 64) := bytes.take r.off ++ bytesLE n v ++ bytes.drop (r.off + n)
match r.kind with
| .abs64 => put 8 t
| .abs32 => put 4 t
| .prel32 => put 4 (t - (addr + BitVec.ofNat 64 r.off))
| _ => bytes
def MultiPlacedLib.
The initial bytes of data object d under mpl: its relocated bytes followed by d. zero bytes, at address mpl.ρ d..
def MultiPlacedLib.patchedData (lib : MultiPlacedLib) (mpl : MultiPlacement) (d : SymDataInfo) :
List (BitVec 8) :=
d.relocs.foldl (lib.patchDataReloc mpl (mpl.ρ d.sym)) d.bytes ++ List.replicate d.zeroTail 0#8def RangesDisjoint
Two half-open address ranges [b1, b1 + n1) and [b2, b2 + n2) in Nat do not overlap.
def RangesDisjoint (b1 n1 b2 n2 : Nat) : Prop :=
b1 + n1 ≤ b2 ∨ b2 + n2 ≤ b1
structure MultiPlacedLib.
A linker placing the multi-block library under mpl applies every relocation without overflow:
-
every block
iisPlaceableunder its projectedlib.;blockPlacement mpl i -
distinct blocks occupy disjoint address ranges;
-
symbol names across all blocks are distinct (
symbols_nodup); -
data object symbols are distinct, not block symbols, aligned at
mpl.ρ d.without wrapping, and each data relocation is an in-bounds, in-rangesym abs64/abs32/prel32.
structure MultiPlacedLib.Placeable (lib : MultiPlacedLib) (mpl : MultiPlacement) : Prop where
blocks_ok : ∀ (i : Nat) (hi : i < lib.blocks.length),
lib.blocks[i].Placeable (lib.blockPlacement mpl i)
blocks_disjoint : ∀ (i j : Nat) (hi : i < lib.blocks.length) (hj : j < lib.blocks.length),
i < j → RangesDisjoint (mpl.base i).toNat lib.blocks[i].code.length
(mpl.base j).toNat lib.blocks[j].code.length
symbols_nodup : ((lib.blocks.flatMap (·.symbols)).map (·.1)).Nodup
data_nodup : (lib.data.map (·.sym)).Nodup
data_ok : ∀ d ∈ lib.data,
lookupBlockSym d.sym lib.blocks = none ∧ 0 < d.align ∧ (mpl.ρ d.sym).toNat % d.align = 0 ∧
(mpl.ρ d.sym).toNat + d.bytes.length + d.zeroTail ≤ 2 ^ 64
data_relocs_ok : ∀ d ∈ lib.data, ∀ r ∈ d.relocs,
0 < dataRelocWidth r.kind ∧ r.off + dataRelocWidth r.kind ≤ d.bytes.length ∧
(r.kind = .abs32 → Abs32InRange (addSignedAddr (lib.resolve mpl r.sym) r.addend)) ∧
(r.kind = .prel32 → Prel32InRange (mpl.ρ d.sym + BitVec.ofNat 64 r.off)
(addSignedAddr (lib.resolve mpl r.sym) r.addend))
def MultiPlacedLib.
Block i of lib placed under mpl as a Program.
def MultiPlacedLib.blockProgram (lib : MultiPlacedLib) (mpl : MultiPlacement) (i : Nat)
(externs : String → Option (State → State) := fun _ => none)
(overrides : String → Option (State → State) := fun _ => none)
(svc : State → State := Syscall.standard) : Program :=
(lib.blockLib i).program (lib.blockPlacement mpl i) externs overrides svc
def MultiPlacedLib.
Runtime address of block symbol name under mpl (mpl. if name is not a block symbol).
def MultiPlacedLib.fnAddr (lib : MultiPlacedLib) (mpl : MultiPlacement) (name : String) :
BitVec 64 :=
match lookupBlockSym name lib.blocks with
| some (i, off) => mpl.base i + BitVec.ofNat 64 off
| none => mpl.base 02 declarations not shown (lines 649–660): theorems, which Lean checks, or definitions that no headline statement uses
Arm64., Arm64.
end Arm64.Obj