github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/link/x86/asm.go (about) 1 // Inferno utils/8l/asm.c 2 // https://bitbucket.org/inferno-os/inferno-os/src/master/utils/8l/asm.c 3 // 4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6 // Portions Copyright © 1997-1999 Vita Nuova Limited 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8 // Portions Copyright © 2004,2006 Bruce Ellis 9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11 // Portions Copyright © 2009 The Go Authors. All rights reserved. 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining a copy 14 // of this software and associated documentation files (the "Software"), to deal 15 // in the Software without restriction, including without limitation the rights 16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 // copies of the Software, and to permit persons to whom the Software is 18 // furnished to do so, subject to the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be included in 21 // all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 // THE SOFTWARE. 30 31 package x86 32 33 import ( 34 "debug/elf" 35 "log" 36 37 "github.com/go-asm/go/cmd/link/ld" 38 "github.com/go-asm/go/cmd/link/loader" 39 "github.com/go-asm/go/cmd/link/sym" 40 "github.com/go-asm/go/cmd/objabi" 41 "github.com/go-asm/go/cmd/sys" 42 ) 43 44 func gentext(ctxt *ld.Link, ldr *loader.Loader) { 45 if ctxt.DynlinkingGo() { 46 // We need get_pc_thunk. 47 } else { 48 switch ctxt.BuildMode { 49 case ld.BuildModeCArchive: 50 if !ctxt.IsELF { 51 return 52 } 53 case ld.BuildModePIE, ld.BuildModeCShared, ld.BuildModePlugin: 54 // We need get_pc_thunk. 55 default: 56 return 57 } 58 } 59 60 // Generate little thunks that load the PC of the next instruction into a register. 61 thunks := make([]loader.Sym, 0, 7+len(ctxt.Textp)) 62 for _, r := range [...]struct { 63 name string 64 num uint8 65 }{ 66 {"ax", 0}, 67 {"cx", 1}, 68 {"dx", 2}, 69 {"bx", 3}, 70 // sp 71 {"bp", 5}, 72 {"si", 6}, 73 {"di", 7}, 74 } { 75 thunkfunc := ldr.CreateSymForUpdate("__x86.get_pc_thunk."+r.name, 0) 76 thunkfunc.SetType(sym.STEXT) 77 ldr.SetAttrLocal(thunkfunc.Sym(), true) 78 o := func(op ...uint8) { 79 for _, op1 := range op { 80 thunkfunc.AddUint8(op1) 81 } 82 } 83 // 8b 04 24 mov (%esp),%eax 84 // Destination register is in bits 3-5 of the middle byte, so add that in. 85 o(0x8b, 0x04+r.num<<3, 0x24) 86 // c3 ret 87 o(0xc3) 88 89 thunks = append(thunks, thunkfunc.Sym()) 90 } 91 ctxt.Textp = append(thunks, ctxt.Textp...) // keep Textp in dependency order 92 93 initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt) 94 if initfunc == nil { 95 return 96 } 97 98 o := func(op ...uint8) { 99 for _, op1 := range op { 100 initfunc.AddUint8(op1) 101 } 102 } 103 104 // go.link.addmoduledata: 105 // 53 push %ebx 106 // e8 00 00 00 00 call __x86.get_pc_thunk.cx + R_CALL __x86.get_pc_thunk.cx 107 // 8d 81 00 00 00 00 lea 0x0(%ecx), %eax + R_PCREL ctxt.Moduledata 108 // 8d 99 00 00 00 00 lea 0x0(%ecx), %ebx + R_GOTPC _GLOBAL_OFFSET_TABLE_ 109 // e8 00 00 00 00 call runtime.addmoduledata@plt + R_CALL runtime.addmoduledata 110 // 5b pop %ebx 111 // c3 ret 112 113 o(0x53) 114 115 o(0xe8) 116 initfunc.AddSymRef(ctxt.Arch, ldr.Lookup("__x86.get_pc_thunk.cx", 0), 0, objabi.R_CALL, 4) 117 118 o(0x8d, 0x81) 119 initfunc.AddPCRelPlus(ctxt.Arch, ctxt.Moduledata, 6) 120 121 o(0x8d, 0x99) 122 gotsym := ldr.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0) 123 initfunc.AddSymRef(ctxt.Arch, gotsym, 12, objabi.R_PCREL, 4) 124 o(0xe8) 125 initfunc.AddSymRef(ctxt.Arch, addmoduledata, 0, objabi.R_CALL, 4) 126 127 o(0x5b) 128 129 o(0xc3) 130 } 131 132 func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { 133 targ := r.Sym() 134 var targType sym.SymKind 135 if targ != 0 { 136 targType = ldr.SymType(targ) 137 } 138 139 switch r.Type() { 140 default: 141 if r.Type() >= objabi.ElfRelocOffset { 142 ldr.Errorf(s, "unexpected relocation type %d (%s)", r.Type(), sym.RelocName(target.Arch, r.Type())) 143 return false 144 } 145 146 // Handle relocations found in ELF object files. 147 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PC32): 148 if targType == sym.SDYNIMPORT { 149 ldr.Errorf(s, "unexpected R_386_PC32 relocation for dynamic symbol %s", ldr.SymName(targ)) 150 } 151 if targType == 0 || targType == sym.SXREF { 152 ldr.Errorf(s, "unknown symbol %s in pcrel", ldr.SymName(targ)) 153 } 154 su := ldr.MakeSymbolUpdater(s) 155 su.SetRelocType(rIdx, objabi.R_PCREL) 156 su.SetRelocAdd(rIdx, r.Add()+4) 157 return true 158 159 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_PLT32): 160 su := ldr.MakeSymbolUpdater(s) 161 su.SetRelocType(rIdx, objabi.R_PCREL) 162 su.SetRelocAdd(rIdx, r.Add()+4) 163 if targType == sym.SDYNIMPORT { 164 addpltsym(target, ldr, syms, targ) 165 su.SetRelocSym(rIdx, syms.PLT) 166 su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymPlt(targ))) 167 } 168 169 return true 170 171 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32), 172 objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOT32X): 173 su := ldr.MakeSymbolUpdater(s) 174 if targType != sym.SDYNIMPORT { 175 // have symbol 176 sData := ldr.Data(s) 177 178 if r.Off() >= 2 && sData[r.Off()-2] == 0x8b { 179 su.MakeWritable() 180 181 // turn MOVL of GOT entry into LEAL of symbol address, relative to GOT. 182 writeableData := su.Data() 183 writeableData[r.Off()-2] = 0x8d 184 su.SetRelocType(rIdx, objabi.R_GOTOFF) 185 return true 186 } 187 188 if r.Off() >= 2 && sData[r.Off()-2] == 0xff && sData[r.Off()-1] == 0xb3 { 189 su.MakeWritable() 190 // turn PUSHL of GOT entry into PUSHL of symbol itself. 191 // use unnecessary SS prefix to keep instruction same length. 192 writeableData := su.Data() 193 writeableData[r.Off()-2] = 0x36 194 writeableData[r.Off()-1] = 0x68 195 su.SetRelocType(rIdx, objabi.R_ADDR) 196 return true 197 } 198 199 ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ)) 200 return false 201 } 202 203 ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_386_GLOB_DAT)) 204 su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym 205 su.SetRelocSym(rIdx, 0) 206 su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) 207 return true 208 209 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTOFF): 210 su := ldr.MakeSymbolUpdater(s) 211 su.SetRelocType(rIdx, objabi.R_GOTOFF) 212 return true 213 214 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_GOTPC): 215 su := ldr.MakeSymbolUpdater(s) 216 su.SetRelocType(rIdx, objabi.R_PCREL) 217 su.SetRelocSym(rIdx, syms.GOT) 218 su.SetRelocAdd(rIdx, r.Add()+4) 219 return true 220 221 case objabi.ElfRelocOffset + objabi.RelocType(elf.R_386_32): 222 if targType == sym.SDYNIMPORT { 223 ldr.Errorf(s, "unexpected R_386_32 relocation for dynamic symbol %s", ldr.SymName(targ)) 224 } 225 su := ldr.MakeSymbolUpdater(s) 226 su.SetRelocType(rIdx, objabi.R_ADDR) 227 return true 228 229 case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 0: 230 su := ldr.MakeSymbolUpdater(s) 231 su.SetRelocType(rIdx, objabi.R_ADDR) 232 if targType == sym.SDYNIMPORT { 233 ldr.Errorf(s, "unexpected reloc for dynamic symbol %s", ldr.SymName(targ)) 234 } 235 return true 236 237 case objabi.MachoRelocOffset + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1: 238 su := ldr.MakeSymbolUpdater(s) 239 if targType == sym.SDYNIMPORT { 240 addpltsym(target, ldr, syms, targ) 241 su.SetRelocSym(rIdx, syms.PLT) 242 su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) 243 su.SetRelocType(rIdx, objabi.R_PCREL) 244 return true 245 } 246 247 su.SetRelocType(rIdx, objabi.R_PCREL) 248 return true 249 250 case objabi.MachoRelocOffset + ld.MACHO_FAKE_GOTPCREL: 251 su := ldr.MakeSymbolUpdater(s) 252 if targType != sym.SDYNIMPORT { 253 // have symbol 254 // turn MOVL of GOT entry into LEAL of symbol itself 255 sData := ldr.Data(s) 256 if r.Off() < 2 || sData[r.Off()-2] != 0x8b { 257 ldr.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", ldr.SymName(targ)) 258 return false 259 } 260 261 su.MakeWritable() 262 writeableData := su.Data() 263 writeableData[r.Off()-2] = 0x8d 264 su.SetRelocType(rIdx, objabi.R_PCREL) 265 return true 266 } 267 268 ld.AddGotSym(target, ldr, syms, targ, uint32(elf.R_386_GLOB_DAT)) 269 su.SetRelocSym(rIdx, syms.GOT) 270 su.SetRelocAdd(rIdx, r.Add()+int64(ldr.SymGot(targ))) 271 su.SetRelocType(rIdx, objabi.R_PCREL) 272 return true 273 } 274 275 // Handle references to ELF symbols from our own object files. 276 if targType != sym.SDYNIMPORT { 277 return true 278 } 279 280 // Reread the reloc to incorporate any changes in type above. 281 relocs := ldr.Relocs(s) 282 r = relocs.At(rIdx) 283 284 switch r.Type() { 285 case objabi.R_CALL, 286 objabi.R_PCREL: 287 if target.IsExternal() { 288 // External linker will do this relocation. 289 return true 290 } 291 addpltsym(target, ldr, syms, targ) 292 su := ldr.MakeSymbolUpdater(s) 293 su.SetRelocSym(rIdx, syms.PLT) 294 su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ))) 295 return true 296 297 case objabi.R_ADDR: 298 if ldr.SymType(s) != sym.SDATA { 299 break 300 } 301 if target.IsElf() { 302 ld.Adddynsym(ldr, target, syms, targ) 303 rel := ldr.MakeSymbolUpdater(syms.Rel) 304 rel.AddAddrPlus(target.Arch, s, int64(r.Off())) 305 rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(targ)), uint32(elf.R_386_32))) 306 su := ldr.MakeSymbolUpdater(s) 307 su.SetRelocType(rIdx, objabi.R_CONST) // write r->add during relocsym 308 su.SetRelocSym(rIdx, 0) 309 return true 310 } 311 } 312 313 return false 314 } 315 316 func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { 317 out.Write32(uint32(sectoff)) 318 319 elfsym := ld.ElfSymForReloc(ctxt, r.Xsym) 320 siz := r.Size 321 switch r.Type { 322 default: 323 return false 324 case objabi.R_ADDR, objabi.R_DWARFSECREF: 325 if siz == 4 { 326 out.Write32(uint32(elf.R_386_32) | uint32(elfsym)<<8) 327 } else { 328 return false 329 } 330 case objabi.R_GOTPCREL: 331 if siz == 4 { 332 out.Write32(uint32(elf.R_386_GOTPC)) 333 if ldr.SymName(r.Xsym) != "_GLOBAL_OFFSET_TABLE_" { 334 out.Write32(uint32(sectoff)) 335 out.Write32(uint32(elf.R_386_GOT32) | uint32(elfsym)<<8) 336 } 337 } else { 338 return false 339 } 340 case objabi.R_CALL: 341 if siz == 4 { 342 if ldr.SymType(r.Xsym) == sym.SDYNIMPORT { 343 out.Write32(uint32(elf.R_386_PLT32) | uint32(elfsym)<<8) 344 } else { 345 out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8) 346 } 347 } else { 348 return false 349 } 350 case objabi.R_PCREL: 351 if siz == 4 { 352 out.Write32(uint32(elf.R_386_PC32) | uint32(elfsym)<<8) 353 } else { 354 return false 355 } 356 case objabi.R_TLS_LE: 357 if siz == 4 { 358 out.Write32(uint32(elf.R_386_TLS_LE) | uint32(elfsym)<<8) 359 } else { 360 return false 361 } 362 case objabi.R_TLS_IE: 363 if siz == 4 { 364 out.Write32(uint32(elf.R_386_GOTPC)) 365 out.Write32(uint32(sectoff)) 366 out.Write32(uint32(elf.R_386_TLS_GOTIE) | uint32(elfsym)<<8) 367 } else { 368 return false 369 } 370 } 371 372 return true 373 } 374 375 func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { 376 return false 377 } 378 379 func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, sectoff int64) bool { 380 var v uint32 381 382 rs := r.Xsym 383 rt := r.Type 384 385 if ldr.SymDynid(rs) < 0 { 386 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)) 387 return false 388 } 389 390 out.Write32(uint32(sectoff)) 391 out.Write32(uint32(ldr.SymDynid(rs))) 392 393 switch rt { 394 default: 395 return false 396 397 case objabi.R_DWARFSECREF: 398 v = ld.IMAGE_REL_I386_SECREL 399 400 case objabi.R_ADDR: 401 v = ld.IMAGE_REL_I386_DIR32 402 403 case objabi.R_PEIMAGEOFF: 404 v = ld.IMAGE_REL_I386_DIR32NB 405 406 case objabi.R_CALL, 407 objabi.R_PCREL: 408 v = ld.IMAGE_REL_I386_REL32 409 } 410 411 out.Write16(uint16(v)) 412 413 return true 414 } 415 416 func archreloc(*ld.Target, *loader.Loader, *ld.ArchSyms, loader.Reloc, loader.Sym, int64) (int64, int, bool) { 417 return -1, 0, false 418 } 419 420 func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 { 421 log.Fatalf("unexpected relocation variant") 422 return -1 423 } 424 425 func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, plt, got *loader.SymbolBuilder, dynamic loader.Sym) { 426 if plt.Size() == 0 { 427 // pushl got+4 428 plt.AddUint8(0xff) 429 430 plt.AddUint8(0x35) 431 plt.AddAddrPlus(ctxt.Arch, got.Sym(), 4) 432 433 // jmp *got+8 434 plt.AddUint8(0xff) 435 436 plt.AddUint8(0x25) 437 plt.AddAddrPlus(ctxt.Arch, got.Sym(), 8) 438 439 // zero pad 440 plt.AddUint32(ctxt.Arch, 0) 441 442 // assume got->size == 0 too 443 got.AddAddrPlus(ctxt.Arch, dynamic, 0) 444 445 got.AddUint32(ctxt.Arch, 0) 446 got.AddUint32(ctxt.Arch, 0) 447 } 448 } 449 450 func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym) { 451 if ldr.SymPlt(s) >= 0 { 452 return 453 } 454 455 ld.Adddynsym(ldr, target, syms, s) 456 457 if target.IsElf() { 458 plt := ldr.MakeSymbolUpdater(syms.PLT) 459 got := ldr.MakeSymbolUpdater(syms.GOTPLT) 460 rel := ldr.MakeSymbolUpdater(syms.RelPLT) 461 if plt.Size() == 0 { 462 panic("plt is not set up") 463 } 464 465 // jmpq *got+size 466 plt.AddUint8(0xff) 467 468 plt.AddUint8(0x25) 469 plt.AddAddrPlus(target.Arch, got.Sym(), got.Size()) 470 471 // add to got: pointer to current pos in plt 472 got.AddAddrPlus(target.Arch, plt.Sym(), plt.Size()) 473 474 // pushl $x 475 plt.AddUint8(0x68) 476 477 plt.AddUint32(target.Arch, uint32(rel.Size())) 478 479 // jmp .plt 480 plt.AddUint8(0xe9) 481 482 plt.AddUint32(target.Arch, uint32(-(plt.Size() + 4))) 483 484 // rel 485 rel.AddAddrPlus(target.Arch, got.Sym(), got.Size()-4) 486 487 sDynid := ldr.SymDynid(s) 488 rel.AddUint32(target.Arch, elf.R_INFO32(uint32(sDynid), uint32(elf.R_386_JMP_SLOT))) 489 490 ldr.SetPlt(s, int32(plt.Size()-16)) 491 } else { 492 ldr.Errorf(s, "addpltsym: unsupported binary format") 493 } 494 }