mirror of
https://github.com/golang/go
synced 2024-11-23 06:10:05 -07:00
[dev.regabi] all: merge master (c9fb4eb
) into dev.regabi
Merge List: * 2020-12-22c9fb4eb0a2
cmd/link: handle grouped resource sections * 2020-12-22c06a354bcc
test: trigger SIGSEGV instead of SIGTRAP in issue11656.go * 2020-12-220aa9b4709a
cmd/pack: r command create output file if not exist * 2020-12-224d27c4c223
runtime: correct error handling in several FreeBSD syscall wrappers * 2020-12-229b6147120a
cmd/pack: treat compiler's -linkobj output as "compiler object" * 2020-12-21bc7e4d9257
syscall: don't generate ptrace on iOS * 2020-12-216cff874c47
runtime/metrics: add Read examples * 2020-12-218438a5779b
runtime: use _exit on darwin * 2020-12-21cb95819cf6
runtime: detect netbsd netpoll overrun in sysmon * 2020-12-2153c984d976
runtime: skip wakep call in wakeNetPoller on Plan 9 * 2020-12-219abbe27710
test: skip issue11656.go on mips/mips64/ppc64 Change-Id: Ibb235fbf6a86ebcf50c686dc11f7c02d1865f845
This commit is contained in:
commit
ec741b0447
@ -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...)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
43
src/cmd/link/testdata/testPErsrc-complex/main.go
vendored
Normal file
43
src/cmd/link/testdata/testPErsrc-complex/main.go
vendored
Normal file
@ -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() {}
|
BIN
src/cmd/link/testdata/testPErsrc-complex/rsrc.syso
vendored
Normal file
BIN
src/cmd/link/testdata/testPErsrc-complex/rsrc.syso
vendored
Normal file
Binary file not shown.
@ -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
|
||||
}
|
||||
|
@ -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:]...)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
96
src/runtime/metrics/example_test.go
Normal file
96
src/runtime/metrics/example_test.go
Normal file
@ -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")
|
||||
}
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
14
src/syscall/ptrace_darwin.go
Normal file
14
src/syscall/ptrace_darwin.go
Normal file
@ -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)
|
||||
}
|
12
src/syscall/ptrace_ios.go
Normal file
12
src/syscall/ptrace_ios.go
Normal file
@ -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")
|
||||
}
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user