Home | History | Annotate | Download | only in ld
      1 // Copyright 2013 The Go Authors.  All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package ld
      6 
      7 // Writing and reading of Go object files.
      8 //
      9 // Originally, Go object files were Plan 9 object files, but no longer.
     10 // Now they are more like standard object files, in that each symbol is defined
     11 // by an associated memory image (bytes) and a list of relocations to apply
     12 // during linking. We do not (yet?) use a standard file format, however.
     13 // For now, the format is chosen to be as simple as possible to read and write.
     14 // It may change for reasons of efficiency, or we may even switch to a
     15 // standard file format if there are compelling benefits to doing so.
     16 // See golang.org/s/go13linker for more background.
     17 //
     18 // The file format is:
     19 //
     20 //	- magic header: "\x00\x00go13ld"
     21 //	- byte 1 - version number
     22 //	- sequence of strings giving dependencies (imported packages)
     23 //	- empty string (marks end of sequence)
     24 //	- sequence of defined symbols
     25 //	- byte 0xff (marks end of sequence)
     26 //	- magic footer: "\xff\xffgo13ld"
     27 //
     28 // All integers are stored in a zigzag varint format.
     29 // See golang.org/s/go12symtab for a definition.
     30 //
     31 // Data blocks and strings are both stored as an integer
     32 // followed by that many bytes.
     33 //
     34 // A symbol reference is a string name followed by a version.
     35 // An empty name corresponds to a nil LSym* pointer.
     36 //
     37 // Each symbol is laid out as the following fields (taken from LSym*):
     38 //
     39 //	- byte 0xfe (sanity check for synchronization)
     40 //	- type [int]
     41 //	- name [string]
     42 //	- version [int]
     43 //	- flags [int]
     44 //		1 dupok
     45 //	- size [int]
     46 //	- gotype [symbol reference]
     47 //	- p [data block]
     48 //	- nr [int]
     49 //	- r [nr relocations, sorted by off]
     50 //
     51 // If type == STEXT, there are a few more fields:
     52 //
     53 //	- args [int]
     54 //	- locals [int]
     55 //	- nosplit [int]
     56 //	- flags [int]
     57 //		1 leaf
     58 //		2 C function
     59 //	- nlocal [int]
     60 //	- local [nlocal automatics]
     61 //	- pcln [pcln table]
     62 //
     63 // Each relocation has the encoding:
     64 //
     65 //	- off [int]
     66 //	- siz [int]
     67 //	- type [int]
     68 //	- add [int]
     69 //	- xadd [int]
     70 //	- sym [symbol reference]
     71 //	- xsym [symbol reference]
     72 //
     73 // Each local has the encoding:
     74 //
     75 //	- asym [symbol reference]
     76 //	- offset [int]
     77 //	- type [int]
     78 //	- gotype [symbol reference]
     79 //
     80 // The pcln table has the encoding:
     81 //
     82 //	- pcsp [data block]
     83 //	- pcfile [data block]
     84 //	- pcline [data block]
     85 //	- npcdata [int]
     86 //	- pcdata [npcdata data blocks]
     87 //	- nfuncdata [int]
     88 //	- funcdata [nfuncdata symbol references]
     89 //	- funcdatasym [nfuncdata ints]
     90 //	- nfile [int]
     91 //	- file [nfile symbol references]
     92 //
     93 // The file layout and meaning of type integers are architecture-independent.
     94 //
     95 // TODO(rsc): The file format is good for a first pass but needs work.
     96 //	- There are SymID in the object file that should really just be strings.
     97 //	- The actual symbol memory images are interlaced with the symbol
     98 //	  metadata. They should be separated, to reduce the I/O required to
     99 //	  load just the metadata.
    100 //	- The symbol references should be shortened, either with a symbol
    101 //	  table or by using a simple backward index to an earlier mentioned symbol.
    102 
    103 import (
    104 	"bytes"
    105 	"cmd/internal/obj"
    106 	"fmt"
    107 	"log"
    108 	"strconv"
    109 	"strings"
    110 )
    111 
    112 const (
    113 	startmagic = "\x00\x00go13ld"
    114 	endmagic   = "\xff\xffgo13ld"
    115 )
    116 
    117 func ldobjfile(ctxt *Link, f *obj.Biobuf, pkg string, length int64, pn string) {
    118 	start := obj.Boffset(f)
    119 	ctxt.Version++
    120 	var buf [8]uint8
    121 	obj.Bread(f, buf[:])
    122 	if string(buf[:]) != startmagic {
    123 		log.Fatalf("%s: invalid file start %x %x %x %x %x %x %x %x", pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7])
    124 	}
    125 	c := obj.Bgetc(f)
    126 	if c != 1 {
    127 		log.Fatalf("%s: invalid file version number %d", pn, c)
    128 	}
    129 
    130 	var lib string
    131 	for {
    132 		lib = rdstring(f)
    133 		if lib == "" {
    134 			break
    135 		}
    136 		addlib(ctxt, pkg, pn, lib)
    137 	}
    138 
    139 	for {
    140 		c, err := f.Peek(1)
    141 		if err != nil {
    142 			log.Fatalf("%s: peeking: %v", pn, err)
    143 		}
    144 		if c[0] == 0xff {
    145 			break
    146 		}
    147 		readsym(ctxt, f, pkg, pn)
    148 	}
    149 
    150 	buf = [8]uint8{}
    151 	obj.Bread(f, buf[:])
    152 	if string(buf[:]) != endmagic {
    153 		log.Fatalf("%s: invalid file end", pn)
    154 	}
    155 
    156 	if obj.Boffset(f) != start+length {
    157 		log.Fatalf("%s: unexpected end at %d, want %d", pn, int64(obj.Boffset(f)), int64(start+length))
    158 	}
    159 }
    160 
    161 var readsym_ndup int
    162 
    163 func readsym(ctxt *Link, f *obj.Biobuf, pkg string, pn string) {
    164 	if obj.Bgetc(f) != 0xfe {
    165 		log.Fatalf("readsym out of sync")
    166 	}
    167 	t := rdint(f)
    168 	name := expandpkg(rdstring(f), pkg)
    169 	v := rdint(f)
    170 	if v != 0 && v != 1 {
    171 		log.Fatalf("invalid symbol version %d", v)
    172 	}
    173 	flags := rdint(f)
    174 	dupok := flags & 1
    175 	local := false
    176 	if flags&2 != 0 {
    177 		local = true
    178 	}
    179 	size := rdint(f)
    180 	typ := rdsym(ctxt, f, pkg)
    181 	data := rddata(f)
    182 	nreloc := rdint(f)
    183 
    184 	if v != 0 {
    185 		v = ctxt.Version
    186 	}
    187 	s := Linklookup(ctxt, name, v)
    188 	var dup *LSym
    189 	if s.Type != 0 && s.Type != obj.SXREF {
    190 		if (t == obj.SDATA || t == obj.SBSS || t == obj.SNOPTRBSS) && len(data) == 0 && nreloc == 0 {
    191 			if s.Size < int64(size) {
    192 				s.Size = int64(size)
    193 			}
    194 			if typ != nil && s.Gotype == nil {
    195 				s.Gotype = typ
    196 			}
    197 			return
    198 		}
    199 
    200 		if (s.Type == obj.SDATA || s.Type == obj.SBSS || s.Type == obj.SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 {
    201 			goto overwrite
    202 		}
    203 		if s.Type != obj.SBSS && s.Type != obj.SNOPTRBSS && dupok == 0 && s.Dupok == 0 {
    204 			log.Fatalf("duplicate symbol %s (types %d and %d) in %s and %s", s.Name, s.Type, t, s.File, pn)
    205 		}
    206 		if len(s.P) > 0 {
    207 			dup = s
    208 			s = linknewsym(ctxt, ".dup", readsym_ndup)
    209 			readsym_ndup++ // scratch
    210 		}
    211 	}
    212 
    213 overwrite:
    214 	s.File = pkg
    215 	s.Dupok = uint8(dupok)
    216 	if t == obj.SXREF {
    217 		log.Fatalf("bad sxref")
    218 	}
    219 	if t == 0 {
    220 		log.Fatalf("missing type for %s in %s", name, pn)
    221 	}
    222 	if t == obj.SBSS && (s.Type == obj.SRODATA || s.Type == obj.SNOPTRBSS) {
    223 		t = int(s.Type)
    224 	}
    225 	s.Type = int16(t)
    226 	if s.Size < int64(size) {
    227 		s.Size = int64(size)
    228 	}
    229 	s.Local = local
    230 	if typ != nil { // if bss sym defined multiple times, take type from any one def
    231 		s.Gotype = typ
    232 	}
    233 	if dup != nil && typ != nil {
    234 		dup.Gotype = typ
    235 	}
    236 	s.P = data
    237 	s.P = s.P[:len(data)]
    238 	if nreloc > 0 {
    239 		s.R = make([]Reloc, nreloc)
    240 		s.R = s.R[:nreloc]
    241 		var r *Reloc
    242 		for i := 0; i < nreloc; i++ {
    243 			r = &s.R[i]
    244 			r.Off = rdint32(f)
    245 			r.Siz = rduint8(f)
    246 			r.Type = rdint32(f)
    247 			r.Add = rdint64(f)
    248 			rdint64(f) // Xadd, ignored
    249 			r.Sym = rdsym(ctxt, f, pkg)
    250 			rdsym(ctxt, f, pkg) // Xsym, ignored
    251 		}
    252 	}
    253 
    254 	if len(s.P) > 0 && dup != nil && len(dup.P) > 0 && strings.HasPrefix(s.Name, "gclocals") {
    255 		// content-addressed garbage collection liveness bitmap symbol.
    256 		// double check for hash collisions.
    257 		if !bytes.Equal(s.P, dup.P) {
    258 			log.Fatalf("dupok hash collision for %s in %s and %s", s.Name, s.File, pn)
    259 		}
    260 	}
    261 
    262 	if s.Type == obj.STEXT {
    263 		s.Args = rdint32(f)
    264 		s.Locals = rdint32(f)
    265 		s.Nosplit = rduint8(f)
    266 		v := rdint(f)
    267 		s.Leaf = uint8(v & 1)
    268 		s.Cfunc = uint8(v & 2)
    269 		n := rdint(f)
    270 		var a *Auto
    271 		for i := 0; i < n; i++ {
    272 			a = new(Auto)
    273 			a.Asym = rdsym(ctxt, f, pkg)
    274 			a.Aoffset = rdint32(f)
    275 			a.Name = rdint16(f)
    276 			a.Gotype = rdsym(ctxt, f, pkg)
    277 			a.Link = s.Autom
    278 			s.Autom = a
    279 		}
    280 
    281 		s.Pcln = new(Pcln)
    282 		pc := s.Pcln
    283 		pc.Pcsp.P = rddata(f)
    284 		pc.Pcfile.P = rddata(f)
    285 		pc.Pcline.P = rddata(f)
    286 		n = rdint(f)
    287 		pc.Pcdata = make([]Pcdata, n)
    288 		pc.Npcdata = n
    289 		for i := 0; i < n; i++ {
    290 			pc.Pcdata[i].P = rddata(f)
    291 		}
    292 		n = rdint(f)
    293 		pc.Funcdata = make([]*LSym, n)
    294 		pc.Funcdataoff = make([]int64, n)
    295 		pc.Nfuncdata = n
    296 		for i := 0; i < n; i++ {
    297 			pc.Funcdata[i] = rdsym(ctxt, f, pkg)
    298 		}
    299 		for i := 0; i < n; i++ {
    300 			pc.Funcdataoff[i] = rdint64(f)
    301 		}
    302 		n = rdint(f)
    303 		pc.File = make([]*LSym, n)
    304 		pc.Nfile = n
    305 		for i := 0; i < n; i++ {
    306 			pc.File[i] = rdsym(ctxt, f, pkg)
    307 		}
    308 
    309 		if dup == nil {
    310 			if s.Onlist != 0 {
    311 				log.Fatalf("symbol %s listed multiple times", s.Name)
    312 			}
    313 			s.Onlist = 1
    314 			if ctxt.Etextp != nil {
    315 				ctxt.Etextp.Next = s
    316 			} else {
    317 				ctxt.Textp = s
    318 			}
    319 			ctxt.Etextp = s
    320 		}
    321 	}
    322 
    323 	if ctxt.Debugasm != 0 {
    324 		fmt.Fprintf(ctxt.Bso, "%s ", s.Name)
    325 		if s.Version != 0 {
    326 			fmt.Fprintf(ctxt.Bso, "v=%d ", s.Version)
    327 		}
    328 		if s.Type != 0 {
    329 			fmt.Fprintf(ctxt.Bso, "t=%d ", s.Type)
    330 		}
    331 		if s.Dupok != 0 {
    332 			fmt.Fprintf(ctxt.Bso, "dupok ")
    333 		}
    334 		if s.Cfunc != 0 {
    335 			fmt.Fprintf(ctxt.Bso, "cfunc ")
    336 		}
    337 		if s.Nosplit != 0 {
    338 			fmt.Fprintf(ctxt.Bso, "nosplit ")
    339 		}
    340 		fmt.Fprintf(ctxt.Bso, "size=%d value=%d", int64(s.Size), int64(s.Value))
    341 		if s.Type == obj.STEXT {
    342 			fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x", uint64(s.Args), uint64(s.Locals))
    343 		}
    344 		fmt.Fprintf(ctxt.Bso, "\n")
    345 		var c int
    346 		var j int
    347 		for i := 0; i < len(s.P); {
    348 			fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
    349 			for j = i; j < i+16 && j < len(s.P); j++ {
    350 				fmt.Fprintf(ctxt.Bso, " %02x", s.P[j])
    351 			}
    352 			for ; j < i+16; j++ {
    353 				fmt.Fprintf(ctxt.Bso, "   ")
    354 			}
    355 			fmt.Fprintf(ctxt.Bso, "  ")
    356 			for j = i; j < i+16 && j < len(s.P); j++ {
    357 				c = int(s.P[j])
    358 				if ' ' <= c && c <= 0x7e {
    359 					fmt.Fprintf(ctxt.Bso, "%c", c)
    360 				} else {
    361 					fmt.Fprintf(ctxt.Bso, ".")
    362 				}
    363 			}
    364 
    365 			fmt.Fprintf(ctxt.Bso, "\n")
    366 			i += 16
    367 		}
    368 
    369 		var r *Reloc
    370 		for i := 0; i < len(s.R); i++ {
    371 			r = &s.R[i]
    372 			fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, r.Sym.Name, int64(r.Add))
    373 		}
    374 	}
    375 }
    376 
    377 func rdint64(f *obj.Biobuf) int64 {
    378 	var c int
    379 
    380 	uv := uint64(0)
    381 	for shift := 0; ; shift += 7 {
    382 		if shift >= 64 {
    383 			log.Fatalf("corrupt input")
    384 		}
    385 		c = obj.Bgetc(f)
    386 		uv |= uint64(c&0x7F) << uint(shift)
    387 		if c&0x80 == 0 {
    388 			break
    389 		}
    390 	}
    391 
    392 	return int64(uv>>1) ^ (int64(uint64(uv)<<63) >> 63)
    393 }
    394 
    395 func rdint(f *obj.Biobuf) int {
    396 	n := rdint64(f)
    397 	if int64(int(n)) != n {
    398 		log.Panicf("%v out of range for int", n)
    399 	}
    400 	return int(n)
    401 }
    402 
    403 func rdint32(f *obj.Biobuf) int32 {
    404 	n := rdint64(f)
    405 	if int64(int32(n)) != n {
    406 		log.Panicf("%v out of range for int32", n)
    407 	}
    408 	return int32(n)
    409 }
    410 
    411 func rdint16(f *obj.Biobuf) int16 {
    412 	n := rdint64(f)
    413 	if int64(int16(n)) != n {
    414 		log.Panicf("%v out of range for int16", n)
    415 	}
    416 	return int16(n)
    417 }
    418 
    419 func rduint8(f *obj.Biobuf) uint8 {
    420 	n := rdint64(f)
    421 	if int64(uint8(n)) != n {
    422 		log.Panicf("%v out of range for uint8", n)
    423 	}
    424 	return uint8(n)
    425 }
    426 
    427 func rdstring(f *obj.Biobuf) string {
    428 	n := rdint64(f)
    429 	p := make([]byte, n)
    430 	obj.Bread(f, p)
    431 	return string(p)
    432 }
    433 
    434 func rddata(f *obj.Biobuf) []byte {
    435 	n := rdint64(f)
    436 	p := make([]byte, n)
    437 	obj.Bread(f, p)
    438 	return p
    439 }
    440 
    441 var symbuf []byte
    442 
    443 func rdsym(ctxt *Link, f *obj.Biobuf, pkg string) *LSym {
    444 	n := rdint(f)
    445 	if n == 0 {
    446 		rdint64(f)
    447 		return nil
    448 	}
    449 
    450 	if len(symbuf) < n {
    451 		symbuf = make([]byte, n)
    452 	}
    453 	obj.Bread(f, symbuf[:n])
    454 	p := string(symbuf[:n])
    455 	v := rdint(f)
    456 	if v != 0 {
    457 		v = ctxt.Version
    458 	}
    459 	s := Linklookup(ctxt, expandpkg(p, pkg), v)
    460 
    461 	if v == 0 && s.Name[0] == '$' && s.Type == 0 {
    462 		if strings.HasPrefix(s.Name, "$f32.") {
    463 			x, _ := strconv.ParseUint(s.Name[5:], 16, 32)
    464 			i32 := int32(x)
    465 			s.Type = obj.SRODATA
    466 			s.Local = true
    467 			Adduint32(ctxt, s, uint32(i32))
    468 			s.Reachable = false
    469 		} else if strings.HasPrefix(s.Name, "$f64.") || strings.HasPrefix(s.Name, "$i64.") {
    470 			x, _ := strconv.ParseUint(s.Name[5:], 16, 64)
    471 			i64 := int64(x)
    472 			s.Type = obj.SRODATA
    473 			s.Local = true
    474 			Adduint64(ctxt, s, uint64(i64))
    475 			s.Reachable = false
    476 		}
    477 	}
    478 	if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.") {
    479 		s.Local = true
    480 	}
    481 	return s
    482 }
    483