diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 833b3eb9db..bf95745d8d 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1820,7 +1820,7 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, Errorf(nil, "%v", err) return } - if rsrc != 0 { + if len(rsrc) != 0 { setpersrc(ctxt, rsrc) } ctxt.Textp = append(ctxt.Textp, textp...) diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index adbf516d5c..5edaf54dd2 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -253,7 +253,7 @@ type Dll struct { } var ( - rsrcsym loader.Sym + rsrcsyms []loader.Sym PESECTHEADR int32 PEFILEHEADR int32 pe64 int @@ -1508,46 +1508,56 @@ func (ctxt *Link) dope() { initdynexport(ctxt) } -func setpersrc(ctxt *Link, sym loader.Sym) { - if rsrcsym != 0 { +func setpersrc(ctxt *Link, syms []loader.Sym) { + if len(rsrcsyms) != 0 { Errorf(nil, "too many .rsrc sections") } - - rsrcsym = sym + rsrcsyms = syms } func addpersrc(ctxt *Link) { - if rsrcsym == 0 { + if len(rsrcsyms) == 0 { return } - data := ctxt.loader.Data(rsrcsym) - size := len(data) - h := pefile.addSection(".rsrc", size, size) + var size int64 + for _, rsrcsym := range rsrcsyms { + size += ctxt.loader.SymSize(rsrcsym) + } + h := pefile.addSection(".rsrc", int(size), int(size)) h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA h.checkOffset(ctxt.Out.Offset()) - // relocation - relocs := ctxt.loader.Relocs(rsrcsym) - for i := 0; i < relocs.Count(); i++ { - r := relocs.At(i) - p := data[r.Off():] - val := uint32(int64(h.virtualAddress) + r.Add()) - - // 32-bit little-endian - p[0] = byte(val) - - p[1] = byte(val >> 8) - p[2] = byte(val >> 16) - p[3] = byte(val >> 24) + for _, rsrcsym := range rsrcsyms { + // A split resource happens when the actual resource data and its relocations are + // split across multiple sections, denoted by a $01 or $02 at the end of the .rsrc + // section name. + splitResources := strings.Contains(ctxt.loader.SymName(rsrcsym), ".rsrc$") + relocs := ctxt.loader.Relocs(rsrcsym) + data := ctxt.loader.Data(rsrcsym) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + p := data[r.Off():] + val := uint32(int64(h.virtualAddress) + r.Add()) + if splitResources { + // If we're a split resource section, and that section has relocation + // symbols, then the data that it points to doesn't actually begin at + // the virtual address listed in this current section, but rather + // begins at the section immediately after this one. So, in order to + // calculate the proper virtual address of the data it's pointing to, + // we have to add the length of this section to the virtual address. + // This works because .rsrc sections are divided into two (but not more) + // of these sections. + val += uint32(len(data)) + } + binary.LittleEndian.PutUint32(p, val) + } + ctxt.Out.Write(data) } - - ctxt.Out.Write(data) h.pad(ctxt.Out, uint32(size)) // update data directory pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress - pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize } diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go index 1e6f978531..a5c025de8f 100644 --- a/src/cmd/link/internal/loadpe/ldpe.go +++ b/src/cmd/link/internal/loadpe/ldpe.go @@ -157,8 +157,9 @@ func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loa // Load loads the PE file pn from input. // Symbols are written into syms, and a slice of the text symbols is returned. -// If an .rsrc section is found, its symbol is returned as rsrc. -func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc loader.Sym, err error) { +// If an .rsrc section or set of .rsrc$xx sections is found, its symbols are +// returned as rsrc. +func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc []loader.Sym, err error) { lookup := func(name string, version int) (*loader.SymbolBuilder, loader.Sym) { s := l.LookupOrCreateSym(name, version) sb := l.MakeSymbolUpdater(s) @@ -176,7 +177,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read // TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details) f, err := pe.NewFile(sr) if err != nil { - return nil, 0, err + return nil, nil, err } defer f.Close() @@ -211,21 +212,21 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read bld.SetType(sym.STEXT) default: - return nil, 0, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name) + return nil, nil, fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name) } if bld.Type() != sym.SNOPTRBSS { data, err := sect.Data() if err != nil { - return nil, 0, err + return nil, nil, err } sectdata[sect] = data bld.SetData(data) } bld.SetSize(int64(sect.Size)) sectsyms[sect] = s - if sect.Name == ".rsrc" { - rsrc = s + if sect.Name == ".rsrc" || strings.HasPrefix(sect.Name, ".rsrc$") { + rsrc = append(rsrc, s) } } @@ -246,22 +247,23 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read continue } + splitResources := strings.HasPrefix(rsect.Name, ".rsrc$") sb := l.MakeSymbolUpdater(sectsyms[rsect]) for j, r := range rsect.Relocs { if int(r.SymbolTableIndex) >= len(f.COFFSymbols) { - return nil, 0, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols)) + return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols)) } pesym := &f.COFFSymbols[r.SymbolTableIndex] _, gosym, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion) if err != nil { - return nil, 0, err + return nil, nil, err } if gosym == 0 { name, err := pesym.FullName(f.StringTable) if err != nil { name = string(pesym.Name[:]) } - return nil, 0, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type) + return nil, nil, fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type) } rSym := gosym @@ -271,11 +273,11 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read var rType objabi.RelocType switch arch.Family { default: - return nil, 0, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family) + return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family) case sys.I386, sys.AMD64: switch r.Type { default: - return nil, 0, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type) + return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type) case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32, IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 @@ -302,7 +304,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read case sys.ARM: switch r.Type { default: - return nil, 0, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type) + return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type) case IMAGE_REL_ARM_SECREL: rType = objabi.R_PCREL @@ -323,8 +325,9 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read // ld -r could generate multiple section symbols for the // same section but with different values, we have to take - // that into account - if issect(pesym) { + // that into account, or in the case of split resources, + // the section and its symbols are split into two sections. + if issect(pesym) || splitResources { rAdd += int64(pesym.Value) } @@ -346,7 +349,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read name, err := pesym.FullName(f.StringTable) if err != nil { - return nil, 0, err + return nil, nil, err } if name == "" { continue @@ -384,7 +387,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read bld, s, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion) if err != nil { - return nil, 0, err + return nil, nil, err } if pesym.SectionNumber == 0 { // extern @@ -402,14 +405,14 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read } else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) { sect = f.Sections[pesym.SectionNumber-1] if _, found := sectsyms[sect]; !found { - return nil, 0, fmt.Errorf("%s: %v: missing sect.sym", pn, s) + return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s) } } else { - return nil, 0, fmt.Errorf("%s: %v: sectnum < 0!", pn, s) + return nil, nil, fmt.Errorf("%s: %v: sectnum < 0!", pn, s) } if sect == nil { - return nil, 0, nil + return nil, nil, nil } if l.OuterSym(s) != 0 { @@ -418,7 +421,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read } outerName := l.SymName(l.OuterSym(s)) sectName := l.SymName(sectsyms[sect]) - return nil, 0, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName) + return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName) } bld = makeUpdater(l, bld, s) @@ -429,7 +432,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read bld.SetSize(4) if l.SymType(sectsym) == sym.STEXT { if bld.External() && !bld.DuplicateOK() { - return nil, 0, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s)) + return nil, nil, fmt.Errorf("%s: duplicate symbol definition", l.SymName(s)) } bld.SetExternal(true) } @@ -446,7 +449,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read if l.SymType(s) == sym.STEXT { for ; s != 0; s = l.SubSym(s) { if l.AttrOnList(s) { - return nil, 0, fmt.Errorf("symbol %s listed multiple times", l.SymName(s)) + return nil, nil, fmt.Errorf("symbol %s listed multiple times", l.SymName(s)) } l.SetAttrOnList(s, true) textp = append(textp, s) diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index 4eb02c9e8a..7eeb7ef568 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -786,6 +786,25 @@ func TestPErsrc(t *testing.T) { if !bytes.Contains(b, []byte("Hello Gophers!")) { t.Fatalf("binary does not contain expected content") } + + pkgdir = filepath.Join("testdata", "testPErsrc-complex") + exe = filepath.Join(tmpdir, "a.exe") + cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", exe) + cmd.Dir = pkgdir + // cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("building failed: %v, output:\n%s", err, out) + } + + // Check that the binary contains the rsrc data + b, err = ioutil.ReadFile(exe) + if err != nil { + t.Fatalf("reading output failed: %v", err) + } + if !bytes.Contains(b, []byte("resname RCDATA a.rc")) { + t.Fatalf("binary does not contain expected content") + } } func TestContentAddressableSymbols(t *testing.T) { diff --git a/src/cmd/link/testdata/testPErsrc-complex/main.go b/src/cmd/link/testdata/testPErsrc-complex/main.go new file mode 100644 index 0000000000..affd6eada2 --- /dev/null +++ b/src/cmd/link/testdata/testPErsrc-complex/main.go @@ -0,0 +1,43 @@ +// Copyright 2020 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. + +// Test that a PE rsrc section is handled correctly, when the object files +// have been created by llvm-rc or msvc's rc.exe, which means there's the +// @feat.00 symbol as well as split .rsrc$00 and .rsrc$01 section to deal with. +// +// rsrc.syso is created with: +// windres -i a.rc -o rsrc.syso -O coff +// where this windres calls into llvm-rc and llvm-cvtres. The source file, +// a.rc, simply contains a reference to its own bytes: +// +// resname RCDATA a.rc +// +// Object dumping the resultant rsrc.syso, we can see the split sections and +// the @feat.00 SEH symbol: +// +// rsrc.syso: file format coff-x86-64 +// +// architecture: x86_64 +// start address: 0x0000000000000000 +// +// Export Table: +// Sections: +// Idx Name Size VMA Type +// 0 .rsrc$01 00000068 0000000000000000 DATA +// 1 .rsrc$02 00000018 0000000000000000 DATA +// +// SYMBOL TABLE: +// [ 0](sec -1)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000011 @feat.00 +// [ 1](sec 1)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .rsrc$01 +// AUX scnlen 0x68 nreloc 1 nlnno 0 checksum 0x0 assoc 0 comdat 0 +// [ 3](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .rsrc$02 +// AUX scnlen 0x18 nreloc 0 nlnno 0 checksum 0x0 assoc 0 comdat 0 +// [ 5](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 $R000000 +// RELOCATION RECORDS FOR [.rsrc$01]: +// OFFSET TYPE VALUE +// 0000000000000048 IMAGE_REL_AMD64_ADDR32NB $R000000 + +package main + +func main() {} diff --git a/src/cmd/link/testdata/testPErsrc-complex/rsrc.syso b/src/cmd/link/testdata/testPErsrc-complex/rsrc.syso new file mode 100644 index 0000000000..eff630b8a2 Binary files /dev/null and b/src/cmd/link/testdata/testPErsrc-complex/rsrc.syso differ diff --git a/src/cmd/pack/pack.go b/src/cmd/pack/pack.go index 82546ea7dc..412ea36d60 100644 --- a/src/cmd/pack/pack.go +++ b/src/cmd/pack/pack.go @@ -43,7 +43,7 @@ func main() { ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:]) ar.scan(ar.printContents) case 'r': - ar = openArchive(os.Args[2], os.O_RDWR, os.Args[3:]) + ar = openArchive(os.Args[2], os.O_RDWR|os.O_CREATE, os.Args[3:]) ar.addFiles() case 'c': ar = openArchive(os.Args[2], os.O_RDWR|os.O_TRUNC|os.O_CREATE, os.Args[3:]) @@ -124,10 +124,13 @@ func openArchive(name string, mode int, files []string) *Archive { log.Fatal(err) } var a *archive.Archive - if mode&os.O_CREATE != 0 { // the c command + if mode&os.O_TRUNC != 0 { // the c command a, err = archive.New(f) } else { a, err = archive.Parse(f, verbose) + if err != nil && mode&os.O_CREATE != 0 { // the r command + a, err = archive.New(f) + } } if err != nil { log.Fatal(err) @@ -315,20 +318,25 @@ func (ar *Archive) extractContents1(e *archive.Entry, out io.Writer) { } // isGoCompilerObjFile reports whether file is an object file created -// by the Go compiler, which is an archive file with exactly two entries: -// __.PKGDEF and _go_.o. +// by the Go compiler, which is an archive file with exactly one entry +// of __.PKGDEF, or _go_.o, or both entries. func isGoCompilerObjFile(a *archive.Archive) bool { - if len(a.Entries) != 2 { + switch len(a.Entries) { + case 1: + return (a.Entries[0].Type == archive.EntryGoObj && a.Entries[0].Name == "_go_.o") || + (a.Entries[0].Type == archive.EntryPkgDef && a.Entries[0].Name == "__.PKGDEF") + case 2: + var foundPkgDef, foundGo bool + for _, e := range a.Entries { + if e.Type == archive.EntryPkgDef && e.Name == "__.PKGDEF" { + foundPkgDef = true + } + if e.Type == archive.EntryGoObj && e.Name == "_go_.o" { + foundGo = true + } + } + return foundPkgDef && foundGo + default: return false } - var foundPkgDef, foundGo bool - for _, e := range a.Entries { - if e.Type == archive.EntryPkgDef && e.Name == "__.PKGDEF" { - foundPkgDef = true - } - if e.Type == archive.EntryGoObj && e.Name == "_go_.o" { - foundGo = true - } - } - return foundPkgDef && foundGo } diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go index 218c7acda6..118376f9df 100644 --- a/src/cmd/pack/pack_test.go +++ b/src/cmd/pack/pack_test.go @@ -302,6 +302,95 @@ func TestIssue21703(t *testing.T) { run(goBin, "tool", "compile", "-I", ".", "b.go") } +// Test the "c" command can "see through" the archive generated by the compiler. +// This is peculiar. (See issue #43271) +func TestCreateWithCompilerObj(t *testing.T) { + testenv.MustHaveGoBuild(t) + + dir := tmpDir(t) + defer os.RemoveAll(dir) + src := filepath.Join(dir, "p.go") + prog := "package p; var X = 42\n" + err := os.WriteFile(src, []byte(prog), 0666) + if err != nil { + t.Fatal(err) + } + + run := func(args ...string) string { + return doRun(t, dir, args...) + } + + goBin := testenv.GoToolPath(t) + run(goBin, "build", "cmd/pack") // writes pack binary to dir + run(goBin, "tool", "compile", "-pack", "-o", "p.a", "p.go") + run("./pack", "c", "packed.a", "p.a") + fi, err := os.Stat(filepath.Join(dir, "p.a")) + if err != nil { + t.Fatalf("stat p.a failed: %v", err) + } + fi2, err := os.Stat(filepath.Join(dir, "packed.a")) + if err != nil { + t.Fatalf("stat packed.a failed: %v", err) + } + // For compiler-generated object file, the "c" command is + // expected to get (essentially) the same file back, instead + // of packing it into a new archive with a single entry. + if want, got := fi.Size(), fi2.Size(); want != got { + t.Errorf("packed file with different size: want %d, got %d", want, got) + } + + // Test -linkobj flag as well. + run(goBin, "tool", "compile", "-linkobj", "p2.a", "-o", "p.x", "p.go") + run("./pack", "c", "packed2.a", "p2.a") + fi, err = os.Stat(filepath.Join(dir, "p2.a")) + if err != nil { + t.Fatalf("stat p2.a failed: %v", err) + } + fi2, err = os.Stat(filepath.Join(dir, "packed2.a")) + if err != nil { + t.Fatalf("stat packed2.a failed: %v", err) + } + if want, got := fi.Size(), fi2.Size(); want != got { + t.Errorf("packed file with different size: want %d, got %d", want, got) + } + + run("./pack", "c", "packed3.a", "p.x") + fi, err = os.Stat(filepath.Join(dir, "p.x")) + if err != nil { + t.Fatalf("stat p.x failed: %v", err) + } + fi2, err = os.Stat(filepath.Join(dir, "packed3.a")) + if err != nil { + t.Fatalf("stat packed3.a failed: %v", err) + } + if want, got := fi.Size(), fi2.Size(); want != got { + t.Errorf("packed file with different size: want %d, got %d", want, got) + } +} + +// Test the "r" command creates the output file if it does not exist. +func TestRWithNonexistentFile(t *testing.T) { + testenv.MustHaveGoBuild(t) + + dir := tmpDir(t) + defer os.RemoveAll(dir) + src := filepath.Join(dir, "p.go") + prog := "package p; var X = 42\n" + err := os.WriteFile(src, []byte(prog), 0666) + if err != nil { + t.Fatal(err) + } + + run := func(args ...string) string { + return doRun(t, dir, args...) + } + + goBin := testenv.GoToolPath(t) + run(goBin, "build", "cmd/pack") // writes pack binary to dir + run(goBin, "tool", "compile", "-o", "p.o", "p.go") + run("./pack", "r", "p.a", "p.o") // should succeed +} + // doRun runs a program in a directory and returns the output. func doRun(t *testing.T, dir string, args ...string) string { cmd := exec.Command(args[0], args[1:]...) diff --git a/src/runtime/defs_freebsd_386.go b/src/runtime/defs_freebsd_386.go index 767755425c..f822934d58 100644 --- a/src/runtime/defs_freebsd_386.go +++ b/src/runtime/defs_freebsd_386.go @@ -13,10 +13,11 @@ const ( ) const ( - _EINTR = 0x4 - _EFAULT = 0xe - _EAGAIN = 0x23 - _ENOSYS = 0x4e + _EINTR = 0x4 + _EFAULT = 0xe + _EAGAIN = 0x23 + _ENOSYS = 0x4e + _ETIMEDOUT = 0x3c _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x100000 diff --git a/src/runtime/defs_freebsd_amd64.go b/src/runtime/defs_freebsd_amd64.go index 5a833426fd..0b696cf227 100644 --- a/src/runtime/defs_freebsd_amd64.go +++ b/src/runtime/defs_freebsd_amd64.go @@ -13,10 +13,11 @@ const ( ) const ( - _EINTR = 0x4 - _EFAULT = 0xe - _EAGAIN = 0x23 - _ENOSYS = 0x4e + _EINTR = 0x4 + _EFAULT = 0xe + _EAGAIN = 0x23 + _ENOSYS = 0x4e + _ETIMEDOUT = 0x3c _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x100000 diff --git a/src/runtime/defs_freebsd_arm.go b/src/runtime/defs_freebsd_arm.go index b55dfd88cf..b6f3e790cf 100644 --- a/src/runtime/defs_freebsd_arm.go +++ b/src/runtime/defs_freebsd_arm.go @@ -13,10 +13,11 @@ const ( ) const ( - _EINTR = 0x4 - _EFAULT = 0xe - _EAGAIN = 0x23 - _ENOSYS = 0x4e + _EINTR = 0x4 + _EFAULT = 0xe + _EAGAIN = 0x23 + _ENOSYS = 0x4e + _ETIMEDOUT = 0x3c _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x100000 diff --git a/src/runtime/defs_freebsd_arm64.go b/src/runtime/defs_freebsd_arm64.go index 5b9d504ba6..0759a1238f 100644 --- a/src/runtime/defs_freebsd_arm64.go +++ b/src/runtime/defs_freebsd_arm64.go @@ -13,10 +13,11 @@ const ( ) const ( - _EINTR = 0x4 - _EFAULT = 0xe - _EAGAIN = 0x23 - _ENOSYS = 0x4e + _EINTR = 0x4 + _EFAULT = 0xe + _EAGAIN = 0x23 + _ENOSYS = 0x4e + _ETIMEDOUT = 0x3c _O_NONBLOCK = 0x4 _O_CLOEXEC = 0x100000 diff --git a/src/runtime/metrics/example_test.go b/src/runtime/metrics/example_test.go new file mode 100644 index 0000000000..cade0c38bf --- /dev/null +++ b/src/runtime/metrics/example_test.go @@ -0,0 +1,96 @@ +// Copyright 2020 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 metrics_test + +import ( + "fmt" + "runtime/metrics" +) + +func ExampleRead_readingOneMetric() { + // Name of the metric we want to read. + const myMetric = "/memory/classes/heap/free:bytes" + + // Create a sample for the metric. + sample := make([]metrics.Sample, 1) + sample[0].Name = myMetric + + // Sample the metric. + metrics.Read(sample) + + // Check if the metric is actually supported. + // If it's not, the resulting value will always have + // kind KindBad. + if sample[0].Value.Kind() == metrics.KindBad { + panic(fmt.Sprintf("metric %q no longer supported", myMetric)) + } + + // Handle the result. + // + // It's OK to assume a particular Kind for a metric; + // they're guaranteed not to change. + freeBytes := sample[0].Value.Uint64() + + fmt.Printf("free but not released memory: %d\n", freeBytes) +} + +func ExampleRead_readingAllMetrics() { + // Get descriptions for all supported metrics. + descs := metrics.All() + + // Create a sample for each metric. + samples := make([]metrics.Sample, len(descs)) + for i := range samples { + samples[i].Name = descs[i].Name + } + + // Sample the metrics. Re-use the samples slice if you can! + metrics.Read(samples) + + // Iterate over all results. + for _, sample := range samples { + // Pull out the name and value. + name, value := sample.Name, sample.Value + + // Handle each sample. + switch value.Kind() { + case metrics.KindUint64: + fmt.Printf("%s: %d\n", name, value.Uint64()) + case metrics.KindFloat64: + fmt.Printf("%s: %f\n", name, value.Float64()) + case metrics.KindFloat64Histogram: + // The histogram may be quite large, so let's just pull out + // a crude estimate for the median for the sake of this example. + fmt.Printf("%s: %f\n", name, medianBucket(value.Float64Histogram())) + case metrics.KindBad: + // This should never happen because all metrics are supported + // by construction. + panic("bug in runtime/metrics package!") + default: + // This may happen as new metrics get added. + // + // The safest thing to do here is to simply log it somewhere + // as something to look into, but ignore it for now. + // In the worst case, you might temporarily miss out on a new metric. + fmt.Printf("%s: unexpected metric Kind: %v\n", name, value.Kind()) + } + } +} + +func medianBucket(h *metrics.Float64Histogram) float64 { + total := uint64(0) + for _, count := range h.Counts { + total += count + } + thresh := total / 2 + total = 0 + for i, count := range h.Counts { + total += count + if total > thresh { + return h.Buckets[i] + } + } + panic("should not happen") +} diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go index 730973a202..1c60ee2a57 100644 --- a/src/runtime/os_freebsd.go +++ b/src/runtime/os_freebsd.go @@ -166,7 +166,7 @@ func futexsleep1(addr *uint32, val uint32, ns int64) { utp = &ut } ret := sys_umtx_op(addr, _UMTX_OP_WAIT_UINT_PRIVATE, val, unsafe.Sizeof(*utp), utp) - if ret >= 0 || ret == -_EINTR { + if ret >= 0 || ret == -_EINTR || ret == -_ETIMEDOUT { return } print("umtx_wait addr=", addr, " val=", val, " ret=", ret, "\n") @@ -208,7 +208,6 @@ func newosproc(mp *m) { var oset sigset sigprocmask(_SIG_SETMASK, &sigset_all, &oset) - // TODO: Check for error. ret := thr_new(¶m, int32(unsafe.Sizeof(param))) sigprocmask(_SIG_SETMASK, &oset, nil) if ret < 0 { diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 64e102fb0a..5adcbf07dc 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -2882,7 +2882,9 @@ func wakeNetPoller(when int64) { } else { // There are no threads in the network poller, try to get // one there so it can handle new timers. - wakep() + if GOOS != "plan9" { // Temporary workaround - see issue #42303. + wakep() + } } } @@ -5128,6 +5130,26 @@ func sysmon() { } } mDoFixup() + if GOOS == "netbsd" { + // netpoll is responsible for waiting for timer + // expiration, so we typically don't have to worry + // about starting an M to service timers. (Note that + // sleep for timeSleepUntil above simply ensures sysmon + // starts running again when that timer expiration may + // cause Go code to run again). + // + // However, netbsd has a kernel bug that sometimes + // misses netpollBreak wake-ups, which can lead to + // unbounded delays servicing timers. If we detect this + // overrun, then startm to get something to handle the + // timer. + // + // See issue 42515 and + // https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50094. + if next, _ := timeSleepUntil(); next < now { + startm(nil, false) + } + } if atomic.Load(&scavenge.sysmonWake) != 0 { // Kick the scavenger awake if someone requested it. wakeScavenger() diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go index c89ce78012..55845bf2e5 100644 --- a/src/runtime/sys_darwin.go +++ b/src/runtime/sys_darwin.go @@ -467,7 +467,7 @@ func setNonblock(fd int32) { //go:cgo_import_dynamic libc_pthread_create pthread_create "/usr/lib/libSystem.B.dylib" //go:cgo_import_dynamic libc_pthread_self pthread_self "/usr/lib/libSystem.B.dylib" //go:cgo_import_dynamic libc_pthread_kill pthread_kill "/usr/lib/libSystem.B.dylib" -//go:cgo_import_dynamic libc_exit exit "/usr/lib/libSystem.B.dylib" +//go:cgo_import_dynamic libc_exit _exit "/usr/lib/libSystem.B.dylib" //go:cgo_import_dynamic libc_raise raise "/usr/lib/libSystem.B.dylib" //go:cgo_import_dynamic libc_open open "/usr/lib/libSystem.B.dylib" diff --git a/src/runtime/sys_freebsd_386.s b/src/runtime/sys_freebsd_386.s index c346e719e1..97e6d9ab36 100644 --- a/src/runtime/sys_freebsd_386.s +++ b/src/runtime/sys_freebsd_386.s @@ -13,12 +13,16 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$-4 MOVL $454, AX INT $0x80 + JAE 2(PC) + NEGL AX MOVL AX, ret+20(FP) RET TEXT runtime·thr_new(SB),NOSPLIT,$-4 MOVL $455, AX INT $0x80 + JAE 2(PC) + NEGL AX MOVL AX, ret+8(FP) RET @@ -120,6 +124,8 @@ TEXT runtime·pipe2(SB),NOSPLIT,$12-16 MOVL flags+0(FP), BX MOVL BX, 8(SP) INT $0x80 + JAE 2(PC) + NEGL AX MOVL AX, errno+12(FP) RET diff --git a/src/runtime/sys_freebsd_amd64.s b/src/runtime/sys_freebsd_amd64.s index 010b2ec4d4..07734b0d7d 100644 --- a/src/runtime/sys_freebsd_amd64.s +++ b/src/runtime/sys_freebsd_amd64.s @@ -18,6 +18,8 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0 MOVQ ut+24(FP), R8 MOVL $454, AX SYSCALL + JCC 2(PC) + NEGQ AX MOVL AX, ret+32(FP) RET @@ -26,6 +28,8 @@ TEXT runtime·thr_new(SB),NOSPLIT,$0 MOVL size+8(FP), SI MOVL $455, AX SYSCALL + JCC 2(PC) + NEGQ AX MOVL AX, ret+16(FP) RET @@ -118,6 +122,8 @@ TEXT runtime·pipe2(SB),NOSPLIT,$0-20 MOVL flags+0(FP), SI MOVL $542, AX SYSCALL + JCC 2(PC) + NEGQ AX MOVL AX, errno+16(FP) RET diff --git a/src/runtime/sys_freebsd_arm.s b/src/runtime/sys_freebsd_arm.s index 1e12f9cfcb..b12e47c576 100644 --- a/src/runtime/sys_freebsd_arm.s +++ b/src/runtime/sys_freebsd_arm.s @@ -51,6 +51,7 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0 ADD $20, R13 // arg 5 is passed on stack MOVW $SYS__umtx_op, R7 SWI $0 + RSB.CS $0, R0 SUB $20, R13 // BCS error MOVW R0, ret+20(FP) @@ -61,6 +62,7 @@ TEXT runtime·thr_new(SB),NOSPLIT,$0 MOVW size+4(FP), R1 MOVW $SYS_thr_new, R7 SWI $0 + RSB.CS $0, R0 MOVW R0, ret+8(FP) RET @@ -144,6 +146,7 @@ TEXT runtime·pipe2(SB),NOSPLIT,$0-16 MOVW flags+0(FP), R1 MOVW $SYS_pipe2, R7 SWI $0 + RSB.CS $0, R0 MOVW R0, errno+12(FP) RET diff --git a/src/runtime/sys_freebsd_arm64.s b/src/runtime/sys_freebsd_arm64.s index 8a4f9b7fa1..1aa09e87ca 100644 --- a/src/runtime/sys_freebsd_arm64.s +++ b/src/runtime/sys_freebsd_arm64.s @@ -60,6 +60,9 @@ TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0 MOVD ut+24(FP), R4 MOVD $SYS__umtx_op, R8 SVC + BCC ok + NEG R0, R0 +ok: MOVW R0, ret+32(FP) RET @@ -69,6 +72,9 @@ TEXT runtime·thr_new(SB),NOSPLIT,$0 MOVW size+8(FP), R1 MOVD $SYS_thr_new, R8 SVC + BCC ok + NEG R0, R0 +ok: MOVW R0, ret+16(FP) RET diff --git a/src/syscall/mksyscall.pl b/src/syscall/mksyscall.pl index 790df3825b..26c93ffe5b 100755 --- a/src/syscall/mksyscall.pl +++ b/src/syscall/mksyscall.pl @@ -125,7 +125,7 @@ while(<>) { # without reading the header. $text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"; - if ($darwin && $func eq "ptrace") { + if ($darwin && $func eq "ptrace1") { # The ptrace function is called from forkAndExecInChild where stack # growth is forbidden. $text .= "//go:nosplit\n" diff --git a/src/syscall/ptrace_darwin.go b/src/syscall/ptrace_darwin.go new file mode 100644 index 0000000000..a873d826b8 --- /dev/null +++ b/src/syscall/ptrace_darwin.go @@ -0,0 +1,14 @@ +// Copyright 2020 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. + +// +build !ios + +package syscall + +// Nosplit because it is called from forkAndExecInChild. +// +//go:nosplit +func ptrace(request int, pid int, addr uintptr, data uintptr) error { + return ptrace1(request, pid, addr, data) +} diff --git a/src/syscall/ptrace_ios.go b/src/syscall/ptrace_ios.go new file mode 100644 index 0000000000..2f61a88a08 --- /dev/null +++ b/src/syscall/ptrace_ios.go @@ -0,0 +1,12 @@ +// Copyright 2020 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 syscall + +// Nosplit because it is called from forkAndExecInChild. +// +//go:nosplit +func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { + panic("unimplemented") +} diff --git a/src/syscall/syscall_darwin_amd64.go b/src/syscall/syscall_darwin_amd64.go index 96fadf7837..687efff770 100644 --- a/src/syscall/syscall_darwin_amd64.go +++ b/src/syscall/syscall_darwin_amd64.go @@ -21,7 +21,7 @@ func setTimeval(sec, usec int64) Timeval { //sys Stat(path string, stat *Stat_t) (err error) = SYS_stat64 //sys Statfs(path string, stat *Statfs_t) (err error) = SYS_statfs64 //sys fstatat(fd int, path string, stat *Stat_t, flags int) (err error) = SYS_fstatat64 -//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error) +//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace func SetKevent(k *Kevent_t, fd, mode, flags int) { k.Ident = uint64(fd) diff --git a/src/syscall/syscall_darwin_arm64.go b/src/syscall/syscall_darwin_arm64.go index d267a4ae6e..ab57117475 100644 --- a/src/syscall/syscall_darwin_arm64.go +++ b/src/syscall/syscall_darwin_arm64.go @@ -21,7 +21,7 @@ func setTimeval(sec, usec int64) Timeval { //sys Stat(path string, stat *Stat_t) (err error) //sys Statfs(path string, stat *Statfs_t) (err error) //sys fstatat(fd int, path string, stat *Stat_t, flags int) (err error) -//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error) +//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace func SetKevent(k *Kevent_t, fd, mode, flags int) { k.Ident = uint64(fd) diff --git a/src/syscall/zsyscall_darwin_amd64.go b/src/syscall/zsyscall_darwin_amd64.go index ee726fb24d..4f2cdf861e 100644 --- a/src/syscall/zsyscall_darwin_amd64.go +++ b/src/syscall/zsyscall_darwin_amd64.go @@ -1971,7 +1971,7 @@ func libc_fstatat64_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT //go:nosplit -func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { +func ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) { _, _, e1 := syscall6(funcPC(libc_ptrace_trampoline), uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) if e1 != 0 { err = errnoErr(e1) diff --git a/src/syscall/zsyscall_darwin_arm64.go b/src/syscall/zsyscall_darwin_arm64.go index ac530f3108..0d8598d816 100644 --- a/src/syscall/zsyscall_darwin_arm64.go +++ b/src/syscall/zsyscall_darwin_arm64.go @@ -1971,7 +1971,7 @@ func libc_fstatat_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT //go:nosplit -func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { +func ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) { _, _, e1 := syscall6(funcPC(libc_ptrace_trampoline), uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) if e1 != 0 { err = errnoErr(e1) diff --git a/test/fixedbugs/issue11656.go b/test/fixedbugs/issue11656.go index 62b36cf790..acd3f4f3e5 100644 --- a/test/fixedbugs/issue11656.go +++ b/test/fixedbugs/issue11656.go @@ -54,27 +54,30 @@ func f(n int) { x uintptr } - // We want to force an illegal instruction, to get a crash - // at a PC value != 0. + // We want to force a seg fault, to get a crash at a PC value != 0. // Not all systems make the data section non-executable. ill := make([]byte, 64) switch runtime.GOARCH { case "386", "amd64": - binary.LittleEndian.PutUint16(ill, 0x0b0f) // ud2 + ill = append(ill, 0x89, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00) // MOVL AX, 0 case "arm": - binary.LittleEndian.PutUint32(ill, 0xe7f000f0) // no name, but permanently undefined + binary.LittleEndian.PutUint32(ill, 0xe3a00000) // MOVW $0, R0 + binary.LittleEndian.PutUint32(ill, 0xe5800000) // MOVW R0, (R0) case "arm64": - binary.LittleEndian.PutUint32(ill, 0xd4207d00) // brk #1000 + binary.LittleEndian.PutUint32(ill, 0xf90003ff) // MOVD ZR, (ZR) case "ppc64": - binary.BigEndian.PutUint32(ill, 0x7fe00008) // trap + binary.BigEndian.PutUint32(ill, 0xf8000000) // MOVD R0, (R0) case "ppc64le": - binary.LittleEndian.PutUint32(ill, 0x7fe00008) // trap + binary.LittleEndian.PutUint32(ill, 0xf8000000) // MOVD R0, (R0) case "mips", "mips64": - binary.BigEndian.PutUint32(ill, 0x00000034) // trap + binary.BigEndian.PutUint32(ill, 0xfc000000) // MOVV R0, (R0) case "mipsle", "mips64le": - binary.LittleEndian.PutUint32(ill, 0x00000034) // trap + binary.LittleEndian.PutUint32(ill, 0xfc000000) // MOVV R0, (R0) case "s390x": - binary.BigEndian.PutUint32(ill, 0) // undefined instruction + ill = append(ill, 0xa7, 0x09, 0x00, 0x00) // MOVD $0, R0 + ill = append(ill, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x24) // MOVD R0, (R0) + case "riscv64": + binary.LittleEndian.PutUint32(ill, 0x00003023) // MOV X0, (X0) default: // Just leave it as 0 and hope for the best. }