func skiptopkgdef(b *obj.Biobuf) bool { /* archive header */ p := obj.Brdline(b, '\n') if p == "" { return false } if obj.Blinelen(b) != 8 { return false } if p != "!<arch>\n" { return false } /* symbol table may be first; skip it */ sz := arsize(b, "__.GOSYMDEF") if sz >= 0 { obj.Bseek(b, int64(sz), 1) } else { obj.Bseek(b, 8, 0) } /* package export block is next */ sz = arsize(b, "__.PKGDEF") if sz <= 0 { return false } return true }
/* * look for the next file in an archive. * adapted from libmach. */ func nextar(bp *obj.Biobuf, off int64, a *ArHdr) int64 { if off&1 != 0 { off++ } obj.Bseek(bp, off, 0) buf := make([]byte, SAR_HDR) if n := obj.Bread(bp, buf); n < len(buf) { if n >= 0 { return 0 } return -1 } a.name = artrim(buf[0:16]) a.date = artrim(buf[16:28]) a.uid = artrim(buf[28:34]) a.gid = artrim(buf[34:40]) a.mode = artrim(buf[40:48]) a.size = artrim(buf[48:58]) a.fmag = artrim(buf[58:60]) arsize := atolwhex(a.size) if arsize&1 != 0 { arsize++ } return int64(arsize) + SAR_HDR }
// hostArchive reads an archive file holding host objects and links in // required objects. The general format is the same as a Go archive // file, but it has an armap listing symbols and the objects that // define them. This is used for the compiler support library // libgcc.a. func hostArchive(name string) { f, err := obj.Bopenr(name) if err != nil { if os.IsNotExist(err) { // It's OK if we don't have a libgcc file at all. return } Exitf("cannot open file %s: %v", name, err) } defer obj.Bterm(f) magbuf := make([]byte, len(ARMAG)) if obj.Bread(f, magbuf) != len(magbuf) { Exitf("file %s too short", name) } var arhdr ArHdr l := nextar(f, obj.Boffset(f), &arhdr) if l <= 0 { Exitf("%s missing armap", name) } var armap archiveMap if arhdr.name == "/" || arhdr.name == "/SYM64/" { armap = readArmap(name, f, arhdr) } else { Exitf("%s missing armap", name) } loaded := make(map[uint64]bool) any := true for any { var load []uint64 for s := Ctxt.Allsym; s != nil; s = s.Allsym { for _, r := range s.R { if r.Sym != nil && r.Sym.Type&obj.SMASK == obj.SXREF { if off := armap[r.Sym.Name]; off != 0 && !loaded[off] { load = append(load, off) loaded[off] = true } } } } for _, off := range load { l := nextar(f, int64(off), &arhdr) if l <= 0 { Exitf("%s missing archive entry at offset %d", name, off) } pname := fmt.Sprintf("%s(%s)", name, arhdr.name) l = atolwhex(arhdr.size) h := ldobj(f, "libgcc", l, pname, name, ArchiveObj) obj.Bseek(f, h.off, 0) h.ld(f, h.pkg, h.length, h.pn) } any = len(load) > 0 } }
func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { if symtab.sym != nil { return 0 } strbuf := make([]byte, symtab.strsize) if obj.Bseek(m.f, m.base+int64(symtab.stroff), 0) < 0 || obj.Bread(m.f, strbuf) != len(strbuf) { return -1 } symsize := 12 if m.is64 { symsize = 16 } n := int(symtab.nsym * uint32(symsize)) symbuf := make([]byte, n) if obj.Bseek(m.f, m.base+int64(symtab.symoff), 0) < 0 || obj.Bread(m.f, symbuf) != len(symbuf) { return -1 } sym := make([]LdMachoSym, symtab.nsym) p := symbuf var s *LdMachoSym var v uint32 for i := 0; uint32(i) < symtab.nsym; i++ { s = &sym[i] v = m.e.Uint32(p) if v >= symtab.strsize { return -1 } s.name = cstring(strbuf[v:]) s.type_ = uint8(p[4]) s.sectnum = uint8(p[5]) s.desc = m.e.Uint16(p[6:]) if m.is64 { s.value = m.e.Uint64(p[8:]) } else { s.value = uint64(m.e.Uint32(p[8:])) } p = p[symsize:] } symtab.str = strbuf symtab.sym = sym return 0 }
func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int { n := int(d.nindirectsyms) p := make([]byte, n*4) if obj.Bseek(m.f, m.base+int64(d.indirectsymoff), 0) < 0 || obj.Bread(m.f, p) != len(p) { return -1 } d.indir = make([]uint32, n) for i := 0; i < n; i++ { d.indir[i] = m.e.Uint32(p[4*i:]) } return 0 }
func pemap(peobj *PeObj, sect *PeSect) int { if sect.base != nil { return 0 } sect.base = make([]byte, sect.sh.SizeOfRawData) if sect.sh.PointerToRawData == 0 { // .bss doesn't have data in object file return 0 } if obj.Bseek(peobj.f, int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 || obj.Bread(peobj.f, sect.base) != len(sect.base) { return -1 } return 0 }
func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int { if sect.rel != nil || sect.nreloc == 0 { return 0 } rel := make([]LdMachoRel, sect.nreloc) n := int(sect.nreloc * 8) buf := make([]byte, n) if obj.Bseek(m.f, m.base+int64(sect.reloff), 0) < 0 || obj.Bread(m.f, buf) != n { return -1 } var p []byte var r *LdMachoRel var v uint32 for i := 0; uint32(i) < sect.nreloc; i++ { r = &rel[i] p = buf[i*8:] r.addr = m.e.Uint32(p) // TODO(rsc): Wrong interpretation for big-endian bitfields? if r.addr&0x80000000 != 0 { // scatterbrained relocation r.scattered = 1 v = r.addr >> 24 r.addr &= 0xFFFFFF r.type_ = uint8(v & 0xF) v >>= 4 r.length = 1 << (v & 3) v >>= 2 r.pcrel = uint8(v & 1) r.value = m.e.Uint32(p[4:]) } else { v = m.e.Uint32(p[4:]) r.symnum = v & 0xFFFFFF v >>= 24 r.pcrel = uint8(v & 1) v >>= 1 r.length = 1 << (v & 3) v >>= 2 r.extrn = uint8(v & 1) v >>= 1 r.type_ = uint8(v) } } sect.rel = rel return 0 }
func hostobjs() { var f *obj.Biobuf var h *Hostobj for i := 0; i < len(hostobj); i++ { h = &hostobj[i] var err error f, err = obj.Bopenr(h.file) if f == nil { Exitf("cannot reopen %s: %v", h.pn, err) } obj.Bseek(f, h.off, 0) h.ld(f, h.pkg, h.length, h.pn) obj.Bterm(f) } }
func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) { if sect.base != nil { return nil } if sect.off+sect.size > uint64(elfobj.length) { err = fmt.Errorf("elf section past end of file") return err } sect.base = make([]byte, sect.size) err = fmt.Errorf("short read") if obj.Bseek(elfobj.f, int64(uint64(elfobj.base)+sect.off), 0) < 0 || obj.Bread(elfobj.f, sect.base) != len(sect.base) { return err } return nil }
func dumpobj() { var err error bout, err = obj.Bopenw(outfile) if err != nil { Flusherrors() fmt.Printf("can't create %s: %v\n", outfile, err) errorexit() } startobj := int64(0) var arhdr [ArhdrSize]byte if writearchive != 0 { obj.Bwritestring(bout, "!<arch>\n") arhdr = [ArhdrSize]byte{} bout.Write(arhdr[:]) startobj = obj.Boffset(bout) } fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) dumpexport() if writearchive != 0 { bout.Flush() size := obj.Boffset(bout) - startobj if size&1 != 0 { obj.Bputc(bout, 0) } obj.Bseek(bout, startobj-ArhdrSize, 0) formathdr(arhdr[:], "__.PKGDEF", size) bout.Write(arhdr[:]) bout.Flush() obj.Bseek(bout, startobj+size+(size&1), 0) arhdr = [ArhdrSize]byte{} bout.Write(arhdr[:]) startobj = obj.Boffset(bout) fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) } if pragcgobuf != "" { if writearchive != 0 { // write empty export section; must be before cgo section fmt.Fprintf(bout, "\n$$\n\n$$\n\n") } fmt.Fprintf(bout, "\n$$ // cgo\n") fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf) } fmt.Fprintf(bout, "\n!\n") externs := len(externdcl) dumpglobls() dumptypestructs() // Dump extra globals. tmp := externdcl if externdcl != nil { externdcl = externdcl[externs:] } dumpglobls() externdcl = tmp dumpdata() obj.Writeobjdirect(Ctxt, bout) if writearchive != 0 { bout.Flush() size := obj.Boffset(bout) - startobj if size&1 != 0 { obj.Bputc(bout, 0) } obj.Bseek(bout, startobj-ArhdrSize, 0) formathdr(arhdr[:], "_go_.o", size) bout.Write(arhdr[:]) } obj.Bterm(bout) }
func objfile(lib *Library) { pkg := pathtoprefix(lib.Pkg) if Debug['v'] > 1 { fmt.Fprintf(&Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), lib.File, pkg) } Bso.Flush() var err error var f *obj.Biobuf f, err = obj.Bopenr(lib.File) if err != nil { Exitf("cannot open file %s: %v", lib.File, err) } magbuf := make([]byte, len(ARMAG)) if obj.Bread(f, magbuf) != len(magbuf) || !strings.HasPrefix(string(magbuf), ARMAG) { /* load it as a regular file */ l := obj.Bseek(f, 0, 2) obj.Bseek(f, 0, 0) ldobj(f, pkg, l, lib.File, lib.File, FileObj) obj.Bterm(f) return } /* skip over optional __.GOSYMDEF and process __.PKGDEF */ off := obj.Boffset(f) var arhdr ArHdr l := nextar(f, off, &arhdr) var pname string if l <= 0 { Diag("%s: short read on archive file symbol header", lib.File) goto out } if strings.HasPrefix(arhdr.name, symname) { off += l l = nextar(f, off, &arhdr) if l <= 0 { Diag("%s: short read on archive file symbol header", lib.File) goto out } } if !strings.HasPrefix(arhdr.name, pkgname) { Diag("%s: cannot find package header", lib.File) goto out } if Buildmode == BuildmodeShared { before := obj.Boffset(f) pkgdefBytes := make([]byte, atolwhex(arhdr.size)) obj.Bread(f, pkgdefBytes) hash := sha1.Sum(pkgdefBytes) lib.hash = hash[:] obj.Bseek(f, before, 0) } off += l if Debug['u'] != 0 { ldpkg(f, pkg, atolwhex(arhdr.size), lib.File, Pkgdef) } /* * load all the object files from the archive now. * this gives us sequential file access and keeps us * from needing to come back later to pick up more * objects. it breaks the usual C archive model, but * this is Go, not C. the common case in Go is that * we need to load all the objects, and then we throw away * the individual symbols that are unused. * * loading every object will also make it possible to * load foreign objects not referenced by __.GOSYMDEF. */ for { l = nextar(f, off, &arhdr) if l == 0 { break } if l < 0 { Exitf("%s: malformed archive", lib.File) } off += l pname = fmt.Sprintf("%s(%s)", lib.File, arhdr.name) l = atolwhex(arhdr.size) ldobj(f, pkg, l, pname, lib.File, ArchiveObj) } out: obj.Bterm(f) }
func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, whence int) { eof := obj.Boffset(f) + length start := obj.Boffset(f) c1 := obj.Bgetc(f) c2 := obj.Bgetc(f) c3 := obj.Bgetc(f) c4 := obj.Bgetc(f) obj.Bseek(f, start, 0) magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) if magic == 0x7f454c46 { // \x7F E L F ldhostobj(ldelf, f, pkg, length, pn, file) return } if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe { ldhostobj(ldmacho, f, pkg, length, pn, file) return } if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 { ldhostobj(ldpe, f, pkg, length, pn, file) return } /* check the header */ line := obj.Brdline(f, '\n') if line == "" { if obj.Blinelen(f) > 0 { Diag("%s: not an object file", pn) return } Diag("truncated object file: %s", pn) return } if !strings.HasPrefix(line, "go object ") { if strings.HasSuffix(pn, ".go") { Exitf("%cl: input %s is not .%c file (use %cg to compile .go files)", Thearch.Thechar, pn, Thearch.Thechar, Thearch.Thechar) } if line == Thestring { // old header format: just $GOOS Diag("%s: stale object file", pn) return } Diag("%s: not an object file", pn) return } // First, check that the basic goos, goarch, and version match. t := fmt.Sprintf("%s %s %s ", goos, obj.Getgoarch(), obj.Getgoversion()) line = strings.TrimRight(line, "\n") if !strings.HasPrefix(line[10:]+" ", t) && Debug['f'] == 0 { Diag("%s: object is [%s] expected [%s]", pn, line[10:], t) return } // Second, check that longer lines match each other exactly, // so that the Go compiler and write additional information // that must be the same from run to run. if len(line) >= len(t)+10 { if theline == "" { theline = line[10:] } else if theline != line[10:] { Diag("%s: object is [%s] expected [%s]", pn, line[10:], theline) return } } /* skip over exports and other info -- ends with \n!\n */ import0 := obj.Boffset(f) c1 = '\n' // the last line ended in \n c2 = obj.Bgetc(f) c3 = obj.Bgetc(f) for c1 != '\n' || c2 != '!' || c3 != '\n' { c1 = c2 c2 = c3 c3 = obj.Bgetc(f) if c3 == obj.Beof { Diag("truncated object file: %s", pn) return } } import1 := obj.Boffset(f) obj.Bseek(f, import0, 0) ldpkg(f, pkg, import1-import0-2, pn, whence) // -2 for !\n obj.Bseek(f, import1, 0) ldobjfile(Ctxt, f, pkg, eof-obj.Boffset(f), pn) }
func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn) } Ctxt.Version++ base := int32(obj.Boffset(f)) var add uint64 var e binary.ByteOrder var elfobj *ElfObj var err error var flag int var hdr *ElfHdrBytes var hdrbuf [64]uint8 var info uint64 var is64 int var j int var n int var name string var p []byte var r []Reloc var rela int var rp *Reloc var rsect *ElfSect var s *LSym var sect *ElfSect var sym ElfSym var symbols []*LSym if obj.Bread(f, hdrbuf[:]) != len(hdrbuf) { goto bad } hdr = new(ElfHdrBytes) binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter if string(hdr.Ident[:4]) != "\x7FELF" { goto bad } switch hdr.Ident[5] { case ElfDataLsb: e = binary.LittleEndian case ElfDataMsb: e = binary.BigEndian default: goto bad } // read header elfobj = new(ElfObj) elfobj.e = e elfobj.f = f elfobj.base = int64(base) elfobj.length = length elfobj.name = pn is64 = 0 if hdr.Ident[4] == ElfClass64 { is64 = 1 hdr := new(ElfHdrBytes64) binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) elfobj.version = e.Uint32(hdr.Version[:]) elfobj.phoff = e.Uint64(hdr.Phoff[:]) elfobj.shoff = e.Uint64(hdr.Shoff[:]) elfobj.flags = e.Uint32(hdr.Flags[:]) elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) } else { elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) elfobj.version = e.Uint32(hdr.Version[:]) elfobj.entry = uint64(e.Uint32(hdr.Entry[:])) elfobj.phoff = uint64(e.Uint32(hdr.Phoff[:])) elfobj.shoff = uint64(e.Uint32(hdr.Shoff[:])) elfobj.flags = e.Uint32(hdr.Flags[:]) elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) } elfobj.is64 = is64 if uint32(hdr.Ident[6]) != elfobj.version { goto bad } if e.Uint16(hdr.Type[:]) != ElfTypeRelocatable { Diag("%s: elf but not elf relocatable object", pn) return } switch Thearch.Thechar { default: Diag("%s: elf %s unimplemented", pn, Thestring) return case '5': if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 { Diag("%s: elf object but not arm", pn) return } case '6': if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 { Diag("%s: elf object but not amd64", pn) return } case '7': if e != binary.LittleEndian || elfobj.machine != ElfMachArm64 || hdr.Ident[4] != ElfClass64 { Diag("%s: elf object but not arm64", pn) return } case '8': if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 { Diag("%s: elf object but not 386", pn) return } case '9': if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 { Diag("%s: elf object but not ppc64", pn) return } } // load section list into memory. elfobj.sect = make([]ElfSect, elfobj.shnum) elfobj.nsect = uint(elfobj.shnum) for i := 0; uint(i) < elfobj.nsect; i++ { if obj.Bseek(f, int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) < 0 { goto bad } sect = &elfobj.sect[i] if is64 != 0 { var b ElfSectBytes64 if err = binary.Read(f, e, &b); err != nil { goto bad } sect.nameoff = uint32(e.Uint32(b.Name[:])) sect.type_ = e.Uint32(b.Type[:]) sect.flags = e.Uint64(b.Flags[:]) sect.addr = e.Uint64(b.Addr[:]) sect.off = e.Uint64(b.Off[:]) sect.size = e.Uint64(b.Size[:]) sect.link = e.Uint32(b.Link[:]) sect.info = e.Uint32(b.Info[:]) sect.align = e.Uint64(b.Align[:]) sect.entsize = e.Uint64(b.Entsize[:]) } else { var b ElfSectBytes if err = binary.Read(f, e, &b); err != nil { goto bad } sect.nameoff = uint32(e.Uint32(b.Name[:])) sect.type_ = e.Uint32(b.Type[:]) sect.flags = uint64(e.Uint32(b.Flags[:])) sect.addr = uint64(e.Uint32(b.Addr[:])) sect.off = uint64(e.Uint32(b.Off[:])) sect.size = uint64(e.Uint32(b.Size[:])) sect.link = e.Uint32(b.Link[:]) sect.info = e.Uint32(b.Info[:]) sect.align = uint64(e.Uint32(b.Align[:])) sect.entsize = uint64(e.Uint32(b.Entsize[:])) } } // read section string table and translate names if elfobj.shstrndx >= uint32(elfobj.nsect) { err = fmt.Errorf("shstrndx out of range %d >= %d", elfobj.shstrndx, elfobj.nsect) goto bad } sect = &elfobj.sect[elfobj.shstrndx] if err = elfmap(elfobj, sect); err != nil { goto bad } for i := 0; uint(i) < elfobj.nsect; i++ { if elfobj.sect[i].nameoff != 0 { elfobj.sect[i].name = cstring(sect.base[elfobj.sect[i].nameoff:]) } } // load string table for symbols into memory. elfobj.symtab = section(elfobj, ".symtab") if elfobj.symtab == nil { // our work is done here - no symbols means nothing can refer to this file return } if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) { Diag("%s: elf object has symbol table with invalid string table link", pn) return } elfobj.symstr = &elfobj.sect[elfobj.symtab.link] if is64 != 0 { elfobj.nsymtab = int(elfobj.symtab.size / ELF64SYMSIZE) } else { elfobj.nsymtab = int(elfobj.symtab.size / ELF32SYMSIZE) } if err = elfmap(elfobj, elfobj.symtab); err != nil { goto bad } if err = elfmap(elfobj, elfobj.symstr); err != nil { goto bad } // load text and data segments into memory. // they are not as small as the section lists, but we'll need // the memory anyway for the symbol images, so we might // as well use one large chunk. // create symbols for elfmapped sections for i := 0; uint(i) < elfobj.nsect; i++ { sect = &elfobj.sect[i] if sect.type_ == SHT_ARM_ATTRIBUTES && sect.name == ".ARM.attributes" { if err = elfmap(elfobj, sect); err != nil { goto bad } parseArmAttributes(e, sect.base[:sect.size]) } if (sect.type_ != ElfSectProgbits && sect.type_ != ElfSectNobits) || sect.flags&ElfSectFlagAlloc == 0 { continue } if sect.type_ != ElfSectNobits { if err = elfmap(elfobj, sect); err != nil { goto bad } } name = fmt.Sprintf("%s(%s)", pkg, sect.name) s = Linklookup(Ctxt, name, Ctxt.Version) switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) { default: err = fmt.Errorf("unexpected flags for ELF section %s", sect.name) goto bad case ElfSectFlagAlloc: s.Type = obj.SRODATA case ElfSectFlagAlloc + ElfSectFlagWrite: if sect.type_ == ElfSectNobits { s.Type = obj.SNOPTRBSS } else { s.Type = obj.SNOPTRDATA } case ElfSectFlagAlloc + ElfSectFlagExec: s.Type = obj.STEXT } if sect.name == ".got" || sect.name == ".toc" { s.Type = obj.SELFGOT } if sect.type_ == ElfSectProgbits { s.P = sect.base s.P = s.P[:sect.size] } s.Size = int64(sect.size) s.Align = int32(sect.align) sect.sym = s } // enter sub-symbols into symbol table. // symbol 0 is the null symbol. symbols = make([]*LSym, elfobj.nsymtab) for i := 1; i < elfobj.nsymtab; i++ { if err = readelfsym(elfobj, i, &sym, 1); err != nil { goto bad } symbols[i] = sym.sym if sym.type_ != ElfSymTypeFunc && sym.type_ != ElfSymTypeObject && sym.type_ != ElfSymTypeNone { continue } if sym.shndx == ElfSymShnCommon { s = sym.sym if uint64(s.Size) < sym.size { s.Size = int64(sym.size) } if s.Type == 0 || s.Type == obj.SXREF { s.Type = obj.SNOPTRBSS } continue } if uint(sym.shndx) >= elfobj.nsect || sym.shndx == 0 { continue } // even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols if sym.sym == nil { continue } sect = &elfobj.sect[sym.shndx:][0] if sect.sym == nil { if strings.HasPrefix(sym.name, ".Linfo_string") { // clang does this continue } Diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type_) continue } s = sym.sym if s.Outer != nil { if s.Dupok != 0 { continue } Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) } s.Sub = sect.sym.Sub sect.sym.Sub = s s.Type = sect.sym.Type | s.Type&^obj.SMASK | obj.SSUB if s.Cgoexport&CgoExportDynamic == 0 { s.Dynimplib = "" // satisfy dynimport } s.Value = int64(sym.value) s.Size = int64(sym.size) s.Outer = sect.sym if sect.sym.Type == obj.STEXT { if s.External != 0 && s.Dupok == 0 { Diag("%s: duplicate definition of %s", pn, s.Name) } s.External = 1 } if elfobj.machine == ElfMachPower64 { flag = int(sym.other) >> 5 if 2 <= flag && flag <= 6 { s.Localentry = 1 << uint(flag-2) } else if flag == 7 { Diag("%s: invalid sym.other 0x%x for %s", pn, sym.other, s.Name) } } } // Sort outer lists by address, adding to textp. // This keeps textp in increasing address order. for i := 0; uint(i) < elfobj.nsect; i++ { s = elfobj.sect[i].sym if s == nil { continue } if s.Sub != nil { s.Sub = listsort(s.Sub, valuecmp, listsubp) } if s.Type == obj.STEXT { if s.Onlist != 0 { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Onlist = 1 if Ctxt.Etextp != nil { Ctxt.Etextp.Next = s } else { Ctxt.Textp = s } Ctxt.Etextp = s for s = s.Sub; s != nil; s = s.Sub { if s.Onlist != 0 { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Onlist = 1 Ctxt.Etextp.Next = s Ctxt.Etextp = s } } } // load relocations for i := 0; uint(i) < elfobj.nsect; i++ { rsect = &elfobj.sect[i] if rsect.type_ != ElfSectRela && rsect.type_ != ElfSectRel { continue } if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil { continue } sect = &elfobj.sect[rsect.info] if err = elfmap(elfobj, rsect); err != nil { goto bad } rela = 0 if rsect.type_ == ElfSectRela { rela = 1 } n = int(rsect.size / uint64(4+4*is64) / uint64(2+rela)) r = make([]Reloc, n) p = rsect.base for j = 0; j < n; j++ { add = 0 rp = &r[j] if is64 != 0 { // 64-bit rel/rela rp.Off = int32(e.Uint64(p)) p = p[8:] info = e.Uint64(p) p = p[8:] if rela != 0 { add = e.Uint64(p) p = p[8:] } } else { // 32-bit rel/rela rp.Off = int32(e.Uint32(p)) p = p[4:] info = uint64(e.Uint32(p)) info = info>>8<<32 | info&0xff // convert to 64-bit info p = p[4:] if rela != 0 { add = uint64(e.Uint32(p)) p = p[4:] } } if info&0xffffffff == 0 { // skip R_*_NONE relocation j-- n-- continue } if info>>32 == 0 { // absolute relocation, don't bother reading the null symbol rp.Sym = nil } else { if err = readelfsym(elfobj, int(info>>32), &sym, 0); err != nil { goto bad } sym.sym = symbols[info>>32] if sym.sym == nil { err = fmt.Errorf("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect.sym.Name, j, int(info>>32), sym.name, sym.shndx, sym.type_) goto bad } rp.Sym = sym.sym } rp.Type = int32(reltype(pn, int(uint32(info)), &rp.Siz)) if rela != 0 { rp.Add = int64(add) } else { // load addend from image if rp.Siz == 4 { rp.Add = int64(e.Uint32(sect.base[rp.Off:])) } else if rp.Siz == 8 { rp.Add = int64(e.Uint64(sect.base[rp.Off:])) } else { Diag("invalid rela size %d", rp.Siz) } } if rp.Siz == 2 { rp.Add = int64(int16(rp.Add)) } if rp.Siz == 4 { rp.Add = int64(int32(rp.Add)) } } //print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add); sort.Sort(rbyoff(r[:n])) // just in case s = sect.sym s.R = r s.R = s.R[:n] } return bad: Diag("%s: malformed elf file: %v", pn, err) }
func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn) } var sect *PeSect Ctxt.Version++ base := int32(obj.Boffset(f)) peobj := new(PeObj) peobj.f = f peobj.base = uint32(base) peobj.name = pn // read header var err error var j int var l uint32 var name string var numaux int var r []Reloc var rp *Reloc var rsect *PeSect var s *LSym var sym *PeSym var symbuf [18]uint8 if err = binary.Read(f, binary.LittleEndian, &peobj.fh); err != nil { goto bad } // load section list peobj.sect = make([]PeSect, peobj.fh.NumberOfSections) peobj.nsect = uint(peobj.fh.NumberOfSections) for i := 0; i < int(peobj.fh.NumberOfSections); i++ { if err = binary.Read(f, binary.LittleEndian, &peobj.sect[i].sh); err != nil { goto bad } peobj.sect[i].size = uint64(peobj.sect[i].sh.SizeOfRawData) peobj.sect[i].name = cstring(peobj.sect[i].sh.Name[:]) } // TODO return error if found .cormeta // load string table obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) if obj.Bread(f, symbuf[:4]) != 4 { goto bad } l = Le32(symbuf[:]) peobj.snames = make([]byte, l) obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) if obj.Bread(f, peobj.snames) != len(peobj.snames) { goto bad } // rewrite section names if they start with / for i := 0; i < int(peobj.fh.NumberOfSections); i++ { if peobj.sect[i].name == "" { continue } if peobj.sect[i].name[0] != '/' { continue } n, _ := strconv.Atoi(peobj.sect[i].name[1:]) peobj.sect[i].name = cstring(peobj.snames[n:]) } // read symbols peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols) peobj.npesym = uint(peobj.fh.NumberOfSymbols) obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0) for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 { obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0) if obj.Bread(f, symbuf[:]) != len(symbuf) { goto bad } if (symbuf[0] == 0) && (symbuf[1] == 0) && (symbuf[2] == 0) && (symbuf[3] == 0) { l = Le32(symbuf[4:]) peobj.pesym[i].name = cstring(peobj.snames[l:]) // sym name length <= 8 } else { peobj.pesym[i].name = cstring(symbuf[:8]) } peobj.pesym[i].value = Le32(symbuf[8:]) peobj.pesym[i].sectnum = Le16(symbuf[12:]) peobj.pesym[i].sclass = symbuf[16] peobj.pesym[i].aux = symbuf[17] peobj.pesym[i].type_ = Le16(symbuf[14:]) numaux = int(peobj.pesym[i].aux) if numaux < 0 { numaux = 0 } } // create symbols for mapped sections for i := 0; uint(i) < peobj.nsect; i++ { sect = &peobj.sect[i] if sect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { continue } if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { // This has been seen for .idata sections, which we // want to ignore. See issues 5106 and 5273. continue } if pemap(peobj, sect) < 0 { goto bad } name = fmt.Sprintf("%s(%s)", pkg, sect.name) s = Linklookup(Ctxt, name, Ctxt.Version) switch sect.sh.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) { case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata s.Type = obj.SRODATA case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss s.Type = obj.SNOPTRBSS case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data s.Type = obj.SNOPTRDATA case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text s.Type = obj.STEXT default: err = fmt.Errorf("unexpected flags %#06x for PE section %s", sect.sh.Characteristics, sect.name) goto bad } s.P = sect.base s.P = s.P[:sect.size] s.Size = int64(sect.size) sect.sym = s if sect.name == ".rsrc" { setpersrc(sect.sym) } } // load relocations for i := 0; uint(i) < peobj.nsect; i++ { rsect = &peobj.sect[i] if rsect.sym == nil || rsect.sh.NumberOfRelocations == 0 { continue } if rsect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { continue } if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { // This has been seen for .idata sections, which we // want to ignore. See issues 5106 and 5273. continue } r = make([]Reloc, rsect.sh.NumberOfRelocations) obj.Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0) for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ { rp = &r[j] if obj.Bread(f, symbuf[:10]) != 10 { goto bad } rva := Le32(symbuf[0:]) symindex := Le32(symbuf[4:]) type_ := Le16(symbuf[8:]) if err = readpesym(peobj, int(symindex), &sym); err != nil { goto bad } if sym.sym == nil { err = fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", sym.name, symindex, sym.type_) goto bad } rp.Sym = sym.sym rp.Siz = 4 rp.Off = int32(rva) switch type_ { default: Diag("%s: unknown relocation type %d;", pn, type_) fallthrough case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32, IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 IMAGE_REL_AMD64_ADDR32NB: rp.Type = obj.R_PCREL rp.Add = int64(int32(Le32(rsect.base[rp.Off:]))) case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32: rp.Type = obj.R_ADDR // load addend from image rp.Add = int64(int32(Le32(rsect.base[rp.Off:]))) case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 rp.Siz = 8 rp.Type = obj.R_ADDR // load addend from image rp.Add = int64(Le64(rsect.base[rp.Off:])) } // ld -r could generate multiple section symbols for the // same section but with different values, we have to take // that into account if issect(&peobj.pesym[symindex]) { rp.Add += int64(peobj.pesym[symindex].value) } } sort.Sort(rbyoff(r[:rsect.sh.NumberOfRelocations])) s = rsect.sym s.R = r s.R = s.R[:rsect.sh.NumberOfRelocations] } // enter sub-symbols into symbol table. for i := 0; uint(i) < peobj.npesym; i++ { if peobj.pesym[i].name == "" { continue } if issect(&peobj.pesym[i]) { continue } if uint(peobj.pesym[i].sectnum) > peobj.nsect { continue } if peobj.pesym[i].sectnum > 0 { sect = &peobj.sect[peobj.pesym[i].sectnum-1] if sect.sym == nil { continue } } if err = readpesym(peobj, i, &sym); err != nil { goto bad } s = sym.sym if sym.sectnum == 0 { // extern if s.Type == obj.SDYNIMPORT { s.Plt = -2 // flag for dynimport in PE object files. } if s.Type == obj.SXREF && sym.value > 0 { // global data s.Type = obj.SNOPTRDATA s.Size = int64(sym.value) } continue } else if sym.sectnum > 0 && uint(sym.sectnum) <= peobj.nsect { sect = &peobj.sect[sym.sectnum-1] if sect.sym == nil { Diag("%s: %s sym == 0!", pn, s.Name) } } else { Diag("%s: %s sectnum < 0!", pn, s.Name) } if sect == nil { return } if s.Outer != nil { if s.Attr.DuplicateOK() { continue } Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) } s.Sub = sect.sym.Sub sect.sym.Sub = s s.Type = sect.sym.Type | obj.SSUB s.Value = int64(sym.value) s.Size = 4 s.Outer = sect.sym if sect.sym.Type == obj.STEXT { if s.Attr.External() && !s.Attr.DuplicateOK() { Diag("%s: duplicate definition of %s", pn, s.Name) } s.Attr |= AttrExternal } } // Sort outer lists by address, adding to textp. // This keeps textp in increasing address order. for i := 0; uint(i) < peobj.nsect; i++ { s = peobj.sect[i].sym if s == nil { continue } if s.Sub != nil { s.Sub = listsort(s.Sub, valuecmp, listsubp) } if s.Type == obj.STEXT { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList if Ctxt.Etextp != nil { Ctxt.Etextp.Next = s } else { Ctxt.Textp = s } Ctxt.Etextp = s for s = s.Sub; s != nil; s = s.Sub { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList Ctxt.Etextp.Next = s Ctxt.Etextp = s } } } return bad: Diag("%s: malformed pe file: %v", pn, err) }
func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { var err error var j int var is64 bool var secaddr uint64 var hdr [7 * 4]uint8 var cmdp []byte var dat []byte var ncmd uint32 var cmdsz uint32 var ty uint32 var sz uint32 var off uint32 var m *LdMachoObj var e binary.ByteOrder var sect *LdMachoSect var rel *LdMachoRel var rpi int var s *LSym var s1 *LSym var outer *LSym var c *LdMachoCmd var symtab *LdMachoSymtab var dsymtab *LdMachoDysymtab var sym *LdMachoSym var r []Reloc var rp *Reloc var name string Ctxt.Version++ base := obj.Boffset(f) if obj.Bread(f, hdr[:]) != len(hdr) { goto bad } if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { e = binary.BigEndian } else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { e = binary.LittleEndian } else { err = fmt.Errorf("bad magic - not mach-o file") goto bad } is64 = e.Uint32(hdr[:]) == 0xFEEDFACF ncmd = e.Uint32([]byte(hdr[4*4:])) cmdsz = e.Uint32([]byte(hdr[5*4:])) if ncmd > 0x10000 || cmdsz >= 0x01000000 { err = fmt.Errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz) goto bad } if is64 { var tmp [4]uint8 obj.Bread(f, tmp[:4]) // skip reserved word in header } m = new(LdMachoObj) m.f = f m.e = e m.cputype = uint(e.Uint32([]byte(hdr[1*4:]))) m.subcputype = uint(e.Uint32([]byte(hdr[2*4:]))) m.filetype = e.Uint32([]byte(hdr[3*4:])) m.ncmd = uint(ncmd) m.flags = e.Uint32([]byte(hdr[6*4:])) m.is64 = is64 m.base = base m.length = length m.name = pn switch Thearch.Thechar { default: Diag("%s: mach-o %s unimplemented", pn, Thestring) return case '6': if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 { Diag("%s: mach-o object but not amd64", pn) return } case '8': if e != binary.LittleEndian || m.cputype != LdMachoCpu386 { Diag("%s: mach-o object but not 386", pn) return } } m.cmd = make([]LdMachoCmd, ncmd) off = uint32(len(hdr)) cmdp = make([]byte, cmdsz) if obj.Bread(f, cmdp) != len(cmdp) { err = fmt.Errorf("reading cmds: %v", err) goto bad } // read and parse load commands c = nil symtab = nil dsymtab = nil for i := 0; uint32(i) < ncmd; i++ { ty = e.Uint32(cmdp) sz = e.Uint32(cmdp[4:]) m.cmd[i].off = off unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz)) cmdp = cmdp[sz:] off += sz if ty == LdMachoCmdSymtab { if symtab != nil { err = fmt.Errorf("multiple symbol tables") goto bad } symtab = &m.cmd[i].sym macholoadsym(m, symtab) } if ty == LdMachoCmdDysymtab { dsymtab = &m.cmd[i].dsym macholoaddsym(m, dsymtab) } if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) { if c != nil { err = fmt.Errorf("multiple load commands") goto bad } c = &m.cmd[i] } } // load text and data segments into memory. // they are not as small as the load commands, but we'll need // the memory anyway for the symbol images, so we might // as well use one large chunk. if c == nil { err = fmt.Errorf("no load command") goto bad } if symtab == nil { // our work is done here - no symbols means nothing can refer to this file return } if int64(c.seg.fileoff+c.seg.filesz) >= length { err = fmt.Errorf("load segment out of range") goto bad } dat = make([]byte, c.seg.filesz) if obj.Bseek(f, m.base+int64(c.seg.fileoff), 0) < 0 || obj.Bread(f, dat) != len(dat) { err = fmt.Errorf("cannot load object data: %v", err) goto bad } for i := 0; uint32(i) < c.seg.nsect; i++ { sect = &c.seg.sect[i] if sect.segname != "__TEXT" && sect.segname != "__DATA" { continue } if sect.name == "__eh_frame" { continue } name = fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name) s = Linklookup(Ctxt, name, Ctxt.Version) if s.Type != 0 { err = fmt.Errorf("duplicate %s/%s", sect.segname, sect.name) goto bad } if sect.flags&0xff == 1 { // S_ZEROFILL s.P = make([]byte, sect.size) } else { s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size] } s.Size = int64(len(s.P)) if sect.segname == "__TEXT" { if sect.name == "__text" { s.Type = obj.STEXT } else { s.Type = obj.SRODATA } } else { if sect.name == "__bss" { s.Type = obj.SNOPTRBSS s.P = s.P[:0] } else { s.Type = obj.SNOPTRDATA } } sect.sym = s } // enter sub-symbols into symbol table. // have to guess sizes from next symbol. for i := 0; uint32(i) < symtab.nsym; i++ { sym = &symtab.sym[i] if sym.type_&N_STAB != 0 { continue } // TODO: check sym->type against outer->type. name = sym.name if name[0] == '_' && name[1] != '\x00' { name = name[1:] } v := 0 if sym.type_&N_EXT == 0 { v = Ctxt.Version } s = Linklookup(Ctxt, name, v) if sym.type_&N_EXT == 0 { s.Attr |= AttrDuplicateOK } sym.sym = s if sym.sectnum == 0 { // undefined continue } if uint32(sym.sectnum) > c.seg.nsect { err = fmt.Errorf("reference to invalid section %d", sym.sectnum) goto bad } sect = &c.seg.sect[sym.sectnum-1] outer = sect.sym if outer == nil { err = fmt.Errorf("reference to invalid section %s/%s", sect.segname, sect.name) continue } if s.Outer != nil { if s.Attr.DuplicateOK() { continue } Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) } s.Type = outer.Type | obj.SSUB s.Sub = outer.Sub outer.Sub = s s.Outer = outer s.Value = int64(sym.value - sect.addr) if !s.Attr.CgoExportDynamic() { s.Dynimplib = "" // satisfy dynimport } if outer.Type == obj.STEXT { if s.Attr.External() && !s.Attr.DuplicateOK() { Diag("%s: duplicate definition of %s", pn, s.Name) } s.Attr |= AttrExternal } sym.sym = s } // Sort outer lists by address, adding to textp. // This keeps textp in increasing address order. for i := 0; uint32(i) < c.seg.nsect; i++ { sect = &c.seg.sect[i] s = sect.sym if s == nil { continue } if s.Sub != nil { s.Sub = listsort(s.Sub, valuecmp, listsubp) // assign sizes, now that we know symbols in sorted order. for s1 = s.Sub; s1 != nil; s1 = s1.Sub { if s1.Sub != nil { s1.Size = s1.Sub.Value - s1.Value } else { s1.Size = s.Value + s.Size - s1.Value } } } if s.Type == obj.STEXT { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList if Ctxt.Etextp != nil { Ctxt.Etextp.Next = s } else { Ctxt.Textp = s } Ctxt.Etextp = s for s1 = s.Sub; s1 != nil; s1 = s1.Sub { if s1.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s1.Name) } s1.Attr |= AttrOnList Ctxt.Etextp.Next = s1 Ctxt.Etextp = s1 } } } // load relocations for i := 0; uint32(i) < c.seg.nsect; i++ { sect = &c.seg.sect[i] s = sect.sym if s == nil { continue } macholoadrel(m, sect) if sect.rel == nil { continue } r = make([]Reloc, sect.nreloc) rpi = 0 Reloc: for j = 0; uint32(j) < sect.nreloc; j++ { rp = &r[rpi] rel = §.rel[j] if rel.scattered != 0 { if Thearch.Thechar != '8' { // mach-o only uses scattered relocation on 32-bit platforms Diag("unexpected scattered relocation") continue } // on 386, rewrite scattered 4/1 relocation and some // scattered 2/1 relocation into the pseudo-pc-relative // reference that it is. // assume that the second in the pair is in this section // and use that as the pc-relative base. if uint32(j+1) >= sect.nreloc { err = fmt.Errorf("unsupported scattered relocation %d", int(rel.type_)) goto bad } if sect.rel[j+1].scattered == 0 || sect.rel[j+1].type_ != 1 || (rel.type_ != 4 && rel.type_ != 2) || uint64(sect.rel[j+1].value) < sect.addr || uint64(sect.rel[j+1].value) >= sect.addr+sect.size { err = fmt.Errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_)) goto bad } rp.Siz = rel.length rp.Off = int32(rel.addr) // NOTE(rsc): I haven't worked out why (really when) // we should ignore the addend on a // scattered relocation, but it seems that the // common case is we ignore it. // It's likely that this is not strictly correct // and that the math should look something // like the non-scattered case below. rp.Add = 0 // want to make it pc-relative aka relative to rp->off+4 // but the scatter asks for relative to off = sect->rel[j+1].value - sect->addr. // adjust rp->add accordingly. rp.Type = obj.R_PCREL rp.Add += int64(uint64(int64(rp.Off)+4) - (uint64(sect.rel[j+1].value) - sect.addr)) // now consider the desired symbol. // find the section where it lives. var ks *LdMachoSect for k := 0; uint32(k) < c.seg.nsect; k++ { ks = &c.seg.sect[k] if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size { if ks.sym != nil { rp.Sym = ks.sym rp.Add += int64(uint64(rel.value) - ks.addr) } else if ks.segname == "__IMPORT" && ks.name == "__pointers" { // handle reference to __IMPORT/__pointers. // how much worse can this get? // why are we supporting 386 on the mac anyway? rp.Type = 512 + MACHO_FAKE_GOTPCREL // figure out which pointer this is a reference to. k = int(uint64(ks.res1) + (uint64(rel.value)-ks.addr)/4) // load indirect table for __pointers // fetch symbol number if dsymtab == nil || k < 0 || uint32(k) >= dsymtab.nindirectsyms || dsymtab.indir == nil { err = fmt.Errorf("invalid scattered relocation: indirect symbol reference out of range") goto bad } k = int(dsymtab.indir[k]) if k < 0 || uint32(k) >= symtab.nsym { err = fmt.Errorf("invalid scattered relocation: symbol reference out of range") goto bad } rp.Sym = symtab.sym[k].sym } else { err = fmt.Errorf("unsupported scattered relocation: reference to %s/%s", ks.segname, ks.name) goto bad } rpi++ // skip #1 of 2 rel; continue skips #2 of 2. j++ continue Reloc } } err = fmt.Errorf("unsupported scattered relocation: invalid address %#x", rel.addr) goto bad } rp.Siz = rel.length rp.Type = 512 + (int32(rel.type_) << 1) + int32(rel.pcrel) rp.Off = int32(rel.addr) // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). if Thearch.Thechar == '6' && rel.extrn == 0 && rel.type_ == 1 { // Calculate the addend as the offset into the section. // // The rip-relative offset stored in the object file is encoded // as follows: // // movsd 0x00000360(%rip),%xmm0 // // To get the absolute address of the value this rip-relative address is pointing // to, we must add the address of the next instruction to it. This is done by // taking the address of the relocation and adding 4 to it (since the rip-relative // offset can at most be 32 bits long). To calculate the offset into the section the // relocation is referencing, we subtract the vaddr of the start of the referenced // section found in the original object file. // // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] secaddr = c.seg.sect[rel.symnum-1].addr rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr) } else { rp.Add = int64(int32(e.Uint32(s.P[rp.Off:]))) } // For i386 Mach-O PC-relative, the addend is written such that // it *is* the PC being subtracted. Use that to make // it match our version of PC-relative. if rel.pcrel != 0 && Thearch.Thechar == '8' { rp.Add += int64(rp.Off) + int64(rp.Siz) } if rel.extrn == 0 { if rel.symnum < 1 || rel.symnum > c.seg.nsect { err = fmt.Errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect) goto bad } rp.Sym = c.seg.sect[rel.symnum-1].sym if rp.Sym == nil { err = fmt.Errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name) goto bad } // References to symbols in other sections // include that information in the addend. // We only care about the delta from the // section base. if Thearch.Thechar == '8' { rp.Add -= int64(c.seg.sect[rel.symnum-1].addr) } } else { if rel.symnum >= symtab.nsym { err = fmt.Errorf("invalid relocation: symbol reference out of range") goto bad } rp.Sym = symtab.sym[rel.symnum].sym } rpi++ } sort.Sort(rbyoff(r[:rpi])) s.R = r s.R = s.R[:rpi] } return bad: Diag("%s: malformed mach-o file: %v", pn, err) }
func dumpobj() { var err error bout, err = obj.Bopenw(outfile) if err != nil { Flusherrors() fmt.Printf("can't create %s: %v\n", outfile, err) errorexit() } startobj := int64(0) var arhdr [ArhdrSize]byte if writearchive != 0 { obj.Bwritestring(bout, "!<arch>\n") arhdr = [ArhdrSize]byte{} obj.Bwrite(bout, arhdr[:]) startobj = obj.Boffset(bout) } fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) dumpexport() if writearchive != 0 { obj.Bflush(bout) size := obj.Boffset(bout) - startobj if size&1 != 0 { obj.Bputc(bout, 0) } obj.Bseek(bout, startobj-ArhdrSize, 0) formathdr(arhdr[:], "__.PKGDEF", size) obj.Bwrite(bout, arhdr[:]) obj.Bflush(bout) obj.Bseek(bout, startobj+size+(size&1), 0) arhdr = [ArhdrSize]byte{} obj.Bwrite(bout, arhdr[:]) startobj = obj.Boffset(bout) fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) } if pragcgobuf != "" { if writearchive != 0 { // write empty export section; must be before cgo section fmt.Fprintf(bout, "\n$$\n\n$$\n\n") } fmt.Fprintf(bout, "\n$$ // cgo\n") fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf) } fmt.Fprintf(bout, "\n!\n") var externs *NodeList if externdcl != nil { externs = externdcl.End } dumpglobls() dumptypestructs() // Dump extra globals. tmp := externdcl if externs != nil { externdcl = externs.Next } dumpglobls() externdcl = tmp zero := Pkglookup("zerovalue", Runtimepkg) ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA) dumpdata() obj.Writeobjdirect(Ctxt, bout) if writearchive != 0 { obj.Bflush(bout) size := obj.Boffset(bout) - startobj if size&1 != 0 { obj.Bputc(bout, 0) } obj.Bseek(bout, startobj-ArhdrSize, 0) name := fmt.Sprintf("_go_.%c", Thearch.Thechar) formathdr(arhdr[:], name, size) obj.Bwrite(bout, arhdr[:]) } obj.Bterm(bout) }