summaryrefslogtreecommitdiffstats
path: root/src/cmd/link/internal/x86/asm.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/link/internal/x86/asm.go')
-rw-r--r--src/cmd/link/internal/x86/asm.go492
1 files changed, 492 insertions, 0 deletions
diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go
new file mode 100644
index 0000000..af0ce11
--- /dev/null
+++ b/src/cmd/link/internal/x86/asm.go
@@ -0,0 +1,492 @@
+// 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, elf.R_INFO32(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, elf.R_INFO32(uint32(sDynid), uint32(elf.R_386_JMP_SLOT)))
+
+ ldr.SetPlt(s, int32(plt.Size()-16))
+ } else {
+ ldr.Errorf(s, "addpltsym: unsupported binary format")
+ }
+}