golang asm 代码
文件路径:/src/cmd/link/internal/x86/asm.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.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_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, 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")
}
}