// 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)) } if targType == 0 || targType == sym.SXREF { 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_PEIMAGEOFF: v = ld.IMAGE_REL_I386_DIR32NB 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, []byte) int64 { log.Fatalf("unexpected relocation variant") return -1 } func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, 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") } }