func (r *objReader) readSym() { if c, err := r.rd.ReadByte(); c != symPrefix || err != nil { log.Fatalln("readSym out of sync") } t := obj.SymKind(r.readInt()) s := r.readSymIndex() flags := r.readInt() dupok := flags&1 != 0 local := flags&2 != 0 makeTypelink := flags&4 != 0 size := r.readInt() typ := r.readSymIndex() data := r.readData() nreloc := r.readInt() pkg := pathtoprefix(r.lib.Pkg) isdup := false var dup *Symbol if s.Type != 0 && s.Type != obj.SXREF { if (t == obj.SDATA || t == obj.SBSS || t == obj.SNOPTRBSS) && len(data) == 0 && nreloc == 0 { if s.Size < int64(size) { s.Size = int64(size) } if typ != nil && s.Gotype == nil { s.Gotype = typ } return } if (s.Type == obj.SDATA || s.Type == obj.SBSS || s.Type == obj.SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 { goto overwrite } if s.Type != obj.SBSS && s.Type != obj.SNOPTRBSS && !dupok && !s.Attr.DuplicateOK() { log.Fatalf("duplicate symbol %s (types %d and %d) in %s and %s", s.Name, s.Type, t, s.File, r.pn) } if len(s.P) > 0 { dup = s s = r.dupSym isdup = true } } overwrite: s.File = pkg if dupok { s.Attr |= AttrDuplicateOK } if t == obj.SXREF { log.Fatalf("bad sxref") } if t == 0 { log.Fatalf("missing type for %s in %s", s.Name, r.pn) } if t == obj.SBSS && (s.Type == obj.SRODATA || s.Type == obj.SNOPTRBSS) { t = s.Type } s.Type = t if s.Size < int64(size) { s.Size = int64(size) } s.Attr.Set(AttrLocal, local) s.Attr.Set(AttrMakeTypelink, makeTypelink) if typ != nil { s.Gotype = typ } if isdup && typ != nil { // if bss sym defined multiple times, take type from any one def dup.Gotype = typ } s.P = data if nreloc > 0 { s.R = r.reloc[:nreloc:nreloc] if !isdup { r.reloc = r.reloc[nreloc:] } for i := 0; i < nreloc; i++ { s.R[i] = Reloc{ Off: r.readInt32(), Siz: r.readUint8(), Type: obj.RelocType(r.readInt32()), Add: r.readInt64(), Sym: r.readSymIndex(), } } } if s.Type == obj.STEXT { s.FuncInfo = new(FuncInfo) pc := s.FuncInfo pc.Args = r.readInt32() pc.Locals = r.readInt32() if r.readUint8() != 0 { s.Attr |= AttrNoSplit } flags := r.readInt() if flags&(1<<2) != 0 { s.Attr |= AttrReflectMethod } n := r.readInt() pc.Autom = r.autom[:n:n] if !isdup { r.autom = r.autom[n:] } for i := 0; i < n; i++ { pc.Autom[i] = Auto{ Asym: r.readSymIndex(), Aoffset: r.readInt32(), Name: r.readInt16(), Gotype: r.readSymIndex(), } } pc.Pcsp.P = r.readData() pc.Pcfile.P = r.readData() pc.Pcline.P = r.readData() n = r.readInt() pc.Pcdata = r.pcdata[:n:n] if !isdup { r.pcdata = r.pcdata[n:] } for i := 0; i < n; i++ { pc.Pcdata[i].P = r.readData() } n = r.readInt() pc.Funcdata = r.funcdata[:n:n] pc.Funcdataoff = r.funcdataoff[:n:n] if !isdup { r.funcdata = r.funcdata[n:] r.funcdataoff = r.funcdataoff[n:] } for i := 0; i < n; i++ { pc.Funcdata[i] = r.readSymIndex() } for i := 0; i < n; i++ { pc.Funcdataoff[i] = r.readInt64() } n = r.readInt() pc.File = r.file[:n:n] if !isdup { r.file = r.file[n:] } for i := 0; i < n; i++ { pc.File[i] = r.readSymIndex() } if !dupok { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList r.lib.textp = append(r.lib.textp, s) } else { // there may ba a dup in another package // put into a temp list and add to text later if !isdup { r.lib.dupTextSyms = append(r.lib.dupTextSyms, s) } else { r.lib.dupTextSyms = append(r.lib.dupTextSyms, dup) } } } if s.Type == obj.SDWARFINFO { r.patchDWARFName(s) } }
func ldmacho(ctxt *Link, f *bio.Reader, 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 *Symbol var s1 *Symbol var outer *Symbol var c *ldMachoCmd var symtab *ldMachoSymtab var dsymtab *ldMachoDysymtab var sym *ldMachoSym var r []Reloc var rp *Reloc var name string localSymVersion := ctxt.Syms.IncVersion() base := f.Offset() if _, err := io.ReadFull(f, hdr[:]); err != nil { 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(hdr[4*4:]) cmdsz = e.Uint32(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 { f.Seek(4, 1) // skip reserved word in header } m = new(ldMachoObj) m.f = f m.e = e m.cputype = uint(e.Uint32(hdr[1*4:])) m.subcputype = uint(e.Uint32(hdr[2*4:])) m.filetype = e.Uint32(hdr[3*4:]) m.ncmd = uint(ncmd) m.flags = e.Uint32(hdr[6*4:]) m.is64 = is64 m.base = base m.length = length m.name = pn switch SysArch.Family { default: Errorf(nil, "%s: mach-o %s unimplemented", pn, SysArch.Name) return case sys.AMD64: if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 { Errorf(nil, "%s: mach-o object but not amd64", pn) return } case sys.I386: if e != binary.LittleEndian || m.cputype != LdMachoCpu386 { Errorf(nil, "%s: mach-o object but not 386", pn) return } } m.cmd = make([]ldMachoCmd, ncmd) off = uint32(len(hdr)) cmdp = make([]byte, cmdsz) if _, err2 := io.ReadFull(f, cmdp); err2 != nil { 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 f.Seek(m.base+int64(c.seg.fileoff), 0) < 0 { err = fmt.Errorf("cannot load object data: %v", err) goto bad } if _, err2 := io.ReadFull(f, dat); err2 != nil { 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 = ctxt.Syms.Lookup(name, localSymVersion) 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 = localSymVersion } s = ctxt.Syms.Lookup(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() { Errorf(s, "%s: duplicate symbol definition", pn) } 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) // 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 ctxt.Textp = append(ctxt.Textp, 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.Textp = append(ctxt.Textp, 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 SysArch.Family != sys.I386 { // mach-o only uses scattered relocation on 32-bit platforms Errorf(s, "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 + (obj.RelocType(rel.type_) << 1) + obj.RelocType(rel.pcrel) rp.Off = int32(rel.addr) // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). if SysArch.Family == sys.AMD64 && 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 && SysArch.Family == sys.I386 { 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 SysArch.Family == sys.I386 { 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: Errorf(nil, "%s: malformed mach-o file: %v", pn, err) }
func ldelf(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f ldelf %s\n", obj.Cputime(), pn) } localSymVersion := ctxt.Syms.IncVersion() base := f.Offset() 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 *Symbol var sect *ElfSect var sym ElfSym var symbols []*Symbol if _, err := io.ReadFull(f, hdrbuf[:]); err != nil { 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 = 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 { Errorf(nil, "%s: elf but not elf relocatable object", pn) return } switch SysArch.Family { default: Errorf(nil, "%s: elf %s unimplemented", pn, SysArch.Name) return case sys.MIPS64: if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass64 { Errorf(nil, "%s: elf object but not mips64", pn) return } case sys.ARM: if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 { Errorf(nil, "%s: elf object but not arm", pn) return } case sys.AMD64: if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 { Errorf(nil, "%s: elf object but not amd64", pn) return } case sys.ARM64: if e != binary.LittleEndian || elfobj.machine != ElfMachArm64 || hdr.Ident[4] != ElfClass64 { Errorf(nil, "%s: elf object but not arm64", pn) return } case sys.I386: if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 { Errorf(nil, "%s: elf object but not 386", pn) return } case sys.PPC64: if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 { Errorf(nil, "%s: elf object but not ppc64", pn) return } case sys.S390X: if elfobj.machine != ElfMachS390 || hdr.Ident[4] != ElfClass64 { Errorf(nil, "%s: elf object but not s390x", 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 f.Seek(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 = 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 = 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) { Errorf(nil, "%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(ctxt, 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 = ctxt.Syms.Lookup(name, localSymVersion) 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([]*Symbol, elfobj.nsymtab) for i := 1; i < elfobj.nsymtab; i++ { if err = readelfsym(ctxt, elfobj, i, &sym, 1, localSymVersion); 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] if sect.sym == nil { if strings.HasPrefix(sym.name, ".Linfo_string") { // clang does this continue } if sym.name == "" && sym.type_ == 0 && sect.name == ".debug_str" { // This reportedly happens with clang 3.7 on ARM. // See issue 13139. continue } if strings.HasPrefix(sym.name, ".LASF") { // gcc on s390x does this continue } Errorf(sym.sym, "%s: sym#%d: ignoring symbol in section %d (type %d)", pn, i, sym.shndx, sym.type_) continue } s = sym.sym 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 | s.Type&^obj.SMASK | obj.SSUB if !s.Attr.CgoExportDynamic() { 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.Attr.External() && !s.Attr.DuplicateOK() { Errorf(s, "%s: duplicate symbol definition", pn) } s.Attr |= AttrExternal } if elfobj.machine == ElfMachPower64 { flag = int(sym.other) >> 5 if 2 <= flag && flag <= 6 { s.Localentry = 1 << uint(flag-2) } else if flag == 7 { Errorf(s, "%s: invalid sym.other 0x%x", pn, sym.other) } } } // 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) } if s.Type == obj.STEXT { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList ctxt.Textp = append(ctxt.Textp, 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.Textp = append(ctxt.Textp, 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(ctxt, elfobj, int(info>>32), &sym, 0, 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 = 256 + obj.RelocType(info) rp.Siz = relSize(ctxt, pn, uint32(info)) 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 { Errorf(nil, "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: Errorf(nil, "%s: malformed elf file: %v", pn, err) }
// parseObject parses a single Go object file. // The prefix is the bytes already read from the file, // typically in order to detect that this is an object file. // The object file consists of a textual header ending in "\n!\n" // and then the part we want to parse begins. // The format of that part is defined in a comment at the top // of src/liblink/objfile.c. func (r *objReader) parseObject(prefix []byte) error { r.p.MaxVersion++ h := make([]byte, 0, 256) h = append(h, prefix...) var c1, c2, c3 byte for { c1, c2, c3 = c2, c3, r.readByte() h = append(h, c3) // The new export format can contain 0 bytes. // Don't consider them errors, only look for r.err != nil. if r.err != nil { return errCorruptObject } if c1 == '\n' && c2 == '!' && c3 == '\n' { break } } hs := strings.Fields(string(h)) if len(hs) >= 4 { r.p.Arch = hs[3] } // TODO: extract OS + build ID if/when we need it r.readFull(r.tmp[:8]) if !bytes.Equal(r.tmp[:8], []byte("\x00\x00go17ld")) { return r.error(errCorruptObject) } b := r.readByte() if b != 1 { return r.error(errCorruptObject) } // Direct package dependencies. for { s := r.readString() if s == "" { break } r.p.Imports = append(r.p.Imports, s) } r.p.SymRefs = []SymID{{"", 0}} for { if b := r.readByte(); b != 0xfe { if b != 0xff { return r.error(errCorruptObject) } break } r.readRef() } dataLength := r.readInt() r.readInt() // n relocations - ignore r.readInt() // n pcdata - ignore r.readInt() // n autom - ignore r.readInt() // n funcdata - ignore r.readInt() // n files - ignore r.dataOffset = r.offset r.skip(int64(dataLength)) // Symbols. for { if b := r.readByte(); b != 0xfe { if b != 0xff { return r.error(errCorruptObject) } break } typ := r.readInt() s := &Sym{SymID: r.readSymID()} r.p.Syms = append(r.p.Syms, s) s.Kind = SymKind(typ) flags := r.readInt() s.DupOK = flags&1 != 0 s.Size = r.readInt() s.Type = r.readSymID() s.Data = r.readData() s.Reloc = make([]Reloc, r.readInt()) for i := range s.Reloc { rel := &s.Reloc[i] rel.Offset = r.readInt() rel.Size = r.readInt() rel.Type = obj.RelocType(r.readInt()) rel.Add = r.readInt() rel.Sym = r.readSymID() } if s.Kind == STEXT { f := new(Func) s.Func = f f.Args = r.readInt() f.Frame = r.readInt() flags := r.readInt() f.Leaf = flags&1 != 0 f.NoSplit = r.readInt() != 0 f.Var = make([]Var, r.readInt()) for i := range f.Var { v := &f.Var[i] v.Name = r.readSymID().Name v.Offset = r.readInt() v.Kind = r.readInt() v.Type = r.readSymID() } f.PCSP = r.readData() f.PCFile = r.readData() f.PCLine = r.readData() f.PCData = make([]Data, r.readInt()) for i := range f.PCData { f.PCData[i] = r.readData() } f.FuncData = make([]FuncData, r.readInt()) for i := range f.FuncData { f.FuncData[i].Sym = r.readSymID() } for i := range f.FuncData { f.FuncData[i].Offset = int64(r.readInt()) // TODO } f.File = make([]string, r.readInt()) for i := range f.File { f.File[i] = r.readSymID().Name } } } r.readFull(r.tmp[:7]) if !bytes.Equal(r.tmp[:7], []byte("\xffgo17ld")) { return r.error(errCorruptObject) } return nil }