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