2015-03-08 04:32:55 -04:00
// Inferno utils/5l/asm.c
2020-06-03 13:17:17 +02:00
// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5l/asm.c
2015-03-08 04:32:55 -04:00
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
2016-04-10 14:32:26 -07:00
// Portions Copyright © 2009 The Go Authors. All rights reserved.
2015-03-08 04:32:55 -04:00
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
2015-05-21 13:28:10 -04:00
package arm64
2015-03-08 04:32:55 -04:00
import (
2017-04-18 12:53:25 -07:00
"cmd/internal/objabi"
2017-09-30 21:10:49 +00:00
"cmd/internal/sys"
2015-05-21 13:28:10 -04:00
"cmd/link/internal/ld"
2020-03-11 12:12:41 -04:00
"cmd/link/internal/loader"
2017-10-04 17:54:04 -04:00
"cmd/link/internal/sym"
2017-10-06 16:01:02 -04:00
"debug/elf"
2020-12-14 18:52:13 -05:00
"fmt"
2015-03-08 04:32:55 -04:00
"log"
)
2020-05-15 18:35:05 -04:00
func gentext ( ctxt * ld . Link , ldr * loader . Loader ) {
2020-04-01 13:42:20 -04:00
initfunc , addmoduledata := ld . PrepareAddmoduledata ( ctxt )
if initfunc == nil {
2015-06-24 22:31:24 +12:00
return
}
2020-04-01 13:42:20 -04:00
2015-06-24 22:31:24 +12:00
o := func ( op uint32 ) {
2017-09-30 15:06:44 +00:00
initfunc . AddUint32 ( ctxt . Arch , op )
2015-06-24 22:31:24 +12:00
}
// 0000000000000000 <local.dso_init>:
// 0: 90000000 adrp x0, 0 <runtime.firstmoduledata>
// 0: R_AARCH64_ADR_PREL_PG_HI21 local.moduledata
// 4: 91000000 add x0, x0, #0x0
// 4: R_AARCH64_ADD_ABS_LO12_NC local.moduledata
o ( 0x90000000 )
o ( 0x91000000 )
2020-07-29 13:20:56 -04:00
rel , _ := initfunc . AddRel ( objabi . R_ADDRARM64 )
rel . SetOff ( 0 )
rel . SetSiz ( 8 )
rel . SetSym ( ctxt . Moduledata )
2015-06-24 22:31:24 +12:00
2018-04-27 10:57:14 -04:00
// 8: 14000000 b 0 <runtime.addmoduledata>
2015-06-24 22:31:24 +12:00
// 8: R_AARCH64_CALL26 runtime.addmoduledata
o ( 0x14000000 )
2020-07-29 13:20:56 -04:00
rel2 , _ := initfunc . AddRel ( objabi . R_CALLARM64 )
rel2 . SetOff ( 8 )
rel2 . SetSiz ( 4 )
rel2 . SetSym ( addmoduledata )
2015-06-24 22:31:24 +12:00
}
2015-03-08 14:14:53 +01:00
2020-07-29 13:26:50 -04:00
func adddynrel ( target * ld . Target , ldr * loader . Loader , syms * ld . ArchSyms , s loader . Sym , r loader . Reloc , rIdx int ) bool {
2020-04-24 11:01:10 -04:00
targ := r . Sym ( )
var targType sym . SymKind
if targ != 0 {
targType = ldr . SymType ( targ )
}
2020-10-14 21:15:37 -04:00
const pcrel = 1
2020-04-24 11:01:10 -04:00
switch r . Type ( ) {
2018-09-26 10:12:18 +00:00
default :
2020-04-24 11:01:10 -04:00
if r . Type ( ) >= objabi . ElfRelocOffset {
ldr . Errorf ( s , "unexpected relocation type %d (%s)" , r . Type ( ) , sym . RelocName ( target . Arch , r . Type ( ) ) )
2018-09-26 10:12:18 +00:00
return false
}
// Handle relocations found in ELF object files.
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_PREL32 ) :
2020-04-24 11:01:10 -04:00
if targType == sym . SDYNIMPORT {
ldr . Errorf ( s , "unexpected R_AARCH64_PREL32 relocation for dynamic symbol %s" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2022-05-05 13:46:15 -04:00
if targType == 0 || targType == sym . SXREF {
2020-04-24 11:01:10 -04:00
ldr . Errorf ( s , "unknown symbol %s in pcrel" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_PCREL )
su . SetRelocAdd ( rIdx , r . Add ( ) + 4 )
2018-09-26 10:12:18 +00:00
return true
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_PREL64 ) :
2020-04-24 11:01:10 -04:00
if targType == sym . SDYNIMPORT {
ldr . Errorf ( s , "unexpected R_AARCH64_PREL64 relocation for dynamic symbol %s" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
if targType == 0 || targType == sym . SXREF {
ldr . Errorf ( s , "unknown symbol %s in pcrel" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_PCREL )
su . SetRelocAdd ( rIdx , r . Add ( ) + 8 )
2018-09-26 10:12:18 +00:00
return true
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_CALL26 ) ,
objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_JUMP26 ) :
2020-04-24 11:01:10 -04:00
if targType == sym . SDYNIMPORT {
2020-05-15 18:35:05 -04:00
addpltsym ( target , ldr , syms , targ )
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
2020-05-15 18:35:05 -04:00
su . SetRelocSym ( rIdx , syms . PLT )
2020-04-24 11:01:10 -04:00
su . SetRelocAdd ( rIdx , r . Add ( ) + int64 ( ldr . SymPlt ( targ ) ) )
2018-09-26 10:12:18 +00:00
}
2022-05-05 13:46:15 -04:00
if targType == 0 || targType == sym . SXREF {
2020-04-24 11:01:10 -04:00
ldr . Errorf ( s , "unknown symbol %s in callarm64" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_CALLARM64 )
2018-09-26 10:12:18 +00:00
return true
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_ADR_GOT_PAGE ) ,
objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_LD64_GOT_LO12_NC ) :
2020-04-24 11:01:10 -04:00
if targType != sym . SDYNIMPORT {
2018-09-26 10:12:18 +00:00
// have symbol
// TODO: turn LDR of GOT entry into ADR of symbol itself
}
// fall back to using GOT
// TODO: just needs relocation, no need to put in .dynsym
2020-05-26 13:40:12 -04:00
ld . AddGotSym ( target , ldr , syms , targ , uint32 ( elf . R_AARCH64_GLOB_DAT ) )
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_ARM64_GOT )
2020-05-15 18:35:05 -04:00
su . SetRelocSym ( rIdx , syms . GOT )
2020-04-24 11:01:10 -04:00
su . SetRelocAdd ( rIdx , r . Add ( ) + int64 ( ldr . SymGot ( targ ) ) )
2018-09-26 10:12:18 +00:00
return true
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_ADR_PREL_PG_HI21 ) ,
objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_ADD_ABS_LO12_NC ) :
2020-04-24 11:01:10 -04:00
if targType == sym . SDYNIMPORT {
ldr . Errorf ( s , "unexpected relocation for dynamic symbol %s" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
if targType == 0 || targType == sym . SXREF {
ldr . Errorf ( s , "unknown symbol %s" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_ARM64_PCREL )
2018-09-26 10:12:18 +00:00
return true
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_ABS64 ) :
2020-04-24 11:01:10 -04:00
if targType == sym . SDYNIMPORT {
ldr . Errorf ( s , "unexpected R_AARCH64_ABS64 relocation for dynamic symbol %s" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_ADDR )
2020-03-04 17:25:01 -05:00
if target . IsPIE ( ) && target . IsInternal ( ) {
2020-02-09 17:00:27 -05:00
// For internal linking PIE, this R_ADDR relocation cannot
// be resolved statically. We need to generate a dynamic
// relocation. Let the code below handle it.
break
}
2018-09-26 10:12:18 +00:00
return true
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_LDST8_ABS_LO12_NC ) :
2020-04-24 11:01:10 -04:00
if targType == sym . SDYNIMPORT {
ldr . Errorf ( s , "unexpected relocation for dynamic symbol %s" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_ARM64_LDST8 )
2018-09-26 10:12:18 +00:00
return true
2020-11-18 04:00:57 +00:00
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_LDST16_ABS_LO12_NC ) :
if targType == sym . SDYNIMPORT {
ldr . Errorf ( s , "unexpected relocation for dynamic symbol %s" , ldr . SymName ( targ ) )
}
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_ARM64_LDST16 )
return true
2018-09-26 10:12:18 +00:00
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_LDST32_ABS_LO12_NC ) :
2020-04-24 11:01:10 -04:00
if targType == sym . SDYNIMPORT {
ldr . Errorf ( s , "unexpected relocation for dynamic symbol %s" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_ARM64_LDST32 )
2018-09-26 10:12:18 +00:00
return true
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_LDST64_ABS_LO12_NC ) :
2020-04-24 11:01:10 -04:00
if targType == sym . SDYNIMPORT {
ldr . Errorf ( s , "unexpected relocation for dynamic symbol %s" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_ARM64_LDST64 )
2018-09-26 10:12:18 +00:00
return true
2019-05-11 02:21:22 +10:00
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_LDST128_ABS_LO12_NC ) :
2020-04-24 11:01:10 -04:00
if targType == sym . SDYNIMPORT {
ldr . Errorf ( s , "unexpected relocation for dynamic symbol %s" , ldr . SymName ( targ ) )
2019-05-11 02:21:22 +10:00
}
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_ARM64_LDST128 )
2019-05-11 02:21:22 +10:00
return true
2020-10-14 21:15:37 -04:00
// Handle relocations found in Mach-O object files.
case objabi . MachoRelocOffset + ld . MACHO_ARM64_RELOC_UNSIGNED * 2 :
if targType == sym . SDYNIMPORT {
ldr . Errorf ( s , "unexpected reloc for dynamic symbol %s" , ldr . SymName ( targ ) )
}
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_ADDR )
if target . IsPIE ( ) && target . IsInternal ( ) {
// For internal linking PIE, this R_ADDR relocation cannot
// be resolved statically. We need to generate a dynamic
// relocation. Let the code below handle it.
break
}
return true
case objabi . MachoRelocOffset + ld . MACHO_ARM64_RELOC_BRANCH26 * 2 + pcrel :
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_CALLARM64 )
if targType == sym . SDYNIMPORT {
addpltsym ( target , ldr , syms , targ )
su . SetRelocSym ( rIdx , syms . PLT )
su . SetRelocAdd ( rIdx , int64 ( ldr . SymPlt ( targ ) ) )
}
return true
case objabi . MachoRelocOffset + ld . MACHO_ARM64_RELOC_PAGE21 * 2 + pcrel ,
objabi . MachoRelocOffset + ld . MACHO_ARM64_RELOC_PAGEOFF12 * 2 :
if targType == sym . SDYNIMPORT {
ldr . Errorf ( s , "unexpected relocation for dynamic symbol %s" , ldr . SymName ( targ ) )
}
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_ARM64_PCREL )
return true
case objabi . MachoRelocOffset + ld . MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 * 2 + pcrel ,
objabi . MachoRelocOffset + ld . MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 * 2 :
if targType != sym . SDYNIMPORT {
// have symbol
// turn MOVD sym@GOT (adrp+ldr) into MOVD $sym (adrp+add)
data := ldr . Data ( s )
off := r . Off ( )
if int ( off + 3 ) >= len ( data ) {
ldr . Errorf ( s , "unexpected GOT_LOAD reloc for non-dynamic symbol %s" , ldr . SymName ( targ ) )
return false
}
o := target . Arch . ByteOrder . Uint32 ( data [ off : ] )
su := ldr . MakeSymbolUpdater ( s )
switch {
case ( o >> 24 ) & 0x9f == 0x90 : // adrp
// keep instruction unchanged, change relocation type below
case o >> 24 == 0xf9 : // ldr
// rewrite to add
o = ( 0x91 << 24 ) | ( o & ( 1 << 22 - 1 ) )
su . MakeWritable ( )
su . SetUint32 ( target . Arch , int64 ( off ) , o )
default :
ldr . Errorf ( s , "unexpected GOT_LOAD reloc for non-dynamic symbol %s" , ldr . SymName ( targ ) )
return false
}
su . SetRelocType ( rIdx , objabi . R_ARM64_PCREL )
return true
}
ld . AddGotSym ( target , ldr , syms , targ , 0 )
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocType ( rIdx , objabi . R_ARM64_GOT )
su . SetRelocSym ( rIdx , syms . GOT )
su . SetRelocAdd ( rIdx , int64 ( ldr . SymGot ( targ ) ) )
return true
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
// Reread the reloc to incorporate any changes in type above.
relocs := ldr . Relocs ( s )
2020-07-29 13:26:50 -04:00
r = relocs . At ( rIdx )
2020-04-24 11:01:10 -04:00
switch r . Type ( ) {
2018-05-01 10:48:19 +02:00
case objabi . R_CALL ,
objabi . R_PCREL ,
objabi . R_CALLARM64 :
2020-04-24 11:01:10 -04:00
if targType != sym . SDYNIMPORT {
2018-05-01 10:48:19 +02:00
// nothing to do, the relocation will be laid out in reloc
return true
}
2020-03-04 17:25:01 -05:00
if target . IsExternal ( ) {
2018-05-01 10:48:19 +02:00
// External linker will do this relocation.
return true
}
2020-08-25 01:27:23 +10:00
// Internal linking.
if r . Add ( ) != 0 {
ldr . Errorf ( s , "PLT call with non-zero addend (%v)" , r . Add ( ) )
2020-10-12 13:44:21 -04:00
}
2020-08-25 01:27:23 +10:00
// Build a PLT entry and change the relocation target to that entry.
addpltsym ( target , ldr , syms , targ )
su := ldr . MakeSymbolUpdater ( s )
su . SetRelocSym ( rIdx , syms . PLT )
su . SetRelocAdd ( rIdx , int64 ( ldr . SymPlt ( targ ) ) )
return true
2018-09-26 10:12:18 +00:00
case objabi . R_ADDR :
2020-04-24 11:01:10 -04:00
if ldr . SymType ( s ) == sym . STEXT && target . IsElf ( ) {
2018-09-26 10:12:18 +00:00
// The code is asking for the address of an external
// function. We provide it with the address of the
// correspondent GOT symbol.
2020-05-26 13:40:12 -04:00
ld . AddGotSym ( target , ldr , syms , targ , uint32 ( elf . R_AARCH64_GLOB_DAT ) )
2020-04-24 11:01:10 -04:00
su := ldr . MakeSymbolUpdater ( s )
2020-05-15 18:35:05 -04:00
su . SetRelocSym ( rIdx , syms . GOT )
2020-04-24 11:01:10 -04:00
su . SetRelocAdd ( rIdx , r . Add ( ) + int64 ( ldr . SymGot ( targ ) ) )
2018-09-26 10:12:18 +00:00
return true
}
// Process dynamic relocations for the data sections.
2020-03-04 17:25:01 -05:00
if target . IsPIE ( ) && target . IsInternal ( ) {
2018-09-26 10:12:18 +00:00
// When internally linking, generate dynamic relocations
// for all typical R_ADDR relocations. The exception
// are those R_ADDR that are created as part of generating
// the dynamic relocations and must be resolved statically.
//
// There are three phases relevant to understanding this:
//
// dodata() // we are here
// address() // symbol address assignment
// reloc() // resolution of static R_ADDR relocs
//
// At this point symbol addresses have not been
// assigned yet (as the final size of the .rela section
// will affect the addresses), and so we cannot write
// the Elf64_Rela.r_offset now. Instead we delay it
// until after the 'address' phase of the linker is
// complete. We do this via Addaddrplus, which creates
// a new R_ADDR relocation which will be resolved in
// the 'reloc' phase.
//
// These synthetic static R_ADDR relocs must be skipped
// now, or else we will be caught in an infinite loop
// of generating synthetic relocs for our synthetic
// relocs.
//
// Furthermore, the rela sections contain dynamic
// relocations with R_ADDR relocations on
// Elf64_Rela.r_offset. This field should contain the
// symbol offset as determined by reloc(), not the
// final dynamically linked address as a dynamic
// relocation would provide.
2020-04-24 11:01:10 -04:00
switch ldr . SymName ( s ) {
2018-09-26 10:12:18 +00:00
case ".dynsym" , ".rela" , ".rela.plt" , ".got.plt" , ".dynamic" :
return false
}
} else {
// Either internally linking a static executable,
// in which case we can resolve these relocations
// statically in the 'reloc' phase, or externally
// linking, in which case the relocation will be
// prepared in the 'reloc' phase and passed to the
// external linker in the 'asmb' phase.
2020-04-24 11:01:10 -04:00
if ldr . SymType ( s ) != sym . SDATA && ldr . SymType ( s ) != sym . SRODATA {
2018-09-26 10:12:18 +00:00
break
}
}
2020-03-04 17:25:01 -05:00
if target . IsElf ( ) {
2019-12-06 17:25:51 -05:00
// Generate R_AARCH64_RELATIVE relocations for best
// efficiency in the dynamic linker.
//
// As noted above, symbol addresses have not been
// assigned yet, so we can't generate the final reloc
// entry yet. We ultimately want:
//
// r_offset = s + r.Off
// r_info = R_AARCH64_RELATIVE
// r_addend = targ + r.Add
//
// The dynamic linker will set *offset = base address +
// addend.
//
// AddAddrPlus is used for r_offset and r_addend to
// generate new R_ADDR relocations that will update
// these fields in the 'reloc' phase.
2020-05-15 18:35:05 -04:00
rela := ldr . MakeSymbolUpdater ( syms . Rela )
2020-04-24 11:01:10 -04:00
rela . AddAddrPlus ( target . Arch , s , int64 ( r . Off ( ) ) )
if r . Siz ( ) == 8 {
2020-10-27 14:18:13 +08:00
rela . AddUint64 ( target . Arch , elf . R_INFO ( 0 , uint32 ( elf . R_AARCH64_RELATIVE ) ) )
2018-09-26 10:12:18 +00:00
} else {
2020-04-24 11:01:10 -04:00
ldr . Errorf ( s , "unexpected relocation for dynamic symbol %s" , ldr . SymName ( targ ) )
2018-09-26 10:12:18 +00:00
}
2020-04-24 11:01:10 -04:00
rela . AddAddrPlus ( target . Arch , targ , int64 ( r . Add ( ) ) )
2020-02-11 18:26:38 -05:00
// Not mark r done here. So we still apply it statically,
// so in the file content we'll also have the right offset
// to the relocation target. So it can be examined statically
// (e.g. go version).
2018-09-26 10:12:18 +00:00
return true
}
2020-10-12 13:44:21 -04:00
if target . IsDarwin ( ) {
// Mach-O relocations are a royal pain to lay out.
// They use a compact stateful bytecode representation.
// Here we record what are needed and encode them later.
ld . MachoAddRebase ( s , int64 ( r . Off ( ) ) )
// Not mark r done here. So we still apply it statically,
// so in the file content we'll also have the right offset
// to the relocation target. So it can be examined statically
// (e.g. go version).
return true
}
2021-04-26 22:25:20 -04:00
case objabi . R_ARM64_GOTPCREL :
if target . IsExternal ( ) {
// External linker will do this relocation.
return true
}
if targType != sym . SDYNIMPORT {
ldr . Errorf ( s , "R_ARM64_GOTPCREL target is not SDYNIMPORT symbol: %v" , ldr . SymName ( targ ) )
}
if r . Add ( ) != 0 {
ldr . Errorf ( s , "R_ARM64_GOTPCREL with non-zero addend (%v)" , r . Add ( ) )
}
if target . IsElf ( ) {
ld . AddGotSym ( target , ldr , syms , targ , uint32 ( elf . R_AARCH64_GLOB_DAT ) )
} else {
ld . AddGotSym ( target , ldr , syms , targ , 0 )
}
// turn into two relocations, one for each instruction.
su := ldr . MakeSymbolUpdater ( s )
r . SetType ( objabi . R_ARM64_GOT )
r . SetSiz ( 4 )
r . SetSym ( syms . GOT )
r . SetAdd ( int64 ( ldr . SymGot ( targ ) ) )
r2 , _ := su . AddRel ( objabi . R_ARM64_GOT )
r2 . SetSiz ( 4 )
r2 . SetOff ( r . Off ( ) + 4 )
r2 . SetSym ( syms . GOT )
r2 . SetAdd ( int64 ( ldr . SymGot ( targ ) ) )
2021-04-27 10:57:09 -04:00
return true
2018-05-01 10:48:19 +02:00
}
2016-09-05 23:49:53 -04:00
return false
2015-03-08 04:32:55 -04:00
}
2020-07-28 21:35:53 -04:00
func elfreloc1 ( ctxt * ld . Link , out * ld . OutBuf , ldr * loader . Loader , s loader . Sym , r loader . ExtReloc , ri int , sectoff int64 ) bool {
2020-06-26 19:17:33 -04:00
out . Write64 ( uint64 ( sectoff ) )
2015-04-03 04:37:11 -04:00
2020-05-15 18:35:05 -04:00
elfsym := ld . ElfSymForReloc ( ctxt , r . Xsym )
2020-07-28 21:35:53 -04:00
siz := r . Size
switch r . Type {
2015-04-03 04:37:11 -04:00
default :
2017-08-27 22:00:00 +09:00
return false
2020-04-29 22:00:28 -04:00
case objabi . R_ADDR , objabi . R_DWARFSECREF :
2020-05-11 16:00:13 -04:00
switch siz {
2015-04-03 04:37:11 -04:00
case 4 :
2020-06-26 19:17:33 -04:00
out . Write64 ( uint64 ( elf . R_AARCH64_ABS32 ) | uint64 ( elfsym ) << 32 )
2015-04-03 04:37:11 -04:00
case 8 :
2020-06-26 19:17:33 -04:00
out . Write64 ( uint64 ( elf . R_AARCH64_ABS64 ) | uint64 ( elfsym ) << 32 )
2015-04-03 04:37:11 -04:00
default :
2017-08-27 22:00:00 +09:00
return false
2015-04-03 04:37:11 -04:00
}
2017-04-18 12:53:25 -07:00
case objabi . R_ADDRARM64 :
2015-04-03 04:37:18 -04:00
// two relocations: R_AARCH64_ADR_PREL_PG_HI21 and R_AARCH64_ADD_ABS_LO12_NC
2020-06-26 19:17:33 -04:00
out . Write64 ( uint64 ( elf . R_AARCH64_ADR_PREL_PG_HI21 ) | uint64 ( elfsym ) << 32 )
out . Write64 ( uint64 ( r . Xadd ) )
out . Write64 ( uint64 ( sectoff + 4 ) )
out . Write64 ( uint64 ( elf . R_AARCH64_ADD_ABS_LO12_NC ) | uint64 ( elfsym ) << 32 )
2022-07-11 02:40:14 +00:00
case objabi . R_ARM64_PCREL_LDST8 ,
objabi . R_ARM64_PCREL_LDST16 ,
objabi . R_ARM64_PCREL_LDST32 ,
objabi . R_ARM64_PCREL_LDST64 :
// two relocations: R_AARCH64_ADR_PREL_PG_HI21 and R_AARCH64_LDST{64/32/16/8}_ABS_LO12_NC
out . Write64 ( uint64 ( elf . R_AARCH64_ADR_PREL_PG_HI21 ) | uint64 ( elfsym ) << 32 )
out . Write64 ( uint64 ( r . Xadd ) )
out . Write64 ( uint64 ( sectoff + 4 ) )
var ldstType elf . R_AARCH64
switch r . Type {
case objabi . R_ARM64_PCREL_LDST8 :
ldstType = elf . R_AARCH64_LDST8_ABS_LO12_NC
case objabi . R_ARM64_PCREL_LDST16 :
ldstType = elf . R_AARCH64_LDST16_ABS_LO12_NC
case objabi . R_ARM64_PCREL_LDST32 :
ldstType = elf . R_AARCH64_LDST32_ABS_LO12_NC
case objabi . R_ARM64_PCREL_LDST64 :
ldstType = elf . R_AARCH64_LDST64_ABS_LO12_NC
}
out . Write64 ( uint64 ( ldstType ) | uint64 ( elfsym ) << 32 )
2017-04-18 12:53:25 -07:00
case objabi . R_ARM64_TLS_LE :
2020-06-26 19:17:33 -04:00
out . Write64 ( uint64 ( elf . R_AARCH64_TLSLE_MOVW_TPREL_G0 ) | uint64 ( elfsym ) << 32 )
2017-04-18 12:53:25 -07:00
case objabi . R_ARM64_TLS_IE :
2020-06-26 19:17:33 -04:00
out . Write64 ( uint64 ( elf . R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 ) | uint64 ( elfsym ) << 32 )
out . Write64 ( uint64 ( r . Xadd ) )
out . Write64 ( uint64 ( sectoff + 4 ) )
out . Write64 ( uint64 ( elf . R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC ) | uint64 ( elfsym ) << 32 )
2017-04-18 12:53:25 -07:00
case objabi . R_ARM64_GOTPCREL :
2020-06-26 19:17:33 -04:00
out . Write64 ( uint64 ( elf . R_AARCH64_ADR_GOT_PAGE ) | uint64 ( elfsym ) << 32 )
out . Write64 ( uint64 ( r . Xadd ) )
out . Write64 ( uint64 ( sectoff + 4 ) )
out . Write64 ( uint64 ( elf . R_AARCH64_LD64_GOT_LO12_NC ) | uint64 ( elfsym ) << 32 )
2017-04-18 12:53:25 -07:00
case objabi . R_CALLARM64 :
2020-05-11 16:00:13 -04:00
if siz != 4 {
2017-08-27 22:00:00 +09:00
return false
2015-04-03 04:37:11 -04:00
}
2020-06-26 19:17:33 -04:00
out . Write64 ( uint64 ( elf . R_AARCH64_CALL26 ) | uint64 ( elfsym ) << 32 )
2015-04-03 04:37:11 -04:00
}
2020-06-26 19:17:33 -04:00
out . Write64 ( uint64 ( r . Xadd ) )
2015-04-03 04:37:11 -04:00
2017-08-27 22:00:00 +09:00
return true
2015-03-08 04:32:55 -04:00
}
2021-04-20 14:12:04 -04:00
// sign-extends from 21, 24-bit.
func signext21 ( x int64 ) int64 { return x << ( 64 - 21 ) >> ( 64 - 21 ) }
func signext24 ( x int64 ) int64 { return x << ( 64 - 24 ) >> ( 64 - 24 ) }
2020-12-09 12:14:00 -05:00
2020-07-28 21:35:53 -04:00
func machoreloc1 ( arch * sys . Arch , out * ld . OutBuf , ldr * loader . Loader , s loader . Sym , r loader . ExtReloc , sectoff int64 ) bool {
2015-04-10 21:28:09 -04:00
var v uint32
rs := r . Xsym
2020-07-28 21:35:53 -04:00
rt := r . Type
siz := r . Size
2020-12-14 18:52:13 -05:00
xadd := r . Xadd
if xadd != signext24 ( xadd ) {
// If the relocation target would overflow the addend, then target
// a linker-manufactured label symbol with a smaller addend instead.
2021-04-20 14:12:04 -04:00
label := ldr . Lookup ( offsetLabelName ( ldr , rs , xadd / machoRelocLimit * machoRelocLimit ) , ldr . SymVersion ( rs ) )
2020-12-14 18:52:13 -05:00
if label != 0 {
xadd = ldr . SymValue ( rs ) + xadd - ldr . SymValue ( label )
rs = label
}
if xadd != signext24 ( xadd ) {
ldr . Errorf ( s , "internal error: relocation addend overflow: %s+0x%x" , ldr . SymName ( rs ) , xadd )
}
}
2015-04-10 21:28:09 -04:00
2022-07-11 02:40:14 +00:00
if ldr . SymType ( rs ) == sym . SHOSTOBJ || rt == objabi . R_CALLARM64 ||
rt == objabi . R_ARM64_PCREL_LDST8 || rt == objabi . R_ARM64_PCREL_LDST16 ||
rt == objabi . R_ARM64_PCREL_LDST32 || rt == objabi . R_ARM64_PCREL_LDST64 ||
rt == objabi . R_ADDRARM64 || rt == objabi . R_ARM64_GOTPCREL {
2020-05-06 12:33:36 -04:00
if ldr . SymDynid ( rs ) < 0 {
ldr . Errorf ( s , "reloc %d (%s) to non-macho symbol %s type=%d (%s)" , rt , sym . RelocName ( arch , rt ) , ldr . SymName ( rs ) , ldr . SymType ( rs ) , ldr . SymType ( rs ) )
2017-08-27 22:00:00 +09:00
return false
2015-04-10 21:28:09 -04:00
}
2020-05-06 12:33:36 -04:00
v = uint32 ( ldr . SymDynid ( rs ) )
2015-04-10 21:28:09 -04:00
v |= 1 << 27 // external relocation
} else {
2020-05-06 12:33:36 -04:00
v = uint32 ( ldr . SymSect ( rs ) . Extnum )
2015-04-10 21:28:09 -04:00
if v == 0 {
2020-05-06 12:33:36 -04:00
ldr . Errorf ( s , "reloc %d (%s) to symbol %s in non-macho section %s type=%d (%s)" , rt , sym . RelocName ( arch , rt ) , ldr . SymName ( rs ) , ldr . SymSect ( rs ) . Name , ldr . SymType ( rs ) , ldr . SymType ( rs ) )
2017-08-27 22:00:00 +09:00
return false
2015-04-10 21:28:09 -04:00
}
}
2020-05-06 12:33:36 -04:00
switch rt {
2015-04-10 21:28:09 -04:00
default :
2017-08-27 22:00:00 +09:00
return false
2017-04-18 12:53:25 -07:00
case objabi . R_ADDR :
2015-04-10 21:28:09 -04:00
v |= ld . MACHO_ARM64_RELOC_UNSIGNED << 28
2017-04-18 12:53:25 -07:00
case objabi . R_CALLARM64 :
2020-12-14 18:52:13 -05:00
if xadd != 0 {
2022-08-02 17:39:11 -04:00
out . Write32 ( uint32 ( sectoff ) )
out . Write32 ( ( ld . MACHO_ARM64_RELOC_ADDEND << 28 ) | ( 2 << 25 ) | uint32 ( xadd & 0xffffff ) )
2015-04-10 21:28:09 -04:00
}
v |= 1 << 24 // pc-relative bit
v |= ld . MACHO_ARM64_RELOC_BRANCH26 << 28
2022-07-11 02:40:14 +00:00
case objabi . R_ADDRARM64 ,
objabi . R_ARM64_PCREL_LDST8 ,
objabi . R_ARM64_PCREL_LDST16 ,
objabi . R_ARM64_PCREL_LDST32 ,
objabi . R_ARM64_PCREL_LDST64 :
2020-05-06 12:33:36 -04:00
siz = 4
2015-04-10 21:28:09 -04:00
// Two relocation entries: MACHO_ARM64_RELOC_PAGEOFF12 MACHO_ARM64_RELOC_PAGE21
// if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
if r . Xadd != 0 {
2017-10-01 02:37:20 +00:00
out . Write32 ( uint32 ( sectoff + 4 ) )
2020-12-14 18:52:13 -05:00
out . Write32 ( ( ld . MACHO_ARM64_RELOC_ADDEND << 28 ) | ( 2 << 25 ) | uint32 ( xadd & 0xffffff ) )
2015-04-10 21:28:09 -04:00
}
2017-10-01 02:37:20 +00:00
out . Write32 ( uint32 ( sectoff + 4 ) )
out . Write32 ( v | ( ld . MACHO_ARM64_RELOC_PAGEOFF12 << 28 ) | ( 2 << 25 ) )
2015-04-10 21:28:09 -04:00
if r . Xadd != 0 {
2017-10-01 02:37:20 +00:00
out . Write32 ( uint32 ( sectoff ) )
2020-12-14 18:52:13 -05:00
out . Write32 ( ( ld . MACHO_ARM64_RELOC_ADDEND << 28 ) | ( 2 << 25 ) | uint32 ( xadd & 0xffffff ) )
2015-04-10 21:28:09 -04:00
}
v |= 1 << 24 // pc-relative bit
v |= ld . MACHO_ARM64_RELOC_PAGE21 << 28
2020-10-03 16:26:37 -04:00
case objabi . R_ARM64_GOTPCREL :
siz = 4
// Two relocation entries: MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 MACHO_ARM64_RELOC_GOT_LOAD_PAGE21
// if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
if r . Xadd != 0 {
out . Write32 ( uint32 ( sectoff + 4 ) )
2020-12-14 18:52:13 -05:00
out . Write32 ( ( ld . MACHO_ARM64_RELOC_ADDEND << 28 ) | ( 2 << 25 ) | uint32 ( xadd & 0xffffff ) )
2020-10-03 16:26:37 -04:00
}
out . Write32 ( uint32 ( sectoff + 4 ) )
out . Write32 ( v | ( ld . MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 << 28 ) | ( 2 << 25 ) )
if r . Xadd != 0 {
out . Write32 ( uint32 ( sectoff ) )
2020-12-14 18:52:13 -05:00
out . Write32 ( ( ld . MACHO_ARM64_RELOC_ADDEND << 28 ) | ( 2 << 25 ) | uint32 ( xadd & 0xffffff ) )
2020-10-03 16:26:37 -04:00
}
v |= 1 << 24 // pc-relative bit
v |= ld . MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 << 28
2015-04-10 21:28:09 -04:00
}
2020-05-06 12:33:36 -04:00
switch siz {
2015-04-10 21:28:09 -04:00
default :
2017-08-27 22:00:00 +09:00
return false
2015-04-10 21:28:09 -04:00
case 1 :
v |= 0 << 25
case 2 :
v |= 1 << 25
case 4 :
v |= 2 << 25
case 8 :
v |= 3 << 25
}
2017-10-01 02:37:20 +00:00
out . Write32 ( uint32 ( sectoff ) )
out . Write32 ( v )
2017-08-27 22:00:00 +09:00
return true
2015-03-08 04:32:55 -04:00
}
2021-01-27 12:35:16 -05:00
func pereloc1 ( arch * sys . Arch , out * ld . OutBuf , ldr * loader . Loader , s loader . Sym , r loader . ExtReloc , sectoff int64 ) bool {
rs := r . Xsym
rt := r . Type
2022-07-11 02:40:14 +00:00
if ( rt == objabi . R_ADDRARM64 || rt == objabi . R_ARM64_PCREL_LDST8 || rt == objabi . R_ARM64_PCREL_LDST16 ||
rt == objabi . R_ARM64_PCREL_LDST32 || rt == objabi . R_ARM64_PCREL_LDST64 ) && r . Xadd != signext21 ( r . Xadd ) {
2021-04-20 14:12:04 -04:00
// If the relocation target would overflow the addend, then target
// a linker-manufactured label symbol with a smaller addend instead.
label := ldr . Lookup ( offsetLabelName ( ldr , rs , r . Xadd / peRelocLimit * peRelocLimit ) , ldr . SymVersion ( rs ) )
if label == 0 {
ldr . Errorf ( s , "invalid relocation: %v %s+0x%x" , rt , ldr . SymName ( rs ) , r . Xadd )
return false
}
rs = label
}
if rt == objabi . R_CALLARM64 && r . Xadd != 0 {
label := ldr . Lookup ( offsetLabelName ( ldr , rs , r . Xadd ) , ldr . SymVersion ( rs ) )
if label == 0 {
ldr . Errorf ( s , "invalid relocation: %v %s+0x%x" , rt , ldr . SymName ( rs ) , r . Xadd )
return false
}
rs = label
}
symdynid := ldr . SymDynid ( rs )
if symdynid < 0 {
2021-01-27 12:35:16 -05:00
ldr . Errorf ( s , "reloc %d (%s) to non-coff symbol %s type=%d (%s)" , rt , sym . RelocName ( arch , rt ) , ldr . SymName ( rs ) , ldr . SymType ( rs ) , ldr . SymType ( rs ) )
return false
}
switch rt {
default :
return false
case objabi . R_DWARFSECREF :
2021-04-20 14:12:04 -04:00
out . Write32 ( uint32 ( sectoff ) )
out . Write32 ( uint32 ( symdynid ) )
out . Write16 ( ld . IMAGE_REL_ARM64_SECREL )
2021-01-27 12:35:16 -05:00
case objabi . R_ADDR :
2021-04-20 14:12:04 -04:00
out . Write32 ( uint32 ( sectoff ) )
out . Write32 ( uint32 ( symdynid ) )
2021-01-27 12:35:16 -05:00
if r . Size == 8 {
2021-04-20 14:12:04 -04:00
out . Write16 ( ld . IMAGE_REL_ARM64_ADDR64 )
2021-01-27 12:35:16 -05:00
} else {
2021-04-20 14:12:04 -04:00
out . Write16 ( ld . IMAGE_REL_ARM64_ADDR32 )
2021-01-27 12:35:16 -05:00
}
2021-04-20 14:12:04 -04:00
case objabi . R_ADDRARM64 :
// Note: r.Xadd has been taken care of below, in archreloc.
out . Write32 ( uint32 ( sectoff ) )
out . Write32 ( uint32 ( symdynid ) )
out . Write16 ( ld . IMAGE_REL_ARM64_PAGEBASE_REL21 )
out . Write32 ( uint32 ( sectoff + 4 ) )
out . Write32 ( uint32 ( symdynid ) )
out . Write16 ( ld . IMAGE_REL_ARM64_PAGEOFFSET_12A )
2022-07-11 02:40:14 +00:00
case objabi . R_ARM64_PCREL_LDST8 ,
objabi . R_ARM64_PCREL_LDST16 ,
objabi . R_ARM64_PCREL_LDST32 ,
objabi . R_ARM64_PCREL_LDST64 :
// Note: r.Xadd has been taken care of below, in archreloc.
out . Write32 ( uint32 ( sectoff ) )
out . Write32 ( uint32 ( symdynid ) )
out . Write16 ( ld . IMAGE_REL_ARM64_PAGEBASE_REL21 )
out . Write32 ( uint32 ( sectoff + 4 ) )
out . Write32 ( uint32 ( symdynid ) )
out . Write16 ( ld . IMAGE_REL_ARM64_PAGEOFFSET_12L )
2021-04-20 14:12:04 -04:00
case objabi . R_CALLARM64 :
// Note: r.Xadd has been taken care of above, by using a label pointing into the middle of the function.
out . Write32 ( uint32 ( sectoff ) )
out . Write32 ( uint32 ( symdynid ) )
out . Write16 ( ld . IMAGE_REL_ARM64_BRANCH26 )
}
2021-01-27 12:35:16 -05:00
return true
}
2020-07-29 13:26:50 -04:00
func archreloc ( target * ld . Target , ldr * loader . Loader , syms * ld . ArchSyms , r loader . Reloc , s loader . Sym , val int64 ) ( int64 , int , bool ) {
2020-07-04 18:14:08 -04:00
const noExtReloc = 0
2020-05-04 14:19:22 -04:00
const isOk = true
2021-09-21 17:52:19 -04:00
rs := r . Sym ( )
2020-05-04 14:19:22 -04:00
2020-02-24 21:04:50 -05:00
if target . IsExternal ( ) {
2020-07-04 18:14:08 -04:00
nExtReloc := 0
switch rt := r . Type ( ) ; rt {
2015-04-03 04:37:11 -04:00
default :
2020-05-04 14:24:03 -04:00
case objabi . R_ARM64_GOTPCREL ,
2022-07-11 02:40:14 +00:00
objabi . R_ARM64_PCREL_LDST8 ,
objabi . R_ARM64_PCREL_LDST16 ,
objabi . R_ARM64_PCREL_LDST32 ,
objabi . R_ARM64_PCREL_LDST64 ,
2020-05-04 14:24:03 -04:00
objabi . R_ADDRARM64 :
2020-07-04 18:14:08 -04:00
2015-04-03 04:37:18 -04:00
// set up addend for eventual relocation via outer symbol.
2020-05-04 14:19:22 -04:00
rs , off := ld . FoldSubSymbolOffset ( ldr , rs )
2020-07-17 17:56:17 -04:00
xadd := r . Add ( ) + off
2020-05-04 14:19:22 -04:00
rst := ldr . SymType ( rs )
if rst != sym . SHOSTOBJ && rst != sym . SDYNIMPORT && ldr . SymSect ( rs ) == nil {
ldr . Errorf ( s , "missing section for %s" , ldr . SymName ( rs ) )
2015-04-03 04:37:18 -04:00
}
2020-07-06 18:44:42 -04:00
nExtReloc = 2 // need two ELF/Mach-O relocations. see elfreloc1/machoreloc1
2020-10-03 16:26:37 -04:00
if target . IsDarwin ( ) && xadd != 0 {
2020-07-06 18:44:42 -04:00
nExtReloc = 4 // need another two relocations for non-zero addend
}
2021-04-20 14:12:04 -04:00
if target . IsWindows ( ) {
2015-08-03 14:08:17 +12:00
var o0 , o1 uint32
2020-02-24 21:04:50 -05:00
if target . IsBigEndian ( ) {
cmd/link: fewer allocs in ld.Arch.Archreloc
Archreloc had this signature:
func(*Link, *sym.Reloc, *sym.Symbol, *int64) bool
The last *int64 argument is used as out parameter.
Passed valus could be allocated on stack, but escape analysis
fails here, leading to high number of unwanted allocs.
If instead 4th arg is passed by value, and modified values is returned,
no problems with allocations arise:
func(*Link, *sym.Reloc, *sym.Symbol, int64) (int64, bool)
There are 2 benefits:
1. code becomes more readable.
2. less allocations.
For linking "hello world" example from net/http:
name old time/op new time/op delta
Linker-4 530ms ± 2% 520ms ± 2% -1.83% (p=0.001 n=17+16)
It's top 1 in alloc_objects from memprofile:
flat flat% sum% cum cum%
229379 33.05% 33.05% 229379 33.05% cmd/link/internal/ld.relocsym
...
list relocsym:
229379 229379 (flat, cum) 33.05% of Total
229379 229379 183: var o int64
After the patch, ~230k of int64 allocs (~ 1.75mb) removed.
Passes toolshash-check (toolstash cmp).
Change-Id: I25504fe27967bcff70c4b7338790f3921d15473d
Reviewed-on: https://go-review.googlesource.com/113637
Run-TryBot: Iskander Sharipov <iskander.sharipov@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2018-05-17 19:50:29 +03:00
o0 = uint32 ( val >> 32 )
o1 = uint32 ( val )
2015-08-03 14:08:17 +12:00
} else {
cmd/link: fewer allocs in ld.Arch.Archreloc
Archreloc had this signature:
func(*Link, *sym.Reloc, *sym.Symbol, *int64) bool
The last *int64 argument is used as out parameter.
Passed valus could be allocated on stack, but escape analysis
fails here, leading to high number of unwanted allocs.
If instead 4th arg is passed by value, and modified values is returned,
no problems with allocations arise:
func(*Link, *sym.Reloc, *sym.Symbol, int64) (int64, bool)
There are 2 benefits:
1. code becomes more readable.
2. less allocations.
For linking "hello world" example from net/http:
name old time/op new time/op delta
Linker-4 530ms ± 2% 520ms ± 2% -1.83% (p=0.001 n=17+16)
It's top 1 in alloc_objects from memprofile:
flat flat% sum% cum cum%
229379 33.05% 33.05% 229379 33.05% cmd/link/internal/ld.relocsym
...
list relocsym:
229379 229379 (flat, cum) 33.05% of Total
229379 229379 183: var o int64
After the patch, ~230k of int64 allocs (~ 1.75mb) removed.
Passes toolshash-check (toolstash cmp).
Change-Id: I25504fe27967bcff70c4b7338790f3921d15473d
Reviewed-on: https://go-review.googlesource.com/113637
Run-TryBot: Iskander Sharipov <iskander.sharipov@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2018-05-17 19:50:29 +03:00
o0 = uint32 ( val )
o1 = uint32 ( val >> 32 )
2015-08-03 14:08:17 +12:00
}
2021-04-20 14:12:04 -04:00
// The first instruction (ADRP) has a 21-bit immediate field,
2022-07-11 02:40:14 +00:00
// and the second (ADD or LD/ST) has a 12-bit immediate field.
2021-04-20 14:12:04 -04:00
// The first instruction is only for high bits, but to get the carry bits right we have
// to put the full addend, including the bottom 12 bits again.
// That limits the distance of any addend to only 21 bits.
2022-07-11 02:40:14 +00:00
// But we assume that ADRP's top bit will be interpreted as a sign bit,
2021-04-20 14:12:04 -04:00
// so we only use 20 bits.
// pereloc takes care of introducing new symbol labels
// every megabyte for longer relocations.
xadd := uint32 ( xadd )
o0 |= ( xadd & 3 ) << 29 | ( xadd & 0xffffc ) << 3
2022-07-11 02:40:14 +00:00
switch rt {
case objabi . R_ARM64_PCREL_LDST8 , objabi . R_ADDRARM64 :
o1 |= ( xadd & 0xfff ) << 10
case objabi . R_ARM64_PCREL_LDST16 :
if xadd & 0x1 != 0 {
ldr . Errorf ( s , "offset for 16-bit load/store has unaligned value %d" , xadd & 0xfff )
}
o1 |= ( ( xadd & 0xfff ) >> 1 ) << 10
case objabi . R_ARM64_PCREL_LDST32 :
if xadd & 0x3 != 0 {
ldr . Errorf ( s , "offset for 32-bit load/store has unaligned value %d" , xadd & 0xfff )
}
o1 |= ( ( xadd & 0xfff ) >> 2 ) << 10
case objabi . R_ARM64_PCREL_LDST64 :
if xadd & 0x7 != 0 {
ldr . Errorf ( s , "offset for 64-bit load/store has unaligned value %d" , xadd & 0xfff )
}
o1 |= ( ( xadd & 0xfff ) >> 3 ) << 10
}
2021-04-20 14:12:04 -04:00
2020-02-24 21:04:50 -05:00
if target . IsBigEndian ( ) {
cmd/link: fewer allocs in ld.Arch.Archreloc
Archreloc had this signature:
func(*Link, *sym.Reloc, *sym.Symbol, *int64) bool
The last *int64 argument is used as out parameter.
Passed valus could be allocated on stack, but escape analysis
fails here, leading to high number of unwanted allocs.
If instead 4th arg is passed by value, and modified values is returned,
no problems with allocations arise:
func(*Link, *sym.Reloc, *sym.Symbol, int64) (int64, bool)
There are 2 benefits:
1. code becomes more readable.
2. less allocations.
For linking "hello world" example from net/http:
name old time/op new time/op delta
Linker-4 530ms ± 2% 520ms ± 2% -1.83% (p=0.001 n=17+16)
It's top 1 in alloc_objects from memprofile:
flat flat% sum% cum cum%
229379 33.05% 33.05% 229379 33.05% cmd/link/internal/ld.relocsym
...
list relocsym:
229379 229379 (flat, cum) 33.05% of Total
229379 229379 183: var o int64
After the patch, ~230k of int64 allocs (~ 1.75mb) removed.
Passes toolshash-check (toolstash cmp).
Change-Id: I25504fe27967bcff70c4b7338790f3921d15473d
Reviewed-on: https://go-review.googlesource.com/113637
Run-TryBot: Iskander Sharipov <iskander.sharipov@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2018-05-17 19:50:29 +03:00
val = int64 ( o0 ) << 32 | int64 ( o1 )
2015-08-03 14:08:17 +12:00
} else {
cmd/link: fewer allocs in ld.Arch.Archreloc
Archreloc had this signature:
func(*Link, *sym.Reloc, *sym.Symbol, *int64) bool
The last *int64 argument is used as out parameter.
Passed valus could be allocated on stack, but escape analysis
fails here, leading to high number of unwanted allocs.
If instead 4th arg is passed by value, and modified values is returned,
no problems with allocations arise:
func(*Link, *sym.Reloc, *sym.Symbol, int64) (int64, bool)
There are 2 benefits:
1. code becomes more readable.
2. less allocations.
For linking "hello world" example from net/http:
name old time/op new time/op delta
Linker-4 530ms ± 2% 520ms ± 2% -1.83% (p=0.001 n=17+16)
It's top 1 in alloc_objects from memprofile:
flat flat% sum% cum cum%
229379 33.05% 33.05% 229379 33.05% cmd/link/internal/ld.relocsym
...
list relocsym:
229379 229379 (flat, cum) 33.05% of Total
229379 229379 183: var o int64
After the patch, ~230k of int64 allocs (~ 1.75mb) removed.
Passes toolshash-check (toolstash cmp).
Change-Id: I25504fe27967bcff70c4b7338790f3921d15473d
Reviewed-on: https://go-review.googlesource.com/113637
Run-TryBot: Iskander Sharipov <iskander.sharipov@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2018-05-17 19:50:29 +03:00
val = int64 ( o1 ) << 32 | int64 ( o0 )
2015-08-03 14:08:17 +12:00
}
2015-04-10 21:28:09 -04:00
}
2020-07-04 18:14:08 -04:00
return val , nExtReloc , isOk
2022-08-02 17:39:11 -04:00
case objabi . R_CALLARM64 :
2020-07-04 18:14:08 -04:00
nExtReloc = 1
2022-08-02 17:39:11 -04:00
if target . IsDarwin ( ) && r . Add ( ) != 0 {
nExtReloc = 2 // need another relocation for addend
2020-07-04 18:14:08 -04:00
}
return val , nExtReloc , isOk
2021-04-20 14:12:04 -04:00
2022-08-02 17:39:11 -04:00
case objabi . R_ARM64_TLS_LE :
nExtReloc = 1
return val , nExtReloc , isOk
case objabi . R_ARM64_TLS_IE :
nExtReloc = 2 // need two ELF relocations. see elfreloc1
return val , nExtReloc , isOk
2021-04-20 14:12:04 -04:00
case objabi . R_ADDR :
if target . IsWindows ( ) && r . Add ( ) != 0 {
if r . Siz ( ) == 8 {
val = r . Add ( )
} else if target . IsBigEndian ( ) {
val = int64 ( uint32 ( val ) ) | int64 ( r . Add ( ) ) << 32
} else {
val = val >> 32 << 32 | int64 ( uint32 ( r . Add ( ) ) )
}
return val , 1 , true
}
2015-04-03 04:37:11 -04:00
}
2015-03-08 04:32:55 -04:00
}
2022-07-11 02:40:14 +00:00
switch rt := r . Type ( ) ; rt {
case objabi . R_ADDRARM64 ,
objabi . R_ARM64_PCREL_LDST8 ,
objabi . R_ARM64_PCREL_LDST16 ,
objabi . R_ARM64_PCREL_LDST32 ,
objabi . R_ARM64_PCREL_LDST64 :
2020-05-04 14:19:22 -04:00
t := ldr . SymAddr ( rs ) + r . Add ( ) - ( ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) ) &^ 0xfff )
2015-11-13 08:32:33 +00:00
if t >= 1 << 32 || t < - 1 << 32 {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "program too large, address relocation distance = %d" , t )
2015-11-13 08:32:33 +00:00
}
var o0 , o1 uint32
2020-02-24 21:04:50 -05:00
if target . IsBigEndian ( ) {
cmd/link: fewer allocs in ld.Arch.Archreloc
Archreloc had this signature:
func(*Link, *sym.Reloc, *sym.Symbol, *int64) bool
The last *int64 argument is used as out parameter.
Passed valus could be allocated on stack, but escape analysis
fails here, leading to high number of unwanted allocs.
If instead 4th arg is passed by value, and modified values is returned,
no problems with allocations arise:
func(*Link, *sym.Reloc, *sym.Symbol, int64) (int64, bool)
There are 2 benefits:
1. code becomes more readable.
2. less allocations.
For linking "hello world" example from net/http:
name old time/op new time/op delta
Linker-4 530ms ± 2% 520ms ± 2% -1.83% (p=0.001 n=17+16)
It's top 1 in alloc_objects from memprofile:
flat flat% sum% cum cum%
229379 33.05% 33.05% 229379 33.05% cmd/link/internal/ld.relocsym
...
list relocsym:
229379 229379 (flat, cum) 33.05% of Total
229379 229379 183: var o int64
After the patch, ~230k of int64 allocs (~ 1.75mb) removed.
Passes toolshash-check (toolstash cmp).
Change-Id: I25504fe27967bcff70c4b7338790f3921d15473d
Reviewed-on: https://go-review.googlesource.com/113637
Run-TryBot: Iskander Sharipov <iskander.sharipov@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2018-05-17 19:50:29 +03:00
o0 = uint32 ( val >> 32 )
o1 = uint32 ( val )
2015-11-13 08:32:33 +00:00
} else {
cmd/link: fewer allocs in ld.Arch.Archreloc
Archreloc had this signature:
func(*Link, *sym.Reloc, *sym.Symbol, *int64) bool
The last *int64 argument is used as out parameter.
Passed valus could be allocated on stack, but escape analysis
fails here, leading to high number of unwanted allocs.
If instead 4th arg is passed by value, and modified values is returned,
no problems with allocations arise:
func(*Link, *sym.Reloc, *sym.Symbol, int64) (int64, bool)
There are 2 benefits:
1. code becomes more readable.
2. less allocations.
For linking "hello world" example from net/http:
name old time/op new time/op delta
Linker-4 530ms ± 2% 520ms ± 2% -1.83% (p=0.001 n=17+16)
It's top 1 in alloc_objects from memprofile:
flat flat% sum% cum cum%
229379 33.05% 33.05% 229379 33.05% cmd/link/internal/ld.relocsym
...
list relocsym:
229379 229379 (flat, cum) 33.05% of Total
229379 229379 183: var o int64
After the patch, ~230k of int64 allocs (~ 1.75mb) removed.
Passes toolshash-check (toolstash cmp).
Change-Id: I25504fe27967bcff70c4b7338790f3921d15473d
Reviewed-on: https://go-review.googlesource.com/113637
Run-TryBot: Iskander Sharipov <iskander.sharipov@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2018-05-17 19:50:29 +03:00
o0 = uint32 ( val )
o1 = uint32 ( val >> 32 )
2015-11-13 08:32:33 +00:00
}
o0 |= ( uint32 ( ( t >> 12 ) & 3 ) << 29 ) | ( uint32 ( ( t >> 12 >> 2 ) & 0x7ffff ) << 5 )
2022-07-11 02:40:14 +00:00
switch rt {
case objabi . R_ARM64_PCREL_LDST8 , objabi . R_ADDRARM64 :
o1 |= uint32 ( t & 0xfff ) << 10
case objabi . R_ARM64_PCREL_LDST16 :
if t & 0x1 != 0 {
ldr . Errorf ( s , "offset for 16-bit load/store has unaligned value %d" , t & 0xfff )
}
o1 |= ( uint32 ( t & 0xfff ) >> 1 ) << 10
case objabi . R_ARM64_PCREL_LDST32 :
if t & 0x3 != 0 {
ldr . Errorf ( s , "offset for 32-bit load/store has unaligned value %d" , t & 0xfff )
}
o1 |= ( uint32 ( t & 0xfff ) >> 2 ) << 10
case objabi . R_ARM64_PCREL_LDST64 :
if t & 0x7 != 0 {
ldr . Errorf ( s , "offset for 64-bit load/store has unaligned value %d" , t & 0xfff )
}
o1 |= ( uint32 ( t & 0xfff ) >> 3 ) << 10
}
2015-11-13 08:32:33 +00:00
// when laid out, the instruction order must always be o1, o2.
2020-02-24 21:04:50 -05:00
if target . IsBigEndian ( ) {
2020-07-04 18:14:08 -04:00
return int64 ( o0 ) << 32 | int64 ( o1 ) , noExtReloc , true
2015-11-13 08:32:33 +00:00
}
2020-07-04 18:14:08 -04:00
return int64 ( o1 ) << 32 | int64 ( o0 ) , noExtReloc , true
2018-09-26 10:12:18 +00:00
2017-04-18 12:53:25 -07:00
case objabi . R_ARM64_TLS_LE :
2020-02-24 21:04:50 -05:00
if target . IsDarwin ( ) {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "TLS reloc on unsupported OS %v" , target . HeadType )
2015-08-11 14:10:03 +12:00
}
// The TCB is two pointers. This is not documented anywhere, but is
// de facto part of the ABI.
2020-05-04 14:19:22 -04:00
v := ldr . SymValue ( rs ) + int64 ( 2 * target . Arch . PtrSize )
2015-08-11 14:10:03 +12:00
if v < 0 || v >= 32678 {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "TLS offset out of range %d" , v )
2015-08-11 14:10:03 +12:00
}
2020-07-04 18:14:08 -04:00
return val | ( v << 5 ) , noExtReloc , true
2018-09-26 10:12:18 +00:00
case objabi . R_ARM64_TLS_IE :
2020-02-24 21:04:50 -05:00
if target . IsPIE ( ) && target . IsElf ( ) {
2018-09-26 10:12:18 +00:00
// We are linking the final executable, so we
// can optimize any TLS IE relocation to LE.
2020-05-04 14:19:22 -04:00
2020-02-24 21:04:50 -05:00
if ! target . IsLinux ( ) {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "TLS reloc on unsupported OS %v" , target . HeadType )
2018-09-26 10:12:18 +00:00
}
// The TCB is two pointers. This is not documented anywhere, but is
// de facto part of the ABI.
2020-05-04 14:19:22 -04:00
v := ldr . SymAddr ( rs ) + int64 ( 2 * target . Arch . PtrSize ) + r . Add ( )
2018-09-26 10:12:18 +00:00
if v < 0 || v >= 32678 {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "TLS offset out of range %d" , v )
2018-09-26 10:12:18 +00:00
}
var o0 , o1 uint32
2020-02-24 21:04:50 -05:00
if target . IsBigEndian ( ) {
2018-09-26 10:12:18 +00:00
o0 = uint32 ( val >> 32 )
o1 = uint32 ( val )
} else {
o0 = uint32 ( val )
o1 = uint32 ( val >> 32 )
}
// R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21
// turn ADRP to MOVZ
o0 = 0xd2a00000 | uint32 ( o0 & 0x1f ) | ( uint32 ( ( v >> 16 ) & 0xffff ) << 5 )
// R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC
// turn LD64 to MOVK
if v & 3 != 0 {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "invalid address: %x for relocation type: R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC" , v )
2018-09-26 10:12:18 +00:00
}
o1 = 0xf2800000 | uint32 ( o1 & 0x1f ) | ( uint32 ( v & 0xffff ) << 5 )
// when laid out, the instruction order must always be o0, o1.
2020-02-24 21:04:50 -05:00
if target . IsBigEndian ( ) {
2020-07-04 18:14:08 -04:00
return int64 ( o0 ) << 32 | int64 ( o1 ) , noExtReloc , isOk
2018-09-26 10:12:18 +00:00
}
2020-07-04 18:14:08 -04:00
return int64 ( o1 ) << 32 | int64 ( o0 ) , noExtReloc , isOk
2018-09-26 10:12:18 +00:00
} else {
2020-05-04 14:19:22 -04:00
log . Fatalf ( "cannot handle R_ARM64_TLS_IE (sym %s) when linking internally" , ldr . SymName ( s ) )
2018-09-26 10:12:18 +00:00
}
2017-04-18 12:53:25 -07:00
case objabi . R_CALLARM64 :
2018-09-26 10:12:18 +00:00
var t int64
2020-05-04 14:19:22 -04:00
if ldr . SymType ( rs ) == sym . SDYNIMPORT {
2020-05-15 18:35:05 -04:00
t = ( ldr . SymAddr ( syms . PLT ) + r . Add ( ) ) - ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) )
2018-09-26 10:12:18 +00:00
} else {
2020-05-04 14:19:22 -04:00
t = ( ldr . SymAddr ( rs ) + r . Add ( ) ) - ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) )
2018-09-26 10:12:18 +00:00
}
2015-08-03 14:08:17 +12:00
if t >= 1 << 27 || t < - 1 << 27 {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "program too large, call relocation distance = %d" , t )
2015-08-03 14:08:17 +12:00
}
2020-07-04 18:14:08 -04:00
return val | ( ( t >> 2 ) & 0x03ffffff ) , noExtReloc , true
2018-09-26 10:12:18 +00:00
case objabi . R_ARM64_GOT :
2020-07-21 17:59:48 -04:00
if ( val >> 24 ) & 0x9f == 0x90 {
2018-09-26 10:12:18 +00:00
// R_AARCH64_ADR_GOT_PAGE
// patch instruction: adrp
2020-05-04 14:19:22 -04:00
t := ldr . SymAddr ( rs ) + r . Add ( ) - ( ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) ) &^ 0xfff )
2018-09-26 10:12:18 +00:00
if t >= 1 << 32 || t < - 1 << 32 {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "program too large, address relocation distance = %d" , t )
2018-09-26 10:12:18 +00:00
}
var o0 uint32
o0 |= ( uint32 ( ( t >> 12 ) & 3 ) << 29 ) | ( uint32 ( ( t >> 12 >> 2 ) & 0x7ffff ) << 5 )
2020-07-04 18:14:08 -04:00
return val | int64 ( o0 ) , noExtReloc , isOk
2020-07-21 17:59:48 -04:00
} else if val >> 24 == 0xf9 {
2018-09-26 10:12:18 +00:00
// R_AARCH64_LD64_GOT_LO12_NC
// patch instruction: ldr
2020-05-04 14:19:22 -04:00
t := ldr . SymAddr ( rs ) + r . Add ( ) - ( ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) ) &^ 0xfff )
2018-09-26 10:12:18 +00:00
if t & 7 != 0 {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "invalid address: %x for relocation type: R_AARCH64_LD64_GOT_LO12_NC" , t )
2018-09-26 10:12:18 +00:00
}
var o1 uint32
o1 |= uint32 ( t & 0xfff ) << ( 10 - 3 )
2020-07-04 18:14:08 -04:00
return val | int64 ( uint64 ( o1 ) ) , noExtReloc , isOk
2018-09-26 10:12:18 +00:00
} else {
2020-07-21 17:59:48 -04:00
ldr . Errorf ( s , "unsupported instruction for %x R_GOTARM64" , val )
2018-09-26 10:12:18 +00:00
}
case objabi . R_ARM64_PCREL :
2020-07-21 17:59:48 -04:00
if ( val >> 24 ) & 0x9f == 0x90 {
2018-09-26 10:12:18 +00:00
// R_AARCH64_ADR_PREL_PG_HI21
// patch instruction: adrp
2020-05-04 14:19:22 -04:00
t := ldr . SymAddr ( rs ) + r . Add ( ) - ( ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) ) &^ 0xfff )
2018-09-26 10:12:18 +00:00
if t >= 1 << 32 || t < - 1 << 32 {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "program too large, address relocation distance = %d" , t )
2018-09-26 10:12:18 +00:00
}
o0 := ( uint32 ( ( t >> 12 ) & 3 ) << 29 ) | ( uint32 ( ( t >> 12 >> 2 ) & 0x7ffff ) << 5 )
2020-07-04 18:14:08 -04:00
return val | int64 ( o0 ) , noExtReloc , isOk
2020-10-14 21:15:37 -04:00
} else if ( val >> 24 ) & 0x9f == 0x91 {
// ELF R_AARCH64_ADD_ABS_LO12_NC or Mach-O ARM64_RELOC_PAGEOFF12
2018-09-26 10:12:18 +00:00
// patch instruction: add
2020-05-04 14:19:22 -04:00
t := ldr . SymAddr ( rs ) + r . Add ( ) - ( ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) ) &^ 0xfff )
2018-09-26 10:12:18 +00:00
o1 := uint32 ( t & 0xfff ) << 10
2020-07-04 18:14:08 -04:00
return val | int64 ( o1 ) , noExtReloc , isOk
2020-10-14 21:15:37 -04:00
} else if ( val >> 24 ) & 0x3b == 0x39 {
// Mach-O ARM64_RELOC_PAGEOFF12
// patch ldr/str(b/h/w/d/q) (integer or vector) instructions, which have different scaling factors.
// Mach-O uses same relocation type for them.
shift := uint32 ( val ) >> 30
if shift == 0 && ( val >> 20 ) & 0x048 == 0x048 { // 128-bit vector load
shift = 4
}
t := ldr . SymAddr ( rs ) + r . Add ( ) - ( ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) ) &^ 0xfff )
if t & ( 1 << shift - 1 ) != 0 {
ldr . Errorf ( s , "invalid address: %x for relocation type: ARM64_RELOC_PAGEOFF12" , t )
}
o1 := ( uint32 ( t & 0xfff ) >> shift ) << 10
return val | int64 ( o1 ) , noExtReloc , isOk
2018-09-26 10:12:18 +00:00
} else {
2020-10-14 21:15:37 -04:00
ldr . Errorf ( s , "unsupported instruction for %x R_ARM64_PCREL" , val )
2018-09-26 10:12:18 +00:00
}
case objabi . R_ARM64_LDST8 :
2020-05-04 14:19:22 -04:00
t := ldr . SymAddr ( rs ) + r . Add ( ) - ( ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) ) &^ 0xfff )
2018-09-26 10:12:18 +00:00
o0 := uint32 ( t & 0xfff ) << 10
2020-07-04 18:14:08 -04:00
return val | int64 ( o0 ) , noExtReloc , true
2018-09-26 10:12:18 +00:00
2020-11-18 04:00:57 +00:00
case objabi . R_ARM64_LDST16 :
t := ldr . SymAddr ( rs ) + r . Add ( ) - ( ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) ) &^ 0xfff )
if t & 1 != 0 {
ldr . Errorf ( s , "invalid address: %x for relocation type: R_AARCH64_LDST16_ABS_LO12_NC" , t )
}
o0 := ( uint32 ( t & 0xfff ) >> 1 ) << 10
return val | int64 ( o0 ) , noExtReloc , true
2018-09-26 10:12:18 +00:00
case objabi . R_ARM64_LDST32 :
2020-05-04 14:19:22 -04:00
t := ldr . SymAddr ( rs ) + r . Add ( ) - ( ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) ) &^ 0xfff )
2018-09-26 10:12:18 +00:00
if t & 3 != 0 {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "invalid address: %x for relocation type: R_AARCH64_LDST32_ABS_LO12_NC" , t )
2018-09-26 10:12:18 +00:00
}
o0 := ( uint32 ( t & 0xfff ) >> 2 ) << 10
2020-07-04 18:14:08 -04:00
return val | int64 ( o0 ) , noExtReloc , true
2018-09-26 10:12:18 +00:00
case objabi . R_ARM64_LDST64 :
2020-05-04 14:19:22 -04:00
t := ldr . SymAddr ( rs ) + r . Add ( ) - ( ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) ) &^ 0xfff )
2018-09-26 10:12:18 +00:00
if t & 7 != 0 {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "invalid address: %x for relocation type: R_AARCH64_LDST64_ABS_LO12_NC" , t )
2018-09-26 10:12:18 +00:00
}
o0 := ( uint32 ( t & 0xfff ) >> 3 ) << 10
2020-07-04 18:14:08 -04:00
return val | int64 ( o0 ) , noExtReloc , true
2019-05-11 02:21:22 +10:00
case objabi . R_ARM64_LDST128 :
2020-05-04 14:19:22 -04:00
t := ldr . SymAddr ( rs ) + r . Add ( ) - ( ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) ) &^ 0xfff )
2019-05-11 02:21:22 +10:00
if t & 15 != 0 {
2020-05-04 14:19:22 -04:00
ldr . Errorf ( s , "invalid address: %x for relocation type: R_AARCH64_LDST128_ABS_LO12_NC" , t )
2019-05-11 02:21:22 +10:00
}
o0 := ( uint32 ( t & 0xfff ) >> 4 ) << 10
2020-07-04 18:14:08 -04:00
return val | int64 ( o0 ) , noExtReloc , true
2015-03-08 04:32:55 -04:00
}
2020-07-04 18:14:08 -04:00
return val , 0 , false
2015-03-08 04:32:55 -04:00
}
2021-03-10 15:10:05 -06:00
func archrelocvariant ( * ld . Target , * loader . Loader , loader . Reloc , sym . RelocVariant , loader . Sym , int64 , [ ] byte ) int64 {
2015-03-08 14:14:53 +01:00
log . Fatalf ( "unexpected relocation variant" )
return - 1
2015-03-08 04:32:55 -04:00
}
2020-07-29 13:26:50 -04:00
func extreloc ( target * ld . Target , ldr * loader . Loader , r loader . Reloc , s loader . Sym ) ( loader . ExtReloc , bool ) {
2020-07-17 17:56:17 -04:00
switch rt := r . Type ( ) ; rt {
case objabi . R_ARM64_GOTPCREL ,
2022-07-11 02:40:14 +00:00
objabi . R_ARM64_PCREL_LDST8 ,
objabi . R_ARM64_PCREL_LDST16 ,
objabi . R_ARM64_PCREL_LDST32 ,
objabi . R_ARM64_PCREL_LDST64 ,
2020-07-17 17:56:17 -04:00
objabi . R_ADDRARM64 :
2020-07-28 20:23:16 -04:00
rr := ld . ExtrelocViaOuterSym ( ldr , r , s )
2020-07-17 17:56:17 -04:00
return rr , true
case objabi . R_CALLARM64 ,
objabi . R_ARM64_TLS_LE ,
objabi . R_ARM64_TLS_IE :
2020-07-28 20:23:16 -04:00
return ld . ExtrelocSimple ( ldr , r ) , true
2020-07-17 17:56:17 -04:00
}
2020-07-28 20:23:16 -04:00
return loader . ExtReloc { } , false
2020-07-17 17:56:17 -04:00
}
2020-03-11 12:12:41 -04:00
func elfsetupplt ( ctxt * ld . Link , plt , gotplt * loader . SymbolBuilder , dynamic loader . Sym ) {
if plt . Size ( ) == 0 {
2018-09-26 10:12:18 +00:00
// stp x16, x30, [sp, #-16]!
// identifying information
plt . AddUint32 ( ctxt . Arch , 0xa9bf7bf0 )
// the following two instructions (adrp + ldr) load *got[2] into x17
// adrp x16, &got[0]
2020-03-11 12:12:41 -04:00
plt . AddSymRef ( ctxt . Arch , gotplt . Sym ( ) , 16 , objabi . R_ARM64_GOT , 4 )
plt . SetUint32 ( ctxt . Arch , plt . Size ( ) - 4 , 0x90000010 )
2018-09-26 10:12:18 +00:00
// <imm> is the offset value of &got[2] to &got[0], the same below
// ldr x17, [x16, <imm>]
2020-03-11 12:12:41 -04:00
plt . AddSymRef ( ctxt . Arch , gotplt . Sym ( ) , 16 , objabi . R_ARM64_GOT , 4 )
plt . SetUint32 ( ctxt . Arch , plt . Size ( ) - 4 , 0xf9400211 )
2018-09-26 10:12:18 +00:00
// add x16, x16, <imm>
2020-03-11 12:12:41 -04:00
plt . AddSymRef ( ctxt . Arch , gotplt . Sym ( ) , 16 , objabi . R_ARM64_PCREL , 4 )
plt . SetUint32 ( ctxt . Arch , plt . Size ( ) - 4 , 0x91000210 )
2018-09-26 10:12:18 +00:00
// br x17
plt . AddUint32 ( ctxt . Arch , 0xd61f0220 )
// 3 nop for place holder
plt . AddUint32 ( ctxt . Arch , 0xd503201f )
plt . AddUint32 ( ctxt . Arch , 0xd503201f )
plt . AddUint32 ( ctxt . Arch , 0xd503201f )
// check gotplt.size == 0
2020-03-11 12:12:41 -04:00
if gotplt . Size ( ) != 0 {
ctxt . Errorf ( gotplt . Sym ( ) , "got.plt is not empty at the very beginning" )
2018-09-26 10:12:18 +00:00
}
2020-03-11 12:12:41 -04:00
gotplt . AddAddrPlus ( ctxt . Arch , dynamic , 0 )
2018-09-26 10:12:18 +00:00
gotplt . AddUint64 ( ctxt . Arch , 0 )
gotplt . AddUint64 ( ctxt . Arch , 0 )
}
}
2020-05-15 18:35:05 -04:00
func addpltsym ( target * ld . Target , ldr * loader . Loader , syms * ld . ArchSyms , s loader . Sym ) {
2020-04-24 11:01:10 -04:00
if ldr . SymPlt ( s ) >= 0 {
2018-09-26 10:12:18 +00:00
return
}
2020-05-15 18:35:05 -04:00
ld . Adddynsym ( ldr , target , syms , s )
2018-09-26 10:12:18 +00:00
2020-03-04 17:25:01 -05:00
if target . IsElf ( ) {
2020-05-15 18:35:05 -04:00
plt := ldr . MakeSymbolUpdater ( syms . PLT )
gotplt := ldr . MakeSymbolUpdater ( syms . GOTPLT )
rela := ldr . MakeSymbolUpdater ( syms . RelaPLT )
2020-04-24 11:01:10 -04:00
if plt . Size ( ) == 0 {
2020-03-11 12:12:41 -04:00
panic ( "plt is not set up" )
2018-09-26 10:12:18 +00:00
}
// adrp x16, &got.plt[0]
2020-04-24 11:01:10 -04:00
plt . AddAddrPlus4 ( target . Arch , gotplt . Sym ( ) , gotplt . Size ( ) )
plt . SetUint32 ( target . Arch , plt . Size ( ) - 4 , 0x90000010 )
relocs := plt . Relocs ( )
plt . SetRelocType ( relocs . Count ( ) - 1 , objabi . R_ARM64_GOT )
2018-09-26 10:12:18 +00:00
// <offset> is the offset value of &got.plt[n] to &got.plt[0]
// ldr x17, [x16, <offset>]
2020-04-24 11:01:10 -04:00
plt . AddAddrPlus4 ( target . Arch , gotplt . Sym ( ) , gotplt . Size ( ) )
plt . SetUint32 ( target . Arch , plt . Size ( ) - 4 , 0xf9400211 )
relocs = plt . Relocs ( )
plt . SetRelocType ( relocs . Count ( ) - 1 , objabi . R_ARM64_GOT )
2018-09-26 10:12:18 +00:00
// add x16, x16, <offset>
2020-04-24 11:01:10 -04:00
plt . AddAddrPlus4 ( target . Arch , gotplt . Sym ( ) , gotplt . Size ( ) )
plt . SetUint32 ( target . Arch , plt . Size ( ) - 4 , 0x91000210 )
relocs = plt . Relocs ( )
plt . SetRelocType ( relocs . Count ( ) - 1 , objabi . R_ARM64_PCREL )
2018-09-26 10:12:18 +00:00
// br x17
2020-03-04 17:25:01 -05:00
plt . AddUint32 ( target . Arch , 0xd61f0220 )
2018-09-26 10:12:18 +00:00
// add to got.plt: pointer to plt[0]
2020-04-24 11:01:10 -04:00
gotplt . AddAddrPlus ( target . Arch , plt . Sym ( ) , 0 )
2018-09-26 10:12:18 +00:00
// rela
2020-04-24 11:01:10 -04:00
rela . AddAddrPlus ( target . Arch , gotplt . Sym ( ) , gotplt . Size ( ) - 8 )
sDynid := ldr . SymDynid ( s )
2020-10-27 14:18:13 +08:00
rela . AddUint64 ( target . Arch , elf . R_INFO ( uint32 ( sDynid ) , uint32 ( elf . R_AARCH64_JUMP_SLOT ) ) )
2020-03-04 17:25:01 -05:00
rela . AddUint64 ( target . Arch , 0 )
2018-09-26 10:12:18 +00:00
2020-04-24 11:01:10 -04:00
ldr . SetPlt ( s , int32 ( plt . Size ( ) - 16 ) )
2020-10-12 13:44:21 -04:00
} else if target . IsDarwin ( ) {
ld . AddGotSym ( target , ldr , syms , s , 0 )
sDynid := ldr . SymDynid ( s )
lep := ldr . MakeSymbolUpdater ( syms . LinkEditPLT )
lep . AddUint32 ( target . Arch , uint32 ( sDynid ) )
plt := ldr . MakeSymbolUpdater ( syms . PLT )
ldr . SetPlt ( s , int32 ( plt . Size ( ) ) )
// adrp x16, GOT
plt . AddUint32 ( target . Arch , 0x90000010 )
r , _ := plt . AddRel ( objabi . R_ARM64_GOT )
r . SetOff ( int32 ( plt . Size ( ) - 4 ) )
r . SetSiz ( 4 )
r . SetSym ( syms . GOT )
r . SetAdd ( int64 ( ldr . SymGot ( s ) ) )
// ldr x17, [x16, <offset>]
plt . AddUint32 ( target . Arch , 0xf9400211 )
r , _ = plt . AddRel ( objabi . R_ARM64_GOT )
r . SetOff ( int32 ( plt . Size ( ) - 4 ) )
r . SetSiz ( 4 )
r . SetSym ( syms . GOT )
r . SetAdd ( int64 ( ldr . SymGot ( s ) ) )
// br x17
plt . AddUint32 ( target . Arch , 0xd61f0220 )
2018-09-26 10:12:18 +00:00
} else {
2020-04-24 11:01:10 -04:00
ldr . Errorf ( s , "addpltsym: unsupported binary format" )
2018-09-26 10:12:18 +00:00
}
}
2020-12-14 18:52:13 -05:00
2021-04-20 14:12:04 -04:00
const (
machoRelocLimit = 1 << 23
peRelocLimit = 1 << 20
)
2020-12-14 18:52:13 -05:00
func gensymlate ( ctxt * ld . Link , ldr * loader . Loader ) {
// When external linking on darwin, Mach-O relocation has only signed 24-bit
// addend. For large symbols, we generate "label" symbols in the middle, so
// that relocations can target them with smaller addends.
2021-04-20 14:12:04 -04:00
// On Windows, we only get 21 bits, again (presumably) signed.
if ! ctxt . IsDarwin ( ) && ! ctxt . IsWindows ( ) || ! ctxt . IsExternal ( ) {
2020-12-14 18:52:13 -05:00
return
}
2021-04-20 14:12:04 -04:00
limit := int64 ( machoRelocLimit )
if ctxt . IsWindows ( ) {
limit = peRelocLimit
2020-12-14 18:52:13 -05:00
}
2021-04-20 14:12:04 -04:00
if ctxt . IsDarwin ( ) {
big := false
for _ , seg := range ld . Segments {
if seg . Length >= machoRelocLimit {
big = true
break
}
}
if ! big {
return // skip work if nothing big
}
2020-12-14 18:52:13 -05:00
}
2021-04-20 14:12:04 -04:00
// addLabelSyms adds "label" symbols at s+limit, s+2*limit, etc.
addLabelSyms := func ( s loader . Sym , limit , sz int64 ) {
2020-12-14 18:52:13 -05:00
v := ldr . SymValue ( s )
2021-04-20 14:12:04 -04:00
for off := limit ; off < sz ; off += limit {
p := ldr . LookupOrCreateSym ( offsetLabelName ( ldr , s , off ) , ldr . SymVersion ( s ) )
2020-12-14 18:52:13 -05:00
ldr . SetAttrReachable ( p , true )
ldr . SetSymValue ( p , v + off )
ldr . SetSymSect ( p , ldr . SymSect ( s ) )
2021-04-20 14:12:04 -04:00
if ctxt . IsDarwin ( ) {
ld . AddMachoSym ( ldr , p )
} else if ctxt . IsWindows ( ) {
ld . AddPELabelSym ( ldr , p )
} else {
panic ( "missing case in gensymlate" )
}
// fmt.Printf("gensymlate %s %x\n", ldr.SymName(p), ldr.SymValue(p))
2020-12-14 18:52:13 -05:00
}
}
for s , n := loader . Sym ( 1 ) , loader . Sym ( ldr . NSym ( ) ) ; s < n ; s ++ {
if ! ldr . AttrReachable ( s ) {
continue
}
2022-03-23 18:57:16 -04:00
t := ldr . SymType ( s )
if t == sym . STEXT {
2021-04-20 14:12:04 -04:00
if ctxt . IsDarwin ( ) || ctxt . IsWindows ( ) {
// Cannot relocate into middle of function.
// Generate symbol names for every offset we need in duffcopy/duffzero (only 64 each).
switch ldr . SymName ( s ) {
case "runtime.duffcopy" :
addLabelSyms ( s , 8 , 8 * 64 )
case "runtime.duffzero" :
addLabelSyms ( s , 4 , 4 * 64 )
}
}
continue // we don't target the middle of other functions
2020-12-14 18:52:13 -05:00
}
2022-03-23 18:57:16 -04:00
if t >= sym . SDWARFSECT {
continue // no need to add label for DWARF symbols
}
2020-12-14 18:52:13 -05:00
sz := ldr . SymSize ( s )
2021-04-20 14:12:04 -04:00
if sz <= limit {
2020-12-14 18:52:13 -05:00
continue
}
2021-04-20 14:12:04 -04:00
addLabelSyms ( s , limit , sz )
2020-12-14 18:52:13 -05:00
}
// Also for carrier symbols (for which SymSize is 0)
for _ , ss := range ld . CarrierSymByType {
2021-04-20 14:12:04 -04:00
if ss . Sym != 0 && ss . Size > limit {
addLabelSyms ( ss . Sym , limit , ss . Size )
2020-12-14 18:52:13 -05:00
}
}
}
2021-04-20 14:12:04 -04:00
// offsetLabelName returns the name of the "label" symbol used for a
// relocation targeting s+off. The label symbols is used on Darwin/Windows
// when external linking, so that the addend fits in a Mach-O/PE relocation.
func offsetLabelName ( ldr * loader . Loader , s loader . Sym , off int64 ) string {
if off >> 20 << 20 == off {
return fmt . Sprintf ( "%s+%dMB" , ldr . SymExtname ( s ) , off >> 20 )
}
return fmt . Sprintf ( "%s+%d" , ldr . SymExtname ( s ) , off )
2020-12-14 18:52:13 -05:00
}
2021-04-26 22:25:20 -04:00
2022-11-11 19:22:35 +08:00
// Convert the direct jump relocation r to refer to a trampoline if the target is too far.
2021-04-26 22:25:20 -04:00
func trampoline ( ctxt * ld . Link , ldr * loader . Loader , ri int , rs , s loader . Sym ) {
relocs := ldr . Relocs ( s )
r := relocs . At ( ri )
2021-04-27 10:57:09 -04:00
const pcrel = 1
2021-04-26 22:25:20 -04:00
switch r . Type ( ) {
2021-04-27 10:57:09 -04:00
case objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_CALL26 ) ,
objabi . ElfRelocOffset + objabi . RelocType ( elf . R_AARCH64_JUMP26 ) ,
objabi . MachoRelocOffset + ld . MACHO_ARM64_RELOC_BRANCH26 * 2 + pcrel :
// Host object relocations that will be turned into a PLT call.
// The PLT may be too far. Insert a trampoline for them.
fallthrough
2021-04-26 22:25:20 -04:00
case objabi . R_CALLARM64 :
var t int64
// ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet
// laid out. Conservatively use a trampoline. This should be rare, as we lay out packages
// in dependency order.
if ldr . SymValue ( rs ) != 0 {
t = ldr . SymValue ( rs ) + r . Add ( ) - ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) )
}
2021-04-27 10:57:09 -04:00
if t >= 1 << 27 || t < - 1 << 27 || ldr . SymValue ( rs ) == 0 || ( * ld . FlagDebugTramp > 1 && ( ldr . SymPkg ( s ) == "" || ldr . SymPkg ( s ) != ldr . SymPkg ( rs ) ) ) {
2021-04-26 22:25:20 -04:00
// direct call too far, need to insert trampoline.
// look up existing trampolines first. if we found one within the range
// of direct call, we can reuse it. otherwise create a new one.
var tramp loader . Sym
for i := 0 ; ; i ++ {
oName := ldr . SymName ( rs )
name := oName + fmt . Sprintf ( "%+x-tramp%d" , r . Add ( ) , i )
tramp = ldr . LookupOrCreateSym ( name , int ( ldr . SymVersion ( rs ) ) )
ldr . SetAttrReachable ( tramp , true )
if ldr . SymType ( tramp ) == sym . SDYNIMPORT {
// don't reuse trampoline defined in other module
continue
}
if oName == "runtime.deferreturn" {
ldr . SetIsDeferReturnTramp ( tramp , true )
}
if ldr . SymValue ( tramp ) == 0 {
// either the trampoline does not exist -- we need to create one,
// or found one the address which is not assigned -- this will be
// laid down immediately after the current function. use this one.
break
}
t = ldr . SymValue ( tramp ) - ( ldr . SymValue ( s ) + int64 ( r . Off ( ) ) )
if t >= - 1 << 27 && t < 1 << 27 {
// found an existing trampoline that is not too far
// we can just use it
break
}
}
if ldr . SymType ( tramp ) == 0 {
// trampoline does not exist, create one
trampb := ldr . MakeSymbolUpdater ( tramp )
ctxt . AddTramp ( trampb )
if ldr . SymType ( rs ) == sym . SDYNIMPORT {
if r . Add ( ) != 0 {
ctxt . Errorf ( s , "nonzero addend for DYNIMPORT call: %v+%d" , ldr . SymName ( rs ) , r . Add ( ) )
}
gentrampgot ( ctxt , ldr , trampb , rs )
} else {
gentramp ( ctxt , ldr , trampb , rs , r . Add ( ) )
}
}
// modify reloc to point to tramp, which will be resolved later
sb := ldr . MakeSymbolUpdater ( s )
relocs := sb . Relocs ( )
r := relocs . At ( ri )
r . SetSym ( tramp )
r . SetAdd ( 0 ) // clear the offset embedded in the instruction
}
default :
ctxt . Errorf ( s , "trampoline called with non-jump reloc: %d (%s)" , r . Type ( ) , sym . RelocName ( ctxt . Arch , r . Type ( ) ) )
}
}
// generate a trampoline to target+offset.
func gentramp ( ctxt * ld . Link , ldr * loader . Loader , tramp * loader . SymbolBuilder , target loader . Sym , offset int64 ) {
tramp . SetSize ( 12 ) // 3 instructions
P := make ( [ ] byte , tramp . Size ( ) )
o1 := uint32 ( 0x90000010 ) // adrp x16, target
o2 := uint32 ( 0x91000210 ) // add x16, pc-relative-offset
o3 := uint32 ( 0xd61f0200 ) // br x16
ctxt . Arch . ByteOrder . PutUint32 ( P , o1 )
ctxt . Arch . ByteOrder . PutUint32 ( P [ 4 : ] , o2 )
ctxt . Arch . ByteOrder . PutUint32 ( P [ 8 : ] , o3 )
tramp . SetData ( P )
r , _ := tramp . AddRel ( objabi . R_ADDRARM64 )
r . SetSiz ( 8 )
r . SetSym ( target )
r . SetAdd ( offset )
}
// generate a trampoline to target+offset for a DYNIMPORT symbol via GOT.
func gentrampgot ( ctxt * ld . Link , ldr * loader . Loader , tramp * loader . SymbolBuilder , target loader . Sym ) {
tramp . SetSize ( 12 ) // 3 instructions
P := make ( [ ] byte , tramp . Size ( ) )
o1 := uint32 ( 0x90000010 ) // adrp x16, target@GOT
o2 := uint32 ( 0xf9400210 ) // ldr x16, [x16, offset]
o3 := uint32 ( 0xd61f0200 ) // br x16
ctxt . Arch . ByteOrder . PutUint32 ( P , o1 )
ctxt . Arch . ByteOrder . PutUint32 ( P [ 4 : ] , o2 )
ctxt . Arch . ByteOrder . PutUint32 ( P [ 8 : ] , o3 )
tramp . SetData ( P )
r , _ := tramp . AddRel ( objabi . R_ARM64_GOTPCREL )
r . SetSiz ( 8 )
r . SetSym ( target )
}