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 ldobjfile(ctxt *Link, f *obj.Biobuf, pkg string, length int64, pn string) { start := obj.Boffset(f) ctxt.Version++ var buf [8]uint8 obj.Bread(f, buf[:]) if string(buf[:]) != startmagic { 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]) } c := obj.Bgetc(f) if c != 1 { log.Fatalf("%s: invalid file version number %d", pn, c) } var lib string for { lib = rdstring(f) if lib == "" { break } addlib(ctxt, pkg, pn, lib) } for { c, err := f.Peek(1) if err != nil { log.Fatalf("%s: peeking: %v", pn, err) } if c[0] == 0xff { break } readsym(ctxt, f, pkg, pn) } buf = [8]uint8{} obj.Bread(f, buf[:]) if string(buf[:]) != endmagic { log.Fatalf("%s: invalid file end", pn) } if obj.Boffset(f) != start+length { log.Fatalf("%s: unexpected end at %d, want %d", pn, int64(obj.Boffset(f)), int64(start+length)) } }
// readArmap reads the archive symbol map. func readArmap(filename string, f *obj.Biobuf, arhdr ArHdr) archiveMap { is64 := arhdr.name == "/SYM64/" wordSize := 4 if is64 { wordSize = 8 } l := atolwhex(arhdr.size) contents := make([]byte, l) if obj.Bread(f, contents) != int(l) { Exitf("short read from %s", filename) } var c uint64 if is64 { c = binary.BigEndian.Uint64(contents) } else { c = uint64(binary.BigEndian.Uint32(contents)) } contents = contents[wordSize:] ret := make(archiveMap) names := contents[c*uint64(wordSize):] for i := uint64(0); i < c; i++ { n := 0 for names[n] != 0 { n++ } name := string(names[:n]) names = names[n+1:] // For Mach-O and PE/386 files we strip a leading // underscore from the symbol name. if goos == "darwin" || (goos == "windows" && goarch == "386") { if name[0] == '_' && len(name) > 1 { name = name[1:] } } var off uint64 if is64 { off = binary.BigEndian.Uint64(contents) } else { off = uint64(binary.BigEndian.Uint32(contents)) } contents = contents[wordSize:] ret[name] = off } return ret }
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 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 rdsym(ctxt *Link, f *obj.Biobuf, pkg string) *LSym { n := rdint(f) if n == 0 { rdint64(f) return nil } if len(symbuf) < n { symbuf = make([]byte, n) } obj.Bread(f, symbuf[:n]) p := string(symbuf[:n]) v := rdint(f) if v != 0 { v = ctxt.Version } s := Linklookup(ctxt, expandpkg(p, pkg), v) if v == 0 && s.Name[0] == '$' && s.Type == 0 { if strings.HasPrefix(s.Name, "$f32.") { x, _ := strconv.ParseUint(s.Name[5:], 16, 32) i32 := int32(x) s.Type = obj.SRODATA s.Local = true Adduint32(ctxt, s, uint32(i32)) s.Reachable = false } else if strings.HasPrefix(s.Name, "$f64.") || strings.HasPrefix(s.Name, "$i64.") { x, _ := strconv.ParseUint(s.Name[5:], 16, 64) i64 := int64(x) s.Type = obj.SRODATA s.Local = true Adduint64(ctxt, s, uint64(i64)) s.Reachable = false } } if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.") { s.Local = true } return s }
func rddata(f *obj.Biobuf) []byte { n := rdint64(f) p := make([]byte, n) obj.Bread(f, p) return p }
func rdstring(f *obj.Biobuf) string { n := rdint64(f) p := make([]byte, n) obj.Bread(f, p) return string(p) }
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.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 | obj.SSUB s.Value = int64(sym.value) s.Size = 4 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 } } // 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.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 } } } return bad: Diag("%s: malformed pe file: %v", pn, err) }
func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int) { var p0, p1 int if Debug['g'] != 0 { return } if int64(int(length)) != length { fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename) if Debug['u'] != 0 { errorexit() } return } // In a __.PKGDEF, we only care about the package name. // Don't read all the export data. if length > 1000 && whence == Pkgdef { length = 1000 } bdata := make([]byte, length) if int64(obj.Bread(f, bdata)) != length { fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename) if Debug['u'] != 0 { errorexit() } return } data := string(bdata) // first \n$$ marks beginning of exports - skip rest of line p0 = strings.Index(data, "\n$$") if p0 < 0 { if Debug['u'] != 0 && whence != ArchiveObj { Exitf("cannot find export data in %s", filename) } return } // \n$$B marks the beginning of binary export data - don't skip over the B p0 += 3 for p0 < len(data) && data[p0] != '\n' && data[p0] != 'B' { p0++ } // second marks end of exports / beginning of local data p1 = strings.Index(data[p0:], "\n$$\n") if p1 < 0 && whence == Pkgdef { p1 = len(data) - p0 } if p1 < 0 { fmt.Fprintf(os.Stderr, "%s: cannot find end of exports in %s\n", os.Args[0], filename) if Debug['u'] != 0 { errorexit() } return } p1 += p0 for p0 < p1 && data[p0] != 'B' && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') { p0++ } // don't check this section if we have binary (B) export data // TODO fix this eventually if p0 < p1 && data[p0] != 'B' { if !strings.HasPrefix(data[p0:], "package ") { fmt.Fprintf(os.Stderr, "%s: bad package section in %s - %.20s\n", os.Args[0], filename, data[p0:]) if Debug['u'] != 0 { errorexit() } return } p0 += 8 for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') { p0++ } pname := p0 for p0 < p1 && data[p0] != ' ' && data[p0] != '\t' && data[p0] != '\n' { p0++ } if Debug['u'] != 0 && whence != ArchiveObj && (p0+6 > p1 || !strings.HasPrefix(data[p0:], " safe\n")) { Exitf("load of unsafe package %s", filename) } name := data[pname:p0] for p0 < p1 && data[p0] != '\n' { p0++ } if p0 < p1 { p0++ } if pkg == "main" && name != "main" { Exitf("%s: not package main (package %s)", filename, name) } } // __.PKGDEF has no cgo section - those are in the C compiler-generated object files. if whence == Pkgdef { return } // look for cgo section p0 = strings.Index(data[p1:], "\n$$ // cgo") if p0 >= 0 { p0 += p1 i := strings.IndexByte(data[p0+1:], '\n') if i < 0 { fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename) if Debug['u'] != 0 { errorexit() } return } p0 += 1 + i p1 = strings.Index(data[p0:], "\n$$") if p1 < 0 { p1 = strings.Index(data[p0:], "\n!\n") } if p1 < 0 { fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename) if Debug['u'] != 0 { errorexit() } return } p1 += p0 loadcgo(filename, pkg, data[p0:p1]) } }
// 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. if Debug['v'] != 0 { fmt.Fprintf(&Bso, "skipping libgcc file: %v\n", err) } 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 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.Dupok = 1 } 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.Dupok != 0 { 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.Cgoexport&CgoExportDynamic == 0 { s.Dynimplib = "" // satisfy dynimport } if outer.Type == obj.STEXT { if s.External != 0 && s.Dupok == 0 { Diag("%s: duplicate definition of %s", pn, s.Name) } s.External = 1 } 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.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 s1 = s.Sub; s1 != nil; s1 = s1.Sub { if s1.Onlist != 0 { log.Fatalf("symbol %s listed multiple times", s1.Name) } s1.Onlist = 1 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 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 '0': if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass64 { Diag("%s: elf object but not mips64", pn) 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 } if sym.name == "" && sym.type_ == 0 && sect.name == ".debug_str" { // This reportedly happens with clang 3.7 on ARM. // See issue 13139. 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) }