| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| package ld |
|
|
| import ( |
| "bytes" |
| "debug/elf" |
| "debug/macho" |
| "encoding/base64" |
| "encoding/binary" |
| "fmt" |
| "internal/buildcfg" |
| "internal/platform" |
| "io" |
| "log" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "runtime" |
| "slices" |
| "sort" |
| "strings" |
| "sync" |
| "time" |
|
|
| "cmd/internal/bio" |
| "cmd/internal/goobj" |
| "cmd/internal/hash" |
| "cmd/internal/objabi" |
| "cmd/internal/sys" |
| "cmd/link/internal/loadelf" |
| "cmd/link/internal/loader" |
| "cmd/link/internal/loadmacho" |
| "cmd/link/internal/loadpe" |
| "cmd/link/internal/loadxcoff" |
| "cmd/link/internal/sym" |
| ) |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| type ArchSyms struct { |
| Rel loader.Sym |
| Rela loader.Sym |
| RelPLT loader.Sym |
| RelaPLT loader.Sym |
|
|
| LinkEditGOT loader.Sym |
| LinkEditPLT loader.Sym |
|
|
| TOC loader.Sym |
| DotTOC []loader.Sym |
|
|
| GOT loader.Sym |
| PLT loader.Sym |
| GOTPLT loader.Sym |
|
|
| Tlsg loader.Sym |
| Tlsoffset int |
|
|
| Dynamic loader.Sym |
| DynSym loader.Sym |
| DynStr loader.Sym |
|
|
| unreachableMethod loader.Sym |
|
|
| |
| |
| mainInittasks loader.Sym |
| } |
|
|
| |
| func (ctxt *Link) mkArchSym(name string, ver int, ls *loader.Sym) { |
| *ls = ctxt.loader.LookupOrCreateSym(name, ver) |
| ctxt.loader.SetAttrReachable(*ls, true) |
| } |
|
|
| |
| |
| func (ctxt *Link) mkArchSymVec(name string, ver int, ls []loader.Sym) { |
| ls[ver] = ctxt.loader.LookupOrCreateSym(name, ver) |
| ctxt.loader.SetAttrReachable(ls[ver], true) |
| } |
|
|
| |
| |
| func (ctxt *Link) setArchSyms() { |
| ctxt.mkArchSym(".got", 0, &ctxt.GOT) |
| ctxt.mkArchSym(".plt", 0, &ctxt.PLT) |
| ctxt.mkArchSym(".got.plt", 0, &ctxt.GOTPLT) |
| ctxt.mkArchSym(".dynamic", 0, &ctxt.Dynamic) |
| ctxt.mkArchSym(".dynsym", 0, &ctxt.DynSym) |
| ctxt.mkArchSym(".dynstr", 0, &ctxt.DynStr) |
| ctxt.mkArchSym("runtime.unreachableMethod", abiInternalVer, &ctxt.unreachableMethod) |
|
|
| if ctxt.IsPPC64() { |
| ctxt.mkArchSym("TOC", 0, &ctxt.TOC) |
|
|
| ctxt.DotTOC = make([]loader.Sym, ctxt.MaxVersion()+1) |
| for i := 0; i <= ctxt.MaxVersion(); i++ { |
| if i >= sym.SymVerABICount && i < sym.SymVerStatic { |
| continue |
| } |
| ctxt.mkArchSymVec(".TOC.", i, ctxt.DotTOC) |
| } |
| } |
| if ctxt.IsElf() { |
| ctxt.mkArchSym(".rel", 0, &ctxt.Rel) |
| ctxt.mkArchSym(".rela", 0, &ctxt.Rela) |
| ctxt.mkArchSym(".rel.plt", 0, &ctxt.RelPLT) |
| ctxt.mkArchSym(".rela.plt", 0, &ctxt.RelaPLT) |
| } |
| if ctxt.IsDarwin() { |
| ctxt.mkArchSym(".linkedit.got", 0, &ctxt.LinkEditGOT) |
| ctxt.mkArchSym(".linkedit.plt", 0, &ctxt.LinkEditPLT) |
| } |
| } |
|
|
| type Arch struct { |
| Funcalign int |
| Maxalign int |
| Minalign int |
| Dwarfregsp int |
| Dwarfreglr int |
|
|
| |
| |
| |
| |
| TrampLimit uint64 |
|
|
| |
| |
| |
| |
| CodePad []byte |
|
|
| |
| Plan9Magic uint32 |
| Plan9_64Bit bool |
|
|
| Adddynrel func(*Target, *loader.Loader, *ArchSyms, loader.Sym, loader.Reloc, int) bool |
| Archinit func(*Link) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| Archreloc func(*Target, *loader.Loader, *ArchSyms, loader.Reloc, loader.Sym, |
| int64) (relocatedOffset int64, nExtReloc int, ok bool) |
| |
| |
| |
| |
| |
| |
| |
| |
| Archrelocvariant func(target *Target, ldr *loader.Loader, rel loader.Reloc, |
| rv sym.RelocVariant, sym loader.Sym, offset int64, data []byte) (relocatedOffset int64) |
|
|
| |
| |
| Trampoline func(ctxt *Link, ldr *loader.Loader, ri int, rs, s loader.Sym) |
|
|
| |
| |
| |
| |
| |
| |
| |
| Asmb func(*Link, *loader.Loader) |
| Asmb2 func(*Link, *loader.Loader) |
|
|
| |
| |
| |
| Extreloc func(*Target, *loader.Loader, loader.Reloc, loader.Sym) (loader.ExtReloc, bool) |
|
|
| Gentext func(*Link, *loader.Loader) |
| Machoreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool |
| MachorelocSize uint32 |
| PEreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool |
| Xcoffreloc1 func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool |
|
|
| |
| |
| GenSymsLate func(*Link, *loader.Loader) |
|
|
| |
| |
| |
| |
| |
| |
| TLSIEtoLE func(P []byte, off, size int) |
|
|
| |
| AssignAddress func(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64) |
|
|
| |
| ELF ELFArch |
| } |
|
|
| var ( |
| thearch Arch |
| lcSize int32 |
| rpath Rpath |
| spSize int32 |
| symSize int32 |
| ) |
|
|
| |
| |
| var abiInternalVer = sym.SymVerABIInternal |
|
|
| |
| |
| func (ctxt *Link) DynlinkingGo() bool { |
| if !ctxt.Loaded { |
| panic("DynlinkingGo called before all symbols loaded") |
| } |
| return ctxt.BuildMode == BuildModeShared || ctxt.linkShared || ctxt.BuildMode == BuildModePlugin || ctxt.canUsePlugins |
| } |
|
|
| |
| func (ctxt *Link) CanUsePlugins() bool { |
| if !ctxt.Loaded { |
| panic("CanUsePlugins called before all symbols loaded") |
| } |
| return ctxt.canUsePlugins |
| } |
|
|
| |
| func (ctxt *Link) NeedCodeSign() bool { |
| return ctxt.IsDarwin() && ctxt.IsARM64() |
| } |
|
|
| var ( |
| dynlib []string |
| ldflag []string |
| havedynamic int |
| Funcalign int |
| iscgo bool |
| elfglobalsymndx int |
| interpreter string |
|
|
| debug_s bool |
| HEADR int32 |
|
|
| nerrors int |
| liveness int64 |
|
|
| |
| checkStrictDups int |
| strictDupMsgCount int |
| ) |
|
|
| var ( |
| Segtext sym.Segment |
| Segrodata sym.Segment |
| Segrelrodata sym.Segment |
| Segdata sym.Segment |
| Segdwarf sym.Segment |
| Segpdata sym.Segment |
| Segxdata sym.Segment |
|
|
| Segments = []*sym.Segment{&Segtext, &Segrodata, &Segrelrodata, &Segdata, &Segdwarf, &Segpdata, &Segxdata} |
| ) |
|
|
| const pkgdef = "__.PKGDEF" |
|
|
| var ( |
| |
| |
| |
| externalobj = false |
|
|
| |
| |
| |
| dynimportfail []string |
|
|
| |
| |
| |
| |
| preferlinkext []string |
|
|
| |
| |
| unknownObjFormat = false |
|
|
| theline string |
| ) |
|
|
| func Lflag(ctxt *Link, arg string) { |
| ctxt.Libdir = append(ctxt.Libdir, arg) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| func mayberemoveoutfile() { |
| if fi, err := os.Lstat(*flagOutfile); err == nil && !fi.Mode().IsRegular() { |
| return |
| } |
| os.Remove(*flagOutfile) |
| } |
|
|
| func libinit(ctxt *Link) { |
| if *FlagFuncAlign != 0 { |
| Funcalign = *FlagFuncAlign |
| } else { |
| Funcalign = thearch.Funcalign |
| } |
|
|
| |
| suffix := "" |
|
|
| suffixsep := "" |
| if *flagInstallSuffix != "" { |
| suffixsep = "_" |
| suffix = *flagInstallSuffix |
| } else if *flagRace { |
| suffixsep = "_" |
| suffix = "race" |
| } else if *flagMsan { |
| suffixsep = "_" |
| suffix = "msan" |
| } else if *flagAsan { |
| suffixsep = "_" |
| suffix = "asan" |
| } |
|
|
| if buildcfg.GOROOT != "" { |
| Lflag(ctxt, filepath.Join(buildcfg.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", buildcfg.GOOS, buildcfg.GOARCH, suffixsep, suffix))) |
| } |
|
|
| mayberemoveoutfile() |
|
|
| if err := ctxt.Out.Open(*flagOutfile); err != nil { |
| Exitf("cannot create %s: %v", *flagOutfile, err) |
| } |
|
|
| if *flagEntrySymbol == "" { |
| switch ctxt.BuildMode { |
| case BuildModeCShared, BuildModeCArchive: |
| *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s_lib", buildcfg.GOARCH, buildcfg.GOOS) |
| case BuildModeExe, BuildModePIE: |
| *flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s", buildcfg.GOARCH, buildcfg.GOOS) |
| case BuildModeShared, BuildModePlugin: |
| |
| default: |
| Errorf("unknown *flagEntrySymbol for buildmode %v", ctxt.BuildMode) |
| } |
| } |
| } |
|
|
| func exitIfErrors() { |
| if nerrors != 0 || checkStrictDups > 1 && strictDupMsgCount > 0 { |
| mayberemoveoutfile() |
| Exit(2) |
| } |
|
|
| } |
|
|
| func errorexit() { |
| exitIfErrors() |
| Exit(0) |
| } |
|
|
| func loadinternal(ctxt *Link, name string) *sym.Library { |
| zerofp := goobj.FingerprintType{} |
| if ctxt.linkShared && ctxt.PackageShlib != nil { |
| if shlib := ctxt.PackageShlib[name]; shlib != "" { |
| return addlibpath(ctxt, "internal", "internal", "", name, shlib, zerofp) |
| } |
| } |
| if ctxt.PackageFile != nil { |
| if pname := ctxt.PackageFile[name]; pname != "" { |
| return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp) |
| } |
| ctxt.Logf("loadinternal: cannot find %s\n", name) |
| return nil |
| } |
|
|
| for _, libdir := range ctxt.Libdir { |
| if ctxt.linkShared { |
| shlibname := filepath.Join(libdir, name+".shlibname") |
| if ctxt.Debugvlog != 0 { |
| ctxt.Logf("searching for %s.a in %s\n", name, shlibname) |
| } |
| if _, err := os.Stat(shlibname); err == nil { |
| return addlibpath(ctxt, "internal", "internal", "", name, shlibname, zerofp) |
| } |
| } |
| pname := filepath.Join(libdir, name+".a") |
| if ctxt.Debugvlog != 0 { |
| ctxt.Logf("searching for %s.a in %s\n", name, pname) |
| } |
| if _, err := os.Stat(pname); err == nil { |
| return addlibpath(ctxt, "internal", "internal", pname, name, "", zerofp) |
| } |
| } |
|
|
| if name == "runtime" { |
| Exitf("error: unable to find runtime.a") |
| } |
| ctxt.Logf("warning: unable to find %s.a\n", name) |
| return nil |
| } |
|
|
| |
| func (ctxt *Link) extld() []string { |
| if len(flagExtld) == 0 { |
| |
| |
| |
| switch buildcfg.GOOS { |
| case "darwin", "freebsd", "openbsd": |
| flagExtld = []string{"clang"} |
| default: |
| flagExtld = []string{"gcc"} |
| } |
| } |
| return flagExtld |
| } |
|
|
| |
| |
| func (ctxt *Link) findLibPathCmd(cmd, libname string) string { |
| extld := ctxt.extld() |
| name, args := extld[0], extld[1:] |
| args = append(args, hostlinkArchArgs(ctxt.Arch)...) |
| args = append(args, cmd) |
| if ctxt.Debugvlog != 0 { |
| ctxt.Logf("%s %v\n", extld, args) |
| } |
| out, err := exec.Command(name, args...).Output() |
| if err != nil { |
| if ctxt.Debugvlog != 0 { |
| ctxt.Logf("not using a %s file because compiler failed\n%v\n%s\n", libname, err, out) |
| } |
| return "none" |
| } |
| return strings.TrimSpace(string(out)) |
| } |
|
|
| |
| |
| func (ctxt *Link) findLibPath(libname string) string { |
| return ctxt.findLibPathCmd("--print-file-name="+libname, libname) |
| } |
|
|
| func (ctxt *Link) loadlib() { |
| var flags uint32 |
| if *flagCheckLinkname { |
| flags |= loader.FlagCheckLinkname |
| } |
| switch *FlagStrictDups { |
| case 0: |
| |
| case 1, 2: |
| flags |= loader.FlagStrictDups |
| default: |
| log.Fatalf("invalid -strictdups flag value %d", *FlagStrictDups) |
| } |
| ctxt.loader = loader.NewLoader(flags, &ctxt.ErrorReporter.ErrorReporter) |
| ctxt.ErrorReporter.SymName = func(s loader.Sym) string { |
| return ctxt.loader.SymName(s) |
| } |
|
|
| |
| i := 0 |
| for ; i < len(ctxt.Library); i++ { |
| lib := ctxt.Library[i] |
| if lib.Shlib == "" { |
| if ctxt.Debugvlog > 1 { |
| ctxt.Logf("autolib: %s (from %s)\n", lib.File, lib.Objref) |
| } |
| loadobjfile(ctxt, lib) |
| } |
| } |
|
|
| |
| if *flagRace { |
| loadinternal(ctxt, "runtime/race") |
| } |
| if *flagMsan { |
| loadinternal(ctxt, "runtime/msan") |
| } |
| if *flagAsan { |
| loadinternal(ctxt, "runtime/asan") |
| } |
| loadinternal(ctxt, "runtime") |
| for ; i < len(ctxt.Library); i++ { |
| lib := ctxt.Library[i] |
| if lib.Shlib == "" { |
| loadobjfile(ctxt, lib) |
| } |
| } |
| |
| |
| |
|
|
| iscgo = ctxt.LibraryByPkg["runtime/cgo"] != nil |
|
|
| |
| |
| ctxt.canUsePlugins = ctxt.LibraryByPkg["plugin"] != nil && iscgo && |
| platform.BuildModeSupported("gc", "plugin", buildcfg.GOOS, buildcfg.GOARCH) |
|
|
| |
| determineLinkMode(ctxt) |
|
|
| if ctxt.LinkMode == LinkExternal && !iscgo && !(buildcfg.GOOS == "darwin" && ctxt.BuildMode != BuildModePlugin && ctxt.Arch.Family == sys.AMD64) { |
| |
| |
| |
| |
| if lib := loadinternal(ctxt, "runtime/cgo"); lib != nil && lib.Shlib == "" { |
| if ctxt.BuildMode == BuildModeShared || ctxt.linkShared { |
| Exitf("cannot implicitly include runtime/cgo in a shared library") |
| } |
| for ; i < len(ctxt.Library); i++ { |
| lib := ctxt.Library[i] |
| if lib.Shlib == "" { |
| loadobjfile(ctxt, lib) |
| } |
| } |
| } |
| } |
|
|
| |
| ctxt.loader.LoadSyms(ctxt.Arch) |
|
|
| |
| for _, lib := range ctxt.Library { |
| if lib.Shlib != "" { |
| if ctxt.Debugvlog > 1 { |
| ctxt.Logf("autolib: %s (from %s)\n", lib.Shlib, lib.Objref) |
| } |
| ldshlibsyms(ctxt, lib.Shlib) |
| } |
| } |
|
|
| |
| ctxt.loadcgodirectives() |
|
|
| |
| hostobjs(ctxt) |
| hostlinksetup(ctxt) |
|
|
| if ctxt.LinkMode == LinkInternal && len(hostobj) != 0 { |
| |
| |
| any := false |
| undefs, froms := ctxt.loader.UndefinedRelocTargets(1) |
| if len(undefs) > 0 { |
| any = true |
| if ctxt.Debugvlog > 1 { |
| ctxt.Logf("loadlib: first unresolved is %s [%d] from %s [%d]\n", |
| ctxt.loader.SymName(undefs[0]), undefs[0], |
| ctxt.loader.SymName(froms[0]), froms[0]) |
| } |
| } |
| if any { |
| if *flagLibGCC == "" { |
| *flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc") |
| } |
| if runtime.GOOS == "freebsd" && strings.HasPrefix(filepath.Base(*flagLibGCC), "libclang_rt.builtins") { |
| |
| |
| |
| |
| *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt") |
| } |
| if runtime.GOOS == "openbsd" && *flagLibGCC == "libgcc.a" { |
| |
| |
| |
| *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt") |
| } |
| if ctxt.HeadType == objabi.Hwindows { |
| loadWindowsHostArchives(ctxt) |
| } |
| if *flagLibGCC != "none" { |
| hostArchive(ctxt, *flagLibGCC) |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| isunresolved := symbolsAreUnresolved(ctxt, []string{"__stack_chk_fail_local"}) |
| if isunresolved[0] { |
| if p := ctxt.findLibPath("libc_nonshared.a"); p != "none" { |
| hostArchive(ctxt, p) |
| } |
| if p := ctxt.findLibPath("libssp_nonshared.a"); p != "none" { |
| hostArchive(ctxt, p) |
| } |
| } |
| } |
| } |
|
|
| loadfips(ctxt) |
|
|
| |
| ctxt.Loaded = true |
|
|
| strictDupMsgCount = ctxt.loader.NStrictDupMsgs() |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| func loadWindowsHostArchives(ctxt *Link) { |
| any := true |
| for i := 0; any && i < 2; i++ { |
| |
| |
| isunresolved := symbolsAreUnresolved(ctxt, []string{"atexit"}) |
| if isunresolved[0] { |
| if p := ctxt.findLibPath("crt2.o"); p != "none" { |
| hostObject(ctxt, "crt2", p) |
| } |
| } |
| if *flagRace { |
| if p := ctxt.findLibPath("libsynchronization.a"); p != "none" { |
| hostArchive(ctxt, p) |
| } |
| } |
| if p := ctxt.findLibPath("libmingwex.a"); p != "none" { |
| hostArchive(ctxt, p) |
| } |
| if p := ctxt.findLibPath("libmingw32.a"); p != "none" { |
| hostArchive(ctxt, p) |
| } |
| |
| |
| if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" { |
| hostArchive(ctxt, p) |
| } |
| any = false |
| undefs, froms := ctxt.loader.UndefinedRelocTargets(1) |
| if len(undefs) > 0 { |
| any = true |
| if ctxt.Debugvlog > 1 { |
| ctxt.Logf("loadWindowsHostArchives: remaining unresolved is %s [%d] from %s [%d]\n", |
| ctxt.loader.SymName(undefs[0]), undefs[0], |
| ctxt.loader.SymName(froms[0]), froms[0]) |
| } |
| } |
| } |
| |
| |
| |
| |
| want := []string{"__CTOR_LIST__", "__DTOR_LIST__"} |
| isunresolved := symbolsAreUnresolved(ctxt, want) |
| for k, w := range want { |
| if isunresolved[k] { |
| sb := ctxt.loader.CreateSymForUpdate(w, 0) |
| sb.SetType(sym.SDATA) |
| sb.AddUint64(ctxt.Arch, 0) |
| sb.SetReachable(true) |
| ctxt.loader.SetAttrSpecial(sb.Sym(), true) |
| } |
| } |
|
|
| |
| |
| if err := loadpe.PostProcessImports(); err != nil { |
| Errorf("%v", err) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } |
|
|
| |
| |
| func (ctxt *Link) loadcgodirectives() { |
| l := ctxt.loader |
| hostObjSyms := make(map[loader.Sym]struct{}) |
| for _, d := range ctxt.cgodata { |
| setCgoAttr(ctxt, d.file, d.pkg, d.directives, hostObjSyms) |
| } |
| ctxt.cgodata = nil |
|
|
| if ctxt.LinkMode == LinkInternal { |
| |
| |
| for symIdx := range hostObjSyms { |
| if l.SymType(symIdx) == sym.SHOSTOBJ { |
| |
| |
| |
| |
| su := l.MakeSymbolUpdater(symIdx) |
| if l.SymExtname(symIdx) != "" && l.SymDynimplib(symIdx) != "" && !(l.AttrCgoExportStatic(symIdx) || l.AttrCgoExportDynamic(symIdx)) { |
| su.SetType(sym.SDYNIMPORT) |
| } else { |
| su.SetType(0) |
| } |
| } |
| } |
| } |
| } |
|
|
| |
| |
| func (ctxt *Link) linksetup() { |
| switch ctxt.BuildMode { |
| case BuildModeCShared, BuildModePlugin: |
| symIdx := ctxt.loader.LookupOrCreateSym("runtime.islibrary", 0) |
| sb := ctxt.loader.MakeSymbolUpdater(symIdx) |
| sb.SetType(sym.SNOPTRDATA) |
| sb.AddUint8(1) |
| case BuildModeCArchive: |
| symIdx := ctxt.loader.LookupOrCreateSym("runtime.isarchive", 0) |
| sb := ctxt.loader.MakeSymbolUpdater(symIdx) |
| sb.SetType(sym.SNOPTRDATA) |
| sb.AddUint8(1) |
| } |
|
|
| |
| if ctxt.HeadType == objabi.Hwindows { |
| Peinit(ctxt) |
| } |
|
|
| if ctxt.LinkMode == LinkExternal { |
| |
| |
| *FlagTextAddr = 0 |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if ctxt.BuildMode == BuildModeExe { |
| if havedynamic == 0 && ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Hsolaris { |
| *FlagD = true |
| } |
| } |
|
|
| if ctxt.LinkMode == LinkExternal && ctxt.Arch.Family == sys.PPC64 && buildcfg.GOOS != "aix" { |
| toc := ctxt.loader.LookupOrCreateSym(".TOC.", 0) |
| sb := ctxt.loader.MakeSymbolUpdater(toc) |
| sb.SetType(sym.SDYNIMPORT) |
| } |
|
|
| |
| |
| |
| if buildcfg.GOOS != "android" { |
| tlsg := ctxt.loader.LookupOrCreateSym("runtime.tlsg", 0) |
| sb := ctxt.loader.MakeSymbolUpdater(tlsg) |
|
|
| |
| |
| if sb.Type() == 0 { |
| sb.SetType(sym.STLSBSS) |
| sb.SetSize(int64(ctxt.Arch.PtrSize)) |
| } else if sb.Type() != sym.SDYNIMPORT { |
| Errorf("runtime declared tlsg variable %v", sb.Type()) |
| } |
| ctxt.loader.SetAttrReachable(tlsg, true) |
| ctxt.Tlsg = tlsg |
| } |
|
|
| var moduledata loader.Sym |
| var mdsb *loader.SymbolBuilder |
| if ctxt.BuildMode == BuildModePlugin { |
| moduledata = ctxt.loader.LookupOrCreateSym("local.pluginmoduledata", 0) |
| mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) |
| ctxt.loader.SetAttrLocal(moduledata, true) |
| } else { |
| moduledata = ctxt.loader.LookupOrCreateSym("runtime.firstmoduledata", 0) |
| mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) |
| } |
| if mdsb.Type() != 0 && mdsb.Type() != sym.SDYNIMPORT { |
| |
| |
| |
| |
| |
| mdsb.SetSize(0) |
|
|
| |
| |
| if ctxt.Arch.Family == sys.ARM { |
| goarm := ctxt.loader.LookupOrCreateSym("runtime.goarm", 0) |
| sb := ctxt.loader.MakeSymbolUpdater(goarm) |
| sb.SetType(sym.SNOPTRDATA) |
| sb.SetSize(0) |
| sb.AddUint8(uint8(buildcfg.GOARM.Version)) |
|
|
| goarmsoftfp := ctxt.loader.LookupOrCreateSym("runtime.goarmsoftfp", 0) |
| sb2 := ctxt.loader.MakeSymbolUpdater(goarmsoftfp) |
| sb2.SetType(sym.SNOPTRDATA) |
| sb2.SetSize(0) |
| if buildcfg.GOARM.SoftFloat { |
| sb2.AddUint8(1) |
| } else { |
| sb2.AddUint8(0) |
| } |
| } |
|
|
| |
| |
| |
| memProfile := ctxt.loader.Lookup("runtime.memProfileInternal", abiInternalVer) |
| if memProfile != 0 && !ctxt.loader.AttrReachable(memProfile) && !ctxt.DynlinkingGo() { |
| memProfSym := ctxt.loader.LookupOrCreateSym("runtime.disableMemoryProfiling", 0) |
| sb := ctxt.loader.MakeSymbolUpdater(memProfSym) |
| sb.SetType(sym.SNOPTRDATA) |
| sb.SetSize(0) |
| sb.AddUint8(1) |
| } |
| } else { |
| |
| |
| moduledata = ctxt.loader.LookupOrCreateSym("local.moduledata", 0) |
| mdsb = ctxt.loader.MakeSymbolUpdater(moduledata) |
| ctxt.loader.SetAttrLocal(moduledata, true) |
| } |
| mdsb.SetType(sym.SMODULEDATA) |
| ctxt.loader.SetAttrReachable(moduledata, true) |
| ctxt.Moduledata = moduledata |
|
|
| if ctxt.Arch == sys.Arch386 && ctxt.HeadType != objabi.Hwindows { |
| if (ctxt.BuildMode == BuildModeCArchive && ctxt.IsELF) || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE || ctxt.DynlinkingGo() { |
| got := ctxt.loader.LookupOrCreateSym("_GLOBAL_OFFSET_TABLE_", 0) |
| sb := ctxt.loader.MakeSymbolUpdater(got) |
| sb.SetType(sym.SDYNIMPORT) |
| ctxt.loader.SetAttrReachable(got, true) |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| ctxt.Library = postorder(ctxt.Library) |
| intlibs := []bool{} |
| for _, lib := range ctxt.Library { |
| intlibs = append(intlibs, isRuntimeDepPkg(lib.Pkg)) |
| } |
| ctxt.Textp = ctxt.loader.AssignTextSymbolOrder(ctxt.Library, intlibs, ctxt.Textp) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func (ctxt *Link) mangleTypeSym() { |
| if ctxt.BuildMode != BuildModeShared && !ctxt.linkShared && ctxt.BuildMode != BuildModePlugin && !ctxt.CanUsePlugins() { |
| return |
| } |
|
|
| ldr := ctxt.loader |
| for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { |
| if !ldr.AttrReachable(s) && !ctxt.linkShared { |
| |
| |
| |
| |
| |
| continue |
| } |
| name := ldr.SymName(s) |
| newName := typeSymbolMangle(name) |
| if newName != name { |
| ldr.SetSymExtname(s, newName) |
|
|
| |
| |
| |
| |
| |
| dup := ldr.Lookup(newName, ldr.SymVersion(s)) |
| if dup != 0 { |
| st := ldr.SymType(s) |
| dt := ldr.SymType(dup) |
| if st == sym.Sxxx && dt != sym.Sxxx { |
| ldr.CopySym(dup, s) |
| } |
| } |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| func typeSymbolMangle(name string) string { |
| isType := strings.HasPrefix(name, "type:") |
| if !isType && !strings.Contains(name, "@") { |
| |
| return name |
| } |
| if strings.HasPrefix(name, "type:runtime.") { |
| return name |
| } |
| if strings.HasPrefix(name, "go:string.") { |
| |
| |
| return name |
| } |
| if len(name) <= 14 && !strings.Contains(name, "@") { |
| return name |
| } |
| if isType { |
| hb := hash.Sum32([]byte(name[5:])) |
| prefix := "type:" |
| if name[5] == '.' { |
| prefix = "type:." |
| } |
| return prefix + base64.StdEncoding.EncodeToString(hb[:6]) |
| } |
| |
| i := strings.IndexByte(name, '[') |
| j := strings.LastIndexByte(name, ']') |
| if j == -1 || j <= i { |
| j = len(name) |
| } |
| hb := hash.Sum32([]byte(name[i+1 : j])) |
| return name[:i+1] + base64.StdEncoding.EncodeToString(hb[:6]) + name[j:] |
| } |
|
|
| |
| |
| |
| |
| func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 { |
| if off&1 != 0 { |
| off++ |
| } |
| bp.MustSeek(off, 0) |
| var buf [SAR_HDR]byte |
| if n, err := io.ReadFull(bp, buf[:]); err != nil { |
| if n == 0 && err != io.EOF { |
| return -1 |
| } |
| return 0 |
| } |
|
|
| a.name = artrim(buf[0:16]) |
| a.date = artrim(buf[16:28]) |
| a.uid = artrim(buf[28:34]) |
| a.gid = artrim(buf[34:40]) |
| a.mode = artrim(buf[40:48]) |
| a.size = artrim(buf[48:58]) |
| a.fmag = artrim(buf[58:60]) |
|
|
| arsize := atolwhex(a.size) |
| if arsize&1 != 0 { |
| arsize++ |
| } |
| return arsize + SAR_HDR |
| } |
|
|
| func loadobjfile(ctxt *Link, lib *sym.Library) { |
| pkg := objabi.PathToPrefix(lib.Pkg) |
|
|
| if ctxt.Debugvlog > 1 { |
| ctxt.Logf("ldobj: %s (%s)\n", lib.File, pkg) |
| } |
| f, err := bio.Open(lib.File) |
| if err != nil { |
| Exitf("cannot open file %s: %v", lib.File, err) |
| } |
| defer f.Close() |
| defer func() { |
| if pkg == "main" && !lib.Main { |
| Exitf("%s: not package main", lib.File) |
| } |
| }() |
|
|
| for i := 0; i < len(ARMAG); i++ { |
| if c, err := f.ReadByte(); err == nil && c == ARMAG[i] { |
| continue |
| } |
|
|
| |
| l := f.MustSeek(0, 2) |
| f.MustSeek(0, 0) |
| ldobj(ctxt, f, lib, l, lib.File, lib.File) |
| return |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| var arhdr ArHdr |
| off := f.Offset() |
| for { |
| l := nextar(f, off, &arhdr) |
| if l == 0 { |
| break |
| } |
| if l < 0 { |
| Exitf("%s: malformed archive", lib.File) |
| } |
| off += l |
|
|
| |
| |
| |
| |
| if arhdr.name == pkgdef { |
| continue |
| } |
|
|
| if arhdr.name == "dynimportfail" { |
| dynimportfail = append(dynimportfail, lib.Pkg) |
| } |
| if arhdr.name == "preferlinkext" { |
| |
| |
| if ctxt.LinkMode == LinkAuto { |
| preferlinkext = append(preferlinkext, lib.Pkg) |
| } |
| } |
|
|
| |
| |
| |
| if len(arhdr.name) < 16 { |
| if ext := filepath.Ext(arhdr.name); ext != ".o" && ext != ".syso" { |
| continue |
| } |
| } |
|
|
| pname := fmt.Sprintf("%s(%s)", lib.File, arhdr.name) |
| l = atolwhex(arhdr.size) |
| ldobj(ctxt, f, lib, l, pname, lib.File) |
| } |
| } |
|
|
| type Hostobj struct { |
| ld func(*Link, *bio.Reader, string, int64, string) |
| pkg string |
| pn string |
| file string |
| off int64 |
| length int64 |
| } |
|
|
| var hostobj []Hostobj |
|
|
| |
| |
| var internalpkg = []string{ |
| "crypto/internal/boring", |
| "crypto/internal/boring/syso", |
| "crypto/x509", |
| "net", |
| "os/user", |
| "runtime/cgo", |
| "runtime/race", |
| "runtime/race/internal/amd64v1", |
| "runtime/race/internal/amd64v3", |
| "runtime/msan", |
| "runtime/asan", |
| } |
|
|
| func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType objabi.HeadType, f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj { |
| isinternal := false |
| for _, intpkg := range internalpkg { |
| if pkg == intpkg { |
| isinternal = true |
| break |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| if headType == objabi.Hdragonfly { |
| if pkg == "net" || pkg == "os/user" { |
| isinternal = false |
| } |
| } |
|
|
| if !isinternal { |
| externalobj = true |
| } |
|
|
| hostobj = append(hostobj, Hostobj{}) |
| h := &hostobj[len(hostobj)-1] |
| h.ld = ld |
| h.pkg = pkg |
| h.pn = pn |
| h.file = file |
| h.off = f.Offset() |
| h.length = length |
| return h |
| } |
|
|
| func hostobjs(ctxt *Link) { |
| if ctxt.LinkMode != LinkInternal { |
| return |
| } |
| var h *Hostobj |
|
|
| for i := 0; i < len(hostobj); i++ { |
| h = &hostobj[i] |
| f, err := bio.Open(h.file) |
| if err != nil { |
| Exitf("cannot reopen %s: %v", h.pn, err) |
| } |
| f.MustSeek(h.off, 0) |
| if h.ld == nil { |
| Errorf("%s: unrecognized object file format", h.pn) |
| continue |
| } |
| h.ld(ctxt, f, h.pkg, h.length, h.pn) |
| if *flagCaptureHostObjs != "" { |
| captureHostObj(h) |
| } |
| f.Close() |
| } |
| } |
|
|
| func hostlinksetup(ctxt *Link) { |
| if ctxt.LinkMode != LinkExternal { |
| return |
| } |
|
|
| |
| |
| |
| debug_s = *FlagS |
| *FlagS = false |
|
|
| |
| if *flagTmpdir == "" { |
| dir, err := os.MkdirTemp("", "go-link-") |
| if err != nil { |
| log.Fatal(err) |
| } |
| *flagTmpdir = dir |
| ownTmpDir = true |
| AtExit(func() { |
| os.RemoveAll(*flagTmpdir) |
| }) |
| } |
|
|
| |
| if err := ctxt.Out.Close(); err != nil { |
| Exitf("error closing output file") |
| } |
| mayberemoveoutfile() |
|
|
| p := filepath.Join(*flagTmpdir, "go.o") |
| if err := ctxt.Out.Open(p); err != nil { |
| Exitf("cannot create %s: %v", p, err) |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| func cleanTimeStamps(files []string) { |
| epocht := time.Unix(0, 0) |
| for _, f := range files { |
| if err := os.Chtimes(f, epocht, epocht); err != nil { |
| Exitf("cannot chtimes %s: %v", f, err) |
| } |
| } |
| } |
|
|
| |
| |
| func (ctxt *Link) hostobjCopy() (paths []string) { |
| var wg sync.WaitGroup |
| sema := make(chan struct{}, runtime.NumCPU()) |
| for i, h := range hostobj { |
| h := h |
| dst := filepath.Join(*flagTmpdir, fmt.Sprintf("%06d.o", i)) |
| paths = append(paths, dst) |
| if ctxt.Debugvlog != 0 { |
| ctxt.Logf("host obj copy: %s from pkg %s -> %s\n", h.pn, h.pkg, dst) |
| } |
|
|
| wg.Add(1) |
| go func() { |
| sema <- struct{}{} |
| defer func() { |
| <-sema |
| wg.Done() |
| }() |
| f, err := os.Open(h.file) |
| if err != nil { |
| Exitf("cannot reopen %s: %v", h.pn, err) |
| } |
| defer f.Close() |
| if _, err := f.Seek(h.off, 0); err != nil { |
| Exitf("cannot seek %s: %v", h.pn, err) |
| } |
|
|
| w, err := os.Create(dst) |
| if err != nil { |
| Exitf("cannot create %s: %v", dst, err) |
| } |
| if _, err := io.CopyN(w, f, h.length); err != nil { |
| Exitf("cannot write %s: %v", dst, err) |
| } |
| if err := w.Close(); err != nil { |
| Exitf("cannot close %s: %v", dst, err) |
| } |
| }() |
| } |
| wg.Wait() |
| return paths |
| } |
|
|
| |
| |
| |
| |
| func writeGDBLinkerScript() string { |
| name := "fix_debug_gdb_scripts.ld" |
| path := filepath.Join(*flagTmpdir, name) |
| src := `SECTIONS |
| { |
| .debug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) : |
| { |
| *(.debug_gdb_scripts) |
| } |
| } |
| INSERT AFTER .debug_types; |
| ` |
| err := os.WriteFile(path, []byte(src), 0666) |
| if err != nil { |
| Errorf("WriteFile %s failed: %v", name, err) |
| } |
| return path |
| } |
|
|
| type machoUpdateFunc func(ctxt *Link, exef *os.File, exem *macho.File, outexe string) error |
|
|
| |
| func (ctxt *Link) archive() { |
| if ctxt.BuildMode != BuildModeCArchive { |
| return |
| } |
|
|
| exitIfErrors() |
|
|
| if *flagExtar == "" { |
| const printProgName = "--print-prog-name=ar" |
| cc := ctxt.extld() |
| *flagExtar = "ar" |
| if linkerFlagSupported(ctxt.Arch, cc[0], "", printProgName) { |
| *flagExtar = ctxt.findExtLinkTool("ar") |
| } |
| } |
|
|
| mayberemoveoutfile() |
|
|
| |
| |
| if err := ctxt.Out.Close(); err != nil { |
| Exitf("error closing %v", *flagOutfile) |
| } |
|
|
| argv := []string{*flagExtar, "-q", "-c", "-s"} |
| if ctxt.HeadType == objabi.Haix { |
| argv = append(argv, "-X64") |
| } |
| godotopath := filepath.Join(*flagTmpdir, "go.o") |
| cleanTimeStamps([]string{godotopath}) |
| hostObjCopyPaths := ctxt.hostobjCopy() |
| cleanTimeStamps(hostObjCopyPaths) |
|
|
| argv = append(argv, *flagOutfile) |
| argv = append(argv, godotopath) |
| argv = append(argv, hostObjCopyPaths...) |
|
|
| if ctxt.Debugvlog != 0 { |
| ctxt.Logf("archive: %s\n", strings.Join(argv, " ")) |
| } |
|
|
| |
| |
| |
| |
| |
| if syscallExecSupported && !ownTmpDir { |
| runAtExitFuncs() |
| ctxt.execArchive(argv) |
| panic("should not get here") |
| } |
|
|
| |
| if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil { |
| Exitf("running %s failed: %v\n%s", argv[0], err, out) |
| } |
| } |
|
|
| func (ctxt *Link) hostlink() { |
| if ctxt.LinkMode != LinkExternal || nerrors > 0 { |
| return |
| } |
| if ctxt.BuildMode == BuildModeCArchive { |
| return |
| } |
|
|
| var argv []string |
| argv = append(argv, ctxt.extld()...) |
| argv = append(argv, hostlinkArchArgs(ctxt.Arch)...) |
|
|
| if *FlagS || debug_s { |
| if ctxt.HeadType == objabi.Hdarwin { |
| |
| |
| |
| } else { |
| argv = append(argv, "-s") |
| } |
| } else if *FlagW { |
| if !ctxt.IsAIX() && !ctxt.IsSolaris() { |
| argv = append(argv, "-Wl,-S") |
| } |
| } |
|
|
| |
| |
| combineDwarf := ctxt.IsDarwin() && !*FlagW && machoPlatform == PLATFORM_MACOS |
|
|
| var isMSVC bool |
| wlPrefix := "-Wl,--" |
|
|
| switch ctxt.HeadType { |
| case objabi.Hdarwin: |
| if combineDwarf { |
| |
| |
| argv = append(argv, "-Wl,-headerpad,1144") |
| } |
| if ctxt.DynlinkingGo() && buildcfg.GOOS != "ios" { |
| |
| |
| |
| |
| |
| argv = append(argv, "-Wl,-flat_namespace", "-Wl,-bind_at_load") |
| } |
| if !combineDwarf { |
| argv = append(argv, "-Wl,-S") |
| if debug_s { |
| |
| |
| |
| argv = append(argv, "-Wl,-x") |
| } |
| } |
| if *flagHostBuildid == "none" { |
| argv = append(argv, "-Wl,-no_uuid") |
| } |
| case objabi.Hopenbsd: |
| argv = append(argv, "-pthread") |
| if ctxt.BuildMode != BuildModePIE { |
| argv = append(argv, "-Wl,-nopie") |
| } |
| if linkerFlagSupported(ctxt.Arch, argv[0], "", "-Wl,-z,nobtcfi") { |
| |
| |
| argv = append(argv, "-Wl,-z,nobtcfi") |
| } |
| if ctxt.Arch.InFamily(sys.ARM64) { |
| |
| |
| |
| argv = append(argv, "-Wl,--no-execute-only") |
| } |
| case objabi.Hwindows: |
| isMSVC = ctxt.isMSVC() |
| if isMSVC { |
| |
| |
| |
| |
| wlPrefix = "-Wl,-" |
| } |
|
|
| if windowsgui { |
| argv = append(argv, "-mwindows") |
| } else { |
| argv = append(argv, "-mconsole") |
| } |
| |
| |
|
|
| argv = append(argv, wlPrefix+"tsaware") |
|
|
| |
| argv = append(argv, wlPrefix+"nxcompat") |
|
|
| if !isMSVC { |
| argv = append(argv, fmt.Sprintf("-Wl,--major-os-version=%d", PeMinimumTargetMajorVersion)) |
| argv = append(argv, fmt.Sprintf("-Wl,--minor-os-version=%d", PeMinimumTargetMinorVersion)) |
| argv = append(argv, fmt.Sprintf("-Wl,--major-subsystem-version=%d", PeMinimumTargetMajorVersion)) |
| argv = append(argv, fmt.Sprintf("-Wl,--minor-subsystem-version=%d", PeMinimumTargetMinorVersion)) |
| } |
| case objabi.Haix: |
| argv = append(argv, "-pthread") |
| |
| |
| argv = append(argv, "-Wl,-bnoobjreorder") |
| |
| |
| argv = append(argv, "-mcmodel=large") |
| argv = append(argv, "-Wl,-bbigtoc") |
| } |
|
|
| |
| |
| |
| if ctxt.IsPPC64() && ctxt.IsElf() && buildcfg.GOPPC64 >= 10 { |
| if !linkerFlagSupported(ctxt.Arch, argv[0], "", "-mcpu=power10") { |
| Exitf("The external toolchain does not support -mcpu=power10. " + |
| " This is required to externally link GOPPC64 >= power10") |
| } |
| } |
|
|
| |
| addASLRargs := func(argv []string, val bool) []string { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| var dbopt string |
| var heopt string |
| dbon := wlPrefix + "dynamicbase" |
| heon := wlPrefix + "high-entropy-va" |
| dboff := wlPrefix + "disable-dynamicbase" |
| heoff := wlPrefix + "disable-high-entropy-va" |
| if isMSVC { |
| heon = wlPrefix + "highentropyva" |
| heoff = wlPrefix + "highentropyva:no" |
| dboff = wlPrefix + "dynamicbase:no" |
| } |
| if val { |
| dbopt = dbon |
| heopt = heon |
| } else { |
| |
| newer := linkerFlagSupported(ctxt.Arch, argv[0], "", dboff) |
| if newer { |
| |
| dbopt = dboff |
| heopt = heoff |
| } else { |
| |
| |
| dbopt = "" |
| heopt = "" |
| } |
| } |
| if dbopt != "" { |
| argv = append(argv, dbopt) |
| } |
| |
| if ctxt.Arch.PtrSize >= 8 && heopt != "" { |
| argv = append(argv, heopt) |
| } |
| return argv |
| } |
|
|
| switch ctxt.BuildMode { |
| case BuildModeExe: |
| if ctxt.HeadType == objabi.Hdarwin { |
| if machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64() { |
| argv = append(argv, "-Wl,-no_pie") |
| } |
| } |
| if *flagRace && ctxt.HeadType == objabi.Hwindows { |
| |
| |
| |
| |
| argv = addASLRargs(argv, false) |
| } |
| case BuildModePIE: |
| switch ctxt.HeadType { |
| case objabi.Hdarwin, objabi.Haix: |
| case objabi.Hwindows: |
| if *flagAslr && *flagRace { |
| |
| |
| |
| *flagAslr = false |
| } |
| argv = addASLRargs(argv, *flagAslr) |
| default: |
| |
| if ctxt.UseRelro() { |
| argv = append(argv, "-Wl,-z,relro") |
| } |
| argv = append(argv, "-pie") |
| } |
| case BuildModeCShared: |
| if ctxt.HeadType == objabi.Hdarwin { |
| argv = append(argv, "-dynamiclib") |
| } else { |
| if ctxt.UseRelro() { |
| argv = append(argv, "-Wl,-z,relro") |
| } |
| argv = append(argv, "-shared") |
| if ctxt.HeadType == objabi.Hwindows { |
| argv = addASLRargs(argv, *flagAslr) |
| } else { |
| |
| |
| argv = append(argv, "-Wl,-z,nodelete") |
| |
| argv = append(argv, "-Wl,-Bsymbolic") |
| } |
| } |
| case BuildModeShared: |
| if ctxt.UseRelro() { |
| argv = append(argv, "-Wl,-z,relro") |
| } |
| argv = append(argv, "-shared") |
| case BuildModePlugin: |
| if ctxt.HeadType == objabi.Hdarwin { |
| argv = append(argv, "-dynamiclib") |
| } else { |
| if ctxt.UseRelro() { |
| argv = append(argv, "-Wl,-z,relro") |
| } |
| argv = append(argv, "-shared") |
| } |
| } |
|
|
| var altLinker string |
| if ctxt.IsELF && (ctxt.DynlinkingGo() || *flagBindNow) { |
| |
| |
| |
| |
| |
| argv = append(argv, "-Wl,-z,now") |
| } |
|
|
| if ctxt.IsELF && ctxt.DynlinkingGo() { |
| |
| |
| |
| argv = append(argv, "-Wl,-z,nocopyreloc") |
|
|
| if buildcfg.GOOS == "android" { |
| |
| altLinker = "lld" |
| } |
|
|
| if ctxt.Arch.InFamily(sys.ARM64) && buildcfg.GOOS == "linux" { |
| |
| |
| |
| |
| |
| altLinker = "gold" |
|
|
| |
| |
| |
| name, args := flagExtld[0], flagExtld[1:] |
| args = append(args, "-fuse-ld=gold", "-Wl,--version") |
| cmd := exec.Command(name, args...) |
| if out, err := cmd.CombinedOutput(); err == nil { |
| if !bytes.Contains(out, []byte("GNU gold")) { |
| log.Fatalf("ARM64 external linker must be gold (issue #15696, 22040), but is not: %s", out) |
| } |
| } |
| } |
| } |
| if ctxt.Arch.Family == sys.ARM64 && buildcfg.GOOS == "freebsd" { |
| |
| altLinker = "bfd" |
|
|
| |
| name, args := flagExtld[0], flagExtld[1:] |
| args = append(args, "-fuse-ld=bfd", "-Wl,--version") |
| cmd := exec.Command(name, args...) |
| if out, err := cmd.CombinedOutput(); err == nil { |
| if !bytes.Contains(out, []byte("GNU ld")) { |
| log.Fatalf("ARM64 external linker must be ld.bfd (issue #35197), please install devel/binutils") |
| } |
| } |
| } |
| if altLinker != "" { |
| argv = append(argv, "-fuse-ld="+altLinker) |
| } |
|
|
| if ctxt.IsELF && linkerFlagSupported(ctxt.Arch, argv[0], "", "-Wl,--build-id=0x1234567890abcdef") { |
| if len(buildinfo) > 0 { |
| argv = append(argv, fmt.Sprintf("-Wl,--build-id=0x%x", buildinfo)) |
| } else if *flagHostBuildid == "none" { |
| argv = append(argv, "-Wl,--build-id=none") |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| outopt := *flagOutfile |
| if buildcfg.GOOS == "windows" && runtime.GOOS == "windows" && filepath.Ext(outopt) == "" { |
| outopt += "." |
| } |
| argv = append(argv, "-o") |
| argv = append(argv, outopt) |
|
|
| if rpath.val != "" { |
| argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath.val)) |
| } |
|
|
| if *flagInterpreter != "" { |
| |
| |
| |
| |
| argv = append(argv, fmt.Sprintf("-Wl,--dynamic-linker,%s", *flagInterpreter)) |
| } |
|
|
| |
| switch { |
| case ctxt.IsELF: |
| if ctxt.DynlinkingGo() || ctxt.BuildMode == BuildModeCShared || !linkerFlagSupported(ctxt.Arch, argv[0], altLinker, "-Wl,--export-dynamic-symbol=main") { |
| argv = append(argv, "-rdynamic") |
| } else { |
| var exports []string |
| ctxt.loader.ForAllCgoExportDynamic(func(s loader.Sym) { |
| exports = append(exports, "-Wl,--export-dynamic-symbol="+ctxt.loader.SymExtname(s)) |
| }) |
| sort.Strings(exports) |
| argv = append(argv, exports...) |
| } |
| case ctxt.IsAIX(): |
| fileName := xcoffCreateExportFile(ctxt) |
| argv = append(argv, "-Wl,-bE:"+fileName) |
| case ctxt.IsWindows() && !slices.Contains(flagExtldflags, wlPrefix+"export-all-symbols"): |
| fileName := peCreateExportFile(ctxt, filepath.Base(outopt)) |
| prefix := "" |
| if isMSVC { |
| prefix = "-Wl,-def:" |
| } |
| argv = append(argv, prefix+fileName) |
| } |
|
|
| const unusedArguments = "-Qunused-arguments" |
| if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, unusedArguments) { |
| argv = append(argv, unusedArguments) |
| } |
|
|
| if ctxt.IsWindows() { |
| |
| |
| |
| |
| const noTimeStamp = "-Wl,--no-insert-timestamp" |
| if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, noTimeStamp) { |
| argv = append(argv, noTimeStamp) |
| } |
| } |
|
|
| const compressDWARF = "-Wl,--compress-debug-sections=zlib" |
| if ctxt.compressDWARF && linkerFlagSupported(ctxt.Arch, argv[0], altLinker, compressDWARF) { |
| argv = append(argv, compressDWARF) |
| } |
|
|
| hostObjCopyPaths := ctxt.hostobjCopy() |
| cleanTimeStamps(hostObjCopyPaths) |
| godotopath := filepath.Join(*flagTmpdir, "go.o") |
| cleanTimeStamps([]string{godotopath}) |
|
|
| argv = append(argv, godotopath) |
| argv = append(argv, hostObjCopyPaths...) |
| if ctxt.HeadType == objabi.Haix { |
| |
| |
| argv = append(argv, "-nostartfiles") |
| argv = append(argv, "/lib/crt0_64.o") |
|
|
| extld := ctxt.extld() |
| name, args := extld[0], extld[1:] |
| |
| getPathFile := func(file string) string { |
| args := append(args, "-maix64", "--print-file-name="+file) |
| out, err := exec.Command(name, args...).CombinedOutput() |
| if err != nil { |
| log.Fatalf("running %s failed: %v\n%s", extld, err, out) |
| } |
| return strings.Trim(string(out), "\n") |
| } |
| |
| |
| |
| crtcxa := getPathFile("crtcxa_64.o") |
| if !filepath.IsAbs(crtcxa) { |
| crtcxa = getPathFile("crtcxa.o") |
| } |
| crtdbase := getPathFile("crtdbase_64.o") |
| if !filepath.IsAbs(crtdbase) { |
| crtdbase = getPathFile("crtdbase.o") |
| } |
| argv = append(argv, crtcxa) |
| argv = append(argv, crtdbase) |
| } |
|
|
| if ctxt.linkShared { |
| seenDirs := make(map[string]bool) |
| seenLibs := make(map[string]bool) |
| addshlib := func(path string) { |
| dir, base := filepath.Split(path) |
| if !seenDirs[dir] { |
| argv = append(argv, "-L"+dir) |
| if !rpath.set { |
| argv = append(argv, "-Wl,-rpath="+dir) |
| } |
| seenDirs[dir] = true |
| } |
| base = strings.TrimSuffix(base, ".so") |
| base = strings.TrimPrefix(base, "lib") |
| if !seenLibs[base] { |
| argv = append(argv, "-l"+base) |
| seenLibs[base] = true |
| } |
| } |
| for _, shlib := range ctxt.Shlibs { |
| addshlib(shlib.Path) |
| for _, dep := range shlib.Deps { |
| if dep == "" { |
| continue |
| } |
| libpath := findshlib(ctxt, dep) |
| if libpath != "" { |
| addshlib(libpath) |
| } |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| checkStatic := func(arg string) { |
| if ctxt.IsELF && arg == "-static" { |
| for i := range argv { |
| if argv[i] == "-rdynamic" || strings.HasPrefix(argv[i], "-Wl,--dynamic-linker,") { |
| argv[i] = "-static" |
| } |
| } |
| } |
| } |
|
|
| for _, p := range ldflag { |
| argv = append(argv, p) |
| checkStatic(p) |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| if ctxt.BuildMode == BuildModeExe && !ctxt.linkShared && !(ctxt.IsDarwin() && ctxt.IsARM64()) { |
| |
| for _, nopie := range []string{"-no-pie", "-nopie"} { |
| if linkerFlagSupported(ctxt.Arch, argv[0], altLinker, nopie) { |
| argv = append(argv, nopie) |
| break |
| } |
| } |
| } |
|
|
| for _, p := range flagExtldflags { |
| argv = append(argv, p) |
| checkStatic(p) |
| } |
| if ctxt.HeadType == objabi.Hwindows { |
| |
| |
| extld := ctxt.extld() |
| name, args := extld[0], extld[1:] |
| args = append(args, trimLinkerArgv(flagExtldflags)...) |
| args = append(args, "-Wl,--version") |
| cmd := exec.Command(name, args...) |
| usingLLD := false |
| if out, err := cmd.CombinedOutput(); err == nil { |
| if bytes.Contains(out, []byte("LLD ")) { |
| usingLLD = true |
| } |
| } |
|
|
| |
| |
| if !usingLLD { |
| p := writeGDBLinkerScript() |
| argv = append(argv, "-Wl,-T,"+p) |
| } |
| if *flagRace { |
| |
| |
| |
| |
| if isMSVC || ctxt.findLibPath("libsynchronization.a") != "libsynchronization.a" { |
| argv = append(argv, "-lsynchronization") |
| } |
| } |
| if !isMSVC { |
| |
| |
| argv = append(argv, "-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group") |
| } |
| argv = append(argv, peimporteddlls()...) |
| } |
|
|
| argv = ctxt.passLongArgsInResponseFile(argv, altLinker) |
|
|
| if ctxt.Debugvlog != 0 { |
| ctxt.Logf("host link:") |
| for _, v := range argv { |
| ctxt.Logf(" %q", v) |
| } |
| ctxt.Logf("\n") |
| } |
|
|
| cmd := exec.Command(argv[0], argv[1:]...) |
| out, err := cmd.CombinedOutput() |
| if err != nil { |
| Exitf("running %s failed: %v\n%s\n%s", argv[0], err, cmd, out) |
| } |
|
|
| |
| |
| var save [][]byte |
| var skipLines int |
| for _, line := range bytes.SplitAfter(out, []byte("\n")) { |
| |
| if bytes.Contains(line, []byte("ld: warning: text-based stub file")) { |
| continue |
| } |
|
|
| if skipLines > 0 { |
| skipLines-- |
| continue |
| } |
|
|
| |
| if bytes.Contains(line, []byte("ld: 0711-783")) { |
| skipLines = 2 |
| continue |
| } |
|
|
| save = append(save, line) |
| } |
| out = bytes.Join(save, nil) |
|
|
| if len(out) > 0 { |
| |
| |
| if ctxt.IsDarwin() && ctxt.IsAMD64() { |
| const noPieWarning = "ld: warning: -no_pie is deprecated when targeting new OS versions\n" |
| if i := bytes.Index(out, []byte(noPieWarning)); i >= 0 { |
| |
| out = append(out[:i], out[i+len(noPieWarning):]...) |
| } |
| } |
| if ctxt.IsDarwin() { |
| const bindAtLoadWarning = "ld: warning: -bind_at_load is deprecated on macOS\n" |
| if i := bytes.Index(out, []byte(bindAtLoadWarning)); i >= 0 { |
| |
| |
| |
| |
| out = append(out[:i], out[i+len(bindAtLoadWarning):]...) |
| } |
| } |
| ctxt.Logf("%s", out) |
| } |
|
|
| |
| |
| updateMachoOutFile := func(op string, updateFunc machoUpdateFunc) { |
| |
| rewrittenOutput := *flagOutfile + "~" |
| exef, err := os.Open(*flagOutfile) |
| if err != nil { |
| Exitf("%s: %s failed: %v", os.Args[0], op, err) |
| } |
| defer exef.Close() |
| exem, err := macho.NewFile(exef) |
| if err != nil { |
| Exitf("%s: parsing Mach-O header failed: %v", os.Args[0], err) |
| } |
| if err := updateFunc(ctxt, exef, exem, rewrittenOutput); err != nil { |
| Exitf("%s: %s failed: %v", os.Args[0], op, err) |
| } |
| os.Remove(*flagOutfile) |
| if err := os.Rename(rewrittenOutput, *flagOutfile); err != nil { |
| Exitf("%s: %v", os.Args[0], err) |
| } |
| } |
|
|
| uuidUpdated := false |
| if combineDwarf { |
| |
| dsymutilCmd := ctxt.findExtLinkTool("dsymutil") |
| stripCmd := ctxt.findExtLinkTool("strip") |
|
|
| dsym := filepath.Join(*flagTmpdir, "go.dwarf") |
| cmd := exec.Command(dsymutilCmd, "-f", *flagOutfile, "-o", dsym) |
| |
| |
| |
| |
| |
| dsymDir := filepath.Join(*flagTmpdir, "dsymutil") |
| err := os.MkdirAll(dsymDir, 0777) |
| if err != nil { |
| Exitf("fail to create temp dir: %v", err) |
| } |
| cmd.Env = append(os.Environ(), "DSYMUTIL_REPRODUCER_PATH="+dsymDir) |
| if ctxt.Debugvlog != 0 { |
| ctxt.Logf("host link dsymutil:") |
| for _, v := range cmd.Args { |
| ctxt.Logf(" %q", v) |
| } |
| ctxt.Logf("\n") |
| } |
| if out, err := cmd.CombinedOutput(); err != nil { |
| Exitf("%s: running dsymutil failed: %v\n%s\n%s", os.Args[0], err, cmd, out) |
| } |
| |
| |
| var stripArgs = []string{"-S"} |
| if debug_s { |
| |
| |
| |
| stripArgs = append(stripArgs, "-x") |
| } |
| stripArgs = append(stripArgs, *flagOutfile) |
| if ctxt.Debugvlog != 0 { |
| ctxt.Logf("host link strip: %q", stripCmd) |
| for _, v := range stripArgs { |
| ctxt.Logf(" %q", v) |
| } |
| ctxt.Logf("\n") |
| } |
| cmd = exec.Command(stripCmd, stripArgs...) |
| if out, err := cmd.CombinedOutput(); err != nil { |
| Exitf("%s: running strip failed: %v\n%s\n%s", os.Args[0], err, cmd, out) |
| } |
| |
| if _, err := os.Stat(dsym); err == nil { |
| updateMachoOutFile("combining dwarf", |
| func(ctxt *Link, exef *os.File, exem *macho.File, outexe string) error { |
| return machoCombineDwarf(ctxt, exef, exem, dsym, outexe) |
| }) |
| uuidUpdated = true |
| } |
| } |
| if ctxt.IsDarwin() && !uuidUpdated && len(buildinfo) > 0 { |
| updateMachoOutFile("rewriting uuid", |
| func(ctxt *Link, exef *os.File, exem *macho.File, outexe string) error { |
| return machoRewriteUuid(ctxt, exef, exem, outexe) |
| }) |
| } |
| hostlinkfips(ctxt, *flagOutfile, *flagFipso) |
| if ctxt.NeedCodeSign() { |
| err := machoCodeSign(ctxt, *flagOutfile) |
| if err != nil { |
| Exitf("%s: code signing failed: %v", os.Args[0], err) |
| } |
| } |
| } |
|
|
| |
| |
| func (ctxt *Link) passLongArgsInResponseFile(argv []string, altLinker string) []string { |
| c := 0 |
| for _, arg := range argv { |
| c += len(arg) |
| } |
|
|
| if c < sys.ExecArgLengthLimit { |
| return argv |
| } |
|
|
| |
| response := filepath.Join(*flagTmpdir, "response") |
| if err := os.WriteFile(response, nil, 0644); err != nil { |
| log.Fatalf("failed while testing response file: %v", err) |
| } |
| if !linkerFlagSupported(ctxt.Arch, argv[0], altLinker, "@"+response) { |
| if ctxt.Debugvlog != 0 { |
| ctxt.Logf("not using response file because linker does not support one") |
| } |
| return argv |
| } |
|
|
| var buf bytes.Buffer |
| for _, arg := range argv[1:] { |
| |
| fmt.Fprintf(&buf, "%q\n", arg) |
| } |
| if err := os.WriteFile(response, buf.Bytes(), 0644); err != nil { |
| log.Fatalf("failed while writing response file: %v", err) |
| } |
| if ctxt.Debugvlog != 0 { |
| ctxt.Logf("response file %s contents:\n%s", response, buf.Bytes()) |
| } |
| return []string{ |
| argv[0], |
| "@" + response, |
| } |
| } |
|
|
| var createTrivialCOnce sync.Once |
|
|
| func linkerFlagSupported(arch *sys.Arch, linker, altLinker, flag string) bool { |
| createTrivialCOnce.Do(func() { |
| src := filepath.Join(*flagTmpdir, "trivial.c") |
| if err := os.WriteFile(src, []byte("int main() { return 0; }"), 0666); err != nil { |
| Errorf("WriteFile trivial.c failed: %v", err) |
| } |
| }) |
|
|
| flags := hostlinkArchArgs(arch) |
|
|
| moreFlags := trimLinkerArgv(append(ldflag, flagExtldflags...)) |
| flags = append(flags, moreFlags...) |
|
|
| if altLinker != "" { |
| flags = append(flags, "-fuse-ld="+altLinker) |
| } |
| trivialPath := filepath.Join(*flagTmpdir, "trivial.c") |
| outPath := filepath.Join(*flagTmpdir, "a.out") |
| flags = append(flags, "-o", outPath, flag, trivialPath) |
|
|
| cmd := exec.Command(linker, flags...) |
| cmd.Env = append([]string{"LC_ALL=C"}, os.Environ()...) |
| out, err := cmd.CombinedOutput() |
| |
| |
| return err == nil && !bytes.Contains(out, []byte("unrecognized")) && !bytes.Contains(out, []byte("unknown")) |
| } |
|
|
| |
| |
| func trimLinkerArgv(argv []string) []string { |
| flagsWithNextArgSkip := []string{ |
| "-F", |
| "-l", |
| "-L", |
| "-framework", |
| "-Wl,-framework", |
| "-Wl,-rpath", |
| "-Wl,-undefined", |
| } |
| flagsWithNextArgKeep := []string{ |
| "-arch", |
| "-isysroot", |
| "--sysroot", |
| "-target", |
| "--target", |
| } |
| prefixesToKeep := []string{ |
| "-f", |
| "-m", |
| "-p", |
| "-Wl,", |
| "-arch", |
| "-isysroot", |
| "--sysroot", |
| "-target", |
| "--target", |
| } |
|
|
| var flags []string |
| keep := false |
| skip := false |
| for _, f := range argv { |
| if keep { |
| flags = append(flags, f) |
| keep = false |
| } else if skip { |
| skip = false |
| } else if f == "" || f[0] != '-' { |
| } else if slices.Contains(flagsWithNextArgSkip, f) { |
| skip = true |
| } else if slices.Contains(flagsWithNextArgKeep, f) { |
| flags = append(flags, f) |
| keep = true |
| } else { |
| for _, p := range prefixesToKeep { |
| if strings.HasPrefix(f, p) { |
| flags = append(flags, f) |
| break |
| } |
| } |
| } |
| } |
| return flags |
| } |
|
|
| |
| |
| func hostlinkArchArgs(arch *sys.Arch) []string { |
| switch arch.Family { |
| case sys.I386: |
| return []string{"-m32"} |
| case sys.AMD64: |
| if buildcfg.GOOS == "darwin" { |
| return []string{"-arch", "x86_64", "-m64"} |
| } |
| return []string{"-m64"} |
| case sys.S390X: |
| return []string{"-m64"} |
| case sys.ARM: |
| return []string{"-marm"} |
| case sys.ARM64: |
| if buildcfg.GOOS == "darwin" { |
| return []string{"-arch", "arm64"} |
| } |
| case sys.Loong64: |
| return []string{"-mabi=lp64d"} |
| case sys.MIPS64: |
| return []string{"-mabi=64"} |
| case sys.MIPS: |
| return []string{"-mabi=32"} |
| case sys.PPC64: |
| if buildcfg.GOOS == "aix" { |
| return []string{"-maix64"} |
| } else { |
| return []string{"-m64"} |
| } |
|
|
| } |
| return nil |
| } |
|
|
| var wantHdr = objabi.HeaderString() |
|
|
| |
| |
| |
| func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, file string) *Hostobj { |
| pkg := objabi.PathToPrefix(lib.Pkg) |
|
|
| eof := f.Offset() + length |
| start := f.Offset() |
| c1 := bgetc(f) |
| c2 := bgetc(f) |
| c3 := bgetc(f) |
| c4 := bgetc(f) |
| f.MustSeek(start, 0) |
|
|
| unit := &sym.CompilationUnit{Lib: lib} |
| lib.Units = append(lib.Units, unit) |
|
|
| magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) |
| if magic == 0x7f454c46 { |
| ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { |
| textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn, ehdr.Flags) |
| if err != nil { |
| Errorf("%v", err) |
| return |
| } |
| ehdr.Flags = flags |
| ctxt.Textp = append(ctxt.Textp, textp...) |
| } |
| return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file) |
| } |
|
|
| if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe { |
| ldmacho := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { |
| textp, err := loadmacho.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn) |
| if err != nil { |
| Errorf("%v", err) |
| return |
| } |
| ctxt.Textp = append(ctxt.Textp, textp...) |
| } |
| return ldhostobj(ldmacho, ctxt.HeadType, f, pkg, length, pn, file) |
| } |
|
|
| switch c1<<8 | c2 { |
| case 0x4c01, |
| 0x6486, |
| 0xc401, |
| 0x64aa: |
| ldpe := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { |
| ls, err := loadpe.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn) |
| if err != nil { |
| Errorf("%v", err) |
| return |
| } |
| if len(ls.Resources) != 0 { |
| setpersrc(ctxt, ls.Resources) |
| } |
| if ls.PData != 0 { |
| sehp.pdata = append(sehp.pdata, ls.PData) |
| } |
| if ls.XData != 0 { |
| sehp.xdata = append(sehp.xdata, ls.XData) |
| } |
| ctxt.Textp = append(ctxt.Textp, ls.Textp...) |
| } |
| return ldhostobj(ldpe, ctxt.HeadType, f, pkg, length, pn, file) |
| } |
|
|
| if c1 == 0x01 && (c2 == 0xD7 || c2 == 0xF7) { |
| ldxcoff := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { |
| textp, err := loadxcoff.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn) |
| if err != nil { |
| Errorf("%v", err) |
| return |
| } |
| ctxt.Textp = append(ctxt.Textp, textp...) |
| } |
| return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file) |
| } |
|
|
| if c1 != 'g' || c2 != 'o' || c3 != ' ' || c4 != 'o' { |
| |
| |
| |
| unknownObjFormat = true |
| return ldhostobj(nil, ctxt.HeadType, f, pkg, length, pn, file) |
| } |
|
|
| |
| line, err := f.ReadString('\n') |
| if err != nil { |
| Errorf("truncated object file: %s: %v", pn, err) |
| return nil |
| } |
|
|
| if !strings.HasPrefix(line, "go object ") { |
| if strings.HasSuffix(pn, ".go") { |
| Exitf("%s: uncompiled .go source file", pn) |
| return nil |
| } |
|
|
| if line == ctxt.Arch.Name { |
| |
| Errorf("%s: stale object file", pn) |
| return nil |
| } |
|
|
| Errorf("%s: not an object file: @%d %q", pn, start, line) |
| return nil |
| } |
|
|
| |
| if line != wantHdr { |
| Errorf("%s: linked object header mismatch:\nhave %q\nwant %q\n", pn, line, wantHdr) |
| } |
|
|
| |
| |
| |
| |
| |
| |
|
|
| import0 := f.Offset() |
|
|
| c1 = '\n' |
| c2 = bgetc(f) |
| c3 = bgetc(f) |
| markers := 0 |
| for { |
| if c1 == '\n' { |
| if markers%2 == 0 && c2 == '!' && c3 == '\n' { |
| break |
| } |
| if c2 == '$' && c3 == '$' { |
| markers++ |
| } |
| } |
|
|
| c1 = c2 |
| c2 = c3 |
| c3 = bgetc(f) |
| if c3 == -1 { |
| Errorf("truncated object file: %s", pn) |
| return nil |
| } |
| } |
|
|
| import1 := f.Offset() |
|
|
| f.MustSeek(import0, 0) |
| ldpkg(ctxt, f, lib, import1-import0-2, pn) |
| f.MustSeek(import1, 0) |
|
|
| fingerprint := ctxt.loader.Preload(ctxt.IncVersion(), f, lib, unit, eof-f.Offset()) |
| if !fingerprint.IsZero() { |
| |
| |
| |
| |
| |
| if lib.Fingerprint.IsZero() { |
| lib.Fingerprint = fingerprint |
| } |
| checkFingerprint(lib, fingerprint, lib.Srcref, lib.Fingerprint) |
| } |
|
|
| addImports(ctxt, lib, pn) |
| return nil |
| } |
|
|
| |
| |
| |
| |
| func symbolsAreUnresolved(ctxt *Link, want []string) []bool { |
| returnAllUndefs := -1 |
| undefs, _ := ctxt.loader.UndefinedRelocTargets(returnAllUndefs) |
| seen := make(map[loader.Sym]struct{}) |
| rval := make([]bool, len(want)) |
| wantm := make(map[string]int) |
| for k, w := range want { |
| wantm[w] = k |
| } |
| count := 0 |
| for _, s := range undefs { |
| if _, ok := seen[s]; ok { |
| continue |
| } |
| seen[s] = struct{}{} |
| if k, ok := wantm[ctxt.loader.SymName(s)]; ok { |
| rval[k] = true |
| count++ |
| if count == len(want) { |
| return rval |
| } |
| } |
| } |
| return rval |
| } |
|
|
| |
| |
| |
| func hostObject(ctxt *Link, objname string, path string) { |
| if ctxt.Debugvlog > 1 { |
| ctxt.Logf("hostObject(%s)\n", path) |
| } |
| objlib := sym.Library{ |
| Pkg: objname, |
| } |
| f, err := bio.Open(path) |
| if err != nil { |
| Exitf("cannot open host object %q file %s: %v", objname, path, err) |
| } |
| defer f.Close() |
| h := ldobj(ctxt, f, &objlib, 0, path, path) |
| if h.ld == nil { |
| Exitf("unrecognized object file format in %s", path) |
| } |
| h.file = path |
| h.length = f.MustSeek(0, 2) |
| f.MustSeek(h.off, 0) |
| h.ld(ctxt, f, h.pkg, h.length, h.pn) |
| if *flagCaptureHostObjs != "" { |
| captureHostObj(h) |
| } |
| } |
|
|
| func checkFingerprint(lib *sym.Library, libfp goobj.FingerprintType, src string, srcfp goobj.FingerprintType) { |
| if libfp != srcfp { |
| Exitf("fingerprint mismatch: %s has %x, import from %s expecting %x", lib, libfp, src, srcfp) |
| } |
| } |
|
|
| func readelfsymboldata(ctxt *Link, f *elf.File, sym *elf.Symbol) []byte { |
| data := make([]byte, sym.Size) |
| sect := f.Sections[sym.Section] |
| if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE { |
| Errorf("reading %s from non-data section", sym.Name) |
| } |
| n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr)) |
| if uint64(n) != sym.Size { |
| Errorf("reading contents of %s: %v", sym.Name, err) |
| } |
| return data |
| } |
|
|
| func readwithpad(r io.Reader, sz int32) ([]byte, error) { |
| data := make([]byte, Rnd(int64(sz), 4)) |
| _, err := io.ReadFull(r, data) |
| if err != nil { |
| return nil, err |
| } |
| data = data[:sz] |
| return data, nil |
| } |
|
|
| func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) { |
| for _, sect := range f.Sections { |
| if sect.Type != elf.SHT_NOTE { |
| continue |
| } |
| r := sect.Open() |
| for { |
| var namesize, descsize, noteType int32 |
| err := binary.Read(r, f.ByteOrder, &namesize) |
| if err != nil { |
| if err == io.EOF { |
| break |
| } |
| return nil, fmt.Errorf("read namesize failed: %v", err) |
| } |
| err = binary.Read(r, f.ByteOrder, &descsize) |
| if err != nil { |
| return nil, fmt.Errorf("read descsize failed: %v", err) |
| } |
| err = binary.Read(r, f.ByteOrder, ¬eType) |
| if err != nil { |
| return nil, fmt.Errorf("read type failed: %v", err) |
| } |
| noteName, err := readwithpad(r, namesize) |
| if err != nil { |
| return nil, fmt.Errorf("read name failed: %v", err) |
| } |
| desc, err := readwithpad(r, descsize) |
| if err != nil { |
| return nil, fmt.Errorf("read desc failed: %v", err) |
| } |
| if string(name) == string(noteName) && typ == noteType { |
| return desc, nil |
| } |
| } |
| } |
| return nil, nil |
| } |
|
|
| func findshlib(ctxt *Link, shlib string) string { |
| if filepath.IsAbs(shlib) { |
| return shlib |
| } |
| for _, libdir := range ctxt.Libdir { |
| libpath := filepath.Join(libdir, shlib) |
| if _, err := os.Stat(libpath); err == nil { |
| return libpath |
| } |
| } |
| Errorf("cannot find shared library: %s", shlib) |
| return "" |
| } |
|
|
| func ldshlibsyms(ctxt *Link, shlib string) { |
| var libpath string |
| if filepath.IsAbs(shlib) { |
| libpath = shlib |
| shlib = filepath.Base(shlib) |
| } else { |
| libpath = findshlib(ctxt, shlib) |
| if libpath == "" { |
| return |
| } |
| } |
| for _, processedlib := range ctxt.Shlibs { |
| if processedlib.Path == libpath { |
| return |
| } |
| } |
| if ctxt.Debugvlog > 1 { |
| ctxt.Logf("ldshlibsyms: found library with name %s at %s\n", shlib, libpath) |
| } |
|
|
| f, err := elf.Open(libpath) |
| if err != nil { |
| Errorf("cannot open shared library: %s", libpath) |
| return |
| } |
| |
| |
| |
|
|
| hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG) |
| if err != nil { |
| Errorf("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 { |
| Errorf("cannot read dep list from shared library %s: %v", libpath, err) |
| return |
| } |
| var deps []string |
| for _, dep := range strings.Split(string(depsbytes), "\n") { |
| if dep == "" { |
| continue |
| } |
| if !filepath.IsAbs(dep) { |
| |
| |
| |
| abs := filepath.Join(filepath.Dir(libpath), dep) |
| if _, err := os.Stat(abs); err == nil { |
| dep = abs |
| } |
| } |
| deps = append(deps, dep) |
| } |
|
|
| syms, err := f.DynamicSymbols() |
| if err != nil { |
| Errorf("cannot read symbols from shared library: %s", libpath) |
| return |
| } |
|
|
| symAddr := map[string]uint64{} |
| for _, elfsym := range syms { |
| if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION { |
| continue |
| } |
|
|
| |
| |
| ver := 0 |
| symname := elfsym.Name |
| if elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC && strings.HasPrefix(elfsym.Name, "type:") { |
| ver = abiInternalVer |
| } else if buildcfg.Experiment.RegabiWrappers && elf.ST_TYPE(elfsym.Info) == elf.STT_FUNC { |
| |
| if strings.HasSuffix(elfsym.Name, ".abiinternal") { |
| ver = sym.SymVerABIInternal |
| symname = strings.TrimSuffix(elfsym.Name, ".abiinternal") |
| } else if strings.HasSuffix(elfsym.Name, ".abi0") { |
| ver = 0 |
| symname = strings.TrimSuffix(elfsym.Name, ".abi0") |
| } |
| } |
|
|
| l := ctxt.loader |
| s := l.LookupOrCreateSym(symname, ver) |
|
|
| |
| |
| |
| |
| if l.SymType(s) != 0 && l.SymType(s) != sym.SDYNIMPORT { |
| continue |
| } |
| su := l.MakeSymbolUpdater(s) |
| su.SetType(sym.SDYNIMPORT) |
| l.SetSymElfType(s, elf.ST_TYPE(elfsym.Info)) |
| su.SetSize(int64(elfsym.Size)) |
| if elfsym.Section != elf.SHN_UNDEF { |
| |
| l.SetSymPkg(s, libpath) |
|
|
| |
| |
| sname := l.SymName(s) |
| if strings.HasPrefix(sname, "type:") && !strings.HasPrefix(sname, "type:.") { |
| su.SetData(readelfsymboldata(ctxt, f, &elfsym)) |
| } |
| } |
|
|
| if symname != elfsym.Name { |
| l.SetSymExtname(s, elfsym.Name) |
| } |
| symAddr[elfsym.Name] = elfsym.Value |
| } |
|
|
| |
| |
| |
| relocTarget := map[uint64]string{} |
| addends := false |
| sect := f.SectionByType(elf.SHT_REL) |
| if sect == nil { |
| sect = f.SectionByType(elf.SHT_RELA) |
| if sect == nil { |
| log.Fatalf("can't find SHT_REL or SHT_RELA section of %s", shlib) |
| } |
| addends = true |
| } |
| |
| data, err := sect.Data() |
| if err != nil { |
| log.Fatalf("can't read relocation section of %s: %v", shlib, err) |
| } |
| bo := f.ByteOrder |
| for len(data) > 0 { |
| var off, idx uint64 |
| var addend int64 |
| switch f.Class { |
| case elf.ELFCLASS64: |
| off = bo.Uint64(data) |
| info := bo.Uint64(data[8:]) |
| data = data[16:] |
| if addends { |
| addend = int64(bo.Uint64(data)) |
| data = data[8:] |
| } |
|
|
| idx = info >> 32 |
| typ := info & 0xffff |
| |
| |
| switch typ { |
| case uint64(elf.R_X86_64_64): |
| case uint64(elf.R_AARCH64_ABS64): |
| case uint64(elf.R_LARCH_64): |
| case uint64(elf.R_390_64): |
| case uint64(elf.R_PPC64_ADDR64): |
| default: |
| continue |
| } |
| case elf.ELFCLASS32: |
| off = uint64(bo.Uint32(data)) |
| info := bo.Uint32(data[4:]) |
| data = data[8:] |
| if addends { |
| addend = int64(int32(bo.Uint32(data))) |
| data = data[4:] |
| } |
|
|
| idx = uint64(info >> 8) |
| typ := info & 0xff |
| |
| switch typ { |
| case uint32(elf.R_386_32): |
| case uint32(elf.R_ARM_ABS32): |
| default: |
| continue |
| } |
| default: |
| log.Fatalf("unknown bit size %s", f.Class) |
| } |
| if addend != 0 { |
| continue |
| } |
| relocTarget[off] = syms[idx-1].Name |
| } |
|
|
| ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, symAddr: symAddr, relocTarget: relocTarget}) |
| } |
|
|
| func addsection(ldr *loader.Loader, arch *sys.Arch, seg *sym.Segment, name string, rwx int) *sym.Section { |
| sect := ldr.NewSection() |
| sect.Rwx = uint8(rwx) |
| sect.Name = name |
| sect.Seg = seg |
| sect.Align = int32(arch.PtrSize) |
| seg.Sections = append(seg.Sections, sect) |
| return sect |
| } |
|
|
| func usage() { |
| fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n") |
| objabi.Flagprint(os.Stderr) |
| Exit(2) |
| } |
|
|
| type SymbolType int8 |
|
|
| const ( |
| |
| TextSym SymbolType = 'T' |
| DataSym SymbolType = 'D' |
| BSSSym SymbolType = 'B' |
| UndefinedSym SymbolType = 'U' |
| TLSSym SymbolType = 't' |
| FrameSym SymbolType = 'm' |
| ParamSym SymbolType = 'p' |
| AutoSym SymbolType = 'a' |
|
|
| |
| DeletedAutoSym = 'x' |
| ) |
|
|
| |
| func (ctxt *Link) defineInternal(p string, t sym.SymKind) loader.Sym { |
| s := ctxt.loader.CreateSymForUpdate(p, 0) |
| s.SetType(t) |
| s.SetSpecial(true) |
| s.SetLocal(true) |
| return s.Sym() |
| } |
|
|
| func (ctxt *Link) xdefine(p string, t sym.SymKind, v int64) loader.Sym { |
| s := ctxt.defineInternal(p, t) |
| ctxt.loader.SetSymValue(s, v) |
| return s |
| } |
|
|
| func datoff(ldr *loader.Loader, s loader.Sym, addr int64) int64 { |
| if uint64(addr) >= Segdata.Vaddr { |
| return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff) |
| } |
| if uint64(addr) >= Segtext.Vaddr { |
| return int64(uint64(addr) - Segtext.Vaddr + Segtext.Fileoff) |
| } |
| ldr.Errorf(s, "invalid datoff %#x", addr) |
| return 0 |
| } |
|
|
| func Entryvalue(ctxt *Link) int64 { |
| a := *flagEntrySymbol |
| if a[0] >= '0' && a[0] <= '9' { |
| return atolwhex(a) |
| } |
| ldr := ctxt.loader |
| s := ldr.Lookup(a, 0) |
| if s == 0 { |
| Errorf("missing entry symbol %q", a) |
| return 0 |
| } |
| st := ldr.SymType(s) |
| if st == 0 { |
| return *FlagTextAddr |
| } |
| if !ctxt.IsAIX() && !st.IsText() { |
| ldr.Errorf(s, "entry not text") |
| } |
| return ldr.SymValue(s) |
| } |
|
|
| func (ctxt *Link) callgraph() { |
| if !*FlagC { |
| return |
| } |
|
|
| ldr := ctxt.loader |
| for _, s := range ctxt.Textp { |
| relocs := ldr.Relocs(s) |
| for i := 0; i < relocs.Count(); i++ { |
| r := relocs.At(i) |
| rs := r.Sym() |
| if rs == 0 { |
| continue |
| } |
| if r.Type().IsDirectCall() && ldr.SymType(rs).IsText() { |
| ctxt.Logf("%s calls %s\n", ldr.SymName(s), ldr.SymName(rs)) |
| } |
| } |
| } |
| } |
|
|
| func Rnd(v int64, r int64) int64 { |
| if r <= 0 { |
| return v |
| } |
| v += r - 1 |
| c := v % r |
| if c < 0 { |
| c += r |
| } |
| v -= c |
| return v |
| } |
|
|
| func bgetc(r *bio.Reader) int { |
| c, err := r.ReadByte() |
| if err != nil { |
| if err != io.EOF { |
| log.Fatalf("reading input: %v", err) |
| } |
| return -1 |
| } |
| return int(c) |
| } |
|
|
| type markKind uint8 |
| const ( |
| _ markKind = iota |
| visiting |
| visited |
| ) |
|
|
| func postorder(libs []*sym.Library) []*sym.Library { |
| order := make([]*sym.Library, 0, len(libs)) |
| mark := make(map[*sym.Library]markKind, len(libs)) |
| for _, lib := range libs { |
| dfs(lib, mark, &order) |
| } |
| return order |
| } |
|
|
| func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library) { |
| if mark[lib] == visited { |
| return |
| } |
| if mark[lib] == visiting { |
| panic("found import cycle while visiting " + lib.Pkg) |
| } |
| mark[lib] = visiting |
| for _, i := range lib.Imports { |
| dfs(i, mark, order) |
| } |
| mark[lib] = visited |
| *order = append(*order, lib) |
| } |
|
|
| func ElfSymForReloc(ctxt *Link, s loader.Sym) int32 { |
| |
| |
| les := ctxt.loader.SymLocalElfSym(s) |
| if les != 0 { |
| return les |
| } else { |
| return ctxt.loader.SymElfSym(s) |
| } |
| } |
|
|
| func AddGotSym(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, elfRelocTyp uint32) { |
| if ldr.SymGot(s) >= 0 { |
| return |
| } |
|
|
| Adddynsym(ldr, target, syms, s) |
| got := ldr.MakeSymbolUpdater(syms.GOT) |
| ldr.SetGot(s, int32(got.Size())) |
| got.AddUint(target.Arch, 0) |
|
|
| if target.IsElf() { |
| if target.Arch.PtrSize == 8 { |
| rela := ldr.MakeSymbolUpdater(syms.Rela) |
| rela.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) |
| rela.AddUint64(target.Arch, elf.R_INFO(uint32(ldr.SymDynid(s)), elfRelocTyp)) |
| rela.AddUint64(target.Arch, 0) |
| } else { |
| rel := ldr.MakeSymbolUpdater(syms.Rel) |
| rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s))) |
| rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(s)), elfRelocTyp)) |
| } |
| } else if target.IsDarwin() { |
| leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT) |
| leg.AddUint32(target.Arch, uint32(ldr.SymDynid(s))) |
| if target.IsPIE() && target.IsInternal() { |
| |
| |
| |
| MachoAddBind(int64(ldr.SymGot(s)), s) |
| } |
| } else { |
| ldr.Errorf(s, "addgotsym: unsupported binary format") |
| } |
| } |
|
|
| var hostobjcounter int |
|
|
| |
| |
| |
| |
| |
| func captureHostObj(h *Hostobj) { |
| |
| ofile := fmt.Sprintf("captured-obj-%d.o", hostobjcounter) |
| ifile := fmt.Sprintf("captured-obj-%d.txt", hostobjcounter) |
| hostobjcounter++ |
| opath := filepath.Join(*flagCaptureHostObjs, ofile) |
| ipath := filepath.Join(*flagCaptureHostObjs, ifile) |
|
|
| |
| info := fmt.Sprintf("pkg: %s\npn: %s\nfile: %s\noff: %d\nlen: %d\n", |
| h.pkg, h.pn, h.file, h.off, h.length) |
| if err := os.WriteFile(ipath, []byte(info), 0666); err != nil { |
| log.Fatalf("error writing captured host obj info %s: %v", ipath, err) |
| } |
|
|
| readObjData := func() []byte { |
| inf, err := os.Open(h.file) |
| if err != nil { |
| log.Fatalf("capturing host obj: open failed on %s: %v", h.pn, err) |
| } |
| defer inf.Close() |
| res := make([]byte, h.length) |
| if n, err := inf.ReadAt(res, h.off); err != nil || n != int(h.length) { |
| log.Fatalf("capturing host obj: readat failed on %s: %v", h.pn, err) |
| } |
| return res |
| } |
|
|
| |
| if err := os.WriteFile(opath, readObjData(), 0666); err != nil { |
| log.Fatalf("error writing captured host object %s: %v", opath, err) |
| } |
|
|
| fmt.Fprintf(os.Stderr, "link: info: captured host object %s to %s\n", |
| h.file, opath) |
| } |
|
|
| |
| |
| |
| func (ctxt *Link) findExtLinkTool(toolname string) string { |
| var cc []string |
| cc = append(cc, ctxt.extld()...) |
| cc = append(cc, hostlinkArchArgs(ctxt.Arch)...) |
| cc = append(cc, "--print-prog-name", toolname) |
| out, err := exec.Command(cc[0], cc[1:]...).CombinedOutput() |
| if err != nil { |
| Exitf("%s: finding %s failed: %v\n%s", os.Args[0], toolname, err, out) |
| } |
| cmdpath := strings.TrimRight(string(out), "\r\n") |
| return cmdpath |
| } |
|
|
| |
| |
| func (ctxt *Link) isMSVC() bool { |
| extld := ctxt.extld() |
| name, args := extld[0], extld[1:] |
| args = append(args, trimLinkerArgv(flagExtldflags)...) |
| args = append(args, "--version") |
| cmd := exec.Command(name, args...) |
| if out, err := cmd.CombinedOutput(); err == nil { |
| if bytes.Contains(out, []byte("-msvc\n")) || bytes.Contains(out, []byte("-msvc\r")) { |
| return true |
| } |
| } |
| return false |
| } |
|
|