mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
We used to generate all external relocations in memory, then emit the relocation records at a later pass. The data structures were chosen so that it takes as little memory as possible. Now we just stream out external relocations, and ExtReloc is just a local variable. Change the data structure to avoid repeated read of some fields. Also get rid of ExtRelocView, as it is no longer necessary. Change-Id: I40209bbe4387af231b29788125c3b4ebb0ff4a33 Reviewed-on: https://go-review.googlesource.com/c/go/+/245479 Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Jeremy Faller <jeremy@golang.org>
492 lines
14 KiB
Go
492 lines
14 KiB
Go
// Inferno utils/8l/asm.c
|
|
// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/asm.c
|
|
//
|
|
// 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
|
|
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
|
//
|
|
// 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.
|
|
|
|
package x86
|
|
|
|
import (
|
|
"cmd/internal/objabi"
|
|
"cmd/internal/sys"
|
|
"cmd/link/internal/ld"
|
|
"cmd/link/internal/loader"
|
|
"cmd/link/internal/sym"
|
|
"debug/elf"
|
|
"log"
|
|
)
|
|
|
|
func gentext(ctxt *ld.Link, ldr *loader.Loader) {
|
|
if ctxt.DynlinkingGo() {
|
|
// We need get_pc_thunk.
|
|
} else {
|
|
switch ctxt.BuildMode {
|
|
case ld.BuildModeCArchive:
|
|
if !ctxt.IsELF {
|
|
return
|
|
}
|
|
case ld.BuildModePIE, ld.BuildModeCShared, ld.BuildModePlugin:
|
|
// We need get_pc_thunk.
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
|
|
// Generate little thunks that load the PC of the next instruction into a register.
|
|
thunks := make([]loader.Sym, 0, 7+len(ctxt.Textp))
|
|
for _, r := range [...]struct {
|
|
name string
|
|
num uint8
|
|
}{
|
|
{"ax", 0},
|
|
{"cx", 1},
|
|
{"dx", 2},
|
|
{"bx", 3},
|
|
// sp
|
|
{"bp", 5},
|
|
{"si", 6},
|
|
{"di", 7},
|
|
} {
|
|
thunkfunc := ldr.CreateSymForUpdate("__x86.get_pc_thunk."+r.name, 0)
|
|
thunkfunc.SetType(sym.STEXT)
|
|
ldr.SetAttrLocal(thunkfunc.Sym(), true)
|
|
o := func(op ...uint8) {
|
|
for _, op1 := range op {
|
|
thunkfunc.AddUint8(op1)
|
|
}
|
|
}
|
|
// 8b 04 24 mov (%esp),%eax
|
|
// Destination register is in bits 3-5 of the middle byte, so add that in.
|
|
o(0x8b, 0x04+r.num<<3, 0x24)
|
|
// c3 ret
|
|
o(0xc3)
|
|
|
|
thunks = append(thunks, thunkfunc.Sym())
|
|
}
|
|
ctxt.Textp = append(thunks, ctxt.Textp...) // keep Textp in dependency order
|
|
|
|
initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
|
|
if initfunc == nil {
|
|
return
|
|
}
|
|
|
|
o := func(op ...uint8) {
|
|
for _, op1 := range op {
|
|
initfunc.AddUint8(op1)
|
|
}
|
|
}
|
|
|
|
// go.link.addmoduledata:
|
|
// 53 push %ebx
|
|
// e8 00 00 00 00 call __x86.get_pc_thunk.cx + R_CALL __x86.get_pc_thunk.cx
|
|
// 8d 81 00 00 00 00 lea 0x0(%ecx), %eax + R_PCREL ctxt.Moduledata
|
|
// 8d 99 00 00 00 00 lea 0x0(%ecx), %ebx + R_GOTPC _GLOBAL_OFFSET_TABLE_
|
|
// e8 00 00 00 00 call runtime.addmoduledata@plt + R_CALL runtime.addmoduledata
|
|
// 5b pop %ebx
|
|
// c3 ret
|
|
|
|
o(0x53)
|
|
|
|
o(0xe8)
|
|
initfunc.AddSymRef(ctxt.Arch, ldr.Lookup("__x86.get_pc_thunk.cx", 0), 0, objabi.R_CALL, 4)
|
|
|
|
o(0x8d, 0x81)
|
|
initfunc.AddPCRelPlus(ctxt.Arch, ctxt.Moduledata, 6)
|
|
|
|
o(0x8d, 0x99)
|
|
gotsym := ldr.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0)
|
|
initfunc.AddSymRef(ctxt.Arch, gotsym, 12, objabi.R_PCREL, 4)
|
|
o(0xe8)
|
|
initfunc.AddSymRef(ctxt.Arch, addmoduledata, 0, objabi.R_CALL, 4)
|
|
|
|
o(0x5b)
|
|
|
|
o(0xc3)
|
|
}
|
|
|
|
func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc2, rIdx int) bool {
|
|
targ := r.Sym()
|
|
var targType sym.SymKind
|
|
if targ != 0 {
|
|
targType = ldr.SymType(targ)
|
|
}
|
|
|
|
switch r.Type() {
|
|
default:
|
|
if r.Type() >= objabi.ElfRelocOffset {
|
|
ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type()))
|
|
return false
|
|
}
|
|
|
|
// Handle relocations found in ELF object files.
|
|
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PC32):
|
|
if targType == sym.SDYNIMPORT {
|
|
ldr.Errorf(s, "unexpected R_386_PC32 relocation for dynamic symbol %s", ldr.SymName(targ))
|
|
}
|
|
// TODO(mwhudson): the test of VisibilityHidden here probably doesn't make
|
|
// sense and should be removed when someone has thought about it properly.
|
|
if (targType == 0 || targType == sym.SXREF) && !ldr.AttrVisibilityHidden(targ) {
|
|
ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ))
|
|
}
|
|
su := ldr.MakeSymbolUpdater(s)
|
|
su.SetRelocType(rIdx, objabi.R_PCREL)
|
|
su.SetRelocAdd(rIdx, r.Add()+4)
|
|
return true
|
|
|
|
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PLT32):
|
|
su := ldr.MakeSymbolUpdater(s)
|
|
su.SetRelocType(rIdx, objabi.R_PCREL)
|
|
su.SetRelocAdd(rIdx, r.Add()+4)
|
|
if targType == sym.SDYNIMPORT {
|
|
addpltsym(target, ldr, syms, targ)
|
|
su.SetRelocSym(rIdx, syms.PLT)
|
|
su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ)))
|
|
}
|
|
|
|
return true
|
|
|
|
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32),
|
|
objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32X):
|
|
su := ldr.MakeSymbolUpdater(s)
|
|
if targType != sym.SDYNIMPORT {
|
|
// have symbol
|
|
sData := ldr.Data(s)
|
|
|
|
if r.Off() >= 2 && sData[r.Off()-2] == 0x8b {
|
|
su.MakeWritable()
|
|
|
|
// turn MOVL of GOT entry into LEAL of symbol address, relative to GOT.
|
|
writeableData := su.Data()
|
|
writeableData[r.Off()-2] = 0x8d
|
|
su.SetRelocType(rIdx, objabi.R_GOTOFF)
|
|
return true
|
|
}
|
|
|
|
if r.Off() >= 2 && sData[r.Off()-2] == 0xff && sData[r.Off()-1] == 0xb3 {
|
|
su.MakeWritable()
|
|
// turn PUSHL of GOT entry into PUSHL of symbol itself.
|
|
// use unnecessary SS prefix to keep instruction same length.
|
|
writeableData := su.Data()
|
|
writeableData[r.Off()-2] = 0x36
|
|
writeableData[r.Off()-1] = 0x68
|
|
su.SetRelocType(rIdx, objabi.R_ADDR)
|
|
return true
|
|
}
|
|
|
|
ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
|
|
return false
|
|
}
|
|
|
|
ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_386_GLOB_DAT))
|
|
su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym
|
|
su.SetRelocSym(rIdx, 0)
|
|
su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
|
|
return true
|
|
|
|
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTOFF):
|
|
su := ldr.MakeSymbolUpdater(s)
|
|
su.SetRelocType(rIdx, objabi.R_GOTOFF)
|
|
return true
|
|
|
|
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTPC):
|
|
su := ldr.MakeSymbolUpdater(s)
|
|
su.SetRelocType(rIdx, objabi.R_PCREL)
|
|
su.SetRelocSym(rIdx, syms.GOT)
|
|
su.SetRelocAdd(rIdx, r.Add()+4)
|
|
return true
|
|
|
|
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_32):
|
|
if targType == sym.SDYNIMPORT {
|
|
ldr.Errorf(s, "unexpected R_386_32 relocation for dynamic symbol %s", ldr.SymName(targ))
|
|
}
|
|
su := ldr.MakeSymbolUpdater(s)
|
|
su.SetRelocType(rIdx, objabi.R_ADDR)
|
|
return true
|
|
|
|
case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 0:
|
|
su := ldr.MakeSymbolUpdater(s)
|
|
su.SetRelocType(rIdx, objabi.R_ADDR)
|
|
if targType == sym.SDYNIMPORT {
|
|
ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ))
|
|
}
|
|
return true
|
|
|
|
case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1:
|
|
su := ldr.MakeSymbolUpdater(s)
|
|
if targType == sym.SDYNIMPORT {
|
|
addpltsym(target, ldr, syms, targ)
|
|
su.SetRelocSym(rIdx, syms.PLT)
|
|
su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
|
|
su.SetRelocType(rIdx, objabi.R_PCREL)
|
|
return true
|
|
}
|
|
|
|
su.SetRelocType(rIdx, objabi.R_PCREL)
|
|
return true
|
|
|
|
case objabi.MachoRelocOffset + ld.MACHO_FAKE_GOTPCREL:
|
|
su := ldr.MakeSymbolUpdater(s)
|
|
if targType != sym.SDYNIMPORT {
|
|
// have symbol
|
|
// turn MOVL of GOT entry into LEAL of symbol itself
|
|
sData := ldr.Data(s)
|
|
if r.Off() < 2 || sData[r.Off()-2] != 0x8b {
|
|
ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ))
|
|
return false
|
|
}
|
|
|
|
su.MakeWritable()
|
|
writeableData := su.Data()
|
|
writeableData[r.Off()-2] = 0x8d
|
|
su.SetRelocType(rIdx, objabi.R_PCREL)
|
|
return true
|
|
}
|
|
|
|
ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_386_GLOB_DAT))
|
|
su.SetRelocSym(rIdx, syms.GOT)
|
|
su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ)))
|
|
su.SetRelocType(rIdx, objabi.R_PCREL)
|
|
return true
|
|
}
|
|
|
|
// Handle references to ELF symbols from our own object files.
|
|
if targType != sym.SDYNIMPORT {
|
|
return true
|
|
}
|
|
|
|
// Reread the reloc to incorporate any changes in type above.
|
|
relocs := ldr.Relocs(s)
|
|
r = relocs.At2(rIdx)
|
|
|
|
switch r.Type() {
|
|
case objabi.R_CALL,
|
|
objabi.R_PCREL:
|
|
if target.IsExternal() {
|
|
// External linker will do this relocation.
|
|
return true
|
|
}
|
|
addpltsym(target, ldr, syms, targ)
|
|
su := ldr.MakeSymbolUpdater(s)
|
|
su.SetRelocSym(rIdx, syms.PLT)
|
|
su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
|
|
return true
|
|
|
|
case objabi.R_ADDR:
|
|
if ldr.SymType(s) != sym.SDATA {
|
|
break
|
|
}
|
|
if target.IsElf() {
|
|
ld.Adddynsym(ldr, target, syms, targ)
|
|
rel := ldr.MakeSymbolUpdater(syms.Rel)
|
|
rel.AddAddrPlus(target.Arch, s, int64(r.Off()))
|
|
rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(ldr.SymDynid(targ)), uint32(elf.R_386_32)))
|
|
su := ldr.MakeSymbolUpdater(s)
|
|
su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym
|
|
su.SetRelocSym(rIdx, 0)
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
|
|
out.Write32(uint32(sectoff))
|
|
|
|
elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
|
|
siz := r.Size
|
|
switch r.Type {
|
|
default:
|
|
return false
|
|
case objabi.R_ADDR, objabi.R_DWARFSECREF:
|
|
if siz == 4 {
|
|
out.Write32(uint32(elf.R_386_32) | uint32(elfsym)<<8)
|
|
} else {
|
|
return false
|
|
}
|
|
case objabi.R_GOTPCREL:
|
|
if siz == 4 {
|
|
out.Write32(uint32(elf.R_386_GOTPC))
|
|
if ldr.SymName(r.Xsym) != "_GLOBAL_OFFSET_TABLE_" {
|
|
out.Write32(uint32(sectoff))
|
|
out.Write32(uint32(elf.R_386_GOT32) | uint32(elfsym)<<8)
|
|
}
|
|
} else {
|
|
return false
|
|
}
|
|
case objabi.R_CALL:
|
|
if siz == 4 {
|
|
if ldr.SymType(r.Xsym) == sym.SDYNIMPORT {
|
|
out.Write32(uint32(elf.R_386_PLT32) | uint32(elfsym)<<8)
|
|
} else {
|
|
out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8)
|
|
}
|
|
} else {
|
|
return false
|
|
}
|
|
case objabi.R_PCREL:
|
|
if siz == 4 {
|
|
out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8)
|
|
} else {
|
|
return false
|
|
}
|
|
case objabi.R_TLS_LE:
|
|
if siz == 4 {
|
|
out.Write32(uint32(elf.R_386_TLS_LE) | uint32(elfsym)<<8)
|
|
} else {
|
|
return false
|
|
}
|
|
case objabi.R_TLS_IE:
|
|
if siz == 4 {
|
|
out.Write32(uint32(elf.R_386_GOTPC))
|
|
out.Write32(uint32(sectoff))
|
|
out.Write32(uint32(elf.R_386_TLS_GOTIE) | uint32(elfsym)<<8)
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
|
|
return false
|
|
}
|
|
|
|
func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool {
|
|
var v uint32
|
|
|
|
rs := r.Xsym
|
|
rt := r.Type
|
|
|
|
if ldr.SymDynid(rs) < 0 {
|
|
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
|
|
}
|
|
|
|
out.Write32(uint32(sectoff))
|
|
out.Write32(uint32(ldr.SymDynid(rs)))
|
|
|
|
switch rt {
|
|
default:
|
|
return false
|
|
|
|
case objabi.R_DWARFSECREF:
|
|
v = ld.IMAGE_REL_I386_SECREL
|
|
|
|
case objabi.R_ADDR:
|
|
v = ld.IMAGE_REL_I386_DIR32
|
|
|
|
case objabi.R_CALL,
|
|
objabi.R_PCREL:
|
|
v = ld.IMAGE_REL_I386_REL32
|
|
}
|
|
|
|
out.Write16(uint16(v))
|
|
|
|
return true
|
|
}
|
|
|
|
func archreloc(*ld.Target, *loader.Loader, *ld.ArchSyms, loader.Reloc2, loader.Sym, int64) (int64, int, bool) {
|
|
return -1, 0, false
|
|
}
|
|
|
|
func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc2, sym.RelocVariant, loader.Sym, int64) int64 {
|
|
log.Fatalf("unexpected relocation variant")
|
|
return -1
|
|
}
|
|
|
|
func elfsetupplt(ctxt *ld.Link, plt, got *loader.SymbolBuilder, dynamic loader.Sym) {
|
|
if plt.Size() == 0 {
|
|
// pushl got+4
|
|
plt.AddUint8(0xff)
|
|
|
|
plt.AddUint8(0x35)
|
|
plt.AddAddrPlus(ctxt.Arch, got.Sym(), 4)
|
|
|
|
// jmp *got+8
|
|
plt.AddUint8(0xff)
|
|
|
|
plt.AddUint8(0x25)
|
|
plt.AddAddrPlus(ctxt.Arch, got.Sym(), 8)
|
|
|
|
// zero pad
|
|
plt.AddUint32(ctxt.Arch, 0)
|
|
|
|
// assume got->size == 0 too
|
|
got.AddAddrPlus(ctxt.Arch, dynamic, 0)
|
|
|
|
got.AddUint32(ctxt.Arch, 0)
|
|
got.AddUint32(ctxt.Arch, 0)
|
|
}
|
|
}
|
|
|
|
func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) {
|
|
if ldr.SymPlt(s) >= 0 {
|
|
return
|
|
}
|
|
|
|
ld.Adddynsym(ldr, target, syms, s)
|
|
|
|
if target.IsElf() {
|
|
plt := ldr.MakeSymbolUpdater(syms.PLT)
|
|
got := ldr.MakeSymbolUpdater(syms.GOTPLT)
|
|
rel := ldr.MakeSymbolUpdater(syms.RelPLT)
|
|
if plt.Size() == 0 {
|
|
panic("plt is not set up")
|
|
}
|
|
|
|
// jmpq *got+size
|
|
plt.AddUint8(0xff)
|
|
|
|
plt.AddUint8(0x25)
|
|
plt.AddAddrPlus(target.Arch, got.Sym(), got.Size())
|
|
|
|
// add to got: pointer to current pos in plt
|
|
got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size())
|
|
|
|
// pushl $x
|
|
plt.AddUint8(0x68)
|
|
|
|
plt.AddUint32(target.Arch, uint32(rel.Size()))
|
|
|
|
// jmp .plt
|
|
plt.AddUint8(0xe9)
|
|
|
|
plt.AddUint32(target.Arch, uint32(-(plt.Size() + 4)))
|
|
|
|
// rel
|
|
rel.AddAddrPlus(target.Arch, got.Sym(), got.Size()-4)
|
|
|
|
sDynid := ldr.SymDynid(s)
|
|
rel.AddUint32(target.Arch, ld.ELF32_R_INFO(uint32(sDynid), uint32(elf.R_386_JMP_SLOT)))
|
|
|
|
ldr.SetPlt(s, int32(plt.Size()-16))
|
|
} else {
|
|
ldr.Errorf(s, "addpltsym: unsupported binary format")
|
|
}
|
|
}
|