func reloc() { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f reloc\n", obj.Cputime()) } Bflush(&Bso) for s := Ctxt.Textp; s != nil; s = s.Next { relocsym(s) } for s := datap; s != nil; s = s.Next { relocsym(s) } }
func dynreloc() { // -d suppresses dynamic loader format, so we may as well not // compute these sections or mark their symbols as reachable. if Debug['d'] != 0 && HEADTYPE != obj.Hwindows { return } if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f reloc\n", obj.Cputime()) } Bflush(&Bso) for s := Ctxt.Textp; s != nil; s = s.Next { dynrelocsym(s) } for s := datap; s != nil; s = s.Next { dynrelocsym(s) } if Iself { elfdynhash() } }
/* * add library to library list. * srcref: src file referring to package * objref: object file referring to package * file: object file, e.g., /home/rsc/go/pkg/container/vector.a * pkg: package import path, e.g. container/vector */ func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg string, shlibnamefile string) { for i := 0; i < len(ctxt.Library); i++ { if pkg == ctxt.Library[i].Pkg { return } } if ctxt.Debugvlog > 1 && ctxt.Bso != nil { fmt.Fprintf(ctxt.Bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s shlibnamefile: %s\n", obj.Cputime(), srcref, objref, file, pkg, shlibnamefile) } ctxt.Library = append(ctxt.Library, Library{}) l := &ctxt.Library[len(ctxt.Library)-1] l.Objref = objref l.Srcref = srcref l.File = file l.Pkg = pkg if shlibnamefile != "" { shlibbytes, err := ioutil.ReadFile(shlibnamefile) if err != nil { Diag("cannot read %s: %v", shlibnamefile, err) } l.Shlib = strings.TrimSpace(string(shlibbytes)) } }
func ldpe(f *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(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 Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) if Bread(f, symbuf[:4]) != 4 { goto bad } l = Le32(symbuf[:]) peobj.snames = make([]byte, l) Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) if 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 } l = uint32(obj.Atoi(peobj.sect[i].name[1:])) peobj.sect[i].name = cstring(peobj.snames[l:]) } // read symbols peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols) peobj.npesym = uint(peobj.fh.NumberOfSymbols) Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0) for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 { Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0) if 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) Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0) for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ { rp = &r[j] if 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 deadcode() { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f deadcode\n", obj.Cputime()) } if Buildmode == BuildmodeShared { // Mark all symbols as reachable when building a // shared library. for s := Ctxt.Allsym; s != nil; s = s.Allsym { if s.Type != 0 { mark(s) } } mark(Linkrlookup(Ctxt, "main.main", 0)) mark(Linkrlookup(Ctxt, "main.init", 0)) } else { mark(Linklookup(Ctxt, INITENTRY, 0)) if Linkshared && Buildmode == BuildmodeExe { mark(Linkrlookup(Ctxt, "main.main", 0)) mark(Linkrlookup(Ctxt, "main.init", 0)) } for i := 0; i < len(markextra); i++ { mark(Linklookup(Ctxt, markextra[i], 0)) } for i := 0; i < len(dynexp); i++ { mark(dynexp[i]) } markflood() // keep each beginning with 'typelink.' if the symbol it points at is being kept. for s := Ctxt.Allsym; s != nil; s = s.Allsym { if strings.HasPrefix(s.Name, "go.typelink.") { s.Reachable = len(s.R) == 1 && s.R[0].Sym.Reachable } } // remove dead text but keep file information (z symbols). var last *LSym for s := Ctxt.Textp; s != nil; s = s.Next { if !s.Reachable { continue } // NOTE: Removing s from old textp and adding to new, shorter textp. if last == nil { Ctxt.Textp = s } else { last.Next = s } last = s } if last == nil { Ctxt.Textp = nil Ctxt.Etextp = nil } else { last.Next = nil Ctxt.Etextp = last } } for s := Ctxt.Allsym; s != nil; s = s.Allsym { if strings.HasPrefix(s.Name, "go.weak.") { s.Special = 1 // do not lay out in data segment s.Reachable = true s.Hide = 1 } } // record field tracking references var buf bytes.Buffer var p *LSym for s := Ctxt.Allsym; s != nil; s = s.Allsym { if strings.HasPrefix(s.Name, "go.track.") { s.Special = 1 // do not lay out in data segment s.Hide = 1 if s.Reachable { buf.WriteString(s.Name[9:]) for p = s.Reachparent; p != nil; p = p.Reachparent { buf.WriteString("\t") buf.WriteString(p.Name) } buf.WriteString("\n") } s.Type = obj.SCONST s.Value = 0 } } if tracksym == "" { return } s := Linklookup(Ctxt, tracksym, 0) if !s.Reachable { return } addstrdata(tracksym, buf.String()) }
func dodata() { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f dodata\n", obj.Cputime()) } Bflush(&Bso) var last *LSym datap = nil for s := Ctxt.Allsym; s != nil; s = s.Allsym { if !s.Reachable || s.Special != 0 { continue } if obj.STEXT < s.Type && s.Type < obj.SXREF { if s.Onlist != 0 { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Onlist = 1 if last == nil { datap = s } else { last.Next = s } s.Next = nil last = s } } for s := datap; s != nil; s = s.Next { if int64(len(s.P)) > s.Size { Diag("%s: initialize bounds (%d < %d)", s.Name, int64(s.Size), len(s.P)) } } /* * now that we have the datap list, but before we start * to assign addresses, record all the necessary * dynamic relocations. these will grow the relocation * symbol, which is itself data. * * on darwin, we need the symbol table numbers for dynreloc. */ if HEADTYPE == obj.Hdarwin { machosymorder() } dynreloc() /* some symbols may no longer belong in datap (Mach-O) */ var l **LSym var s *LSym for l = &datap; ; { s = *l if s == nil { break } if s.Type <= obj.STEXT || obj.SXREF <= s.Type { *l = s.Next } else { l = &s.Next } } *l = nil datap = listsort(datap, datcmp, listnextp) /* * allocate sections. list is sorted by type, * so we can just walk it for each piece we want to emit. * segdata is processed before segtext, because we need * to see all symbols in the .data and .bss sections in order * to generate garbage collection information. */ /* begin segdata */ /* skip symbols belonging to segtext */ s = datap for ; s != nil && s.Type < obj.SELFSECT; s = s.Next { } /* writable ELF sections */ datsize := int64(0) var sect *Section for ; s != nil && s.Type < obj.SELFGOT; s = s.Next { sect = addsection(&Segdata, s.Name, 06) sect.Align = symalign(s) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) s.Sect = sect s.Type = obj.SDATA s.Value = int64(uint64(datsize) - sect.Vaddr) growdatsize(&datsize, s) sect.Length = uint64(datsize) - sect.Vaddr } /* .got (and .toc on ppc64) */ if s.Type == obj.SELFGOT { sect := addsection(&Segdata, ".got", 06) sect.Align = maxalign(s, obj.SELFGOT) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) var toc *LSym for ; s != nil && s.Type == obj.SELFGOT; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = obj.SDATA s.Value = int64(uint64(datsize) - sect.Vaddr) // Resolve .TOC. symbol for this object file (ppc64) toc = Linkrlookup(Ctxt, ".TOC.", int(s.Version)) if toc != nil { toc.Sect = sect toc.Outer = s toc.Sub = s.Sub s.Sub = toc toc.Value = 0x8000 } growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr } /* pointer-free data */ sect = addsection(&Segdata, ".noptrdata", 06) sect.Align = maxalign(s, obj.SINITARR-1) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) Linklookup(Ctxt, "runtime.noptrdata", 0).Sect = sect Linklookup(Ctxt, "runtime.enoptrdata", 0).Sect = sect for ; s != nil && s.Type < obj.SINITARR; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = obj.SDATA s.Value = int64(uint64(datsize) - sect.Vaddr) growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr hasinitarr := Linkshared /* shared library initializer */ switch Buildmode { case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared: hasinitarr = true } if hasinitarr { sect := addsection(&Segdata, ".init_array", 06) sect.Align = maxalign(s, obj.SINITARR) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) for ; s != nil && s.Type == obj.SINITARR; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Value = int64(uint64(datsize) - sect.Vaddr) growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr } /* data */ sect = addsection(&Segdata, ".data", 06) sect.Align = maxalign(s, obj.SBSS-1) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) Linklookup(Ctxt, "runtime.data", 0).Sect = sect Linklookup(Ctxt, "runtime.edata", 0).Sect = sect gcdata := Linklookup(Ctxt, "runtime.gcdata", 0) var gen ProgGen proggeninit(&gen, gcdata) for ; s != nil && s.Type < obj.SBSS; s = s.Next { if s.Type == obj.SINITARR { Ctxt.Cursym = s Diag("unexpected symbol type %d", s.Type) } s.Sect = sect s.Type = obj.SDATA datsize = aligndatsize(datsize, s) s.Value = int64(uint64(datsize) - sect.Vaddr) proggenaddsym(&gen, s) // gc growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr proggenfini(&gen, int64(sect.Length)) // gc /* bss */ sect = addsection(&Segdata, ".bss", 06) sect.Align = maxalign(s, obj.SNOPTRBSS-1) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) Linklookup(Ctxt, "runtime.bss", 0).Sect = sect Linklookup(Ctxt, "runtime.ebss", 0).Sect = sect gcbss := Linklookup(Ctxt, "runtime.gcbss", 0) proggeninit(&gen, gcbss) for ; s != nil && s.Type < obj.SNOPTRBSS; s = s.Next { s.Sect = sect datsize = aligndatsize(datsize, s) s.Value = int64(uint64(datsize) - sect.Vaddr) proggenaddsym(&gen, s) // gc growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr proggenfini(&gen, int64(sect.Length)) // gc /* pointer-free bss */ sect = addsection(&Segdata, ".noptrbss", 06) sect.Align = maxalign(s, obj.SNOPTRBSS) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) Linklookup(Ctxt, "runtime.noptrbss", 0).Sect = sect Linklookup(Ctxt, "runtime.enoptrbss", 0).Sect = sect for ; s != nil && s.Type == obj.SNOPTRBSS; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Value = int64(uint64(datsize) - sect.Vaddr) growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr Linklookup(Ctxt, "runtime.end", 0).Sect = sect // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. if datsize != int64(uint32(datsize)) { Diag("data or bss segment too large") } if Iself && Linkmode == LinkExternal && s != nil && s.Type == obj.STLSBSS && HEADTYPE != obj.Hopenbsd { sect := addsection(&Segdata, ".tbss", 06) sect.Align = int32(Thearch.Ptrsize) sect.Vaddr = 0 datsize = 0 for ; s != nil && s.Type == obj.STLSBSS; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Value = int64(uint64(datsize) - sect.Vaddr) growdatsize(&datsize, s) } sect.Length = uint64(datsize) } else { // Might be internal linking but still using cgo. // In that case, the only possible STLSBSS symbol is runtime.tlsg. // Give it offset 0, because it's the only thing here. if s != nil && s.Type == obj.STLSBSS && s.Name == "runtime.tlsg" { s.Value = 0 s = s.Next } } if s != nil { Ctxt.Cursym = nil Diag("unexpected symbol type %d for %s", s.Type, s.Name) } /* * We finished data, begin read-only data. * Not all systems support a separate read-only non-executable data section. * ELF systems do. * OS X and Plan 9 do not. * Windows PE may, but if so we have not implemented it. * And if we're using external linking mode, the point is moot, * since it's not our decision; that code expects the sections in * segtext. */ var segro *Segment if Iself && Linkmode == LinkInternal { segro = &Segrodata } else { segro = &Segtext } s = datap datsize = 0 /* read-only executable ELF, Mach-O sections */ for ; s != nil && s.Type < obj.STYPE; s = s.Next { sect = addsection(&Segtext, s.Name, 04) sect.Align = symalign(s) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) s.Sect = sect s.Type = obj.SRODATA s.Value = int64(uint64(datsize) - sect.Vaddr) growdatsize(&datsize, s) sect.Length = uint64(datsize) - sect.Vaddr } /* read-only data */ sect = addsection(segro, ".rodata", 04) sect.Align = maxalign(s, obj.STYPELINK-1) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = 0 Linklookup(Ctxt, "runtime.rodata", 0).Sect = sect Linklookup(Ctxt, "runtime.erodata", 0).Sect = sect for ; s != nil && s.Type < obj.STYPELINK; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = obj.SRODATA s.Value = int64(uint64(datsize) - sect.Vaddr) growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr /* typelink */ sect = addsection(segro, ".typelink", 04) sect.Align = maxalign(s, obj.STYPELINK) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) Linklookup(Ctxt, "runtime.typelink", 0).Sect = sect Linklookup(Ctxt, "runtime.etypelink", 0).Sect = sect for ; s != nil && s.Type == obj.STYPELINK; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = obj.SRODATA s.Value = int64(uint64(datsize) - sect.Vaddr) growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr /* gosymtab */ sect = addsection(segro, ".gosymtab", 04) sect.Align = maxalign(s, obj.SPCLNTAB-1) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) Linklookup(Ctxt, "runtime.symtab", 0).Sect = sect Linklookup(Ctxt, "runtime.esymtab", 0).Sect = sect for ; s != nil && s.Type < obj.SPCLNTAB; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = obj.SRODATA s.Value = int64(uint64(datsize) - sect.Vaddr) growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr /* gopclntab */ sect = addsection(segro, ".gopclntab", 04) sect.Align = maxalign(s, obj.SELFROSECT-1) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) Linklookup(Ctxt, "runtime.pclntab", 0).Sect = sect Linklookup(Ctxt, "runtime.epclntab", 0).Sect = sect for ; s != nil && s.Type < obj.SELFROSECT; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = obj.SRODATA s.Value = int64(uint64(datsize) - sect.Vaddr) growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr /* read-only ELF, Mach-O sections */ for ; s != nil && s.Type < obj.SELFSECT; s = s.Next { sect = addsection(segro, s.Name, 04) sect.Align = symalign(s) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) s.Sect = sect s.Type = obj.SRODATA s.Value = int64(uint64(datsize) - sect.Vaddr) growdatsize(&datsize, s) sect.Length = uint64(datsize) - sect.Vaddr } // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. if datsize != int64(uint32(datsize)) { Diag("read-only data segment too large") } /* number the sections */ n := int32(1) for sect := Segtext.Sect; sect != nil; sect = sect.Next { sect.Extnum = int16(n) n++ } for sect := Segrodata.Sect; sect != nil; sect = sect.Next { sect.Extnum = int16(n) n++ } for sect := Segdata.Sect; sect != nil; sect = sect.Next { sect.Extnum = int16(n) n++ } }
func pclntab() { funcdata_bytes := int64(0) ftab := Linklookup(Ctxt, "runtime.pclntab", 0) ftab.Type = obj.SPCLNTAB ftab.Reachable = true // See golang.org/s/go12symtab for the format. Briefly: // 8-byte header // nfunc [thearch.ptrsize bytes] // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] // end PC [thearch.ptrsize bytes] // offset to file table [4 bytes] nfunc := int32(0) for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { if container(Ctxt.Cursym) == 0 { nfunc++ } } pclntabNfunc = nfunc Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4) setuint32(Ctxt, ftab, 0, 0xfffffffb) setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc)) setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize)) setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize)) pclntabPclntabOffset = int32(8 + Thearch.Ptrsize) nfunc = 0 var last *LSym var end int32 var funcstart int32 var i int32 var it Pciter var off int32 var pcln *Pcln for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { last = Ctxt.Cursym if container(Ctxt.Cursym) != 0 { continue } pcln = Ctxt.Cursym.Pcln if pcln == nil { pcln = &pclntab_zpcln } if pclntabFirstFunc == nil { pclntabFirstFunc = Ctxt.Cursym } funcstart = int32(len(ftab.P)) funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym) setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize)) // fixed size of struct, checked below off = funcstart end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize) if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) { end += 4 } Symgrow(Ctxt, ftab, int64(end)) // entry uintptr off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym)) // name int32 off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name)))) // args int32 // TODO: Move into funcinfo. off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args))) // frame int32 // This has been removed (it was never set quite correctly anyway). // Nothing should use it. // Leave an obviously incorrect value. // TODO: Remove entirely. off = int32(setuint32(Ctxt, ftab, int64(off), 0x1234567)) if pcln != &pclntab_zpcln { renumberfiles(Ctxt, pcln.File, &pcln.Pcfile) if false { // Sanity check the new numbering for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) { if it.value < 1 || it.value > Ctxt.Nhistfile { Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, Ctxt.Nhistfile) errorexit() } } } } // pcdata off = addpctab(ftab, off, &pcln.Pcsp) off = addpctab(ftab, off, &pcln.Pcfile) off = addpctab(ftab, off, &pcln.Pcline) off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata))) off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata))) for i = 0; i < int32(pcln.Npcdata); i++ { off = addpctab(ftab, off, &pcln.Pcdata[i]) } // funcdata, must be pointer-aligned and we're only int32-aligned. // Missing funcdata will be 0 (nil pointer). if pcln.Nfuncdata > 0 { if off&int32(Thearch.Ptrsize-1) != 0 { off += 4 } for i = 0; i < int32(pcln.Nfuncdata); i++ { if pcln.Funcdata[i] == nil { setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize)) } else { // TODO: Dedup. funcdata_bytes += pcln.Funcdata[i].Size setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i]) } } off += int32(pcln.Nfuncdata) * int32(Thearch.Ptrsize) } if off != end { Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln.Npcdata, pcln.Nfuncdata, Thearch.Ptrsize) errorexit() } nfunc++ } pclntabLastFunc = last // Final entry of table is just end pc. setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size) // Start file table. start := int32(len(ftab.P)) start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) pclntabFiletabOffset = start setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start)) Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4) setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile)) for s := Ctxt.Filesyms; s != nil; s = s.Next { setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name))) } ftab.Size = int64(len(ftab.P)) if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes)) } }
func Ldmain() { Ctxt = linknew(Thelinkarch) Ctxt.Thechar = int32(Thearch.Thechar) Ctxt.Thestring = Thestring Ctxt.Diag = Diag Ctxt.Bso = &Bso Bso = *Binitw(os.Stdout) Debug = [128]int{} nerrors = 0 outfile = "" HEADTYPE = -1 INITTEXT = -1 INITDAT = -1 INITRND = -1 INITENTRY = "" Linkmode = LinkAuto // For testing behavior of go command when tools crash. // Undocumented, not in standard flag parser to avoid // exposing in usage message. for _, arg := range os.Args { if arg == "-crash_for_testing" { *(*int)(nil) = 0 } } if Thearch.Thechar == '5' && Ctxt.Goarm == 5 { Debug['F'] = 1 } obj.Flagcount("1", "use alternate profiling code", &Debug['1']) if Thearch.Thechar == '6' { obj.Flagcount("8", "assume 64-bit addresses", &Debug['8']) } obj.Flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo) obj.Flagcount("C", "check Go calls to C code", &Debug['C']) obj.Flagint64("D", "addr: data address", &INITDAT) obj.Flagstr("E", "sym: entry symbol", &INITENTRY) if Thearch.Thechar == '5' { obj.Flagcount("G", "debug pseudo-ops", &Debug['G']) } obj.Flagfn1("I", "interp: set ELF interp", setinterp) obj.Flagfn1("L", "dir: add dir to library path", Lflag) obj.Flagfn1("H", "head: header type", setheadtype) obj.Flagcount("K", "add stack underflow checks", &Debug['K']) if Thearch.Thechar == '5' { obj.Flagcount("M", "disable software div/mod", &Debug['M']) } obj.Flagcount("O", "print pc-line tables", &Debug['O']) obj.Flagcount("Q", "debug byte-register code gen", &Debug['Q']) if Thearch.Thechar == '5' { obj.Flagcount("P", "debug code generation", &Debug['P']) } obj.Flagint32("R", "rnd: address rounding", &INITRND) obj.Flagcount("nil", "check type signatures", &Debug['S']) obj.Flagint64("T", "addr: text address", &INITTEXT) obj.Flagfn0("V", "print version and exit", doversion) obj.Flagcount("W", "disassemble input", &Debug['W']) obj.Flagfn1("X", "name value: define string data", addstrdata1) obj.Flagcount("Z", "clear stack frame on entry", &Debug['Z']) obj.Flagcount("a", "disassemble output", &Debug['a']) flag.Var(&Buildmode, "buildmode", "build mode to use") obj.Flagcount("c", "dump call graph", &Debug['c']) obj.Flagcount("d", "disable dynamic executable", &Debug['d']) obj.Flagstr("extld", "ld: linker to run in external mode", &extld) obj.Flagstr("extldflags", "ldflags: flags for external linker", &extldflags) obj.Flagcount("f", "ignore version mismatch", &Debug['f']) obj.Flagcount("g", "disable go package data checks", &Debug['g']) obj.Flagstr("installsuffix", "suffix: pkg directory suffix", &flag_installsuffix) obj.Flagstr("k", "sym: set field tracking symbol", &tracksym) obj.Flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode) flag.BoolVar(&Linkshared, "linkshared", false, "link against installed Go shared libraries") obj.Flagcount("n", "dump symbol table", &Debug['n']) obj.Flagstr("o", "outfile: set output file", &outfile) flag.Var(&rpath, "r", "dir1:dir2:...: set ELF dynamic linker search path") obj.Flagcount("race", "enable race detector", &flag_race) obj.Flagcount("s", "disable symbol table", &Debug['s']) var flagShared int if Thearch.Thechar == '5' || Thearch.Thechar == '6' { obj.Flagcount("shared", "generate shared object (implies -linkmode external)", &flagShared) } obj.Flagstr("tmpdir", "dir: leave temporary files in this directory", &tmpdir) obj.Flagcount("u", "reject unsafe packages", &Debug['u']) obj.Flagcount("v", "print link trace", &Debug['v']) obj.Flagcount("w", "disable DWARF generation", &Debug['w']) // Clumsy hack to preserve old behavior of -X taking two arguments. for i := 0; i < len(os.Args); i++ { arg := os.Args[i] if (arg == "--X" || arg == "-X") && i+2 < len(os.Args) { os.Args[i+2] = "-X=VALUE:" + os.Args[i+2] i += 2 } else if (strings.HasPrefix(arg, "--X=") || strings.HasPrefix(arg, "-X=")) && i+1 < len(os.Args) { os.Args[i+1] = "-X=VALUE:" + os.Args[i+1] i++ } } obj.Flagstr("cpuprofile", "file: write cpu profile to file", &cpuprofile) obj.Flagstr("memprofile", "file: write memory profile to file", &memprofile) obj.Flagint64("memprofilerate", "set runtime.MemProfileRate", &memprofilerate) obj.Flagparse(usage) startProfile() Ctxt.Bso = &Bso Ctxt.Debugvlog = int32(Debug['v']) if flagShared != 0 { if Buildmode == BuildmodeExe { Buildmode = BuildmodeCShared } else if Buildmode != BuildmodeCShared { Exitf("-shared and -buildmode=%s are incompatible", Buildmode.String()) } } if Buildmode != BuildmodeShared && flag.NArg() != 1 { usage() } if outfile == "" { if HEADTYPE == obj.Hwindows { outfile = fmt.Sprintf("%c.out.exe", Thearch.Thechar) } else { outfile = fmt.Sprintf("%c.out", Thearch.Thechar) } } libinit() // creates outfile if HEADTYPE == -1 { HEADTYPE = int32(headtype(goos)) } Ctxt.Headtype = int(HEADTYPE) if headstring == "" { headstring = Headstr(int(HEADTYPE)) } Thearch.Archinit() if Linkshared && !Iself { Exitf("-linkshared can only be used on elf systems") } if Debug['v'] != 0 { fmt.Fprintf(&Bso, "HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(INITTEXT), uint64(INITDAT), uint32(INITRND)) } Bflush(&Bso) if Buildmode == BuildmodeShared { for i := 0; i < flag.NArg(); i++ { arg := flag.Arg(i) parts := strings.SplitN(arg, "=", 2) var pkgpath, file string if len(parts) == 1 { pkgpath, file = "main", arg } else { pkgpath, file = parts[0], parts[1] } addlibpath(Ctxt, "command line", "command line", file, pkgpath, "") } } else { addlibpath(Ctxt, "command line", "command line", flag.Arg(0), "main", "") } loadlib() if Thearch.Thechar == '5' { // mark some functions that are only referenced after linker code editing if Debug['F'] != 0 { mark(Linkrlookup(Ctxt, "_sfloat", 0)) } mark(Linklookup(Ctxt, "runtime.read_tls_fallback", 0)) } checkgo() deadcode() callgraph() doelf() if HEADTYPE == obj.Hdarwin { domacho() } dostkcheck() if HEADTYPE == obj.Hwindows { dope() } addexport() Thearch.Gentext() // trampolines, call stubs, etc. textaddress() pclntab() findfunctab() symtab() dodata() address() doweak() reloc() Thearch.Asmb() undef() hostlink() archive() if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f cpu time\n", obj.Cputime()) fmt.Fprintf(&Bso, "%d symbols\n", Ctxt.Nsymbol) fmt.Fprintf(&Bso, "%d liveness data\n", liveness) } Bflush(&Bso) errorexit() }
func objfile(file string, pkg string) { pkg = pathtoprefix(pkg) if Debug['v'] > 1 { fmt.Fprintf(&Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), file, pkg) } Bflush(&Bso) var err error var f *Biobuf f, err = Bopenr(file) if err != nil { Exitf("cannot open file %s: %v", file, err) } magbuf := make([]byte, len(ARMAG)) if Bread(f, magbuf) != len(magbuf) || !strings.HasPrefix(string(magbuf), ARMAG) { /* load it as a regular file */ l := Bseek(f, 0, 2) Bseek(f, 0, 0) ldobj(f, pkg, l, file, file, FileObj) Bterm(f) return } /* skip over optional __.GOSYMDEF and process __.PKGDEF */ off := 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", 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", file) goto out } } if !strings.HasPrefix(arhdr.name, pkgname) { Diag("%s: cannot find package header", file) goto out } off += l if Debug['u'] != 0 { ldpkg(f, pkg, atolwhex(arhdr.size), 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", file) } off += l pname = fmt.Sprintf("%s(%s)", file, arhdr.name) l = atolwhex(arhdr.size) ldobj(f, pkg, l, pname, file, ArchiveObj) } out: Bterm(f) }
func loadlib() { switch Buildmode { case BuildmodeCShared: s := Linklookup(Ctxt, "runtime.islibrary", 0) s.Dupok = 1 Adduint8(Ctxt, s, 1) case BuildmodeCArchive: s := Linklookup(Ctxt, "runtime.isarchive", 0) s.Dupok = 1 Adduint8(Ctxt, s, 1) } loadinternal("runtime") if Thearch.Thechar == '5' { loadinternal("math") } if flag_race != 0 { loadinternal("runtime/race") } var i int for i = 0; i < len(Ctxt.Library); i++ { if Debug['v'] > 1 { fmt.Fprintf(&Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].File, Ctxt.Library[i].Objref) } iscgo = iscgo || Ctxt.Library[i].Pkg == "runtime/cgo" if Ctxt.Library[i].Shlib != "" { ldshlibsyms(Ctxt.Library[i].Shlib) } else { objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg) } } if Linkmode == LinkAuto { if iscgo && externalobj { Linkmode = LinkExternal } else { Linkmode = LinkInternal } // Force external linking for android. if goos == "android" { Linkmode = LinkExternal } // cgo on Darwin must use external linking // we can always use external linking, but then there will be circular // dependency problems when compiling natively (external linking requires // runtime/cgo, runtime/cgo requires cmd/cgo, but cmd/cgo needs to be // compiled using external linking.) if (Thearch.Thechar == '5' || Thearch.Thechar == '7') && HEADTYPE == obj.Hdarwin && iscgo { Linkmode = LinkExternal } } // cmd/7l doesn't support cgo internal linking // This is https://golang.org/issue/10373. if iscgo && goarch == "arm64" { Linkmode = LinkExternal } if Linkmode == LinkExternal && !iscgo { // This indicates a user requested -linkmode=external. // The startup code uses an import of runtime/cgo to decide // whether to initialize the TLS. So give it one. This could // be handled differently but it's an unusual case. loadinternal("runtime/cgo") if i < len(Ctxt.Library) { if Ctxt.Library[i].Shlib != "" { ldshlibsyms(Ctxt.Library[i].Shlib) } else { objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg) } } } if Linkmode == LinkInternal { // Drop all the cgo_import_static declarations. // Turns out we won't be needing them. for s := Ctxt.Allsym; s != nil; s = s.Allsym { if s.Type == obj.SHOSTOBJ { // If a symbol was marked both // cgo_import_static and cgo_import_dynamic, // then we want to make it cgo_import_dynamic // now. if s.Extname != "" && s.Dynimplib != "" && s.Cgoexport == 0 { s.Type = obj.SDYNIMPORT } else { s.Type = 0 } } } } tlsg := Linklookup(Ctxt, "runtime.tlsg", 0) // For most ports, runtime.tlsg is a placeholder symbol for TLS // relocation. However, the Android and Darwin arm ports need it // to be a real variable. // // TODO(crawshaw): android should require leaving the tlsg->type // alone (as the runtime-provided SNOPTRBSS) just like darwin/arm. // But some other part of the linker is expecting STLSBSS. if tlsg.Type != obj.SDYNIMPORT && (goos != "darwin" || Thearch.Thechar != '5') { tlsg.Type = obj.STLSBSS } tlsg.Size = int64(Thearch.Ptrsize) tlsg.Reachable = true Ctxt.Tlsg = tlsg // Now that we know the link mode, trim the dynexp list. x := CgoExportDynamic if Linkmode == LinkExternal { x = CgoExportStatic } w := 0 for i := 0; i < len(dynexp); i++ { if int(dynexp[i].Cgoexport)&x != 0 { dynexp[w] = dynexp[i] w++ } } dynexp = dynexp[:w] // In internal link mode, read the host object files. if Linkmode == LinkInternal { hostobjs() } else { hostlinksetup() } // We've loaded all the code now. // If there are no dynamic libraries needed, gcc disables dynamic linking. // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) // assumes that a dynamic binary always refers to at least one dynamic library. // Rather than be a source of test cases for glibc, disable dynamic linking // the same way that gcc would. // // Exception: on OS X, programs such as Shark only work with dynamic // binaries, so leave it enabled on OS X (Mach-O) binaries. // Also leave it enabled on Solaris which doesn't support // statically linked binaries. if Buildmode == BuildmodeExe && havedynamic == 0 && HEADTYPE != obj.Hdarwin && HEADTYPE != obj.Hsolaris { Debug['d'] = 1 } importcycles() }
func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { // These symbols won't show up in the first loop below because we // skip STEXT symbols. Normal STEXT symbols are emitted by walking textp. s := Linklookup(Ctxt, "runtime.text", 0) if s.Type == obj.STEXT { put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil) } s = Linklookup(Ctxt, "runtime.etext", 0) if s.Type == obj.STEXT { put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil) } for s := Ctxt.Allsym; s != nil; s = s.Allsym { if s.Hide != 0 || (s.Name[0] == '.' && s.Version == 0 && s.Name != ".rathole") { continue } switch s.Type & obj.SMASK { case obj.SCONST, obj.SRODATA, obj.SSYMTAB, obj.SPCLNTAB, obj.SINITARR, obj.SDATA, obj.SNOPTRDATA, obj.SELFROSECT, obj.SMACHOGOT, obj.STYPE, obj.SSTRING, obj.SGOSTRING, obj.SGOFUNC, obj.SWINDOWS: if !s.Reachable { continue } put(s, s.Name, 'D', Symaddr(s), s.Size, int(s.Version), s.Gotype) case obj.SBSS, obj.SNOPTRBSS: if !s.Reachable { continue } if len(s.P) > 0 { Diag("%s should not be bss (size=%d type=%d special=%d)", s.Name, int(len(s.P)), s.Type, s.Special) } put(s, s.Name, 'B', Symaddr(s), s.Size, int(s.Version), s.Gotype) case obj.SFILE: put(nil, s.Name, 'f', s.Value, 0, int(s.Version), nil) case obj.SHOSTOBJ: if HEADTYPE == obj.Hwindows || Iself { put(s, s.Name, 'U', s.Value, 0, int(s.Version), nil) } case obj.SDYNIMPORT: if !s.Reachable { continue } put(s, s.Extname, 'U', 0, 0, int(s.Version), nil) case obj.STLSBSS: if Linkmode == LinkExternal && HEADTYPE != obj.Hopenbsd { var type_ int if goos == "android" { type_ = 'B' } else { type_ = 't' } put(s, s.Name, type_, Symaddr(s), s.Size, int(s.Version), s.Gotype) } } } var a *Auto var off int32 for s := Ctxt.Textp; s != nil; s = s.Next { put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype) // NOTE(ality): acid can't produce a stack trace without .frame symbols put(nil, ".frame", 'm', int64(s.Locals)+int64(Thearch.Ptrsize), 0, 0, nil) for a = s.Autom; a != nil; a = a.Link { // Emit a or p according to actual offset, even if label is wrong. // This avoids negative offsets, which cannot be encoded. if a.Name != obj.A_AUTO && a.Name != obj.A_PARAM { continue } // compute offset relative to FP if a.Name == obj.A_PARAM { off = a.Aoffset } else { off = a.Aoffset - int32(Thearch.Ptrsize) } // FP if off >= 0 { put(nil, a.Asym.Name, 'p', int64(off), 0, 0, a.Gotype) continue } // SP if off <= int32(-Thearch.Ptrsize) { put(nil, a.Asym.Name, 'a', -(int64(off) + int64(Thearch.Ptrsize)), 0, 0, a.Gotype) continue } } } // Otherwise, off is addressing the saved program counter. // Something underhanded is going on. Say nothing. if Debug['v'] != 0 || Debug['n'] != 0 { fmt.Fprintf(&Bso, "%5.2f symsize = %d\n", obj.Cputime(), uint32(Symsize)) } Bflush(&Bso) }
func ldshlibsyms(shlib string) { found := false libpath := "" for _, libdir := range Ctxt.Libdir { libpath = filepath.Join(libdir, shlib) if _, err := os.Stat(libpath); err == nil { found = true break } } if !found { Diag("cannot find shared library: %s", shlib) return } for _, processedname := range Ctxt.Shlibs { if processedname == libpath { return } } if Ctxt.Debugvlog > 1 && Ctxt.Bso != nil { fmt.Fprintf(Ctxt.Bso, "%5.2f ldshlibsyms: found library with name %s at %s\n", obj.Cputime(), shlib, libpath) Bflush(Ctxt.Bso) } f, err := elf.Open(libpath) if err != nil { Diag("cannot open shared library: %s", libpath) return } defer f.Close() syms, err := f.Symbols() if err != nil { Diag("cannot read symbols from shared library: %s", libpath) return } // If a package has a global variable of a type defined in another shared // library, we need to know the gcmask used by the type, if any. To support // this, we read all the runtime.gcbits.* symbols, keep a map of address to // gcmask, and after we're read all the symbols, read the addresses of the // gcmasks symbols out of the type data to look up the gcmask for each type. // This depends on the fact that the runtime.gcbits.* symbols are local (so // the address is actually present in the type data and we don't have to // search all relocations to find the ones which correspond to gcmasks) and // also that the shared library we are linking against has not had the symbol // table removed. gcmasks := make(map[uint64][]byte) types := []*LSym{} for _, s := range syms { if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION { continue } if s.Section == elf.SHN_UNDEF { continue } if strings.HasPrefix(s.Name, "_") { continue } if strings.HasPrefix(s.Name, "runtime.gcbits.0x") { data := make([]byte, s.Size) sect := f.Sections[s.Section] if sect.Type == elf.SHT_PROGBITS { n, err := sect.ReadAt(data, int64(s.Value-sect.Offset)) if uint64(n) != s.Size { Diag("Error reading contents of %s: %v", s.Name, err) } } gcmasks[s.Value] = data } if elf.ST_BIND(s.Info) != elf.STB_GLOBAL { continue } lsym := Linklookup(Ctxt, s.Name, 0) if lsym.Type != 0 && lsym.Dupok == 0 { Diag( "Found duplicate symbol %s reading from %s, first found in %s", s.Name, shlib, lsym.File) } lsym.Type = obj.SDYNIMPORT lsym.File = libpath if strings.HasPrefix(lsym.Name, "type.") { data := make([]byte, s.Size) sect := f.Sections[s.Section] if sect.Type == elf.SHT_PROGBITS { n, err := sect.ReadAt(data, int64(s.Value-sect.Offset)) if uint64(n) != s.Size { Diag("Error reading contents of %s: %v", s.Name, err) } lsym.P = data } if !strings.HasPrefix(lsym.Name, "type..") { types = append(types, lsym) } } } for _, t := range types { if decodetype_noptr(t) != 0 || decodetype_usegcprog(t) != 0 { continue } // The expression on the next line is a copy of the expression from // decodetype_gcmask in decodesym.go, which in turn depends on details of // how the type data is laid out, as seen in gc/reflect.go:dcommontype. addr := decode_inuxi(t.P[1*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize):], Thearch.Ptrsize) tgcmask, ok := gcmasks[addr] if !ok { Diag("bits not found for %s at %d", t.Name, addr) } t.gcmask = tgcmask } // We might have overwritten some functions above (this tends to happen for the // autogenerated type equality/hashing functions) and we don't want to generated // pcln table entries for these any more so unstitch them from the Textp linked // list. var last *LSym for s := Ctxt.Textp; s != nil; s = s.Next { if s.Type == obj.SDYNIMPORT { continue } if last == nil { Ctxt.Textp = s } else { last.Next = s } last = s } if last == nil { Ctxt.Textp = nil Ctxt.Etextp = nil } else { last.Next = nil Ctxt.Etextp = last } Ctxt.Shlibs = append(Ctxt.Shlibs, libpath) }
func ldelf(f *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(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 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 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_ != 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) }