blob: 9b949ebbf81948aaec03448c1c8aed20de0b11af [file] [log] [blame]
// 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.Reloc, 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.At(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.Reloc, loader.Sym, int64) (int64, int, bool) {
return -1, 0, false
}
func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, 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")
}
}