aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/link
diff options
context:
space:
mode:
authorChressie Himpel <chressie@google.com>2022-02-03 19:10:54 +0100
committerChressie Himpel <chressie@google.com>2022-02-03 19:30:02 +0100
commite14fee553a4646d15123caa04f7ca6ddb7b48362 (patch)
tree26086b9981918546f946d12547d097eb0974cc2e /src/cmd/link
parentd382493a20cf005c6631032ebb410a7c2bc768eb (diff)
parent8384fe86a5b7f579a50c7ad423d4dd4eb2d1f117 (diff)
downloadgo-e14fee553a4646d15123caa04f7ca6ddb7b48362.tar.xz
[dev.boringcrypto] all: merge master into dev.boringcrypto
Change-Id: I18dbf4f9fa7e2334fccedd862a523126cf38164e
Diffstat (limited to 'src/cmd/link')
-rw-r--r--src/cmd/link/elf_test.go55
-rw-r--r--src/cmd/link/internal/dwtest/dwtest.go197
-rw-r--r--src/cmd/link/internal/ld/data.go29
-rw-r--r--src/cmd/link/internal/ld/deadcode.go6
-rw-r--r--src/cmd/link/internal/ld/dwarf_test.go520
-rw-r--r--src/cmd/link/internal/ld/elf.go46
-rw-r--r--src/cmd/link/internal/ld/ld.go6
-rw-r--r--src/cmd/link/internal/ld/lib.go21
-rw-r--r--src/cmd/link/internal/ld/main.go4
-rw-r--r--src/cmd/link/internal/ld/outbuf.go14
-rw-r--r--src/cmd/link/internal/ld/sym.go2
-rw-r--r--src/cmd/link/internal/ld/target.go10
-rw-r--r--src/cmd/link/internal/ld/xcoff.go4
-rw-r--r--src/cmd/link/internal/loadelf/ldelf.go2
14 files changed, 602 insertions, 314 deletions
diff --git a/src/cmd/link/elf_test.go b/src/cmd/link/elf_test.go
index 012c0b5169..760d9ea60d 100644
--- a/src/cmd/link/elf_test.go
+++ b/src/cmd/link/elf_test.go
@@ -201,6 +201,61 @@ func TestMinusRSymsWithSameName(t *testing.T) {
}
}
+func TestMergeNoteSections(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ expected := 1
+
+ switch runtime.GOOS {
+ case "linux", "freebsd", "dragonfly":
+ case "openbsd", "netbsd":
+ // These OSes require independent segment
+ expected = 2
+ default:
+ t.Skip("We should only test on elf output.")
+ }
+ t.Parallel()
+
+ goFile := filepath.Join(t.TempDir(), "notes.go")
+ if err := ioutil.WriteFile(goFile, []byte(goSource), 0444); err != nil {
+ t.Fatal(err)
+ }
+ outFile := filepath.Join(t.TempDir(), "notes.exe")
+ goTool := testenv.GoToolPath(t)
+ // sha1sum of "gopher"
+ id := "0xf4e8cd51ce8bae2996dc3b74639cdeaa1f7fee5f"
+ cmd := exec.Command(goTool, "build", "-o", outFile, "-ldflags",
+ "-B "+id, goFile)
+ cmd.Dir = t.TempDir()
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ ef, err := elf.Open(outFile)
+ if err != nil {
+ t.Fatalf("open elf file failed:%v", err)
+ }
+ defer ef.Close()
+ sec := ef.Section(".note.gnu.build-id")
+ if sec == nil {
+ t.Fatalf("can't find gnu build id")
+ }
+
+ sec = ef.Section(".note.go.buildid")
+ if sec == nil {
+ t.Fatalf("can't find go build id")
+ }
+ cnt := 0
+ for _, ph := range ef.Progs {
+ if ph.Type == elf.PT_NOTE {
+ cnt += 1
+ }
+ }
+ if cnt != expected {
+ t.Fatalf("want %d PT_NOTE segment, got %d", expected, cnt)
+ }
+}
+
const pieSourceTemplate = `
package main
diff --git a/src/cmd/link/internal/dwtest/dwtest.go b/src/cmd/link/internal/dwtest/dwtest.go
new file mode 100644
index 0000000000..c68edf4187
--- /dev/null
+++ b/src/cmd/link/internal/dwtest/dwtest.go
@@ -0,0 +1,197 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package dwtest
+
+import (
+ "debug/dwarf"
+ "errors"
+ "fmt"
+ "os"
+)
+
+// Helper type for supporting queries on DIEs within a DWARF
+// .debug_info section. Invoke the populate() method below passing in
+// a dwarf.Reader, which will read in all DIEs and keep track of
+// parent/child relationships. Queries can then be made to ask for
+// DIEs by name or by offset. This will hopefully reduce boilerplate
+// for future test writing.
+
+type Examiner struct {
+ dies []*dwarf.Entry
+ idxByOffset map[dwarf.Offset]int
+ kids map[int][]int
+ parent map[int]int
+ byname map[string][]int
+}
+
+// Populate the Examiner using the DIEs read from rdr.
+func (ex *Examiner) Populate(rdr *dwarf.Reader) error {
+ ex.idxByOffset = make(map[dwarf.Offset]int)
+ ex.kids = make(map[int][]int)
+ ex.parent = make(map[int]int)
+ ex.byname = make(map[string][]int)
+ var nesting []int
+ for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+ if err != nil {
+ return err
+ }
+ if entry.Tag == 0 {
+ // terminator
+ if len(nesting) == 0 {
+ return errors.New("nesting stack underflow")
+ }
+ nesting = nesting[:len(nesting)-1]
+ continue
+ }
+ idx := len(ex.dies)
+ ex.dies = append(ex.dies, entry)
+ if _, found := ex.idxByOffset[entry.Offset]; found {
+ return errors.New("DIE clash on offset")
+ }
+ ex.idxByOffset[entry.Offset] = idx
+ if name, ok := entry.Val(dwarf.AttrName).(string); ok {
+ ex.byname[name] = append(ex.byname[name], idx)
+ }
+ if len(nesting) > 0 {
+ parent := nesting[len(nesting)-1]
+ ex.kids[parent] = append(ex.kids[parent], idx)
+ ex.parent[idx] = parent
+ }
+ if entry.Children {
+ nesting = append(nesting, idx)
+ }
+ }
+ if len(nesting) > 0 {
+ return errors.New("unterminated child sequence")
+ }
+ return nil
+}
+
+func (e *Examiner) DIEs() []*dwarf.Entry {
+ return e.dies
+}
+
+func indent(ilevel int) {
+ for i := 0; i < ilevel; i++ {
+ fmt.Printf(" ")
+ }
+}
+
+// For debugging new tests
+func (ex *Examiner) DumpEntry(idx int, dumpKids bool, ilevel int) {
+ if idx >= len(ex.dies) {
+ fmt.Fprintf(os.Stderr, "DumpEntry: bad DIE %d: index out of range\n", idx)
+ return
+ }
+ entry := ex.dies[idx]
+ indent(ilevel)
+ fmt.Printf("0x%x: %v\n", idx, entry.Tag)
+ for _, f := range entry.Field {
+ indent(ilevel)
+ fmt.Printf("at=%v val=0x%x\n", f.Attr, f.Val)
+ }
+ if dumpKids {
+ ksl := ex.kids[idx]
+ for _, k := range ksl {
+ ex.DumpEntry(k, true, ilevel+2)
+ }
+ }
+}
+
+// Given a DIE offset, return the previously read dwarf.Entry, or nil
+func (ex *Examiner) EntryFromOffset(off dwarf.Offset) *dwarf.Entry {
+ if idx, found := ex.idxByOffset[off]; found && idx != -1 {
+ return ex.entryFromIdx(idx)
+ }
+ return nil
+}
+
+// Return the ID that Examiner uses to refer to the DIE at offset off
+func (ex *Examiner) IdxFromOffset(off dwarf.Offset) int {
+ if idx, found := ex.idxByOffset[off]; found {
+ return idx
+ }
+ return -1
+}
+
+// Return the dwarf.Entry pointer for the DIE with id 'idx'
+func (ex *Examiner) entryFromIdx(idx int) *dwarf.Entry {
+ if idx >= len(ex.dies) || idx < 0 {
+ return nil
+ }
+ return ex.dies[idx]
+}
+
+// Returns a list of child entries for a die with ID 'idx'
+func (ex *Examiner) Children(idx int) []*dwarf.Entry {
+ sl := ex.kids[idx]
+ ret := make([]*dwarf.Entry, len(sl))
+ for i, k := range sl {
+ ret[i] = ex.entryFromIdx(k)
+ }
+ return ret
+}
+
+// Returns parent DIE for DIE 'idx', or nil if the DIE is top level
+func (ex *Examiner) Parent(idx int) *dwarf.Entry {
+ p, found := ex.parent[idx]
+ if !found {
+ return nil
+ }
+ return ex.entryFromIdx(p)
+}
+
+// ParentCU returns the enclosing compilation unit DIE for the DIE
+// with a given index, or nil if for some reason we can't establish a
+// parent.
+func (ex *Examiner) ParentCU(idx int) *dwarf.Entry {
+ for {
+ parentDie := ex.Parent(idx)
+ if parentDie == nil {
+ return nil
+ }
+ if parentDie.Tag == dwarf.TagCompileUnit {
+ return parentDie
+ }
+ idx = ex.IdxFromOffset(parentDie.Offset)
+ }
+}
+
+// FileRef takes a given DIE by index and a numeric file reference
+// (presumably from a decl_file or call_file attribute), looks up the
+// reference in the .debug_line file table, and returns the proper
+// string for it. We need to know which DIE is making the reference
+// so as to find the right compilation unit.
+func (ex *Examiner) FileRef(dw *dwarf.Data, dieIdx int, fileRef int64) (string, error) {
+
+ // Find the parent compilation unit DIE for the specified DIE.
+ cuDie := ex.ParentCU(dieIdx)
+ if cuDie == nil {
+ return "", fmt.Errorf("no parent CU DIE for DIE with idx %d?", dieIdx)
+ }
+ // Construct a line reader and then use it to get the file string.
+ lr, lrerr := dw.LineReader(cuDie)
+ if lrerr != nil {
+ return "", fmt.Errorf("d.LineReader: %v", lrerr)
+ }
+ files := lr.Files()
+ if fileRef < 0 || int(fileRef) > len(files)-1 {
+ return "", fmt.Errorf("Examiner.FileRef: malformed file reference %d", fileRef)
+ }
+ return files[fileRef].Name, nil
+}
+
+// Return a list of all DIEs with name 'name'. When searching for DIEs
+// by name, keep in mind that the returned results will include child
+// DIEs such as params/variables. For example, asking for all DIEs named
+// "p" for even a small program will give you 400-500 entries.
+func (ex *Examiner) Named(name string) []*dwarf.Entry {
+ sl := ex.byname[name]
+ ret := make([]*dwarf.Entry, len(sl))
+ for i, k := range sl {
+ ret[i] = ex.entryFromIdx(k)
+ }
+ return ret
+}
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 4d85977d43..95a8e0facb 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -2169,11 +2169,10 @@ func (ctxt *Link) buildinfo() {
return
}
+ // Write the buildinfo symbol, which go version looks for.
+ // The code reading this data is in package debug/buildinfo.
ldr := ctxt.loader
s := ldr.CreateSymForUpdate(".go.buildinfo", 0)
- // On AIX, .go.buildinfo must be in the symbol table as
- // it has relocations.
- s.SetNotInSymbolTable(!ctxt.IsAIX())
s.SetType(sym.SBUILDINFO)
s.SetAlign(16)
// The \xff is invalid UTF-8, meant to make it less likely
@@ -2186,16 +2185,24 @@ func (ctxt *Link) buildinfo() {
if ctxt.Arch.ByteOrder == binary.BigEndian {
data[len(prefix)+1] = 1
}
+ data[len(prefix)+1] |= 2 // signals new pointer-free format
+ data = appendString(data, strdata["runtime.buildVersion"])
+ data = appendString(data, strdata["runtime.modinfo"])
+ // MacOS linker gets very upset if the size os not a multiple of alignment.
+ for len(data)%16 != 0 {
+ data = append(data, 0)
+ }
s.SetData(data)
s.SetSize(int64(len(data)))
- r, _ := s.AddRel(objabi.R_ADDR)
- r.SetOff(16)
- r.SetSiz(uint8(ctxt.Arch.PtrSize))
- r.SetSym(ldr.LookupOrCreateSym("runtime.buildVersion", 0))
- r, _ = s.AddRel(objabi.R_ADDR)
- r.SetOff(16 + int32(ctxt.Arch.PtrSize))
- r.SetSiz(uint8(ctxt.Arch.PtrSize))
- r.SetSym(ldr.LookupOrCreateSym("runtime.modinfo", 0))
+}
+
+// appendString appends s to data, prefixed by its varint-encoded length.
+func appendString(data []byte, s string) []byte {
+ var v [binary.MaxVarintLen64]byte
+ n := binary.PutUvarint(v[:], uint64(len(s)))
+ data = append(data, v[:n]...)
+ data = append(data, s...)
+ return data
}
// assign addresses to text
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 7b57a85cde..dba22323b0 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -71,12 +71,6 @@ func (d *deadcodePass) init() {
// runtime.unreachableMethod is a function that will throw if called.
// We redirect unreachable methods to it.
names = append(names, "runtime.unreachableMethod")
- if !d.ctxt.linkShared && d.ctxt.BuildMode != BuildModePlugin {
- // runtime.buildVersion and runtime.modinfo are referenced in .go.buildinfo section
- // (see function buildinfo in data.go). They should normally be reachable from the
- // runtime. Just make it explicit, in case.
- names = append(names, "runtime.buildVersion", "runtime.modinfo")
- }
if d.ctxt.BuildMode == BuildModePlugin {
names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go.plugin.tabs")
diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go
index db9002491e..2f9bf25d10 100644
--- a/src/cmd/link/internal/ld/dwarf_test.go
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -7,9 +7,9 @@ package ld
import (
intdwarf "cmd/internal/dwarf"
objfilepkg "cmd/internal/objfile" // renamed to avoid conflict with objfile function
+ "cmd/link/internal/dwtest"
"debug/dwarf"
"debug/pe"
- "errors"
"fmt"
"internal/buildcfg"
"internal/testenv"
@@ -352,8 +352,8 @@ func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFil
}
rdr := d.Reader()
- ex := examiner{}
- if err := ex.populate(rdr); err != nil {
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
t.Fatalf("error reading DWARF: %v", err)
}
@@ -373,7 +373,7 @@ func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFil
}
// Walk main's children and select variable "i".
- mainIdx := ex.idxFromOffset(maindie.Offset)
+ mainIdx := ex.IdxFromOffset(maindie.Offset)
childDies := ex.Children(mainIdx)
var iEntry *dwarf.Entry
for _, child := range childDies {
@@ -396,7 +396,10 @@ func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFil
if !fileIdxOK {
t.Errorf("missing or invalid DW_AT_decl_file for main")
}
- file := ex.FileRef(t, d, mainIdx, fileIdx)
+ file, err := ex.FileRef(d, mainIdx, fileIdx)
+ if err != nil {
+ t.Fatalf("FileRef: %v", err)
+ }
base := filepath.Base(file)
if base != expectFile {
t.Errorf("DW_AT_decl_file for main is %v, want %v", base, expectFile)
@@ -424,191 +427,6 @@ func TestVarDeclCoordsWithLineDirective(t *testing.T) {
"foobar.go", 202, "//line /foobar.go:200")
}
-// Helper class for supporting queries on DIEs within a DWARF .debug_info
-// section. Invoke the populate() method below passing in a dwarf.Reader,
-// which will read in all DIEs and keep track of parent/child
-// relationships. Queries can then be made to ask for DIEs by name or
-// by offset. This will hopefully reduce boilerplate for future test
-// writing.
-
-type examiner struct {
- dies []*dwarf.Entry
- idxByOffset map[dwarf.Offset]int
- kids map[int][]int
- parent map[int]int
- byname map[string][]int
-}
-
-// Populate the examiner using the DIEs read from rdr.
-func (ex *examiner) populate(rdr *dwarf.Reader) error {
- ex.idxByOffset = make(map[dwarf.Offset]int)
- ex.kids = make(map[int][]int)
- ex.parent = make(map[int]int)
- ex.byname = make(map[string][]int)
- var nesting []int
- for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
- if err != nil {
- return err
- }
- if entry.Tag == 0 {
- // terminator
- if len(nesting) == 0 {
- return errors.New("nesting stack underflow")
- }
- nesting = nesting[:len(nesting)-1]
- continue
- }
- idx := len(ex.dies)
- ex.dies = append(ex.dies, entry)
- if _, found := ex.idxByOffset[entry.Offset]; found {
- return errors.New("DIE clash on offset")
- }
- ex.idxByOffset[entry.Offset] = idx
- if name, ok := entry.Val(dwarf.AttrName).(string); ok {
- ex.byname[name] = append(ex.byname[name], idx)
- }
- if len(nesting) > 0 {
- parent := nesting[len(nesting)-1]
- ex.kids[parent] = append(ex.kids[parent], idx)
- ex.parent[idx] = parent
- }
- if entry.Children {
- nesting = append(nesting, idx)
- }
- }
- if len(nesting) > 0 {
- return errors.New("unterminated child sequence")
- }
- return nil
-}
-
-func indent(ilevel int) {
- for i := 0; i < ilevel; i++ {
- fmt.Printf(" ")
- }
-}
-
-// For debugging new tests
-func (ex *examiner) dumpEntry(idx int, dumpKids bool, ilevel int) error {
- if idx >= len(ex.dies) {
- msg := fmt.Sprintf("bad DIE %d: index out of range\n", idx)
- return errors.New(msg)
- }
- entry := ex.dies[idx]
- indent(ilevel)
- fmt.Printf("0x%x: %v\n", idx, entry.Tag)
- for _, f := range entry.Field {
- indent(ilevel)
- fmt.Printf("at=%v val=0x%x\n", f.Attr, f.Val)
- }
- if dumpKids {
- ksl := ex.kids[idx]
- for _, k := range ksl {
- ex.dumpEntry(k, true, ilevel+2)
- }
- }
- return nil
-}
-
-// Given a DIE offset, return the previously read dwarf.Entry, or nil
-func (ex *examiner) entryFromOffset(off dwarf.Offset) *dwarf.Entry {
- if idx, found := ex.idxByOffset[off]; found && idx != -1 {
- return ex.entryFromIdx(idx)
- }
- return nil
-}
-
-// Return the ID that examiner uses to refer to the DIE at offset off
-func (ex *examiner) idxFromOffset(off dwarf.Offset) int {
- if idx, found := ex.idxByOffset[off]; found {
- return idx
- }
- return -1
-}
-
-// Return the dwarf.Entry pointer for the DIE with id 'idx'
-func (ex *examiner) entryFromIdx(idx int) *dwarf.Entry {
- if idx >= len(ex.dies) || idx < 0 {
- return nil
- }
- return ex.dies[idx]
-}
-
-// Returns a list of child entries for a die with ID 'idx'
-func (ex *examiner) Children(idx int) []*dwarf.Entry {
- sl := ex.kids[idx]
- ret := make([]*dwarf.Entry, len(sl))
- for i, k := range sl {
- ret[i] = ex.entryFromIdx(k)
- }
- return ret
-}
-
-// Returns parent DIE for DIE 'idx', or nil if the DIE is top level
-func (ex *examiner) Parent(idx int) *dwarf.Entry {
- p, found := ex.parent[idx]
- if !found {
- return nil
- }
- return ex.entryFromIdx(p)
-}
-
-// ParentCU returns the enclosing compilation unit DIE for the DIE
-// with a given index, or nil if for some reason we can't establish a
-// parent.
-func (ex *examiner) ParentCU(idx int) *dwarf.Entry {
- for {
- parentDie := ex.Parent(idx)
- if parentDie == nil {
- return nil
- }
- if parentDie.Tag == dwarf.TagCompileUnit {
- return parentDie
- }
- idx = ex.idxFromOffset(parentDie.Offset)
- }
-}
-
-// FileRef takes a given DIE by index and a numeric file reference
-// (presumably from a decl_file or call_file attribute), looks up the
-// reference in the .debug_line file table, and returns the proper
-// string for it. We need to know which DIE is making the reference
-// so as find the right compilation unit.
-func (ex *examiner) FileRef(t *testing.T, dw *dwarf.Data, dieIdx int, fileRef int64) string {
-
- // Find the parent compilation unit DIE for the specified DIE.
- cuDie := ex.ParentCU(dieIdx)
- if cuDie == nil {
- t.Fatalf("no parent CU DIE for DIE with idx %d?", dieIdx)
- return ""
- }
- // Construct a line reader and then use it to get the file string.
- lr, lrerr := dw.LineReader(cuDie)
- if lrerr != nil {
- t.Fatal("d.LineReader: ", lrerr)
- return ""
- }
- files := lr.Files()
- if fileRef < 0 || int(fileRef) > len(files)-1 {
- t.Fatalf("examiner.FileRef: malformed file reference %d", fileRef)
- return ""
- }
- return files[fileRef].Name
-}
-
-// Return a list of all DIEs with name 'name'. When searching for DIEs
-// by name, keep in mind that the returned results will include child
-// DIEs such as params/variables. For example, asking for all DIEs named
-// "p" for even a small program will give you 400-500 entries.
-func (ex *examiner) Named(name string) []*dwarf.Entry {
- sl := ex.byname[name]
- ret := make([]*dwarf.Entry, len(sl))
- for i, k := range sl {
- ret[i] = ex.entryFromIdx(k)
- }
- return ret
-}
-
func TestInlinedRoutineRecords(t *testing.T) {
testenv.MustHaveGoBuild(t)
@@ -656,8 +474,8 @@ func main() {
expectedInl := []string{"main.cand"}
rdr := d.Reader()
- ex := examiner{}
- if err := ex.populate(rdr); err != nil {
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
t.Fatalf("error reading DWARF: %v", err)
}
@@ -677,7 +495,7 @@ func main() {
}
// Walk main's children and pick out the inlined subroutines
- mainIdx := ex.idxFromOffset(maindie.Offset)
+ mainIdx := ex.IdxFromOffset(maindie.Offset)
childDies := ex.Children(mainIdx)
exCount := 0
for _, child := range childDies {
@@ -687,7 +505,7 @@ func main() {
if !originOK {
t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset)
}
- originDIE := ex.entryFromOffset(ooff)
+ originDIE := ex.EntryFromOffset(ooff)
if originDIE == nil {
t.Fatalf("can't locate origin DIE at off %v", ooff)
}
@@ -696,7 +514,7 @@ func main() {
// to see child variables there, even if (perhaps due to
// optimization) there are no references to them from the
// inlined subroutine DIE.
- absFcnIdx := ex.idxFromOffset(ooff)
+ absFcnIdx := ex.IdxFromOffset(ooff)
absFcnChildDies := ex.Children(absFcnIdx)
if len(absFcnChildDies) != 2 {
t.Fatalf("expected abstract function: expected 2 children, got %d children", len(absFcnChildDies))
@@ -735,7 +553,11 @@ func main() {
if !cfOK {
t.Fatalf("no call_file attr for inlined subroutine at offset %v", child.Offset)
}
- file := ex.FileRef(t, d, mainIdx, cf)
+ file, err := ex.FileRef(d, mainIdx, cf)
+ if err != nil {
+ t.Errorf("FileRef: %v", err)
+ continue
+ }
base := filepath.Base(file)
if base != "test.go" {
t.Errorf("bad call_file attribute, found '%s', want '%s'",
@@ -747,7 +569,7 @@ func main() {
// Walk the child variables of the inlined routine. Each
// of them should have a distinct abstract origin-- if two
// vars point to the same origin things are definitely broken.
- inlIdx := ex.idxFromOffset(child.Offset)
+ inlIdx := ex.IdxFromOffset(child.Offset)
inlChildDies := ex.Children(inlIdx)
for _, k := range inlChildDies {
ooff, originOK := k.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
@@ -780,15 +602,15 @@ func abstractOriginSanity(t *testing.T, pkgDir string, flags string) {
t.Fatalf("error reading DWARF: %v", err)
}
rdr := d.Reader()
- ex := examiner{}
- if err := ex.populate(rdr); err != nil {
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
t.Fatalf("error reading DWARF: %v", err)
}
// Make a pass through all DIEs looking for abstract origin
// references.
abscount := 0
- for i, die := range ex.dies {
+ for i, die := range ex.DIEs() {
// Does it have an abstract origin?
ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
if !originOK {
@@ -797,9 +619,9 @@ func abstractOriginSanity(t *testing.T, pkgDir string, flags string) {
// All abstract origin references should be resolvable.
abscount += 1
- originDIE := ex.entryFromOffset(ooff)
+ originDIE := ex.EntryFromOffset(ooff)
if originDIE == nil {
- ex.dumpEntry(i, false, 0)
+ ex.DumpEntry(i, false, 0)
t.Fatalf("unresolved abstract origin ref in DIE at offset 0x%x\n", die.Offset)
}
@@ -807,7 +629,7 @@ func abstractOriginSanity(t *testing.T, pkgDir string, flags string) {
// K2, ... KN}. If X has an abstract origin of A, then for
// each KJ, the abstract origin of KJ should be a child of A.
// Note that this same rule doesn't hold for non-variable DIEs.
- pidx := ex.idxFromOffset(die.Offset)
+ pidx := ex.IdxFromOffset(die.Offset)
if pidx < 0 {
t.Fatalf("can't locate DIE id")
}
@@ -821,15 +643,15 @@ func abstractOriginSanity(t *testing.T, pkgDir string, flags string) {
if !originOK {
continue
}
- childOriginDIE := ex.entryFromOffset(kooff)
+ childOriginDIE := ex.EntryFromOffset(kooff)
if childOriginDIE == nil {
- ex.dumpEntry(i, false, 0)
+ ex.DumpEntry(i, false, 0)
t.Fatalf("unresolved abstract origin ref in DIE at offset %x", kid.Offset)
}
- coidx := ex.idxFromOffset(childOriginDIE.Offset)
+ coidx := ex.IdxFromOffset(childOriginDIE.Offset)
childOriginParent := ex.Parent(coidx)
if childOriginParent != originDIE {
- ex.dumpEntry(i, false, 0)
+ ex.DumpEntry(i, false, 0)
t.Fatalf("unexpected parent of abstract origin DIE at offset %v", childOriginDIE.Offset)
}
}
@@ -977,8 +799,8 @@ func main() {
}
rdr := d.Reader()
- ex := examiner{}
- if err := ex.populate(rdr); err != nil {
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
t.Fatalf("error reading DWARF: %v", err)
}
dies := ex.Named("*main.X")
@@ -1501,8 +1323,8 @@ func TestIssue39757(t *testing.T) {
t.Fatalf("error parsing DWARF: %v", err)
}
rdr := dw.Reader()
- ex := examiner{}
- if err := ex.populate(rdr); err != nil {
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
t.Fatalf("error reading DWARF: %v", err)
}
@@ -1521,7 +1343,7 @@ func TestIssue39757(t *testing.T) {
highpc := maindie.Val(dwarf.AttrHighpc).(uint64)
// Now read the line table for the 'main' compilation unit.
- mainIdx := ex.idxFromOffset(maindie.Offset)
+ mainIdx := ex.IdxFromOffset(maindie.Offset)
cuentry := ex.Parent(mainIdx)
if cuentry == nil {
t.Fatalf("main.main DIE appears orphaned")
@@ -1635,6 +1457,66 @@ func TestIssue42484(t *testing.T) {
f.Close()
}
+// processParams examines the formal parameter children of subprogram
+// DIE "die" using the explorer "ex" and returns a string that
+// captures the name, order, and classification of the subprogram's
+// input and output parameters. For example, for the go function
+//
+// func foo(i1 int, f1 float64) (string, bool) {
+//
+// this function would return a string something like
+//
+// i1:0:1 f1:1:1 ~r0:2:2 ~r1:3:2
+//
+// where each chunk above is of the form NAME:ORDER:INOUTCLASSIFICATION
+//
+func processParams(die *dwarf.Entry, ex *dwtest.Examiner) string {
+ // Values in the returned map are of the form <order>:<varparam>
+ // where order is the order within the child DIE list of the
+ // param, and <varparam> is an integer:
+ //
+ // -1: varparm attr not found
+ // 1: varparm found with value false
+ // 2: varparm found with value true
+ //
+ foundParams := make(map[string]string)
+
+ // Walk the subprogram DIE's children looking for params.
+ pIdx := ex.IdxFromOffset(die.Offset)
+ childDies := ex.Children(pIdx)
+ idx := 0
+ for _, child := range childDies {
+ if child.Tag == dwarf.TagFormalParameter {
+ // NB: a setting of DW_AT_variable_parameter indicates
+ // that the param in question is an output parameter; we
+ // want to see this attribute set to TRUE for all Go
+ // return params. It would be OK to have it missing for
+ // input parameters, but for the moment we verify that the
+ // attr is present but set to false.
+ st := -1
+ if vp, ok := child.Val(dwarf.AttrVarParam).(bool); ok {
+ if vp {
+ st = 2
+ } else {
+ st = 1
+ }
+ }
+ if name, ok := child.Val(dwarf.AttrName).(string); ok {
+ foundParams[name] = fmt.Sprintf("%d:%d", idx, st)
+ idx++
+ }
+ }
+ }
+
+ found := make([]string, 0, len(foundParams))
+ for k, v := range foundParams {
+ found = append(found, fmt.Sprintf("%s:%s", k, v))
+ }
+ sort.Strings(found)
+
+ return fmt.Sprintf("%+v", found)
+}
+
func TestOutputParamAbbrevAndAttr(t *testing.T) {
testenv.MustHaveGoBuild(t)
@@ -1674,8 +1556,8 @@ func main() {
}
rdr := d.Reader()
- ex := examiner{}
- if err := ex.populate(rdr); err != nil {
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
t.Fatalf("error reading DWARF: %v", err)
}
@@ -1694,56 +1576,15 @@ func main() {
t.Fatalf("unexpected tag %v on main.ABC DIE", abcdie.Tag)
}
- // A setting of DW_AT_variable_parameter indicates that the
- // param in question is an output parameter; we want to see this
- // attribute set to TRUE for all Go return params. It would be
- // OK to have it missing for input parameters, but for the moment
- // we verify that the attr is present but set to false.
-
- // Values in this map are of the form <order>:<varparam>
- // where order is the order within the child DIE list of the param,
- // and <varparam> is an integer:
- //
- // -1: varparm attr not found
- // 1: varparm found with value false
- // 2: varparm found with value true
- //
- foundParams := make(map[string]string)
-
- // Walk ABCs's children looking for params.
- abcIdx := ex.idxFromOffset(abcdie.Offset)
- childDies := ex.Children(abcIdx)
- idx := 0
- for _, child := range childDies {
- if child.Tag == dwarf.TagFormalParameter {
- st := -1
- if vp, ok := child.Val(dwarf.AttrVarParam).(bool); ok {
- if vp {
- st = 2
- } else {
- st = 1
- }
- }
- if name, ok := child.Val(dwarf.AttrName).(string); ok {
- foundParams[name] = fmt.Sprintf("%d:%d", idx, st)
- idx++
- }
- }
- }
-
- // Digest the result.
- found := make([]string, 0, len(foundParams))
- for k, v := range foundParams {
- found = append(found, fmt.Sprintf("%s:%s", k, v))
- }
- sort.Strings(found)
+ // Call a helper to collect param info.
+ found := processParams(abcdie, &ex)
// Make sure we see all of the expected params in the proper
- // order, that they have the varparam attr, and the varparm is set
- // for the returns.
+ // order, that they have the varparam attr, and the varparam is
+ // set for the returns.
expected := "[c1:0:1 c2:1:1 c3:2:1 d1:3:1 d2:4:1 d3:5:1 d4:6:1 f1:7:1 f2:8:1 f3:9:1 g1:10:1 r1:11:2 r2:12:2 r3:13:2 r4:14:2 r5:15:2 r6:16:2]"
- if fmt.Sprintf("%+v", found) != expected {
- t.Errorf("param check failed, wanted %s got %s\n",
+ if found != expected {
+ t.Errorf("param check failed, wanted:\n%s\ngot:\n%s\n",
expected, found)
}
}
@@ -1835,8 +1676,8 @@ func main() {
}
rdr.Seek(0)
- ex := examiner{}
- if err := ex.populate(rdr); err != nil {
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
t.Fatalf("error reading DWARF: %v", err)
}
for _, typeName := range []string{"main.CustomInt", "map[int]main.CustomInt"} {
@@ -1849,3 +1690,156 @@ func main() {
}
}
}
+
+func TestOptimizedOutParamHandling(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9; no DWARF symbol table in executables")
+ }
+ t.Parallel()
+
+ // This test is intended to verify that the compiler emits DWARF
+ // DIE entries for all input and output parameters, and that:
+ //
+ // - attributes are set correctly for output params,
+ // - things appear in the proper order
+ // - things work properly for both register-resident
+ // params and params passed on the stack
+ // - things work for both referenced and unreferenced params
+ // - things work for named return values un-named return vals
+ //
+ // The scenarios below don't cover all possible permutations and
+ // combinations, but they hit a bunch of the high points.
+
+ const prog = `
+package main
+
+// First testcase. All input params in registers, all params used.
+
+//go:noinline
+func tc1(p1, p2 int, p3 string) (int, string) {
+ return p1 + p2, p3 + "foo"
+}
+
+// Second testcase. Some params in registers, some on stack.
+
+//go:noinline
+func tc2(p1 int, p2 [128]int, p3 string) (int, string, [128]int) {
+ return p1 + p2[p1], p3 + "foo", [128]int{p1}
+}
+
+// Third testcase. Named return params.
+
+//go:noinline
+func tc3(p1 int, p2 [128]int, p3 string) (r1 int, r2 bool, r3 string, r4 [128]int) {
+ if p1 == 101 {
+ r1 = p1 + p2[p1]
+ r2 = p3 == "foo"
+ r4 = [128]int{p1}
+ return
+ } else {
+ return p1 - p2[p1+3], false, "bar", [128]int{p1 + 2}
+ }
+}
+
+// Fourth testcase. Some thing are used, some are unused.
+
+//go:noinline
+func tc4(p1, p1un int, p2, p2un [128]int, p3, p3un string) (r1 int, r1un int, r2 bool, r3 string, r4, r4un [128]int) {
+ if p1 == 101 {
+ r1 = p1 + p2[p2[0]]
+ r2 = p3 == "foo"
+ r4 = [128]int{p1}
+ return
+ } else {
+ return p1, -1, true, "plex", [128]int{p1 + 2}, [128]int{-1}
+ }
+}
+
+func main() {
+ {
+ r1, r2 := tc1(3, 4, "five")
+ println(r1, r2)
+ }
+ {
+ x := [128]int{9}
+ r1, r2, r3 := tc2(3, x, "five")
+ println(r1, r2, r3[0])
+ }
+ {
+ x := [128]int{9}
+ r1, r2, r3, r4 := tc3(3, x, "five")
+ println(r1, r2, r3, r4[0])
+ }
+ {
+ x := [128]int{3}
+ y := [128]int{7}
+ r1, r1u, r2, r3, r4, r4u := tc4(0, 1, x, y, "a", "b")
+ println(r1, r1u, r2, r3, r4[0], r4u[1])
+ }
+
+}
+`
+ dir := t.TempDir()
+ f := gobuild(t, dir, prog, DefaultOpt)
+ defer f.Close()
+
+ d, err := f.DWARF()
+ if err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ rdr := d.Reader()
+ ex := dwtest.Examiner{}
+ if err := ex.Populate(rdr); err != nil {
+ t.Fatalf("error reading DWARF: %v", err)
+ }
+
+ testcases := []struct {
+ tag string
+ expected string
+ }{
+ {
+ tag: "tc1",
+ expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2]",
+ },
+ {
+ tag: "tc2",
+ expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2 ~r2:5:2]",
+ },
+ {
+ tag: "tc3",
+ expected: "[p1:0:1 p2:1:1 p3:2:1 r1:3:2 r2:4:2 r3:5:2 r4:6:2]",
+ },
+ {
+ tag: "tc4",
+ expected: "[p1:0:1 p1un:1:1 p2:2:1 p2un:3:1 p3:4:1 p3un:5:1 r1:6:2 r1un:7:2 r2:8:2 r3:9:2 r4:10:2 r4un:11:2]",
+ },
+ }
+
+ for _, tc := range testcases {
+ // Locate the proper DIE
+ which := fmt.Sprintf("main.%s", tc.tag)
+ tcs := ex.Named(which)
+ if len(tcs) == 0 {
+ t.Fatalf("unable to locate DIE for " + which)
+ }
+ if len(tcs) != 1 {
+ t.Fatalf("more than one " + which + " DIE")
+ }
+ die := tcs[0]
+
+ // Vet the DIE
+ if die.Tag != dwarf.TagSubprogram {
+ t.Fatalf("unexpected tag %v on "+which+" DIE", die.Tag)
+ }
+
+ // Examine params for this subprogram.
+ foundParams := processParams(die, &ex)
+ if foundParams != tc.expected {
+ t.Errorf("check failed for testcase %s -- wanted:\n%s\ngot:%s\n",
+ tc.tag, tc.expected, foundParams)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index fb75c761a1..1bdfb3369c 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -1080,7 +1080,12 @@ func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr {
}
if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen {
- sh.Type = uint32(elf.SHT_PROGBITS)
+ switch sect.Name {
+ case ".init_array":
+ sh.Type = uint32(elf.SHT_INIT_ARRAY)
+ default:
+ sh.Type = uint32(elf.SHT_PROGBITS)
+ }
} else {
sh.Type = uint32(elf.SHT_NOBITS)
}
@@ -1682,13 +1687,18 @@ func asmbElf(ctxt *Link) {
var pph *ElfPhdr
var pnote *ElfPhdr
+ getpnote := func() *ElfPhdr {
+ if pnote == nil {
+ pnote = newElfPhdr()
+ pnote.Type = elf.PT_NOTE
+ pnote.Flags = elf.PF_R
+ }
+ return pnote
+ }
if *flagRace && ctxt.IsNetbsd() {
sh := elfshname(".note.netbsd.pax")
resoff -= int64(elfnetbsdpax(sh, uint64(startva), uint64(resoff)))
- pnote = newElfPhdr()
- pnote.Type = elf.PT_NOTE
- pnote.Flags = elf.PF_R
- phsh(pnote, sh)
+ phsh(getpnote(), sh)
}
if ctxt.LinkMode == LinkExternal {
/* skip program headers */
@@ -1787,7 +1797,6 @@ func asmbElf(ctxt *Link) {
phsh(ph, sh)
}
- pnote = nil
if ctxt.HeadType == objabi.Hnetbsd || ctxt.HeadType == objabi.Hopenbsd {
var sh *ElfShdr
switch ctxt.HeadType {
@@ -1799,34 +1808,23 @@ func asmbElf(ctxt *Link) {
sh = elfshname(".note.openbsd.ident")
resoff -= int64(elfopenbsdsig(sh, uint64(startva), uint64(resoff)))
}
-
- pnote = newElfPhdr()
- pnote.Type = elf.PT_NOTE
- pnote.Flags = elf.PF_R
- phsh(pnote, sh)
+ // netbsd and openbsd require ident in an independent segment.
+ pnotei := newElfPhdr()
+ pnotei.Type = elf.PT_NOTE
+ pnotei.Flags = elf.PF_R
+ phsh(pnotei, sh)
}
if len(buildinfo) > 0 {
sh := elfshname(".note.gnu.build-id")
resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff)))
-
- if pnote == nil {
- pnote = newElfPhdr()
- pnote.Type = elf.PT_NOTE
- pnote.Flags = elf.PF_R
- }
-
- phsh(pnote, sh)
+ phsh(getpnote(), sh)
}
if *flagBuildid != "" {
sh := elfshname(".note.go.buildid")
resoff -= int64(elfgobuildid(sh, uint64(startva), uint64(resoff)))
-
- pnote := newElfPhdr()
- pnote.Type = elf.PT_NOTE
- pnote.Flags = elf.PF_R
- phsh(pnote, sh)
+ phsh(getpnote(), sh)
}
// Additions to the reserved area must be above this line.
diff --git a/src/cmd/link/internal/ld/ld.go b/src/cmd/link/internal/ld/ld.go
index 7ff9c41f96..954921844c 100644
--- a/src/cmd/link/internal/ld/ld.go
+++ b/src/cmd/link/internal/ld/ld.go
@@ -85,6 +85,12 @@ func (ctxt *Link) readImportCfg(file string) {
log.Fatalf(`%s:%d: invalid packageshlib: syntax is "packageshlib path=filename"`, file, lineNum)
}
ctxt.PackageShlib[before] = after
+ case "modinfo":
+ s, err := strconv.Unquote(args)
+ if err != nil {
+ log.Fatalf("%s:%d: invalid modinfo: %v", file, lineNum, err)
+ }
+ addstrdata1(ctxt, "runtime.modinfo="+s)
}
}
}
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 4aca36db98..5b82dc287d 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1104,7 +1104,6 @@ func hostlinksetup(ctxt *Link) {
*flagTmpdir = dir
ownTmpDir = true
AtExit(func() {
- ctxt.Out.Close()
os.RemoveAll(*flagTmpdir)
})
}
@@ -1271,7 +1270,10 @@ func (ctxt *Link) hostlink() {
if ctxt.DynlinkingGo() && buildcfg.GOOS != "ios" {
// -flat_namespace is deprecated on iOS.
// It is useful for supporting plugins. We don't support plugins on iOS.
- argv = append(argv, "-Wl,-flat_namespace")
+ // -flat_namespace may cause the dynamic linker to hang at forkExec when
+ // resolving a lazy binding. See issue 38824.
+ // Force eager resolution to work around.
+ argv = append(argv, "-Wl,-flat_namespace", "-Wl,-bind_at_load")
}
if !combineDwarf {
argv = append(argv, "-Wl,-S") // suppress STAB (symbolic debugging) symbols
@@ -1500,8 +1502,19 @@ func (ctxt *Link) hostlink() {
}
return strings.Trim(string(out), "\n")
}
- argv = append(argv, getPathFile("crtcxa.o"))
- argv = append(argv, getPathFile("crtdbase.o"))
+ // Since GCC version 11, the 64-bit version of GCC starting files
+ // are now suffixed by "_64". Even under "-maix64" multilib directory
+ // "crtcxa.o" is 32-bit.
+ 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 {
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
index a1d86965e4..26f9db8ec4 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -172,6 +172,10 @@ func Main(arch *sys.Arch, theArch Arch) {
usage()
}
+ if *FlagD && ctxt.UsesLibc() {
+ Exitf("dynamic linking required on %s; -d flag cannot be used", buildcfg.GOOS)
+ }
+
checkStrictDups = *FlagStrictDups
if !buildcfg.Experiment.RegabiWrappers {
diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go
index 9d5e8854fe..1d21dce9c5 100644
--- a/src/cmd/link/internal/ld/outbuf.go
+++ b/src/cmd/link/internal/ld/outbuf.go
@@ -131,6 +131,20 @@ func (out *OutBuf) Close() error {
return nil
}
+// ErrorClose closes the output file (if any).
+// It is supposed to be called only at exit on error, so it doesn't do
+// any clean up or buffer flushing, just closes the file.
+func (out *OutBuf) ErrorClose() {
+ if out.isView {
+ panic(viewCloseError)
+ }
+ if out.f == nil {
+ return
+ }
+ out.f.Close() // best effort, ignore error
+ out.f = nil
+}
+
// isMmapped returns true if the OutBuf is mmaped.
func (out *OutBuf) isMmapped() bool {
return len(out.buf) != 0
diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go
index 72639962e2..d51a59ef46 100644
--- a/src/cmd/link/internal/ld/sym.go
+++ b/src/cmd/link/internal/ld/sym.go
@@ -60,7 +60,7 @@ func linknew(arch *sys.Arch) *Link {
AtExit(func() {
if nerrors > 0 {
- ctxt.Out.Close()
+ ctxt.Out.ErrorClose()
mayberemoveoutfile()
}
})
diff --git a/src/cmd/link/internal/ld/target.go b/src/cmd/link/internal/ld/target.go
index f68de8fff1..58d45d1504 100644
--- a/src/cmd/link/internal/ld/target.go
+++ b/src/cmd/link/internal/ld/target.go
@@ -185,3 +185,13 @@ func (t *Target) mustSetHeadType() {
func (t *Target) IsBigEndian() bool {
return t.Arch.ByteOrder == binary.BigEndian
}
+
+func (t *Target) UsesLibc() bool {
+ t.mustSetHeadType()
+ switch t.HeadType {
+ case objabi.Haix, objabi.Hdarwin, objabi.Hopenbsd, objabi.Hsolaris, objabi.Hwindows:
+ // platforms where we use libc for syscalls.
+ return true
+ }
+ return false
+}
diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go
index aba6138c83..aaddf19d16 100644
--- a/src/cmd/link/internal/ld/xcoff.go
+++ b/src/cmd/link/internal/ld/xcoff.go
@@ -1290,10 +1290,6 @@ func Xcoffadddynrel(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader
}
func (ctxt *Link) doxcoff() {
- if *FlagD {
- // All XCOFF files have dynamic symbols because of the syscalls.
- Exitf("-d is not available on AIX")
- }
ldr := ctxt.loader
// TOC
diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go
index b4f565a153..d05d8e3b4b 100644
--- a/src/cmd/link/internal/loadelf/ldelf.go
+++ b/src/cmd/link/internal/loadelf/ldelf.go
@@ -599,7 +599,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
if strings.HasPrefix(elfsym.name, ".LASF") { // gcc on s390x does this
continue
}
- return errorf("%v: sym#%d: ignoring symbol in section %d (type %d)", elfsym.sym, i, elfsym.shndx, elfsym.type_)
+ return errorf("%v: sym#%d (%s): ignoring symbol in section %d (type %d)", elfsym.sym, i, elfsym.name, elfsym.shndx, elfsym.type_)
}
s := elfsym.sym