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