diff options
Diffstat (limited to 'src/cmd/link/internal/x86/asm.go')
-rw-r--r-- | src/cmd/link/internal/x86/asm.go | 492 |
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") + } +} |