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 != 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() } }
func Ldmain() { Ctxt = linknew(Thelinkarch) Ctxt.Thechar = int32(Thearch.Thechar) Ctxt.Thestring = Thestring Ctxt.Diag = Diag Ctxt.Bso = &Bso Bso = *obj.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 silently. // Undocumented, not in standard flag parser to avoid // exposing in usage message. for _, arg := range os.Args { if arg == "-crash_for_testing" { os.Exit(2) } } if Thearch.Thechar == '6' && obj.Getgoos() == "plan9" { obj.Flagcount("8", "use 64-bit addresses in symbol table", &Debug['8']) } obj.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) obj.Flagcount("C", "check Go calls to C code", &Debug['C']) obj.Flagint64("D", "set data segment `address`", &INITDAT) obj.Flagstr("E", "set `entry` symbol name", &INITENTRY) obj.Flagfn1("I", "use `linker` as ELF dynamic linker", setinterp) obj.Flagfn1("L", "add specified `directory` to library path", Lflag) obj.Flagfn1("H", "set header `type`", setheadtype) obj.Flagint32("R", "set address rounding `quantum`", &INITRND) obj.Flagint64("T", "set text segment `address`", &INITTEXT) obj.Flagfn0("V", "print version and exit", doversion) obj.Flagcount("W", "disassemble input", &Debug['W']) obj.Flagfn1("X", "add string value `definition` of the form importpath.name=value", addstrdata1) obj.Flagcount("a", "disassemble output", &Debug['a']) obj.Flagstr("buildid", "record `id` as Go toolchain build id", &buildid) flag.Var(&Buildmode, "buildmode", "set build `mode`") obj.Flagcount("c", "dump call graph", &Debug['c']) obj.Flagcount("d", "disable dynamic executable", &Debug['d']) obj.Flagstr("extld", "use `linker` when linking in external mode", &extld) obj.Flagstr("extldflags", "pass `flags` to external linker", &extldflags) obj.Flagcount("f", "ignore version mismatch", &Debug['f']) obj.Flagcount("g", "disable go package data checks", &Debug['g']) obj.Flagcount("h", "halt on error", &Debug['h']) obj.Flagstr("installsuffix", "set package directory `suffix`", &flag_installsuffix) obj.Flagstr("k", "set field tracking `symbol`", &tracksym) obj.Flagfn1("linkmode", "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", "write output to `file`", &outfile) flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...") 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", "use `directory` for temporary files", &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']) obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile) obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile) obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate) // Clumsy hack to preserve old two-argument -X name val syntax for old scripts. // Rewrite that syntax into new syntax -X name=val. // TODO(rsc): Delete this hack in Go 1.6 or later. var args []string for i := 0; i < len(os.Args); i++ { arg := os.Args[i] if (arg == "-X" || arg == "--X") && i+2 < len(os.Args) && !strings.Contains(os.Args[i+1], "=") { fmt.Fprintf(os.Stderr, "link: warning: option %s %s %s may not work in future releases; use %s %s=%s\n", arg, os.Args[i+1], os.Args[i+2], arg, os.Args[i+1], os.Args[i+2]) args = append(args, arg) args = append(args, os.Args[i+1]+"="+os.Args[i+2]) i += 2 continue } if (strings.HasPrefix(arg, "-X=") || strings.HasPrefix(arg, "--X=")) && i+1 < len(os.Args) && strings.Count(arg, "=") == 1 { fmt.Fprintf(os.Stderr, "link: warning: option %s %s may not work in future releases; use %s=%s\n", arg, os.Args[i+1], arg, os.Args[i+1]) args = append(args, arg+"="+os.Args[i+1]) i++ continue } args = append(args, arg) } os.Args = args obj.Flagparse(usage) startProfile() Ctxt.Bso = &Bso Ctxt.Debugvlog = int32(Debug['v']) if flagShared != 0 { if Buildmode == BuildmodeUnset { Buildmode = BuildmodeCShared } else if Buildmode != BuildmodeCShared { Exitf("-shared and -buildmode=%s are incompatible", Buildmode.String()) } } if Buildmode == BuildmodeUnset { Buildmode = BuildmodeExe } if Buildmode != BuildmodeShared && flag.NArg() != 1 { usage() } if outfile == "" { outfile = "a.out" if HEADTYPE == obj.Hwindows { outfile += ".exe" } } libinit() // creates outfile if HEADTYPE == -1 { HEADTYPE = int32(headtype(goos)) } Ctxt.Headtype = int(HEADTYPE) if headstring == "" { headstring = Headstr(int(HEADTYPE)) } if HEADTYPE == obj.Hhaiku { Linkmode = LinkExternal } 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)) } Bso.Flush() 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] } pkglistfornote = append(pkglistfornote, pkgpath...) pkglistfornote = append(pkglistfornote, '\n') 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 Ctxt.Goarm == 5 { mark(Linkrlookup(Ctxt, "_sfloat", 0)) } mark(Linklookup(Ctxt, "runtime.read_tls_fallback", 0)) } checkgo() checkstrdata() deadcode() callgraph() doelf() if HEADTYPE == obj.Hdarwin { domacho() } dostkcheck() if HEADTYPE == obj.Hwindows { dope() } addexport() Thearch.Gentext() // trampolines, call stubs, etc. textbuildid() 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) } Bso.Flush() errorexit() }
func preprocess(ctxt *obj.Link, cursym *obj.LSym) { // TODO(minux): add morestack short-cuts with small fixed frame-size. ctxt.Cursym = cursym // a switch for enabling/disabling instruction scheduling nosched := true if cursym.Text == nil || cursym.Text.Link == nil { return } p := cursym.Text textstksiz := p.To.Offset cursym.Args = p.To.Val.(int32) cursym.Locals = int32(textstksiz) /* * find leaf subroutines * strip NOPs * expand RET * expand BECOME pseudo */ if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f noops\n", obj.Cputime()) } var q *obj.Prog var q1 *obj.Prog for p := cursym.Text; p != nil; p = p.Link { switch p.As { /* too hard, just leave alone */ case obj.ATEXT: q = p p.Mark |= LABEL | LEAF | SYNC if p.Link != nil { p.Link.Mark |= LABEL } /* too hard, just leave alone */ case AMOVW, AMOVV: q = p if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL { p.Mark |= LABEL | SYNC break } if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL { p.Mark |= LABEL | SYNC } /* too hard, just leave alone */ case ASYSCALL, AWORD, ATLBWR, ATLBWI, ATLBP, ATLBR: q = p p.Mark |= LABEL | SYNC case ANOR: q = p if p.To.Type == obj.TYPE_REG { if p.To.Reg == REGZERO { p.Mark |= LABEL | SYNC } } case ABGEZAL, ABLTZAL, AJAL, obj.ADUFFZERO, obj.ADUFFCOPY: cursym.Text.Mark &^= LEAF fallthrough case AJMP, ABEQ, ABGEZ, ABGTZ, ABLEZ, ABLTZ, ABNE, ABFPT, ABFPF: if p.As == ABFPT || p.As == ABFPF { // We don't treat ABFPT and ABFPF as branches here, // so that we will always fill nop (0x0) in their // delay slot during assembly. // This is to workaround a kernel FPU emulator bug // where it uses the user stack to simulate the // instruction in the delay slot if it's not 0x0, // and somehow that leads to SIGSEGV when the kernel // jump to the stack. p.Mark |= SYNC } else { p.Mark |= BRANCH } q = p q1 = p.Pcond if q1 != nil { for q1.As == obj.ANOP { q1 = q1.Link p.Pcond = q1 } if q1.Mark&LEAF == 0 { q1.Mark |= LABEL } } //else { // p.Mark |= LABEL //} q1 = p.Link if q1 != nil { q1.Mark |= LABEL } continue case ARET: q = p if p.Link != nil { p.Link.Mark |= LABEL } continue case obj.ANOP: q1 = p.Link q.Link = q1 /* q is non-nop */ q1.Mark |= p.Mark continue default: q = p continue } } var mov, add obj.As if ctxt.Mode&Mips64 != 0 { add = AADDV mov = AMOVV } else { add = AADDU mov = AMOVW } autosize := int32(0) var p1 *obj.Prog var p2 *obj.Prog for p := cursym.Text; p != nil; p = p.Link { o := p.As switch o { case obj.ATEXT: autosize = int32(textstksiz + ctxt.FixedFrameSize()) if (p.Mark&LEAF != 0) && autosize <= int32(ctxt.FixedFrameSize()) { autosize = 0 } else if autosize&4 != 0 && ctxt.Mode&Mips64 != 0 { autosize += 4 } p.To.Offset = int64(autosize) - ctxt.FixedFrameSize() if p.From3.Offset&obj.NOSPLIT == 0 { p = stacksplit(ctxt, p, autosize) // emit split check } q = p if autosize != 0 { // Make sure to save link register for non-empty frame, even if // it is a leaf function, so that traceback works. // Store link register before decrement SP, so if a signal comes // during the execution of the function prologue, the traceback // code will not see a half-updated stack frame. q = obj.Appendp(ctxt, q) q.As = mov q.Lineno = p.Lineno q.From.Type = obj.TYPE_REG q.From.Reg = REGLINK q.To.Type = obj.TYPE_MEM q.To.Offset = int64(-autosize) q.To.Reg = REGSP q = obj.Appendp(ctxt, q) q.As = add q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = int64(-autosize) q.To.Type = obj.TYPE_REG q.To.Reg = REGSP q.Spadj = +autosize } else if cursym.Text.Mark&LEAF == 0 { if cursym.Text.From3.Offset&obj.NOSPLIT != 0 { if ctxt.Debugvlog != 0 { ctxt.Logf("save suppressed in: %s\n", cursym.Name) } cursym.Text.Mark |= LEAF } } if cursym.Text.Mark&LEAF != 0 { cursym.Set(obj.AttrLeaf, true) break } if cursym.Text.From3.Offset&obj.WRAPPER != 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOV g_panic(g), R1 // BEQ R1, end // MOV panic_argp(R1), R2 // ADD $(autosize+FIXED_FRAME), R29, R3 // BNE R2, R3, end // ADD $FIXED_FRAME, R29, R2 // MOV R2, panic_argp(R1) // end: // NOP // // The NOP is needed to give the jumps somewhere to land. // It is a liblink NOP, not an mips NOP: it encodes to 0 instruction bytes. q = obj.Appendp(ctxt, q) q.As = mov q.From.Type = obj.TYPE_MEM q.From.Reg = REGG q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic q.To.Type = obj.TYPE_REG q.To.Reg = REG_R1 q = obj.Appendp(ctxt, q) q.As = ABEQ q.From.Type = obj.TYPE_REG q.From.Reg = REG_R1 q.To.Type = obj.TYPE_BRANCH q.Mark |= BRANCH p1 = q q = obj.Appendp(ctxt, q) q.As = mov q.From.Type = obj.TYPE_MEM q.From.Reg = REG_R1 q.From.Offset = 0 // Panic.argp q.To.Type = obj.TYPE_REG q.To.Reg = REG_R2 q = obj.Appendp(ctxt, q) q.As = add q.From.Type = obj.TYPE_CONST q.From.Offset = int64(autosize) + ctxt.FixedFrameSize() q.Reg = REGSP q.To.Type = obj.TYPE_REG q.To.Reg = REG_R3 q = obj.Appendp(ctxt, q) q.As = ABNE q.From.Type = obj.TYPE_REG q.From.Reg = REG_R2 q.Reg = REG_R3 q.To.Type = obj.TYPE_BRANCH q.Mark |= BRANCH p2 = q q = obj.Appendp(ctxt, q) q.As = add q.From.Type = obj.TYPE_CONST q.From.Offset = ctxt.FixedFrameSize() q.Reg = REGSP q.To.Type = obj.TYPE_REG q.To.Reg = REG_R2 q = obj.Appendp(ctxt, q) q.As = mov q.From.Type = obj.TYPE_REG q.From.Reg = REG_R2 q.To.Type = obj.TYPE_MEM q.To.Reg = REG_R1 q.To.Offset = 0 // Panic.argp q = obj.Appendp(ctxt, q) q.As = obj.ANOP p1.Pcond = q p2.Pcond = q } case ARET: if p.From.Type == obj.TYPE_CONST { ctxt.Diag("using BECOME (%v) is not supported!", p) break } retSym := p.To.Sym p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction p.To.Sym = nil if cursym.Text.Mark&LEAF != 0 { if autosize == 0 { p.As = AJMP p.From = obj.Addr{} if retSym != nil { // retjmp p.To.Type = obj.TYPE_BRANCH p.To.Name = obj.NAME_EXTERN p.To.Sym = retSym } else { p.To.Type = obj.TYPE_MEM p.To.Reg = REGLINK p.To.Offset = 0 } p.Mark |= BRANCH break } p.As = add p.From.Type = obj.TYPE_CONST p.From.Offset = int64(autosize) p.To.Type = obj.TYPE_REG p.To.Reg = REGSP p.Spadj = -autosize q = ctxt.NewProg() q.As = AJMP q.Lineno = p.Lineno q.To.Type = obj.TYPE_MEM q.To.Offset = 0 q.To.Reg = REGLINK q.Mark |= BRANCH q.Spadj = +autosize q.Link = p.Link p.Link = q break } p.As = mov p.From.Type = obj.TYPE_MEM p.From.Offset = 0 p.From.Reg = REGSP p.To.Type = obj.TYPE_REG p.To.Reg = REG_R4 if retSym != nil { // retjmp from non-leaf, need to restore LINK register p.To.Reg = REGLINK } if autosize != 0 { q = ctxt.NewProg() q.As = add q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = int64(autosize) q.To.Type = obj.TYPE_REG q.To.Reg = REGSP q.Spadj = -autosize q.Link = p.Link p.Link = q } q1 = ctxt.NewProg() q1.As = AJMP q1.Lineno = p.Lineno if retSym != nil { // retjmp q1.To.Type = obj.TYPE_BRANCH q1.To.Name = obj.NAME_EXTERN q1.To.Sym = retSym } else { q1.To.Type = obj.TYPE_MEM q1.To.Offset = 0 q1.To.Reg = REG_R4 } q1.Mark |= BRANCH q1.Spadj = +autosize q1.Link = q.Link q.Link = q1 case AADD, AADDU, AADDV, AADDVU: if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { p.Spadj = int32(-p.From.Offset) } } } if nosched { // if we don't do instruction scheduling, simply add // NOP after each branch instruction. for p = cursym.Text; p != nil; p = p.Link { if p.Mark&BRANCH != 0 { addnop(ctxt, p) } } return } // instruction scheduling q = nil // p - 1 q1 = cursym.Text // top of block o := 0 // count of instructions for p = cursym.Text; p != nil; p = p1 { p1 = p.Link o++ if p.Mark&NOSCHED != 0 { if q1 != p { sched(ctxt, q1, q) } for ; p != nil; p = p.Link { if p.Mark&NOSCHED == 0 { break } q = p } p1 = p q1 = p o = 0 continue } if p.Mark&(LABEL|SYNC) != 0 { if q1 != p { sched(ctxt, q1, q) } q1 = p o = 1 } if p.Mark&(BRANCH|SYNC) != 0 { sched(ctxt, q1, p) q1 = p1 o = 0 } if o >= NSCHED { sched(ctxt, q1, p) q1 = p1 o = 0 } q = p } }
func ldelf(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f ldelf %s\n", obj.Cputime(), pn) } localSymVersion := ctxt.Syms.IncVersion() base := f.Offset() var add uint64 var e binary.ByteOrder var elfobj *ElfObj var err error var flag int var hdr *ElfHdrBytes var hdrbuf [64]uint8 var info uint64 var is64 int var j int var n int var name string var p []byte var r []Reloc var rela int var rp *Reloc var rsect *ElfSect var s *Symbol var sect *ElfSect var sym ElfSym var symbols []*Symbol if _, err := io.ReadFull(f, hdrbuf[:]); err != nil { goto bad } hdr = new(ElfHdrBytes) binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter if string(hdr.Ident[:4]) != "\x7FELF" { goto bad } switch hdr.Ident[5] { case ElfDataLsb: e = binary.LittleEndian case ElfDataMsb: e = binary.BigEndian default: goto bad } // read header elfobj = new(ElfObj) elfobj.e = e elfobj.f = f elfobj.base = base elfobj.length = length elfobj.name = pn is64 = 0 if hdr.Ident[4] == ElfClass64 { is64 = 1 hdr := new(ElfHdrBytes64) binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) elfobj.version = e.Uint32(hdr.Version[:]) elfobj.phoff = e.Uint64(hdr.Phoff[:]) elfobj.shoff = e.Uint64(hdr.Shoff[:]) elfobj.flags = e.Uint32(hdr.Flags[:]) elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) } else { elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) elfobj.version = e.Uint32(hdr.Version[:]) elfobj.entry = uint64(e.Uint32(hdr.Entry[:])) elfobj.phoff = uint64(e.Uint32(hdr.Phoff[:])) elfobj.shoff = uint64(e.Uint32(hdr.Shoff[:])) elfobj.flags = e.Uint32(hdr.Flags[:]) elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) } elfobj.is64 = is64 if uint32(hdr.Ident[6]) != elfobj.version { goto bad } if e.Uint16(hdr.Type[:]) != ElfTypeRelocatable { Errorf(nil, "%s: elf but not elf relocatable object", pn) return } switch SysArch.Family { default: Errorf(nil, "%s: elf %s unimplemented", pn, SysArch.Name) return case sys.MIPS64: if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass64 { Errorf(nil, "%s: elf object but not mips64", pn) return } case sys.ARM: if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 { Errorf(nil, "%s: elf object but not arm", pn) return } case sys.AMD64: if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 { Errorf(nil, "%s: elf object but not amd64", pn) return } case sys.ARM64: if e != binary.LittleEndian || elfobj.machine != ElfMachArm64 || hdr.Ident[4] != ElfClass64 { Errorf(nil, "%s: elf object but not arm64", pn) return } case sys.I386: if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 { Errorf(nil, "%s: elf object but not 386", pn) return } case sys.PPC64: if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 { Errorf(nil, "%s: elf object but not ppc64", pn) return } case sys.S390X: if elfobj.machine != ElfMachS390 || hdr.Ident[4] != ElfClass64 { Errorf(nil, "%s: elf object but not s390x", pn) return } } // load section list into memory. elfobj.sect = make([]ElfSect, elfobj.shnum) elfobj.nsect = uint(elfobj.shnum) for i := 0; uint(i) < elfobj.nsect; i++ { if f.Seek(int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) < 0 { goto bad } sect = &elfobj.sect[i] if is64 != 0 { var b ElfSectBytes64 if err = binary.Read(f, e, &b); err != nil { goto bad } sect.nameoff = e.Uint32(b.Name[:]) sect.type_ = e.Uint32(b.Type[:]) sect.flags = e.Uint64(b.Flags[:]) sect.addr = e.Uint64(b.Addr[:]) sect.off = e.Uint64(b.Off[:]) sect.size = e.Uint64(b.Size[:]) sect.link = e.Uint32(b.Link[:]) sect.info = e.Uint32(b.Info[:]) sect.align = e.Uint64(b.Align[:]) sect.entsize = e.Uint64(b.Entsize[:]) } else { var b ElfSectBytes if err = binary.Read(f, e, &b); err != nil { goto bad } sect.nameoff = e.Uint32(b.Name[:]) sect.type_ = e.Uint32(b.Type[:]) sect.flags = uint64(e.Uint32(b.Flags[:])) sect.addr = uint64(e.Uint32(b.Addr[:])) sect.off = uint64(e.Uint32(b.Off[:])) sect.size = uint64(e.Uint32(b.Size[:])) sect.link = e.Uint32(b.Link[:]) sect.info = e.Uint32(b.Info[:]) sect.align = uint64(e.Uint32(b.Align[:])) sect.entsize = uint64(e.Uint32(b.Entsize[:])) } } // read section string table and translate names if elfobj.shstrndx >= uint32(elfobj.nsect) { err = fmt.Errorf("shstrndx out of range %d >= %d", elfobj.shstrndx, elfobj.nsect) goto bad } sect = &elfobj.sect[elfobj.shstrndx] if err = elfmap(elfobj, sect); err != nil { goto bad } for i := 0; uint(i) < elfobj.nsect; i++ { if elfobj.sect[i].nameoff != 0 { elfobj.sect[i].name = cstring(sect.base[elfobj.sect[i].nameoff:]) } } // load string table for symbols into memory. elfobj.symtab = section(elfobj, ".symtab") if elfobj.symtab == nil { // our work is done here - no symbols means nothing can refer to this file return } if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) { Errorf(nil, "%s: elf object has symbol table with invalid string table link", pn) return } elfobj.symstr = &elfobj.sect[elfobj.symtab.link] if is64 != 0 { elfobj.nsymtab = int(elfobj.symtab.size / ELF64SYMSIZE) } else { elfobj.nsymtab = int(elfobj.symtab.size / ELF32SYMSIZE) } if err = elfmap(elfobj, elfobj.symtab); err != nil { goto bad } if err = elfmap(elfobj, elfobj.symstr); err != nil { goto bad } // load text and data segments into memory. // they are not as small as the section lists, but we'll need // the memory anyway for the symbol images, so we might // as well use one large chunk. // create symbols for elfmapped sections for i := 0; uint(i) < elfobj.nsect; i++ { sect = &elfobj.sect[i] if sect.type_ == SHT_ARM_ATTRIBUTES && sect.name == ".ARM.attributes" { if err = elfmap(elfobj, sect); err != nil { goto bad } parseArmAttributes(ctxt, e, sect.base[:sect.size]) } if (sect.type_ != ElfSectProgbits && sect.type_ != ElfSectNobits) || sect.flags&ElfSectFlagAlloc == 0 { continue } if sect.type_ != ElfSectNobits { if err = elfmap(elfobj, sect); err != nil { goto bad } } name = fmt.Sprintf("%s(%s)", pkg, sect.name) s = ctxt.Syms.Lookup(name, localSymVersion) switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) { default: err = fmt.Errorf("unexpected flags for ELF section %s", sect.name) goto bad case ElfSectFlagAlloc: s.Type = obj.SRODATA case ElfSectFlagAlloc + ElfSectFlagWrite: if sect.type_ == ElfSectNobits { s.Type = obj.SNOPTRBSS } else { s.Type = obj.SNOPTRDATA } case ElfSectFlagAlloc + ElfSectFlagExec: s.Type = obj.STEXT } if sect.name == ".got" || sect.name == ".toc" { s.Type = obj.SELFGOT } if sect.type_ == ElfSectProgbits { s.P = sect.base s.P = s.P[:sect.size] } s.Size = int64(sect.size) s.Align = int32(sect.align) sect.sym = s } // enter sub-symbols into symbol table. // symbol 0 is the null symbol. symbols = make([]*Symbol, elfobj.nsymtab) for i := 1; i < elfobj.nsymtab; i++ { if err = readelfsym(ctxt, elfobj, i, &sym, 1, localSymVersion); err != nil { goto bad } symbols[i] = sym.sym if sym.type_ != ElfSymTypeFunc && sym.type_ != ElfSymTypeObject && sym.type_ != ElfSymTypeNone { continue } if sym.shndx == ElfSymShnCommon { s = sym.sym if uint64(s.Size) < sym.size { s.Size = int64(sym.size) } if s.Type == 0 || s.Type == obj.SXREF { s.Type = obj.SNOPTRBSS } continue } if uint(sym.shndx) >= elfobj.nsect || sym.shndx == 0 { continue } // even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols if sym.sym == nil { continue } sect = &elfobj.sect[sym.shndx] if sect.sym == nil { if strings.HasPrefix(sym.name, ".Linfo_string") { // clang does this continue } if sym.name == "" && sym.type_ == 0 && sect.name == ".debug_str" { // This reportedly happens with clang 3.7 on ARM. // See issue 13139. continue } if strings.HasPrefix(sym.name, ".LASF") { // gcc on s390x does this continue } Errorf(sym.sym, "%s: sym#%d: ignoring symbol in section %d (type %d)", pn, i, sym.shndx, sym.type_) continue } s = sym.sym if s.Outer != nil { if s.Attr.DuplicateOK() { continue } Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) } s.Sub = sect.sym.Sub sect.sym.Sub = s s.Type = sect.sym.Type | s.Type&^obj.SMASK | obj.SSUB if !s.Attr.CgoExportDynamic() { s.Dynimplib = "" // satisfy dynimport } s.Value = int64(sym.value) s.Size = int64(sym.size) s.Outer = sect.sym if sect.sym.Type == obj.STEXT { if s.Attr.External() && !s.Attr.DuplicateOK() { Errorf(s, "%s: duplicate symbol definition", pn) } s.Attr |= AttrExternal } if elfobj.machine == ElfMachPower64 { flag = int(sym.other) >> 5 if 2 <= flag && flag <= 6 { s.Localentry = 1 << uint(flag-2) } else if flag == 7 { Errorf(s, "%s: invalid sym.other 0x%x", pn, sym.other) } } } // Sort outer lists by address, adding to textp. // This keeps textp in increasing address order. for i := 0; uint(i) < elfobj.nsect; i++ { s = elfobj.sect[i].sym if s == nil { continue } if s.Sub != nil { s.Sub = listsort(s.Sub) } if s.Type == obj.STEXT { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList ctxt.Textp = append(ctxt.Textp, s) for s = s.Sub; s != nil; s = s.Sub { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList ctxt.Textp = append(ctxt.Textp, s) } } } // load relocations for i := 0; uint(i) < elfobj.nsect; i++ { rsect = &elfobj.sect[i] if rsect.type_ != ElfSectRela && rsect.type_ != ElfSectRel { continue } if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil { continue } sect = &elfobj.sect[rsect.info] if err = elfmap(elfobj, rsect); err != nil { goto bad } rela = 0 if rsect.type_ == ElfSectRela { rela = 1 } n = int(rsect.size / uint64(4+4*is64) / uint64(2+rela)) r = make([]Reloc, n) p = rsect.base for j = 0; j < n; j++ { add = 0 rp = &r[j] if is64 != 0 { // 64-bit rel/rela rp.Off = int32(e.Uint64(p)) p = p[8:] info = e.Uint64(p) p = p[8:] if rela != 0 { add = e.Uint64(p) p = p[8:] } } else { // 32-bit rel/rela rp.Off = int32(e.Uint32(p)) p = p[4:] info = uint64(e.Uint32(p)) info = info>>8<<32 | info&0xff // convert to 64-bit info p = p[4:] if rela != 0 { add = uint64(e.Uint32(p)) p = p[4:] } } if info&0xffffffff == 0 { // skip R_*_NONE relocation j-- n-- continue } if info>>32 == 0 { // absolute relocation, don't bother reading the null symbol rp.Sym = nil } else { if err = readelfsym(ctxt, elfobj, int(info>>32), &sym, 0, 0); err != nil { goto bad } sym.sym = symbols[info>>32] if sym.sym == nil { err = fmt.Errorf("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect.sym.Name, j, int(info>>32), sym.name, sym.shndx, sym.type_) goto bad } rp.Sym = sym.sym } rp.Type = 256 + obj.RelocType(info) rp.Siz = relSize(ctxt, pn, uint32(info)) if rela != 0 { rp.Add = int64(add) } else { // load addend from image if rp.Siz == 4 { rp.Add = int64(e.Uint32(sect.base[rp.Off:])) } else if rp.Siz == 8 { rp.Add = int64(e.Uint64(sect.base[rp.Off:])) } else { Errorf(nil, "invalid rela size %d", rp.Siz) } } if rp.Siz == 2 { rp.Add = int64(int16(rp.Add)) } if rp.Siz == 4 { rp.Add = int64(int32(rp.Add)) } } //print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add); sort.Sort(rbyoff(r[:n])) // just in case s = sect.sym s.R = r s.R = s.R[:n] } return bad: Errorf(nil, "%s: malformed elf file: %v", pn, err) }
// Main is the main entry point for the linker code. func Main() { ctxt := linknew(SysArch) ctxt.Bso = bufio.NewWriter(os.Stdout) nerrors = 0 HEADTYPE = -1 Linkmode = LinkAuto // For testing behavior of go command when tools crash silently. // Undocumented, not in standard flag parser to avoid // exposing in usage message. for _, arg := range os.Args { if arg == "-crash_for_testing" { os.Exit(2) } } // TODO(matloob): define these above and then check flag values here if SysArch.Family == sys.AMD64 && obj.Getgoos() == "plan9" { flag.BoolVar(&Flag8, "8", false, "use 64-bit addresses in symbol table") } obj.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) obj.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) }) obj.Flagfn1("H", "set header `type`", setheadtype) obj.Flagfn0("V", "print version and exit", doversion) obj.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) }) obj.Flagcount("v", "print link trace", &ctxt.Debugvlog) obj.Flagfn1("linkmode", "set link `mode` (internal, external, auto)", setlinkmode) var flagShared bool if SysArch.InFamily(sys.ARM, sys.AMD64) { flag.BoolVar(&flagShared, "shared", false, "generate shared object (implies -linkmode external)") } obj.Flagparse(usage) startProfile() if flagShared { if Buildmode == BuildmodeUnset { Buildmode = BuildmodeCShared } else if Buildmode != BuildmodeCShared { Exitf("-shared and -buildmode=%s are incompatible", Buildmode.String()) } } if Buildmode == BuildmodeUnset { Buildmode = BuildmodeExe } if Buildmode != BuildmodeShared && flag.NArg() != 1 { usage() } if *flagOutfile == "" { *flagOutfile = "a.out" if HEADTYPE == obj.Hwindows { *flagOutfile += ".exe" } } interpreter = *flagInterpreter libinit(ctxt) // creates outfile if HEADTYPE == -1 { HEADTYPE = int32(headtype(goos)) } ctxt.Headtype = int(HEADTYPE) if headstring == "" { headstring = Headstr(int(HEADTYPE)) } Thearch.Archinit(ctxt) if *FlagLinkshared && !Iself { Exitf("-linkshared can only be used on elf systems") } if ctxt.Debugvlog != 0 { ctxt.Logf("HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(*FlagTextAddr), uint64(*FlagDataAddr), uint32(*FlagRound)) } 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] } pkglistfornote = append(pkglistfornote, pkgpath...) pkglistfornote = append(pkglistfornote, '\n') addlibpath(ctxt, "command line", "command line", file, pkgpath, "") } } else { addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "") } ctxt.loadlib() ctxt.checkstrdata() deadcode(ctxt) fieldtrack(ctxt) ctxt.callgraph() ctxt.doelf() if HEADTYPE == obj.Hdarwin { ctxt.domacho() } ctxt.dostkcheck() if HEADTYPE == obj.Hwindows { ctxt.dope() } ctxt.addexport() Thearch.Gentext(ctxt) // trampolines, call stubs, etc. ctxt.textbuildid() ctxt.textaddress() ctxt.pclntab() ctxt.findfunctab() ctxt.symtab() ctxt.dodata() ctxt.address() ctxt.reloc() Thearch.Asmb(ctxt) ctxt.undef() ctxt.hostlink() ctxt.archive() if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f cpu time\n", obj.Cputime()) ctxt.Logf("%d symbols\n", len(ctxt.Allsym)) ctxt.Logf("%d liveness data\n", liveness) } ctxt.Bso.Flush() errorexit() }
func span0(ctxt *obj.Link, cursym *obj.LSym) { p := cursym.Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } ctxt.Cursym = cursym ctxt.Autosize = int32(p.To.Offset + 8) if oprange[AOR&obj.AMask].start == nil { buildop(ctxt) } c := int64(0) p.Pc = c var m int var o *Optab for p = p.Link; p != nil; p = p.Link { ctxt.Curp = p p.Pc = c o = oplook(ctxt, p) m = int(o.size) if m == 0 { if p.As != obj.ANOP && p.As != obj.AFUNCDATA && p.As != obj.APCDATA && p.As != obj.AUSEFIELD { ctxt.Diag("zero-width instruction\n%v", p) } continue } c += int64(m) } cursym.Size = c /* * if any procedure is large enough to * generate a large SBRA branch, then * generate extra passes putting branches * around jmps to fix. this is rare. */ bflag := 1 var otxt int64 var q *obj.Prog for bflag != 0 { if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f span1\n", obj.Cputime()) } bflag = 0 c = 0 for p = cursym.Text.Link; p != nil; p = p.Link { p.Pc = c o = oplook(ctxt, p) // very large conditional branches if o.type_ == 6 && p.Pcond != nil { otxt = p.Pcond.Pc - c if otxt < -(1<<17)+10 || otxt >= (1<<17)-10 { q = ctxt.NewProg() q.Link = p.Link p.Link = q q.As = AJMP q.Lineno = p.Lineno q.To.Type = obj.TYPE_BRANCH q.Pcond = p.Pcond p.Pcond = q q = ctxt.NewProg() q.Link = p.Link p.Link = q q.As = AJMP q.Lineno = p.Lineno q.To.Type = obj.TYPE_BRANCH q.Pcond = q.Link.Link addnop(ctxt, p.Link) addnop(ctxt, p) bflag = 1 } } m = int(o.size) if m == 0 { if p.As != obj.ANOP && p.As != obj.AFUNCDATA && p.As != obj.APCDATA && p.As != obj.AUSEFIELD { ctxt.Diag("zero-width instruction\n%v", p) } continue } c += int64(m) } cursym.Size = c } c += -c & (FuncAlign - 1) cursym.Size = c /* * lay out the code, emitting code and data relocations. */ obj.Symgrow(ctxt, cursym, cursym.Size) bp := cursym.P var i int32 var out [4]uint32 for p := cursym.Text.Link; p != nil; p = p.Link { ctxt.Pc = p.Pc ctxt.Curp = p o = oplook(ctxt, p) if int(o.size) > 4*len(out) { log.Fatalf("out array in span0 is too small, need at least %d for %v", o.size/4, p) } asmout(ctxt, p, o, out[:]) for i = 0; i < int32(o.size/4); i++ { ctxt.Arch.ByteOrder.PutUint32(bp, out[i]) bp = bp[4:] } } }
// Main is the main entry point for the linker code. func Main() { ctxt := linknew(SysArch) ctxt.Bso = bufio.NewWriter(os.Stdout) // For testing behavior of go command when tools crash silently. // Undocumented, not in standard flag parser to avoid // exposing in usage message. for _, arg := range os.Args { if arg == "-crash_for_testing" { os.Exit(2) } } // TODO(matloob): define these above and then check flag values here if SysArch.Family == sys.AMD64 && obj.GOOS == "plan9" { flag.BoolVar(&Flag8, "8", false, "use 64-bit addresses in symbol table") } obj.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) obj.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) }) obj.Flagfn0("V", "print version and exit", doversion) obj.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) }) obj.Flagcount("v", "print link trace", &ctxt.Debugvlog) obj.Flagparse(usage) startProfile() if Buildmode == BuildmodeUnset { Buildmode = BuildmodeExe } if Buildmode != BuildmodeShared && flag.NArg() != 1 { usage() } if *flagOutfile == "" { *flagOutfile = "a.out" if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui { *flagOutfile += ".exe" } } interpreter = *flagInterpreter libinit(ctxt) // creates outfile if Headtype == obj.Hunknown { Headtype.Set(obj.GOOS) } ctxt.computeTLSOffset() Thearch.Archinit(ctxt) if *FlagLinkshared && !Iself { Exitf("-linkshared can only be used on elf systems") } if ctxt.Debugvlog != 0 { ctxt.Logf("HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", Headtype, uint64(*FlagTextAddr), uint64(*FlagDataAddr), uint32(*FlagRound)) } switch Buildmode { case 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] } pkglistfornote = append(pkglistfornote, pkgpath...) pkglistfornote = append(pkglistfornote, '\n') addlibpath(ctxt, "command line", "command line", file, pkgpath, "") } case BuildmodePlugin: pluginName := strings.TrimSuffix(filepath.Base(flag.Arg(0)), ".a") addlibpath(ctxt, "command line", "command line", flag.Arg(0), pluginName, "") default: addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "") } ctxt.loadlib() ctxt.checkstrdata() deadcode(ctxt) fieldtrack(ctxt) ctxt.callgraph() ctxt.doelf() if Headtype == obj.Hdarwin { ctxt.domacho() } ctxt.dostkcheck() if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui { ctxt.dope() } ctxt.addexport() Thearch.Gentext(ctxt) // trampolines, call stubs, etc. ctxt.textbuildid() ctxt.textaddress() ctxt.pclntab() ctxt.findfunctab() ctxt.typelink() ctxt.symtab() ctxt.dodata() ctxt.address() ctxt.reloc() Thearch.Asmb(ctxt) ctxt.undef() ctxt.hostlink() ctxt.archive() if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f cpu time\n", obj.Cputime()) ctxt.Logf("%d symbols\n", len(ctxt.Syms.Allsym)) ctxt.Logf("%d liveness data\n", liveness) } ctxt.Bso.Flush() errorexit() }
func ldshlibsyms(shlib string) { libpath := findshlib(shlib) if libpath == "" { return } for _, processedlib := range Ctxt.Shlibs { if processedlib.Path == 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) Ctxt.Bso.Flush() } f, err := elf.Open(libpath) if err != nil { Diag("cannot open shared library: %s", libpath) return } hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG) if err != nil { Diag("cannot read ABI hash from shared library %s: %v", libpath, err) return } depsbytes, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GODEPS_TAG) if err != nil { Diag("cannot read dep list from shared library %s: %v", libpath, err) return } deps := strings.Split(string(depsbytes), "\n") syms, err := f.DynamicSymbols() if err != nil { Diag("cannot read symbols from shared library: %s", libpath) return } for _, elfsym := range syms { if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION { continue } lsym := Linklookup(Ctxt, elfsym.Name, 0) if lsym.Type != 0 && lsym.Type != obj.SDYNIMPORT && lsym.Dupok == 0 { if (lsym.Type != obj.SBSS && lsym.Type != obj.SNOPTRBSS) || len(lsym.R) != 0 || len(lsym.P) != 0 || f.Sections[elfsym.Section].Type != elf.SHT_NOBITS { Diag("Found duplicate symbol %s reading from %s, first found in %s", elfsym.Name, shlib, lsym.File) } if lsym.Size > int64(elfsym.Size) { // If the existing symbol is a BSS value that is // larger than the one read from the shared library, // keep references to that. Conversely, if the // version from the shared libray is larger, we want // to make all references be to that. continue } } lsym.Type = obj.SDYNIMPORT lsym.ElfType = elf.ST_TYPE(elfsym.Info) lsym.Size = int64(elfsym.Size) if elfsym.Section != elf.SHN_UNDEF { // Set .File for the library that actually defines the symbol. lsym.File = libpath // The decodetype_* functions in decodetype.go need access to // the type data. if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") { lsym.P = readelfsymboldata(f, &elfsym) } } } // 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, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f}) }
func preprocess(ctxt *obj.Link, cursym *obj.LSym) { // TODO(minux): add morestack short-cuts with small fixed frame-size. ctxt.Cursym = cursym if cursym.Text == nil || cursym.Text.Link == nil { return } p := cursym.Text textstksiz := p.To.Offset if textstksiz == -8 { // Compatibility hack. p.From3.Offset |= obj.NOFRAME textstksiz = 0 } if textstksiz%8 != 0 { ctxt.Diag("frame size %d not a multiple of 8", textstksiz) } if p.From3.Offset&obj.NOFRAME != 0 { if textstksiz != 0 { ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz) } } cursym.Args = p.To.Val.(int32) cursym.Locals = int32(textstksiz) /* * find leaf subroutines * strip NOPs * expand RET * expand BECOME pseudo */ if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", obj.Cputime()) } ctxt.Bso.Flush() var q *obj.Prog var q1 *obj.Prog for p := cursym.Text; p != nil; p = p.Link { switch p.As { /* too hard, just leave alone */ case obj.ATEXT: q = p p.Mark |= LABEL | LEAF | SYNC if p.Link != nil { p.Link.Mark |= LABEL } case ANOR: q = p if p.To.Type == obj.TYPE_REG { if p.To.Reg == REGZERO { p.Mark |= LABEL | SYNC } } case ALWAR, ASTWCCC, AECIWX, AECOWX, AEIEIO, AICBI, AISYNC, ATLBIE, ATLBIEL, ASLBIA, ASLBIE, ASLBMFEE, ASLBMFEV, ASLBMTE, ADCBF, ADCBI, ADCBST, ADCBT, ADCBTST, ADCBZ, ASYNC, ATLBSYNC, APTESYNC, ATW, AWORD, ARFI, ARFCI, ARFID, AHRFID: q = p p.Mark |= LABEL | SYNC continue case AMOVW, AMOVWZ, AMOVD: q = p if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL { p.Mark |= LABEL | SYNC } continue case AFABS, AFABSCC, AFADD, AFADDCC, AFCTIW, AFCTIWCC, AFCTIWZ, AFCTIWZCC, AFDIV, AFDIVCC, AFMADD, AFMADDCC, AFMOVD, AFMOVDU, /* case AFMOVDS: */ AFMOVS, AFMOVSU, /* case AFMOVSD: */ AFMSUB, AFMSUBCC, AFMUL, AFMULCC, AFNABS, AFNABSCC, AFNEG, AFNEGCC, AFNMADD, AFNMADDCC, AFNMSUB, AFNMSUBCC, AFRSP, AFRSPCC, AFSUB, AFSUBCC: q = p p.Mark |= FLOAT continue case ABL, ABCL, obj.ADUFFZERO, obj.ADUFFCOPY: cursym.Text.Mark &^= LEAF fallthrough case ABC, ABEQ, ABGE, ABGT, ABLE, ABLT, ABNE, ABR, ABVC, ABVS: p.Mark |= BRANCH q = p q1 = p.Pcond if q1 != nil { for q1.As == obj.ANOP { q1 = q1.Link p.Pcond = q1 } if q1.Mark&LEAF == 0 { q1.Mark |= LABEL } } else { p.Mark |= LABEL } q1 = p.Link if q1 != nil { q1.Mark |= LABEL } continue case AFCMPO, AFCMPU: q = p p.Mark |= FCMP | FLOAT continue case obj.ARET: q = p if p.Link != nil { p.Link.Mark |= LABEL } continue case obj.ANOP: q1 = p.Link q.Link = q1 /* q is non-nop */ q1.Mark |= p.Mark continue default: q = p continue } } autosize := int32(0) var aoffset int var mov int var o int var p1 *obj.Prog var p2 *obj.Prog for p := cursym.Text; p != nil; p = p.Link { o = int(p.As) switch o { case obj.ATEXT: mov = AMOVD aoffset = 0 autosize = int32(textstksiz) if p.Mark&LEAF != 0 && autosize == 0 && p.From3.Offset&obj.NOFRAME == 0 { // A leaf function with no locals has no frame. p.From3.Offset |= obj.NOFRAME } if p.From3.Offset&obj.NOFRAME == 0 { // If there is a stack frame at all, it includes // space to save the LR. autosize += int32(ctxt.FixedFrameSize()) } p.To.Offset = int64(autosize) q = p if ctxt.Flag_shared != 0 && cursym.Name != "runtime.duffzero" && cursym.Name != "runtime.duffcopy" { // When compiling Go into PIC, all functions must start // with instructions to load the TOC pointer into r2: // // addis r2, r12, .TOC.-func@ha // addi r2, r2, .TOC.-func@l+4 // // We could probably skip this prologue in some situations // but it's a bit subtle. However, it is both safe and // necessary to leave the prologue off duffzero and // duffcopy as we rely on being able to jump to a specific // instruction offset for them. // // These are AWORDS because there is no (afaict) way to // generate the addis instruction except as part of the // load of a large constant, and in that case there is no // way to use r12 as the source. q = obj.Appendp(ctxt, q) q.As = AWORD q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = 0x3c4c0000 q = obj.Appendp(ctxt, q) q.As = AWORD q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = 0x38420000 rel := obj.Addrel(ctxt.Cursym) rel.Off = 0 rel.Siz = 8 rel.Sym = obj.Linklookup(ctxt, ".TOC.", 0) rel.Type = obj.R_ADDRPOWER_PCREL } if cursym.Text.From3.Offset&obj.NOSPLIT == 0 { q = stacksplit(ctxt, q, autosize) // emit split check } if autosize != 0 { /* use MOVDU to adjust R1 when saving R31, if autosize is small */ if cursym.Text.Mark&LEAF == 0 && autosize >= -BIG && autosize <= BIG { mov = AMOVDU aoffset = int(-autosize) } else { q = obj.Appendp(ctxt, q) q.As = AADD q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = int64(-autosize) q.To.Type = obj.TYPE_REG q.To.Reg = REGSP q.Spadj = +autosize } } else if cursym.Text.Mark&LEAF == 0 { // A very few functions that do not return to their caller // (e.g. gogo) are not identified as leaves but still have // no frame. cursym.Text.Mark |= LEAF } if cursym.Text.Mark&LEAF != 0 { cursym.Leaf = 1 break } q = obj.Appendp(ctxt, q) q.As = AMOVD q.Lineno = p.Lineno q.From.Type = obj.TYPE_REG q.From.Reg = REG_LR q.To.Type = obj.TYPE_REG q.To.Reg = REGTMP q = obj.Appendp(ctxt, q) q.As = int16(mov) q.Lineno = p.Lineno q.From.Type = obj.TYPE_REG q.From.Reg = REGTMP q.To.Type = obj.TYPE_MEM q.To.Offset = int64(aoffset) q.To.Reg = REGSP if q.As == AMOVDU { q.Spadj = int32(-aoffset) } if ctxt.Flag_shared != 0 { q = obj.Appendp(ctxt, q) q.As = AMOVD q.Lineno = p.Lineno q.From.Type = obj.TYPE_REG q.From.Reg = REG_R2 q.To.Type = obj.TYPE_MEM q.To.Reg = REGSP q.To.Offset = 24 } if cursym.Text.From3.Offset&obj.WRAPPER != 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVD g_panic(g), R3 // CMP R0, R3 // BEQ end // MOVD panic_argp(R3), R4 // ADD $(autosize+8), R1, R5 // CMP R4, R5 // BNE end // ADD $8, R1, R6 // MOVD R6, panic_argp(R3) // end: // NOP // // The NOP is needed to give the jumps somewhere to land. // It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes. q = obj.Appendp(ctxt, q) q.As = AMOVD q.From.Type = obj.TYPE_MEM q.From.Reg = REGG q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic q.To.Type = obj.TYPE_REG q.To.Reg = REG_R3 q = obj.Appendp(ctxt, q) q.As = ACMP q.From.Type = obj.TYPE_REG q.From.Reg = REG_R0 q.To.Type = obj.TYPE_REG q.To.Reg = REG_R3 q = obj.Appendp(ctxt, q) q.As = ABEQ q.To.Type = obj.TYPE_BRANCH p1 = q q = obj.Appendp(ctxt, q) q.As = AMOVD q.From.Type = obj.TYPE_MEM q.From.Reg = REG_R3 q.From.Offset = 0 // Panic.argp q.To.Type = obj.TYPE_REG q.To.Reg = REG_R4 q = obj.Appendp(ctxt, q) q.As = AADD q.From.Type = obj.TYPE_CONST q.From.Offset = int64(autosize) + ctxt.FixedFrameSize() q.Reg = REGSP q.To.Type = obj.TYPE_REG q.To.Reg = REG_R5 q = obj.Appendp(ctxt, q) q.As = ACMP q.From.Type = obj.TYPE_REG q.From.Reg = REG_R4 q.To.Type = obj.TYPE_REG q.To.Reg = REG_R5 q = obj.Appendp(ctxt, q) q.As = ABNE q.To.Type = obj.TYPE_BRANCH p2 = q q = obj.Appendp(ctxt, q) q.As = AADD q.From.Type = obj.TYPE_CONST q.From.Offset = ctxt.FixedFrameSize() q.Reg = REGSP q.To.Type = obj.TYPE_REG q.To.Reg = REG_R6 q = obj.Appendp(ctxt, q) q.As = AMOVD q.From.Type = obj.TYPE_REG q.From.Reg = REG_R6 q.To.Type = obj.TYPE_MEM q.To.Reg = REG_R3 q.To.Offset = 0 // Panic.argp q = obj.Appendp(ctxt, q) q.As = obj.ANOP p1.Pcond = q p2.Pcond = q } case obj.ARET: if p.From.Type == obj.TYPE_CONST { ctxt.Diag("using BECOME (%v) is not supported!", p) break } retTarget := p.To.Sym if cursym.Text.Mark&LEAF != 0 { if autosize == 0 { p.As = ABR p.From = obj.Addr{} if retTarget == nil { p.To.Type = obj.TYPE_REG p.To.Reg = REG_LR } else { p.To.Type = obj.TYPE_BRANCH p.To.Sym = retTarget } p.Mark |= BRANCH break } p.As = AADD p.From.Type = obj.TYPE_CONST p.From.Offset = int64(autosize) p.To.Type = obj.TYPE_REG p.To.Reg = REGSP p.Spadj = -autosize q = ctxt.NewProg() q.As = ABR q.Lineno = p.Lineno q.To.Type = obj.TYPE_REG q.To.Reg = REG_LR q.Mark |= BRANCH q.Spadj = +autosize q.Link = p.Link p.Link = q break } p.As = AMOVD p.From.Type = obj.TYPE_MEM p.From.Offset = 0 p.From.Reg = REGSP p.To.Type = obj.TYPE_REG p.To.Reg = REGTMP q = ctxt.NewProg() q.As = AMOVD q.Lineno = p.Lineno q.From.Type = obj.TYPE_REG q.From.Reg = REGTMP q.To.Type = obj.TYPE_REG q.To.Reg = REG_LR q.Link = p.Link p.Link = q p = q if false { // Debug bad returns q = ctxt.NewProg() q.As = AMOVD q.Lineno = p.Lineno q.From.Type = obj.TYPE_MEM q.From.Offset = 0 q.From.Reg = REGTMP q.To.Type = obj.TYPE_REG q.To.Reg = REGTMP q.Link = p.Link p.Link = q p = q } if autosize != 0 { q = ctxt.NewProg() q.As = AADD q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = int64(autosize) q.To.Type = obj.TYPE_REG q.To.Reg = REGSP q.Spadj = -autosize q.Link = p.Link p.Link = q } q1 = ctxt.NewProg() q1.As = ABR q1.Lineno = p.Lineno if retTarget == nil { q1.To.Type = obj.TYPE_REG q1.To.Reg = REG_LR } else { q1.To.Type = obj.TYPE_BRANCH q1.To.Sym = retTarget } q1.Mark |= BRANCH q1.Spadj = +autosize q1.Link = q.Link q.Link = q1 case AADD: if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { p.Spadj = int32(-p.From.Offset) } } } }
func asmb() { if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) } ld.Bso.Flush() if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f codeblk\n", obj.Cputime()) } ld.Bso.Flush() if ld.Iself { ld.Asmbelfsetup() } sect := ld.Segtext.Sect ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) for sect = sect.Next; sect != nil; sect = sect.Next { ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) } if ld.Segrodata.Filelen > 0 { if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) machlink := int64(0) if ld.HEADTYPE == obj.Hdarwin { if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } dwarfoff := ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) ld.Cseek(dwarfoff) ld.Segdwarf.Fileoff = uint64(ld.Cpos()) ld.Dwarfemitdebugsections() ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff machlink = ld.Domacholink() } switch ld.HEADTYPE { default: ld.Diag("unknown header type %d", ld.HEADTYPE) fallthrough case obj.Hplan9, obj.Helf: break case obj.Hdarwin: ld.Debug['8'] = 1 /* 64-bit addresses */ case obj.Hlinux, obj.Hfreebsd, obj.Hnetbsd, obj.Hopenbsd, obj.Hdragonfly, obj.Hsolaris: ld.Debug['8'] = 1 /* 64-bit addresses */ case obj.Hnacl, obj.Hwindows: break } ld.Symsize = 0 ld.Spsize = 0 ld.Lcsize = 0 symo := int64(0) if ld.Debug['s'] == 0 { if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) } ld.Bso.Flush() switch ld.HEADTYPE { default: case obj.Hplan9, obj.Helf: ld.Debug['s'] = 1 symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) case obj.Hdarwin: symo = int64(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink)) case obj.Hlinux, obj.Hfreebsd, obj.Hnetbsd, obj.Hopenbsd, obj.Hdragonfly, obj.Hsolaris, obj.Hnacl: symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) symo = ld.Rnd(symo, int64(ld.INITRND)) case obj.Hwindows: symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) symo = ld.Rnd(symo, ld.PEFILEALIGN) } ld.Cseek(symo) switch ld.HEADTYPE { default: if ld.Iself { ld.Cseek(symo) ld.Asmelfsym() ld.Cflush() ld.Cwrite(ld.Elfstrdat) if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } ld.Dwarfemitdebugsections() if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc() } } case obj.Hplan9: ld.Asmplan9sym() ld.Cflush() sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) if sym != nil { ld.Lcsize = int32(len(sym.P)) for i := 0; int32(i) < ld.Lcsize; i++ { ld.Cput(uint8(sym.P[i])) } ld.Cflush() } case obj.Hwindows: if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } ld.Dwarfemitdebugsections() case obj.Hdarwin: if ld.Linkmode == ld.LinkExternal { ld.Machoemitreloc() } } } if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(0) switch ld.HEADTYPE { default: case obj.Hplan9: /* plan9 */ magic := int32(4*26*26 + 7) magic |= 0x00008000 /* fat header */ ld.Lputb(uint32(magic)) /* magic */ ld.Lputb(uint32(ld.Segtext.Filelen)) /* sizes */ ld.Lputb(uint32(ld.Segdata.Filelen)) ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) ld.Lputb(uint32(ld.Symsize)) /* nsyms */ vl := ld.Entryvalue() ld.Lputb(PADDR(uint32(vl))) /* va of entry */ ld.Lputb(uint32(ld.Spsize)) /* sp offsets */ ld.Lputb(uint32(ld.Lcsize)) /* line offsets */ ld.Vputb(uint64(vl)) /* va of entry */ case obj.Hdarwin: ld.Asmbmacho() case obj.Hlinux, obj.Hfreebsd, obj.Hnetbsd, obj.Hopenbsd, obj.Hdragonfly, obj.Hsolaris, obj.Hnacl: ld.Asmbelf(symo) case obj.Hwindows: ld.Asmbpe() } ld.Cflush() }
func asmb(ctxt *ld.Link) { if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f asmb\n", obj.Cputime()) } ctxt.Bso.Flush() if ld.Iself { ld.Asmbelfsetup(ctxt) } sect := ld.Segtext.Sect ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) // 0xCC is INT $3 - breakpoint instruction ld.CodeblkPad(ctxt, int64(sect.Vaddr), int64(sect.Length), []byte{0xCC}) for sect = sect.Next; sect != nil; sect = sect.Next { ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) } if ld.Segrodata.Filelen > 0 { if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f rodatblk\n", obj.Cputime()) } ctxt.Bso.Flush() ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f datblk\n", obj.Cputime()) } ctxt.Bso.Flush() ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) ld.Cseek(int64(ld.Segdwarf.Fileoff)) ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) machlink := uint32(0) if ld.HEADTYPE == obj.Hdarwin { machlink = uint32(ld.Domacholink(ctxt)) } ld.Symsize = 0 ld.Spsize = 0 ld.Lcsize = 0 symo := uint32(0) if !*ld.FlagS { // TODO: rationalize if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f sym\n", obj.Cputime()) } ctxt.Bso.Flush() switch ld.HEADTYPE { default: if ld.Iself { symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound))) } case obj.Hplan9: symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) case obj.Hdarwin: symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink)) case obj.Hwindows: symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN)) } ld.Cseek(int64(symo)) switch ld.HEADTYPE { default: if ld.Iself { if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f elfsym\n", obj.Cputime()) } ld.Asmelfsym(ctxt) ld.Cflush() ld.Cwrite(ld.Elfstrdat) if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc(ctxt) } } case obj.Hplan9: ld.Asmplan9sym(ctxt) ld.Cflush() sym := ld.Linklookup(ctxt, "pclntab", 0) if sym != nil { ld.Lcsize = int32(len(sym.P)) for i := 0; int32(i) < ld.Lcsize; i++ { ld.Cput(sym.P[i]) } ld.Cflush() } case obj.Hwindows: if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f dwarf\n", obj.Cputime()) } case obj.Hdarwin: if ld.Linkmode == ld.LinkExternal { ld.Machoemitreloc(ctxt) } } } if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f headr\n", obj.Cputime()) } ctxt.Bso.Flush() ld.Cseek(0) switch ld.HEADTYPE { default: case obj.Hplan9: /* plan9 */ magic := int32(4*11*11 + 7) ld.Lputb(uint32(magic)) /* magic */ ld.Lputb(uint32(ld.Segtext.Filelen)) /* sizes */ ld.Lputb(uint32(ld.Segdata.Filelen)) ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) ld.Lputb(uint32(ld.Symsize)) /* nsyms */ ld.Lputb(uint32(ld.Entryvalue(ctxt))) /* va of entry */ ld.Lputb(uint32(ld.Spsize)) /* sp offsets */ ld.Lputb(uint32(ld.Lcsize)) /* line offsets */ case obj.Hdarwin: ld.Asmbmacho(ctxt) case obj.Hlinux, obj.Hfreebsd, obj.Hnetbsd, obj.Hopenbsd, obj.Hnacl: ld.Asmbelf(ctxt, int64(symo)) case obj.Hwindows: ld.Asmbpe(ctxt) } ld.Cflush() }
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.DynamicSymbols() if err != nil { Diag("cannot read symbols from shared library: %s", libpath) return } 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 } 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 = SDYNIMPORT lsym.File = libpath } // 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 == 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 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) // Find container symbols, mark them with SCONTAINER for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { if Ctxt.Cursym.Outer != nil { Ctxt.Cursym.Outer.Type |= obj.SCONTAINER } } 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)) } }
/* * This is the main entry point for generating dwarf. After emitting * the mandatory debug_abbrev section, it calls writelines() to set up * the per-compilation unit part of the DIE tree, while simultaneously * emitting the debug_line section. When the final tree contains * forward references, it will write the debug_info section in 2 * passes. * */ func dwarfgeneratedebugsyms(ctxt *Link) { if *FlagW { // disable dwarf return } if *FlagS && HEADTYPE != obj.Hdarwin { return } if HEADTYPE == obj.Hplan9 { return } if Linkmode == LinkExternal { if !Iself && HEADTYPE != obj.Hdarwin { return } } if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f dwarf\n", obj.Cputime()) } // Forctxt.Diagnostic messages. newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") // Some types that must exist to define other ones. newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>", 0) newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "void", 0) newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer", 0) die := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, "uintptr", 0) // needed for array size newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(SysArch.PtrSize), 0) newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, obj.KindUintptr, 0) // Prototypes needed for type synthesis. prototypedies = map[string]*dwarf.DWDie{ "type.runtime.stringStructDWARF": nil, "type.runtime.slice": nil, "type.runtime.hmap": nil, "type.runtime.bmap": nil, "type.runtime.sudog": nil, "type.runtime.waitq": nil, "type.runtime.hchan": nil, } // Needed by the prettyprinter code for interface inspection. defgotype(ctxt, lookup_or_diag(ctxt, "type.runtime._type")) defgotype(ctxt, lookup_or_diag(ctxt, "type.runtime.interfacetype")) defgotype(ctxt, lookup_or_diag(ctxt, "type.runtime.itab")) genasmsym(ctxt, defdwsymb) syms := writeabbrev(ctxt, nil) syms, funcs := writelines(ctxt, syms) syms = writeframes(ctxt, syms) synthesizestringtypes(ctxt, dwtypes.Child) synthesizeslicetypes(ctxt, dwtypes.Child) synthesizemaptypes(ctxt, dwtypes.Child) synthesizechantypes(ctxt, dwtypes.Child) reversetree(&dwroot.Child) reversetree(&dwtypes.Child) reversetree(&dwglobals.Child) movetomodule(&dwtypes) movetomodule(&dwglobals) // Need to reorder symbols so SDWARFINFO is after all SDWARFSECT // (but we need to generate dies before writepub) infosyms := writeinfo(ctxt, nil, funcs) syms = writepub(ctxt, ".debug_pubnames", ispubname, syms) syms = writepub(ctxt, ".debug_pubtypes", ispubtype, syms) syms = writearanges(ctxt, syms) syms = writegdbscript(ctxt, syms) syms = append(syms, infosyms...) dwarfp = syms }
func asmb(ctxt *ld.Link) { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f asmb\n", obj.Cputime()) } if ld.Iself { ld.Asmbelfsetup(ctxt) } sect := ld.Segtext.Sect ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) for sect = sect.Next; sect != nil; sect = sect.Next { ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) } if ld.Segrodata.Filelen > 0 { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f rodatblk\n", obj.Cputime()) } ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f datblk\n", obj.Cputime()) } ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) ld.Cseek(int64(ld.Segdwarf.Fileoff)) ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) /* output symbol table */ ld.Symsize = 0 ld.Lcsize = 0 symo := uint32(0) if !*ld.FlagS { if !ld.Iself { ctxt.Diag("unsupported executable format") } if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f sym\n", obj.Cputime()) } symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound))) ld.Cseek(int64(symo)) if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f elfsym\n", obj.Cputime()) } ld.Asmelfsym(ctxt) ld.Cflush() ld.Cwrite(ld.Elfstrdat) if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f dwarf\n", obj.Cputime()) } if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc(ctxt) } } ctxt.Cursym = nil if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f header\n", obj.Cputime()) } ld.Cseek(0) switch ld.HEADTYPE { default: ctxt.Diag("unsupported operating system") case obj.Hlinux: ld.Asmbelf(ctxt, int64(symo)) } ld.Cflush() if *ld.FlagC { fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) fmt.Printf("symsize=%d\n", ld.Symsize) fmt.Printf("lcsize=%d\n", ld.Lcsize) fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) } }
// deadcode marks all reachable symbols. // // The basis of the dead code elimination is a flood fill of symbols, // following their relocations, beginning at INITENTRY. // // This flood fill is wrapped in logic for pruning unused methods. // All methods are mentioned by relocations on their receiver's *rtype. // These relocations are specially defined as R_METHODOFF by the compiler // so we can detect and manipulated them here. // // There are three ways a method of a reachable type can be invoked: // // 1. direct call // 2. through a reachable interface type // 3. reflect.Value.Call, .Method, or reflect.Method.Func // // The first case is handled by the flood fill, a directly called method // is marked as reachable. // // The second case is handled by decomposing all reachable interface // types into method signatures. Each encountered method is compared // against the interface method signatures, if it matches it is marked // as reachable. This is extremely conservative, but easy and correct. // // The third case is handled by looking to see if any of: // - reflect.Value.Call is reachable // - reflect.Value.Method is reachable // - reflect.Type.Method or MethodByName is called. // If any of these happen, all bets are off and all exported methods // of reachable types are marked reachable. // // Any unreached text symbols are removed from ctxt.Textp. func deadcode(ctxt *Link) { if Debug['v'] != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f deadcode\n", obj.Cputime()) } d := &deadcodepass{ ctxt: ctxt, ifaceMethod: make(map[methodsig]bool), } // First, flood fill any symbols directly reachable in the call // graph from INITENTRY. Ignore all methods not directly called. d.init() d.flood() callSym := Linkrlookup(ctxt, "reflect.Value.Call", 0) methSym := Linkrlookup(ctxt, "reflect.Value.Method", 0) reflectSeen := false if DynlinkingGo() { // Exported methods may satisfy interfaces we don't know // about yet when dynamically linking. reflectSeen = true } for { if !reflectSeen { if d.reflectMethod || (callSym != nil && callSym.Attr.Reachable()) || (methSym != nil && methSym.Attr.Reachable()) { // Methods might be called via reflection. Give up on // static analysis, mark all exported methods of // all reachable types as reachable. reflectSeen = true } } // Mark all methods that could satisfy a discovered // interface as reachable. We recheck old marked interfaces // as new types (with new methods) may have been discovered // in the last pass. var rem []methodref for _, m := range d.markableMethods { if (reflectSeen && m.isExported()) || d.ifaceMethod[m.m] { d.markMethod(m) } else { rem = append(rem, m) } } d.markableMethods = rem if len(d.markQueue) == 0 { // No new work was discovered. Done. break } d.flood() } // Remove all remaining unreached R_METHODOFF relocations. for _, m := range d.markableMethods { for _, r := range m.r { d.cleanupReloc(r) } } if Buildmode != BuildmodeShared { // Keep a typelink or itablink if the symbol it points at is being kept. // (When BuildmodeShared, always keep typelinks and itablinks.) for _, s := range ctxt.Allsym { if strings.HasPrefix(s.Name, "go.typelink.") || strings.HasPrefix(s.Name, "go.itablink.") { s.Attr.Set(AttrReachable, len(s.R) == 1 && s.R[0].Sym.Attr.Reachable()) } } } // Remove dead text but keep file information (z symbols). textp := make([]*LSym, 0, len(ctxt.Textp)) for _, s := range ctxt.Textp { if s.Attr.Reachable() { textp = append(textp, s) } } ctxt.Textp = textp }
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 STEXT < s.Type && s.Type < 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 == 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 <= STEXT || 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 < SELFSECT; s = s.Next { } /* writable ELF sections */ datsize := int64(0) var sect *Section for ; s != nil && s.Type < 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 = 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 == SELFGOT { sect := addsection(&Segdata, ".got", 06) sect.Align = maxalign(s, SELFGOT) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) var toc *LSym for ; s != nil && s.Type == SELFGOT; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = 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, 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 < SINITARR; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = 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, SINITARR) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = uint64(datsize) for ; s != nil && s.Type == 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, 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 < SBSS; s = s.Next { if s.Type == SINITARR { Ctxt.Cursym = s Diag("unexpected symbol type %d", s.Type) } s.Sect = sect s.Type = 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, 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 < 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, 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 == 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 == STLSBSS && HEADTYPE != Hopenbsd { sect := addsection(&Segdata, ".tbss", 06) sect.Align = int32(Thearch.Ptrsize) sect.Vaddr = 0 datsize = 0 for ; s != nil && s.Type == 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 == 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 < 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 = 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, 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 < STYPELINK; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = 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, 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 == STYPELINK; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = 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, 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 < SPCLNTAB; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = 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, 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 < SELFROSECT; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Type = 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 < 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 = 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 asmb(ctxt *ld.Link) { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f asmb\n", obj.Cputime()) } if ld.Iself { ld.Asmbelfsetup(ctxt) } sect := ld.Segtext.Sect ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) for sect = sect.Next; sect != nil; sect = sect.Next { ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length)) } if ld.Segrodata.Filelen > 0 { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f rodatblk\n", obj.Cputime()) } ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f datblk\n", obj.Cputime()) } ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) ld.Cseek(int64(ld.Segdwarf.Fileoff)) ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) /* output symbol table */ ld.Symsize = 0 ld.Lcsize = 0 symo := uint32(0) if !*ld.FlagS { // TODO: rationalize if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f sym\n", obj.Cputime()) } switch ld.HEADTYPE { default: if ld.Iself { symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound))) } case obj.Hplan9: symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) } ld.Cseek(int64(symo)) switch ld.HEADTYPE { default: if ld.Iself { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f elfsym\n", obj.Cputime()) } ld.Asmelfsym(ctxt) ld.Cflush() ld.Cwrite(ld.Elfstrdat) if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc(ctxt) } } case obj.Hplan9: ld.Asmplan9sym(ctxt) ld.Cflush() sym := ld.Linklookup(ctxt, "pclntab", 0) if sym != nil { ld.Lcsize = int32(len(sym.P)) for i := 0; int32(i) < ld.Lcsize; i++ { ld.Cput(sym.P[i]) } ld.Cflush() } } } ctxt.Cursym = nil if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f header\n", obj.Cputime()) } ld.Cseek(0) switch ld.HEADTYPE { default: case obj.Hplan9: /* plan 9 */ magic := uint32(4*18*18 + 7) if ld.SysArch == sys.ArchMIPS64LE { magic = uint32(4*26*26 + 7) } ld.Thearch.Lput(magic) /* magic */ ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */ ld.Thearch.Lput(uint32(ld.Segdata.Filelen)) ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) ld.Thearch.Lput(uint32(ld.Symsize)) /* nsyms */ ld.Thearch.Lput(uint32(ld.Entryvalue(ctxt))) /* va of entry */ ld.Thearch.Lput(0) ld.Thearch.Lput(uint32(ld.Lcsize)) case obj.Hlinux, obj.Hfreebsd, obj.Hnetbsd, obj.Hopenbsd, obj.Hnacl: ld.Asmbelf(ctxt, int64(symo)) } ld.Cflush() if *ld.FlagC { fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) fmt.Printf("symsize=%d\n", ld.Symsize) fmt.Printf("lcsize=%d\n", ld.Lcsize) fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) } }
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)) } Bso.Flush() }
func asmb() { if ld.Debug['v'] != 0 { fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime()) } ld.Bso.Flush() if ld.Iself { ld.Asmbelfsetup() } sect := ld.Segtext.Sect ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) for sect = sect.Next; sect != nil; sect = sect.Next { ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) } if ld.Segrodata.Filelen > 0 { if ld.Debug['v'] != 0 { fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } if ld.Debug['v'] != 0 { fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) ld.Cseek(int64(ld.Segdwarf.Fileoff)) ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) /* output symbol table */ ld.Symsize = 0 ld.Lcsize = 0 symo := uint32(0) if ld.Debug['s'] == 0 { // TODO: rationalize if ld.Debug['v'] != 0 { fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime()) } ld.Bso.Flush() switch ld.HEADTYPE { default: if ld.Iself { symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) } case obj.Hplan9: symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) } ld.Cseek(int64(symo)) switch ld.HEADTYPE { default: if ld.Iself { if ld.Debug['v'] != 0 { fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime()) } ld.Asmelfsym() ld.Cflush() ld.Cwrite(ld.Elfstrdat) if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc() } } case obj.Hplan9: ld.Asmplan9sym() ld.Cflush() sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) if sym != nil { ld.Lcsize = int32(len(sym.P)) for i := 0; int32(i) < ld.Lcsize; i++ { ld.Cput(sym.P[i]) } ld.Cflush() } } } ld.Ctxt.Cursym = nil if ld.Debug['v'] != 0 { fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(0) switch ld.HEADTYPE { default: case obj.Hplan9: /* plan 9 */ ld.Thearch.Lput(0x647) /* magic */ ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */ ld.Thearch.Lput(uint32(ld.Segdata.Filelen)) ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) ld.Thearch.Lput(uint32(ld.Symsize)) /* nsyms */ ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */ ld.Thearch.Lput(0) ld.Thearch.Lput(uint32(ld.Lcsize)) case obj.Hlinux, obj.Hfreebsd, obj.Hnetbsd, obj.Hopenbsd, obj.Hnacl: ld.Asmbelf(int64(symo)) } ld.Cflush() if ld.Debug['c'] != 0 { fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) fmt.Printf("symsize=%d\n", ld.Symsize) fmt.Printf("lcsize=%d\n", ld.Lcsize) fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) } }
func objfile(lib *Library) { pkg := pathtoprefix(lib.Pkg) if Debug['v'] > 1 { fmt.Fprintf(&Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), lib.File, pkg) } Bso.Flush() var err error var f *obj.Biobuf f, err = obj.Bopenr(lib.File) if err != nil { Exitf("cannot open file %s: %v", lib.File, err) } magbuf := make([]byte, len(ARMAG)) if obj.Bread(f, magbuf) != len(magbuf) || !strings.HasPrefix(string(magbuf), ARMAG) { /* load it as a regular file */ l := obj.Bseek(f, 0, 2) obj.Bseek(f, 0, 0) ldobj(f, pkg, l, lib.File, lib.File, FileObj) obj.Bterm(f) return } /* skip over optional __.GOSYMDEF and process __.PKGDEF */ off := obj.Boffset(f) var arhdr ArHdr l := nextar(f, off, &arhdr) var pname string if l <= 0 { Diag("%s: short read on archive file symbol header", lib.File) goto out } if strings.HasPrefix(arhdr.name, symname) { off += l l = nextar(f, off, &arhdr) if l <= 0 { Diag("%s: short read on archive file symbol header", lib.File) goto out } } if !strings.HasPrefix(arhdr.name, pkgname) { Diag("%s: cannot find package header", lib.File) goto out } if Buildmode == BuildmodeShared { before := obj.Boffset(f) pkgdefBytes := make([]byte, atolwhex(arhdr.size)) obj.Bread(f, pkgdefBytes) hash := sha1.Sum(pkgdefBytes) lib.hash = hash[:] obj.Bseek(f, before, 0) } off += l if Debug['u'] != 0 { ldpkg(f, pkg, atolwhex(arhdr.size), lib.File, Pkgdef) } /* * load all the object files from the archive now. * this gives us sequential file access and keeps us * from needing to come back later to pick up more * objects. it breaks the usual C archive model, but * this is Go, not C. the common case in Go is that * we need to load all the objects, and then we throw away * the individual symbols that are unused. * * loading every object will also make it possible to * load foreign objects not referenced by __.GOSYMDEF. */ for { l = nextar(f, off, &arhdr) if l == 0 { break } if l < 0 { Exitf("%s: malformed archive", lib.File) } off += l pname = fmt.Sprintf("%s(%s)", lib.File, arhdr.name) l = atolwhex(arhdr.size) ldobj(f, pkg, l, pname, lib.File, ArchiveObj) } out: obj.Bterm(f) }
func 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 _, processedlib := range Ctxt.Shlibs { if processedlib.Path == 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) Ctxt.Bso.Flush() } 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{} var hash []byte 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.") { gcmasks[s.Value] = readelfsymboldata(f, &s) } if s.Name == "go.link.abihashbytes" { hash = readelfsymboldata(f, &s) } 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.ElfType = elf.ST_TYPE(s.Info) lsym.File = libpath if strings.HasPrefix(lsym.Name, "type.") { if f.Sections[s.Section].Type == elf.SHT_PROGBITS { lsym.P = readelfsymboldata(f, &s) } if !strings.HasPrefix(lsym.Name, "type..") { types = append(types, lsym) } } } for _, t := range types { if decodetype_noptr(t) != 0 || decodetype_usegcprog(t) != 0 { continue } addr := decodetype_gcprog_shlib(t) 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, Shlib{Path: libpath, Hash: hash}) }
func deadcode() { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f deadcode\n", obj.Cputime()) } mark(Linklookup(Ctxt, INITENTRY, 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 } else { last.Next = nil } 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 = SCONST s.Value = 0 } } if tracksym == "" { return } s := Linklookup(Ctxt, tracksym, 0) if !s.Reachable { return } addstrdata(tracksym, buf.String()) }
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]) } } 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 { if DynlinkingGo() { Exitf("cannot implicitly include runtime/cgo in a shared library") } objfile(Ctxt.Library[i]) } } } 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 ldpeError(ctxt *Link, input *bio.Reader, pkg string, length int64, pn string) error { if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f ldpe %s\n", obj.Cputime(), pn) } localSymVersion := ctxt.Syms.IncVersion() sectsyms := make(map[*pe.Section]*Symbol) sectdata := make(map[*pe.Section][]byte) // Some input files are archives containing multiple of // object files, and pe.NewFile seeks to the start of // input file and get confused. Create section reader // to stop pe.NewFile looking before current position. sr := io.NewSectionReader((*peBiobuf)(input), input.Offset(), 1<<63-1) // TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details) f, err := pe.NewFile(sr) if err != nil { return err } defer f.Close() // TODO return error if found .cormeta // create symbols for mapped sections for _, sect := range f.Sections { if sect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { continue } if sect.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 } data, err := sect.Data() if err != nil { return err } sectdata[sect] = data name := fmt.Sprintf("%s(%s)", pkg, sect.Name) s := ctxt.Syms.Lookup(name, localSymVersion) switch sect.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: return fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name) } s.P = data s.Size = int64(len(data)) sectsyms[sect] = s if sect.Name == ".rsrc" { setpersrc(ctxt, s) } } // load relocations for _, rsect := range f.Sections { if _, found := sectsyms[rsect]; !found { continue } if rsect.NumberOfRelocations == 0 { continue } if rsect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { continue } if rsect.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 } rs := make([]Reloc, rsect.NumberOfRelocations) for j, r := range rsect.Relocs { rp := &rs[j] if int(r.SymbolTableIndex) >= len(f.COFFSymbols) { return fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols)) } pesym := &f.COFFSymbols[r.SymbolTableIndex] gosym, err := readpesym(ctxt, f, pesym, sectsyms, localSymVersion) if err != nil { return err } if gosym == nil { name, err := pesym.FullName(f.StringTable) if err != nil { name = string(pesym.Name[:]) } return fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type) } rp.Sym = gosym rp.Siz = 4 rp.Off = int32(r.VirtualAddress) switch r.Type { default: Errorf(sectsyms[rsect], "%s: unknown relocation type %d;", pn, r.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(sectdata[rsect][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(sectdata[rsect][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(sectdata[rsect][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(pesym) { rp.Add += int64(pesym.Value) } } sort.Sort(rbyoff(rs[:rsect.NumberOfRelocations])) s := sectsyms[rsect] s.R = rs s.R = s.R[:rsect.NumberOfRelocations] } // enter sub-symbols into symbol table. for i, numaux := 0, 0; i < len(f.COFFSymbols); i += numaux + 1 { pesym := &f.COFFSymbols[i] numaux = int(pesym.NumberOfAuxSymbols) name, err := pesym.FullName(f.StringTable) if err != nil { return err } if name == "" { continue } if issect(pesym) { continue } if int(pesym.SectionNumber) > len(f.Sections) { continue } if pesym.SectionNumber == IMAGE_SYM_DEBUG { continue } var sect *pe.Section if pesym.SectionNumber > 0 { sect = f.Sections[pesym.SectionNumber-1] if _, found := sectsyms[sect]; !found { continue } } s, err := readpesym(ctxt, f, pesym, sectsyms, localSymVersion) if err != nil { return err } if pesym.SectionNumber == 0 { // extern if s.Type == obj.SDYNIMPORT { s.Plt = -2 // flag for dynimport in PE object files. } if s.Type == obj.SXREF && pesym.Value > 0 { // global data s.Type = obj.SNOPTRDATA s.Size = int64(pesym.Value) } continue } else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) { sect = f.Sections[pesym.SectionNumber-1] if _, found := sectsyms[sect]; !found { Errorf(s, "%s: missing sect.sym", pn) } } else { Errorf(s, "%s: sectnum < 0!", pn) } if sect == nil { return nil } if s.Outer != nil { if s.Attr.DuplicateOK() { continue } Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sectsyms[sect].Name) } sectsym := sectsyms[sect] s.Sub = sectsym.Sub sectsym.Sub = s s.Type = sectsym.Type | obj.SSUB s.Value = int64(pesym.Value) s.Size = 4 s.Outer = sectsym if sectsym.Type == obj.STEXT { if s.Attr.External() && !s.Attr.DuplicateOK() { Errorf(s, "%s: duplicate symbol definition", pn) } s.Attr |= AttrExternal } } // Sort outer lists by address, adding to textp. // This keeps textp in increasing address order. for _, sect := range f.Sections { s := sectsyms[sect] if s == nil { continue } if s.Sub != nil { s.Sub = listsort(s.Sub) } if s.Type == obj.STEXT { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList ctxt.Textp = append(ctxt.Textp, s) for s = s.Sub; s != nil; s = s.Sub { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList ctxt.Textp = append(ctxt.Textp, s) } } } return nil }
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 = SRODATA case ElfSectFlagAlloc + ElfSectFlagWrite: if sect.type_ == ElfSectNobits { s.Type = SNOPTRBSS } else { s.Type = SNOPTRDATA } case ElfSectFlagAlloc + ElfSectFlagExec: s.Type = STEXT } if sect.name == ".got" || sect.name == ".toc" { s.Type = 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) if symbols == nil { Diag("out of memory") Errorexit() } 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 == SXREF { s.Type = 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 } Diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) Errorexit() } s.Sub = sect.sym.Sub sect.sym.Sub = s s.Type = sect.sym.Type | s.Type&^SMASK | 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 == 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 == STEXT { if s.Onlist != 0 { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Onlist = 1 if Ctxt.Etextp != nil { Ctxt.Etextp.Next = s } else { Ctxt.Textp = s } Ctxt.Etextp = s for s = s.Sub; s != nil; s = s.Sub { if s.Onlist != 0 { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Onlist = 1 Ctxt.Etextp.Next = s Ctxt.Etextp = s } } } // load relocations for i := 0; uint(i) < elfobj.nsect; i++ { rsect = &elfobj.sect[i] if rsect.type_ != ElfSectRela && rsect.type_ != ElfSectRel { continue } if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil { continue } sect = &elfobj.sect[rsect.info] if err = elfmap(elfobj, rsect); err != nil { goto bad } rela = 0 if rsect.type_ == ElfSectRela { rela = 1 } n = int(rsect.size / uint64(4+4*is64) / uint64(2+rela)) r = make([]Reloc, n) p = rsect.base for j = 0; j < n; j++ { add = 0 rp = &r[j] if is64 != 0 { // 64-bit rel/rela rp.Off = int32(e.Uint64(p)) p = p[8:] info = e.Uint64(p) p = p[8:] if rela != 0 { add = e.Uint64(p) p = p[8:] } } else { // 32-bit rel/rela rp.Off = int32(e.Uint32(p)) p = p[4:] info = uint64(e.Uint32(p)) info = info>>8<<32 | info&0xff // convert to 64-bit info p = p[4:] if rela != 0 { add = uint64(e.Uint32(p)) p = p[4:] } } if info&0xffffffff == 0 { // skip R_*_NONE relocation j-- n-- continue } if info>>32 == 0 { // absolute relocation, don't bother reading the null symbol rp.Sym = nil } else { if err = readelfsym(elfobj, int(info>>32), &sym, 0); err != nil { goto bad } sym.sym = symbols[info>>32] if sym.sym == nil { err = fmt.Errorf("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect.sym.Name, j, int(info>>32), sym.name, sym.shndx, sym.type_) goto bad } rp.Sym = sym.sym } rp.Type = int32(reltype(pn, int(uint32(info)), &rp.Siz)) if rela != 0 { rp.Add = int64(add) } else { // load addend from image if rp.Siz == 4 { rp.Add = int64(e.Uint32(sect.base[rp.Off:])) } else if rp.Siz == 8 { rp.Add = int64(e.Uint64(sect.base[rp.Off:])) } else { Diag("invalid rela size %d", rp.Siz) } } if rp.Siz == 2 { rp.Add = int64(int16(rp.Add)) } if rp.Siz == 4 { rp.Add = int64(int32(rp.Add)) } } //print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add); sort.Sort(rbyoff(r[:n])) // just in case s = sect.sym s.R = r s.R = s.R[:n] } return bad: Diag("%s: malformed elf file: %v", pn, err) }
func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn) } var sect *PeSect Ctxt.Version++ base := int32(obj.Boffset(f)) peobj := new(PeObj) peobj.f = f peobj.base = uint32(base) peobj.name = pn // read header var err error var j int var l uint32 var name string var numaux int var r []Reloc var rp *Reloc var rsect *PeSect var s *LSym var sym *PeSym var symbuf [18]uint8 if err = binary.Read(f, binary.LittleEndian, &peobj.fh); err != nil { goto bad } // load section list peobj.sect = make([]PeSect, peobj.fh.NumberOfSections) peobj.nsect = uint(peobj.fh.NumberOfSections) for i := 0; i < int(peobj.fh.NumberOfSections); i++ { if err = binary.Read(f, binary.LittleEndian, &peobj.sect[i].sh); err != nil { goto bad } peobj.sect[i].size = uint64(peobj.sect[i].sh.SizeOfRawData) peobj.sect[i].name = cstring(peobj.sect[i].sh.Name[:]) } // TODO return error if found .cormeta // load string table obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) if obj.Bread(f, symbuf[:4]) != 4 { goto bad } l = Le32(symbuf[:]) peobj.snames = make([]byte, l) obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) if obj.Bread(f, peobj.snames) != len(peobj.snames) { goto bad } // rewrite section names if they start with / for i := 0; i < int(peobj.fh.NumberOfSections); i++ { if peobj.sect[i].name == "" { continue } if peobj.sect[i].name[0] != '/' { continue } n, _ := strconv.Atoi(peobj.sect[i].name[1:]) peobj.sect[i].name = cstring(peobj.snames[n:]) } // read symbols peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols) peobj.npesym = uint(peobj.fh.NumberOfSymbols) obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0) for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 { obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0) if obj.Bread(f, symbuf[:]) != len(symbuf) { goto bad } if (symbuf[0] == 0) && (symbuf[1] == 0) && (symbuf[2] == 0) && (symbuf[3] == 0) { l = Le32(symbuf[4:]) peobj.pesym[i].name = cstring(peobj.snames[l:]) // sym name length <= 8 } else { peobj.pesym[i].name = cstring(symbuf[:8]) } peobj.pesym[i].value = Le32(symbuf[8:]) peobj.pesym[i].sectnum = Le16(symbuf[12:]) peobj.pesym[i].sclass = symbuf[16] peobj.pesym[i].aux = symbuf[17] peobj.pesym[i].type_ = Le16(symbuf[14:]) numaux = int(peobj.pesym[i].aux) if numaux < 0 { numaux = 0 } } // create symbols for mapped sections for i := 0; uint(i) < peobj.nsect; i++ { sect = &peobj.sect[i] if sect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { continue } if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { // This has been seen for .idata sections, which we // want to ignore. See issues 5106 and 5273. continue } if pemap(peobj, sect) < 0 { goto bad } name = fmt.Sprintf("%s(%s)", pkg, sect.name) s = Linklookup(Ctxt, name, Ctxt.Version) switch sect.sh.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) { case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata s.Type = obj.SRODATA case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss s.Type = obj.SNOPTRBSS case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data s.Type = obj.SNOPTRDATA case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text s.Type = obj.STEXT default: err = fmt.Errorf("unexpected flags %#06x for PE section %s", sect.sh.Characteristics, sect.name) goto bad } s.P = sect.base s.P = s.P[:sect.size] s.Size = int64(sect.size) sect.sym = s if sect.name == ".rsrc" { setpersrc(sect.sym) } } // load relocations for i := 0; uint(i) < peobj.nsect; i++ { rsect = &peobj.sect[i] if rsect.sym == nil || rsect.sh.NumberOfRelocations == 0 { continue } if rsect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { continue } if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { // This has been seen for .idata sections, which we // want to ignore. See issues 5106 and 5273. continue } r = make([]Reloc, rsect.sh.NumberOfRelocations) obj.Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0) for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ { rp = &r[j] if obj.Bread(f, symbuf[:10]) != 10 { goto bad } rva := Le32(symbuf[0:]) symindex := Le32(symbuf[4:]) type_ := Le16(symbuf[8:]) if err = readpesym(peobj, int(symindex), &sym); err != nil { goto bad } if sym.sym == nil { err = fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", sym.name, symindex, sym.type_) goto bad } rp.Sym = sym.sym rp.Siz = 4 rp.Off = int32(rva) switch type_ { default: Diag("%s: unknown relocation type %d;", pn, type_) fallthrough case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32, IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 IMAGE_REL_AMD64_ADDR32NB: rp.Type = obj.R_PCREL rp.Add = int64(int32(Le32(rsect.base[rp.Off:]))) case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32: rp.Type = obj.R_ADDR // load addend from image rp.Add = int64(int32(Le32(rsect.base[rp.Off:]))) case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 rp.Siz = 8 rp.Type = obj.R_ADDR // load addend from image rp.Add = int64(Le64(rsect.base[rp.Off:])) } // ld -r could generate multiple section symbols for the // same section but with different values, we have to take // that into account if issect(&peobj.pesym[symindex]) { rp.Add += int64(peobj.pesym[symindex].value) } } sort.Sort(rbyoff(r[:rsect.sh.NumberOfRelocations])) s = rsect.sym s.R = r s.R = s.R[:rsect.sh.NumberOfRelocations] } // enter sub-symbols into symbol table. for i := 0; uint(i) < peobj.npesym; i++ { if peobj.pesym[i].name == "" { continue } if issect(&peobj.pesym[i]) { continue } if uint(peobj.pesym[i].sectnum) > peobj.nsect { continue } if peobj.pesym[i].sectnum > 0 { sect = &peobj.sect[peobj.pesym[i].sectnum-1] if sect.sym == nil { continue } } if err = readpesym(peobj, i, &sym); err != nil { goto bad } s = sym.sym if sym.sectnum == 0 { // extern if s.Type == obj.SDYNIMPORT { s.Plt = -2 // flag for dynimport in PE object files. } if s.Type == obj.SXREF && sym.value > 0 { // global data s.Type = obj.SNOPTRDATA s.Size = int64(sym.value) } continue } else if sym.sectnum > 0 && uint(sym.sectnum) <= peobj.nsect { sect = &peobj.sect[sym.sectnum-1] if sect.sym == nil { Diag("%s: %s sym == 0!", pn, s.Name) } } else { Diag("%s: %s sectnum < 0!", pn, s.Name) } if sect == nil { return } if s.Outer != nil { if s.Attr.DuplicateOK() { continue } Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) } s.Sub = sect.sym.Sub sect.sym.Sub = s s.Type = sect.sym.Type | obj.SSUB s.Value = int64(sym.value) s.Size = 4 s.Outer = sect.sym if sect.sym.Type == obj.STEXT { if s.Attr.External() && !s.Attr.DuplicateOK() { Diag("%s: duplicate definition of %s", pn, s.Name) } s.Attr |= AttrExternal } } // Sort outer lists by address, adding to textp. // This keeps textp in increasing address order. for i := 0; uint(i) < peobj.nsect; i++ { s = peobj.sect[i].sym if s == nil { continue } if s.Sub != nil { s.Sub = listsort(s.Sub, valuecmp, listsubp) } if s.Type == obj.STEXT { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList if Ctxt.Etextp != nil { Ctxt.Etextp.Next = s } else { Ctxt.Textp = s } Ctxt.Etextp = s for s = s.Sub; s != nil; s = s.Sub { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList Ctxt.Etextp.Next = s Ctxt.Etextp = s } } } return bad: Diag("%s: malformed pe file: %v", pn, err) }
func asmb() { if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) } ld.Bso.Flush() if ld.Iself { ld.Asmbelfsetup() } sect := ld.Segtext.Sect ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) for sect = sect.Next; sect != nil; sect = sect.Next { ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) } if ld.Segrodata.Filelen > 0 { if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(int64(ld.Segrodata.Fileoff)) ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) } if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) machlink := uint32(0) if ld.HEADTYPE == obj.Hdarwin { if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) ld.Cseek(int64(dwarfoff)) ld.Segdwarf.Fileoff = uint64(ld.Cpos()) ld.Dwarfemitdebugsections() ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff machlink = uint32(ld.Domacholink()) } ld.Symsize = 0 ld.Spsize = 0 ld.Lcsize = 0 symo := uint32(0) if ld.Debug['s'] == 0 { // TODO: rationalize if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) } ld.Bso.Flush() switch ld.HEADTYPE { default: if ld.Iself { symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) } case obj.Hplan9: symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) case obj.Hdarwin: symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink)) case obj.Hwindows: symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN)) } ld.Cseek(int64(symo)) switch ld.HEADTYPE { default: if ld.Iself { if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) } ld.Asmelfsym() ld.Cflush() ld.Cwrite(ld.Elfstrdat) if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } ld.Dwarfemitdebugsections() if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc() } } case obj.Hplan9: ld.Asmplan9sym() ld.Cflush() sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) if sym != nil { ld.Lcsize = int32(len(sym.P)) for i := 0; int32(i) < ld.Lcsize; i++ { ld.Cput(uint8(sym.P[i])) } ld.Cflush() } case obj.Hwindows: if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } ld.Dwarfemitdebugsections() case obj.Hdarwin: if ld.Linkmode == ld.LinkExternal { ld.Machoemitreloc() } } } if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(0) switch ld.HEADTYPE { default: case obj.Hplan9: /* plan9 */ magic := int32(4*11*11 + 7) ld.Lputb(uint32(magic)) /* magic */ ld.Lputb(uint32(ld.Segtext.Filelen)) /* sizes */ ld.Lputb(uint32(ld.Segdata.Filelen)) ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) ld.Lputb(uint32(ld.Symsize)) /* nsyms */ ld.Lputb(uint32(ld.Entryvalue())) /* va of entry */ ld.Lputb(uint32(ld.Spsize)) /* sp offsets */ ld.Lputb(uint32(ld.Lcsize)) /* line offsets */ case obj.Hdarwin: ld.Asmbmacho() case obj.Hlinux, obj.Hfreebsd, obj.Hnetbsd, obj.Hopenbsd, obj.Hnacl: ld.Asmbelf(int64(symo)) case obj.Hwindows: ld.Asmbpe() } ld.Cflush() }
func dodata() { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f dodata\n", obj.Cputime()) } Bso.Flush() 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 if UseRelro() { // "read only" data with relocations needs to go in its own section // when building a shared library. We do this by boosting objects of // type SXXX with relocations to type SXXXRELRO. for s := datap; s != nil; s = s.Next { if (s.Type >= obj.STYPE && s.Type <= obj.SFUNCTAB && len(s.R) > 0) || s.Type == obj.SGOSTRING { s.Type += (obj.STYPERELRO - obj.STYPE) if s.Outer != nil { s.Outer.Type = s.Type } } } // Check that we haven't made two symbols with the same .Outer into // different types (because references two symbols with non-nil Outer // become references to the outer symbol + offset it's vital that the // symbol and the outer end up in the same section). for s := datap; s != nil; s = s.Next { if s.Outer != nil && s.Outer.Type != s.Type { Diag("inconsistent types for %s and its Outer %s (%d != %d)", s.Name, s.Outer.Name, s.Type, s.Outer.Type) } } } datap = listsort(datap, datcmp, listnextp) if Iself { // Make .rela and .rela.plt contiguous, the ELF ABI requires this // and Solaris actually cares. var relplt *LSym for l = &datap; *l != nil; l = &(*l).Next { if (*l).Name == ".rel.plt" || (*l).Name == ".rela.plt" { relplt = (*l) *l = (*l).Next break } } if relplt != nil { for s = datap; s != nil; s = s.Next { if s.Name == ".rel" || s.Name == ".rela" { relplt.Next = s.Next s.Next = relplt } } } } /* * 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 var gc GCProg gc.Init("runtime.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) gc.AddSym(s) growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr gc.End(int64(sect.Length)) /* 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 gc = GCProg{} gc.Init("runtime.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) gc.AddSym(s) growdatsize(&datsize, s) } sect.Length = uint64(datsize) - sect.Vaddr gc.End(int64(sect.Length)) /* 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 s != nil && s.Type == obj.STLSBSS { if Iself && (Linkmode == LinkExternal || Debug['d'] == 0) && HEADTYPE != obj.Hopenbsd { sect = addsection(&Segdata, ".tbss", 06) sect.Align = int32(Thearch.Ptrsize) sect.Vaddr = 0 } else { sect = nil } datsize = 0 for ; s != nil && s.Type == obj.STLSBSS; s = s.Next { datsize = aligndatsize(datsize, s) s.Sect = sect s.Value = datsize growdatsize(&datsize, s) } if sect != nil { sect.Length = uint64(datsize) } } 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.STYPERELRO-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.STYPERELRO; 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 // There is some data that are conceptually read-only but are written to by // relocations. On GNU systems, we can arrange for the dynamic linker to // mprotect sections after relocations are applied by giving them write // permissions in the object file and calling them ".data.rel.ro.FOO". We // divide the .rodata section between actual .rodata and .data.rel.ro.rodata, // but for the other sections that this applies to, we just write a read-only // .FOO section or a read-write .data.rel.ro.FOO section depending on the // situation. // TODO(mwhudson): It would make sense to do this more widely, but it makes // the system linker segfault on darwin. relro_perms := 04 relro_prefix := "" if UseRelro() { relro_perms = 06 relro_prefix = ".data.rel.ro" /* data only written by relocations */ sect = addsection(segro, ".data.rel.ro", 06) sect.Align = maxalign(s, obj.STYPELINK-1) datsize = Rnd(datsize, int64(sect.Align)) sect.Vaddr = 0 for ; s != nil && s.Type < obj.STYPELINK; s = s.Next { datsize = aligndatsize(datsize, s) if s.Outer != nil && s.Outer.Sect != nil && s.Outer.Sect != sect { Diag("s.Outer (%s) in different section from s (%s)", s.Outer.Name, s.Name) } 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, relro_prefix+".typelink", relro_perms) 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, relro_prefix+".gosymtab", relro_perms) 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, relro_prefix+".gopclntab", relro_perms) 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++ } }