diff options
| author | Chressie Himpel <chressie@google.com> | 2022-02-03 19:10:54 +0100 |
|---|---|---|
| committer | Chressie Himpel <chressie@google.com> | 2022-02-03 19:30:02 +0100 |
| commit | e14fee553a4646d15123caa04f7ca6ddb7b48362 (patch) | |
| tree | 26086b9981918546f946d12547d097eb0974cc2e /src/cmd/link | |
| parent | d382493a20cf005c6631032ebb410a7c2bc768eb (diff) | |
| parent | 8384fe86a5b7f579a50c7ad423d4dd4eb2d1f117 (diff) | |
| download | go-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.go | 55 | ||||
| -rw-r--r-- | src/cmd/link/internal/dwtest/dwtest.go | 197 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/data.go | 29 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/deadcode.go | 6 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/dwarf_test.go | 520 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/elf.go | 46 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/ld.go | 6 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/lib.go | 21 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/main.go | 4 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/outbuf.go | 14 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/sym.go | 2 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/target.go | 10 | ||||
| -rw-r--r-- | src/cmd/link/internal/ld/xcoff.go | 4 | ||||
| -rw-r--r-- | src/cmd/link/internal/loadelf/ldelf.go | 2 |
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 |
