diff options
Diffstat (limited to 'src/cmd/internal/objabi')
-rw-r--r-- | src/cmd/internal/objabi/autotype.go | 38 | ||||
-rw-r--r-- | src/cmd/internal/objabi/flag.go | 380 | ||||
-rw-r--r-- | src/cmd/internal/objabi/flag_test.go | 26 | ||||
-rw-r--r-- | src/cmd/internal/objabi/funcid.go | 51 | ||||
-rw-r--r-- | src/cmd/internal/objabi/head.go | 114 | ||||
-rw-r--r-- | src/cmd/internal/objabi/line.go | 134 | ||||
-rw-r--r-- | src/cmd/internal/objabi/line_test.go | 50 | ||||
-rw-r--r-- | src/cmd/internal/objabi/path.go | 78 | ||||
-rw-r--r-- | src/cmd/internal/objabi/path_test.go | 92 | ||||
-rw-r--r-- | src/cmd/internal/objabi/pkgspecial.go | 123 | ||||
-rw-r--r-- | src/cmd/internal/objabi/reloctype.go | 429 | ||||
-rw-r--r-- | src/cmd/internal/objabi/reloctype_string.go | 114 | ||||
-rw-r--r-- | src/cmd/internal/objabi/stack.go | 32 | ||||
-rw-r--r-- | src/cmd/internal/objabi/symkind.go | 77 | ||||
-rw-r--r-- | src/cmd/internal/objabi/symkind_string.go | 43 | ||||
-rw-r--r-- | src/cmd/internal/objabi/typekind.go | 40 | ||||
-rw-r--r-- | src/cmd/internal/objabi/util.go | 33 |
17 files changed, 1854 insertions, 0 deletions
diff --git a/src/cmd/internal/objabi/autotype.go b/src/cmd/internal/objabi/autotype.go new file mode 100644 index 0000000..f9d17a3 --- /dev/null +++ b/src/cmd/internal/objabi/autotype.go @@ -0,0 +1,38 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package objabi + +// Auto.name +const ( + A_AUTO = 1 + iota + A_PARAM + A_DELETED_AUTO +) diff --git a/src/cmd/internal/objabi/flag.go b/src/cmd/internal/objabi/flag.go new file mode 100644 index 0000000..ee7d2fe --- /dev/null +++ b/src/cmd/internal/objabi/flag.go @@ -0,0 +1,380 @@ +// Copyright 2015 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 objabi + +import ( + "flag" + "fmt" + "internal/bisect" + "internal/buildcfg" + "io" + "log" + "os" + "reflect" + "sort" + "strconv" + "strings" +) + +func Flagcount(name, usage string, val *int) { + flag.Var((*count)(val), name, usage) +} + +func Flagfn1(name, usage string, f func(string)) { + flag.Var(fn1(f), name, usage) +} + +func Flagprint(w io.Writer) { + flag.CommandLine.SetOutput(w) + flag.PrintDefaults() +} + +func Flagparse(usage func()) { + flag.Usage = usage + os.Args = expandArgs(os.Args) + flag.Parse() +} + +// expandArgs expands "response files" arguments in the provided slice. +// +// A "response file" argument starts with '@' and the rest of that +// argument is a filename with CR-or-CRLF-separated arguments. Each +// argument in the named files can also contain response file +// arguments. See Issue 18468. +// +// The returned slice 'out' aliases 'in' iff the input did not contain +// any response file arguments. +// +// TODO: handle relative paths of recursive expansions in different directories? +// Is there a spec for this? Are relative paths allowed? +func expandArgs(in []string) (out []string) { + // out is nil until we see a "@" argument. + for i, s := range in { + if strings.HasPrefix(s, "@") { + if out == nil { + out = make([]string, 0, len(in)*2) + out = append(out, in[:i]...) + } + slurp, err := os.ReadFile(s[1:]) + if err != nil { + log.Fatal(err) + } + args := strings.Split(strings.TrimSpace(strings.Replace(string(slurp), "\r", "", -1)), "\n") + for i, arg := range args { + args[i] = DecodeArg(arg) + } + out = append(out, expandArgs(args)...) + } else if out != nil { + out = append(out, s) + } + } + if out == nil { + return in + } + return +} + +func AddVersionFlag() { + flag.Var(versionFlag{}, "V", "print version and exit") +} + +var buildID string // filled in by linker + +type versionFlag struct{} + +func (versionFlag) IsBoolFlag() bool { return true } +func (versionFlag) Get() interface{} { return nil } +func (versionFlag) String() string { return "" } +func (versionFlag) Set(s string) error { + name := os.Args[0] + name = name[strings.LastIndex(name, `/`)+1:] + name = name[strings.LastIndex(name, `\`)+1:] + name = strings.TrimSuffix(name, ".exe") + + p := "" + + if s == "goexperiment" { + // test/run.go uses this to discover the full set of + // experiment tags. Report everything. + p = " X:" + strings.Join(buildcfg.Experiment.All(), ",") + } else { + // If the enabled experiments differ from the baseline, + // include that difference. + if goexperiment := buildcfg.Experiment.String(); goexperiment != "" { + p = " X:" + goexperiment + } + } + + // The go command invokes -V=full to get a unique identifier + // for this tool. It is assumed that the release version is sufficient + // for releases, but during development we include the full + // build ID of the binary, so that if the compiler is changed and + // rebuilt, we notice and rebuild all packages. + if s == "full" { + if strings.HasPrefix(buildcfg.Version, "devel") { + p += " buildID=" + buildID + } + } + + fmt.Printf("%s version %s%s\n", name, buildcfg.Version, p) + os.Exit(0) + return nil +} + +// count is a flag.Value that is like a flag.Bool and a flag.Int. +// If used as -name, it increments the count, but -name=x sets the count. +// Used for verbose flag -v. +type count int + +func (c *count) String() string { + return fmt.Sprint(int(*c)) +} + +func (c *count) Set(s string) error { + switch s { + case "true": + *c++ + case "false": + *c = 0 + default: + n, err := strconv.Atoi(s) + if err != nil { + return fmt.Errorf("invalid count %q", s) + } + *c = count(n) + } + return nil +} + +func (c *count) Get() interface{} { + return int(*c) +} + +func (c *count) IsBoolFlag() bool { + return true +} + +func (c *count) IsCountFlag() bool { + return true +} + +type fn1 func(string) + +func (f fn1) Set(s string) error { + f(s) + return nil +} + +func (f fn1) String() string { return "" } + +// DecodeArg decodes an argument. +// +// This function is public for testing with the parallel encoder. +func DecodeArg(arg string) string { + // If no encoding, fastpath out. + if !strings.ContainsAny(arg, "\\\n") { + return arg + } + + var b strings.Builder + var wasBS bool + for _, r := range arg { + if wasBS { + switch r { + case '\\': + b.WriteByte('\\') + case 'n': + b.WriteByte('\n') + default: + // This shouldn't happen. The only backslashes that reach here + // should encode '\n' and '\\' exclusively. + panic("badly formatted input") + } + } else if r == '\\' { + wasBS = true + continue + } else { + b.WriteRune(r) + } + wasBS = false + } + return b.String() +} + +type debugField struct { + name string + help string + concurrentOk bool // true if this field/flag is compatible with concurrent compilation + val interface{} // *int or *string +} + +type DebugFlag struct { + tab map[string]debugField + concurrentOk *bool // this is non-nil only for compiler's DebugFlags, but only compiler has concurrent:ok fields + debugSSA DebugSSA // this is non-nil only for compiler's DebugFlags. +} + +// A DebugSSA function is called to set a -d ssa/... option. +// If nil, those options are reported as invalid options. +// If DebugSSA returns a non-empty string, that text is reported as a compiler error. +// If phase is "help", it should print usage information and terminate the process. +type DebugSSA func(phase, flag string, val int, valString string) string + +// NewDebugFlag constructs a DebugFlag for the fields of debug, which +// must be a pointer to a struct. +// +// Each field of *debug is a different value, named for the lower-case of the field name. +// Each field must be an int or string and must have a `help` struct tag. +// There may be an "Any bool" field, which will be set if any debug flags are set. +// +// The returned flag takes a comma-separated list of settings. +// Each setting is name=value; for ints, name is short for name=1. +// +// If debugSSA is non-nil, any debug flags of the form ssa/... will be +// passed to debugSSA for processing. +func NewDebugFlag(debug interface{}, debugSSA DebugSSA) *DebugFlag { + flag := &DebugFlag{ + tab: make(map[string]debugField), + debugSSA: debugSSA, + } + + v := reflect.ValueOf(debug).Elem() + t := v.Type() + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + ptr := v.Field(i).Addr().Interface() + if f.Name == "ConcurrentOk" { + switch ptr := ptr.(type) { + default: + panic("debug.ConcurrentOk must have type bool") + case *bool: + flag.concurrentOk = ptr + } + continue + } + name := strings.ToLower(f.Name) + help := f.Tag.Get("help") + if help == "" { + panic(fmt.Sprintf("debug.%s is missing help text", f.Name)) + } + concurrent := f.Tag.Get("concurrent") + + switch ptr.(type) { + default: + panic(fmt.Sprintf("debug.%s has invalid type %v (must be int, string, or *bisect.Matcher)", f.Name, f.Type)) + case *int, *string, **bisect.Matcher: + // ok + } + flag.tab[name] = debugField{name, help, concurrent == "ok", ptr} + } + + return flag +} + +func (f *DebugFlag) Set(debugstr string) error { + if debugstr == "" { + return nil + } + for _, name := range strings.Split(debugstr, ",") { + if name == "" { + continue + } + // display help about the debug option itself and quit + if name == "help" { + fmt.Print(debugHelpHeader) + maxLen, names := 0, []string{} + if f.debugSSA != nil { + maxLen = len("ssa/help") + } + for name := range f.tab { + if len(name) > maxLen { + maxLen = len(name) + } + names = append(names, name) + } + sort.Strings(names) + // Indent multi-line help messages. + nl := fmt.Sprintf("\n\t%-*s\t", maxLen, "") + for _, name := range names { + help := f.tab[name].help + fmt.Printf("\t%-*s\t%s\n", maxLen, name, strings.Replace(help, "\n", nl, -1)) + } + if f.debugSSA != nil { + // ssa options have their own help + fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging") + } + os.Exit(0) + } + + val, valstring, haveInt := 1, "", true + if i := strings.IndexAny(name, "=:"); i >= 0 { + var err error + name, valstring = name[:i], name[i+1:] + val, err = strconv.Atoi(valstring) + if err != nil { + val, haveInt = 1, false + } + } + + if t, ok := f.tab[name]; ok { + switch vp := t.val.(type) { + case nil: + // Ignore + case *string: + *vp = valstring + case *int: + if !haveInt { + log.Fatalf("invalid debug value %v", name) + } + *vp = val + case **bisect.Matcher: + var err error + *vp, err = bisect.New(valstring) + if err != nil { + log.Fatalf("debug flag %v: %v", name, err) + } + default: + panic("bad debugtab type") + } + // assembler DebugFlags don't have a ConcurrentOk field to reset, so check against that. + if !t.concurrentOk && f.concurrentOk != nil { + *f.concurrentOk = false + } + } else if f.debugSSA != nil && strings.HasPrefix(name, "ssa/") { + // expect form ssa/phase/flag + // e.g. -d=ssa/generic_cse/time + // _ in phase name also matches space + phase := name[4:] + flag := "debug" // default flag is debug + if i := strings.Index(phase, "/"); i >= 0 { + flag = phase[i+1:] + phase = phase[:i] + } + err := f.debugSSA(phase, flag, val, valstring) + if err != "" { + log.Fatalf(err) + } + // Setting this false for -d=ssa/... preserves old behavior + // of turning off concurrency for any debug flags. + // It's not known for sure if this is necessary, but it is safe. + *f.concurrentOk = false + + } else { + return fmt.Errorf("unknown debug key %s\n", name) + } + } + + return nil +} + +const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>] + +<key> is one of: + +` + +func (f *DebugFlag) String() string { + return "" +} diff --git a/src/cmd/internal/objabi/flag_test.go b/src/cmd/internal/objabi/flag_test.go new file mode 100644 index 0000000..935b9c2 --- /dev/null +++ b/src/cmd/internal/objabi/flag_test.go @@ -0,0 +1,26 @@ +// 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 objabi + +import "testing" + +func TestDecodeArg(t *testing.T) { + t.Parallel() + tests := []struct { + arg, want string + }{ + {"", ""}, + {"hello", "hello"}, + {"hello\\n", "hello\n"}, + {"hello\\nthere", "hello\nthere"}, + {"hello\\\\there", "hello\\there"}, + {"\\\\\\n", "\\\n"}, + } + for _, test := range tests { + if got := DecodeArg(test.arg); got != test.want { + t.Errorf("decodoeArg(%q) = %q, want %q", test.arg, got, test.want) + } + } +} diff --git a/src/cmd/internal/objabi/funcid.go b/src/cmd/internal/objabi/funcid.go new file mode 100644 index 0000000..d9b47f1 --- /dev/null +++ b/src/cmd/internal/objabi/funcid.go @@ -0,0 +1,51 @@ +// Copyright 2018 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 objabi + +import ( + "internal/abi" + "strings" +) + +var funcIDs = map[string]abi.FuncID{ + "abort": abi.FuncID_abort, + "asmcgocall": abi.FuncID_asmcgocall, + "asyncPreempt": abi.FuncID_asyncPreempt, + "cgocallback": abi.FuncID_cgocallback, + "corostart": abi.FuncID_corostart, + "debugCallV2": abi.FuncID_debugCallV2, + "gcBgMarkWorker": abi.FuncID_gcBgMarkWorker, + "rt0_go": abi.FuncID_rt0_go, + "goexit": abi.FuncID_goexit, + "gogo": abi.FuncID_gogo, + "gopanic": abi.FuncID_gopanic, + "handleAsyncEvent": abi.FuncID_handleAsyncEvent, + "main": abi.FuncID_runtime_main, + "mcall": abi.FuncID_mcall, + "morestack": abi.FuncID_morestack, + "mstart": abi.FuncID_mstart, + "panicwrap": abi.FuncID_panicwrap, + "runfinq": abi.FuncID_runfinq, + "sigpanic": abi.FuncID_sigpanic, + "systemstack_switch": abi.FuncID_systemstack_switch, + "systemstack": abi.FuncID_systemstack, + + // Don't show in call stack but otherwise not special. + "deferreturn": abi.FuncIDWrapper, +} + +// Get the function ID for the named function in the named file. +// The function should be package-qualified. +func GetFuncID(name string, isWrapper bool) abi.FuncID { + if isWrapper { + return abi.FuncIDWrapper + } + if strings.HasPrefix(name, "runtime.") { + if id, ok := funcIDs[name[len("runtime."):]]; ok { + return id + } + } + return abi.FuncIDNormal +} diff --git a/src/cmd/internal/objabi/head.go b/src/cmd/internal/objabi/head.go new file mode 100644 index 0000000..3109b5c --- /dev/null +++ b/src/cmd/internal/objabi/head.go @@ -0,0 +1,114 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package objabi + +import "fmt" + +// HeadType is the executable header type. +type HeadType uint8 + +const ( + Hunknown HeadType = iota + Hdarwin + Hdragonfly + Hfreebsd + Hjs + Hlinux + Hnetbsd + Hopenbsd + Hplan9 + Hsolaris + Hwasip1 + Hwindows + Haix +) + +func (h *HeadType) Set(s string) error { + switch s { + case "aix": + *h = Haix + case "darwin", "ios": + *h = Hdarwin + case "dragonfly": + *h = Hdragonfly + case "freebsd": + *h = Hfreebsd + case "js": + *h = Hjs + case "linux", "android": + *h = Hlinux + case "netbsd": + *h = Hnetbsd + case "openbsd": + *h = Hopenbsd + case "plan9": + *h = Hplan9 + case "illumos", "solaris": + *h = Hsolaris + case "wasip1": + *h = Hwasip1 + case "windows": + *h = Hwindows + default: + return fmt.Errorf("invalid headtype: %q", s) + } + return nil +} + +func (h HeadType) String() string { + switch h { + case Haix: + return "aix" + case Hdarwin: + return "darwin" + case Hdragonfly: + return "dragonfly" + case Hfreebsd: + return "freebsd" + case Hjs: + return "js" + case Hlinux: + return "linux" + case Hnetbsd: + return "netbsd" + case Hopenbsd: + return "openbsd" + case Hplan9: + return "plan9" + case Hsolaris: + return "solaris" + case Hwasip1: + return "wasip1" + case Hwindows: + return "windows" + } + return fmt.Sprintf("HeadType(%d)", h) +} diff --git a/src/cmd/internal/objabi/line.go b/src/cmd/internal/objabi/line.go new file mode 100644 index 0000000..80a1137 --- /dev/null +++ b/src/cmd/internal/objabi/line.go @@ -0,0 +1,134 @@ +// Copyright 2009 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 objabi + +import ( + "internal/buildcfg" + "os" + "path/filepath" + "runtime" + "strings" +) + +// WorkingDir returns the current working directory +// (or "/???" if the directory cannot be identified), +// with "/" as separator. +func WorkingDir() string { + var path string + path, _ = os.Getwd() + if path == "" { + path = "/???" + } + return filepath.ToSlash(path) +} + +// AbsFile returns the absolute filename for file in the given directory, +// as rewritten by the rewrites argument. +// For unrewritten paths, AbsFile rewrites a leading $GOROOT prefix to the literal "$GOROOT". +// If the resulting path is the empty string, the result is "??". +// +// The rewrites argument is a ;-separated list of rewrites. +// Each rewrite is of the form "prefix" or "prefix=>replace", +// where prefix must match a leading sequence of path elements +// and is either removed entirely or replaced by the replacement. +func AbsFile(dir, file, rewrites string) string { + abs := file + if dir != "" && !filepath.IsAbs(file) { + abs = filepath.Join(dir, file) + } + + abs, rewritten := ApplyRewrites(abs, rewrites) + if !rewritten && buildcfg.GOROOT != "" && hasPathPrefix(abs, buildcfg.GOROOT) { + abs = "$GOROOT" + abs[len(buildcfg.GOROOT):] + } + + // Rewrite paths to match the slash convention of the target. + // This helps ensure that cross-compiled distributions remain + // bit-for-bit identical to natively compiled distributions. + if runtime.GOOS == "windows" { + abs = strings.ReplaceAll(abs, `\`, "/") + } + + if abs == "" { + abs = "??" + } + return abs +} + +// ApplyRewrites returns the filename for file in the given directory, +// as rewritten by the rewrites argument. +// +// The rewrites argument is a ;-separated list of rewrites. +// Each rewrite is of the form "prefix" or "prefix=>replace", +// where prefix must match a leading sequence of path elements +// and is either removed entirely or replaced by the replacement. +func ApplyRewrites(file, rewrites string) (string, bool) { + start := 0 + for i := 0; i <= len(rewrites); i++ { + if i == len(rewrites) || rewrites[i] == ';' { + if new, ok := applyRewrite(file, rewrites[start:i]); ok { + return new, true + } + start = i + 1 + } + } + + return file, false +} + +// applyRewrite applies the rewrite to the path, +// returning the rewritten path and a boolean +// indicating whether the rewrite applied at all. +func applyRewrite(path, rewrite string) (string, bool) { + prefix, replace := rewrite, "" + if j := strings.LastIndex(rewrite, "=>"); j >= 0 { + prefix, replace = rewrite[:j], rewrite[j+len("=>"):] + } + + if prefix == "" || !hasPathPrefix(path, prefix) { + return path, false + } + if len(path) == len(prefix) { + return replace, true + } + if replace == "" { + return path[len(prefix)+1:], true + } + return replace + path[len(prefix):], true +} + +// Does s have t as a path prefix? +// That is, does s == t or does s begin with t followed by a slash? +// For portability, we allow ASCII case folding, so that hasPathPrefix("a/b/c", "A/B") is true. +// Similarly, we allow slash folding, so that hasPathPrefix("a/b/c", "a\\b") is true. +// We do not allow full Unicode case folding, for fear of causing more confusion +// or harm than good. (For an example of the kinds of things that can go wrong, +// see http://article.gmane.org/gmane.linux.kernel/1853266.) +func hasPathPrefix(s string, t string) bool { + if len(t) > len(s) { + return false + } + var i int + for i = 0; i < len(t); i++ { + cs := int(s[i]) + ct := int(t[i]) + if 'A' <= cs && cs <= 'Z' { + cs += 'a' - 'A' + } + if 'A' <= ct && ct <= 'Z' { + ct += 'a' - 'A' + } + if cs == '\\' { + cs = '/' + } + if ct == '\\' { + ct = '/' + } + if cs != ct { + return false + } + } + return i >= len(s) || s[i] == '/' || s[i] == '\\' +} diff --git a/src/cmd/internal/objabi/line_test.go b/src/cmd/internal/objabi/line_test.go new file mode 100644 index 0000000..1fa0ff1 --- /dev/null +++ b/src/cmd/internal/objabi/line_test.go @@ -0,0 +1,50 @@ +// Copyright 2019 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 objabi + +import ( + "path/filepath" + "runtime" + "testing" +) + +// On Windows, "/foo" is reported as a relative path +// (it is relative to the current drive letter), +// so we need add a drive letter to test absolute path cases. +func drive() string { + if runtime.GOOS == "windows" { + return "c:" + } + return "" +} + +var absFileTests = []struct { + dir string + file string + rewrites string + abs string +}{ + {"/d", "f", "", "/d/f"}, + {"/d", drive() + "/f", "", drive() + "/f"}, + {"/d", "f/g", "", "/d/f/g"}, + {"/d", drive() + "/f/g", "", drive() + "/f/g"}, + + {"/d", "f", "/d/f", "??"}, + {"/d", "f/g", "/d/f", "g"}, + {"/d", "f/g", "/d/f=>h", "h/g"}, + {"/d", "f/g", "/d/f=>/h", "/h/g"}, + {"/d", "f/g", "/d/f=>/h;/d/e=>/i", "/h/g"}, + {"/d", "e/f", "/d/f=>/h;/d/e=>/i", "/i/f"}, +} + +func TestAbsFile(t *testing.T) { + for _, tt := range absFileTests { + abs := filepath.FromSlash(AbsFile(filepath.FromSlash(tt.dir), filepath.FromSlash(tt.file), tt.rewrites)) + want := filepath.FromSlash(tt.abs) + if abs != want { + t.Errorf("AbsFile(%q, %q, %q) = %q, want %q", tt.dir, tt.file, tt.rewrites, abs, want) + } + } +} diff --git a/src/cmd/internal/objabi/path.go b/src/cmd/internal/objabi/path.go new file mode 100644 index 0000000..30301b1 --- /dev/null +++ b/src/cmd/internal/objabi/path.go @@ -0,0 +1,78 @@ +// Copyright 2017 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 objabi + +import ( + "fmt" + "strconv" + "strings" +) + +// PathToPrefix converts raw string to the prefix that will be used in the +// symbol table. All control characters, space, '%' and '"', as well as +// non-7-bit clean bytes turn into %xx. The period needs escaping only in the +// last segment of the path, and it makes for happier users if we escape that as +// little as possible. +func PathToPrefix(s string) string { + slash := strings.LastIndex(s, "/") + // check for chars that need escaping + n := 0 + for r := 0; r < len(s); r++ { + if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F { + n++ + } + } + + // quick exit + if n == 0 { + return s + } + + // escape + const hex = "0123456789abcdef" + p := make([]byte, 0, len(s)+2*n) + for r := 0; r < len(s); r++ { + if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F { + p = append(p, '%', hex[c>>4], hex[c&0xF]) + } else { + p = append(p, c) + } + } + + return string(p) +} + +// PrefixToPath is the inverse of PathToPrefix, replacing escape sequences with +// the original character. +func PrefixToPath(s string) (string, error) { + percent := strings.IndexByte(s, '%') + if percent == -1 { + return s, nil + } + + p := make([]byte, 0, len(s)) + for i := 0; i < len(s); { + if s[i] != '%' { + p = append(p, s[i]) + i++ + continue + } + if i+2 >= len(s) { + // Not enough characters remaining to be a valid escape + // sequence. + return "", fmt.Errorf("malformed prefix %q: escape sequence must contain two hex digits", s) + } + + b, err := strconv.ParseUint(s[i+1:i+3], 16, 8) + if err != nil { + // Not a valid escape sequence. + return "", fmt.Errorf("malformed prefix %q: escape sequence %q must contain two hex digits", s, s[i:i+3]) + } + + p = append(p, byte(b)) + i += 3 + } + return string(p), nil +} diff --git a/src/cmd/internal/objabi/path_test.go b/src/cmd/internal/objabi/path_test.go new file mode 100644 index 0000000..934db3d --- /dev/null +++ b/src/cmd/internal/objabi/path_test.go @@ -0,0 +1,92 @@ +// Copyright 2017 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 objabi + +import ( + "internal/testenv" + "os/exec" + "strings" + "testing" +) + +var escapeTests = []struct { + Path string + Escaped string + }{ + {"foo/bar/v1", "foo/bar/v1"}, + {"foo/bar/v.1", "foo/bar/v%2e1"}, + {"f.o.o/b.a.r/v1", "f.o.o/b.a.r/v1"}, + {"f.o.o/b.a.r/v.1", "f.o.o/b.a.r/v%2e1"}, + {"f.o.o/b.a.r/v..1", "f.o.o/b.a.r/v%2e%2e1"}, + {"f.o.o/b.a.r/v..1.", "f.o.o/b.a.r/v%2e%2e1%2e"}, + {"f.o.o/b.a.r/v%1", "f.o.o/b.a.r/v%251"}, + {"runtime", "runtime"}, + {"sync/atomic", "sync/atomic"}, + {"golang.org/x/tools/godoc", "golang.org/x/tools/godoc"}, + {"foo.bar/baz.quux", "foo.bar/baz%2equux"}, + {"", ""}, + {"%foo%bar", "%25foo%25bar"}, + {"\x01\x00\x7F☺", "%01%00%7f%e2%98%ba"}, + } + +func TestPathToPrefix(t *testing.T) { + for _, tc := range escapeTests { + if got := PathToPrefix(tc.Path); got != tc.Escaped { + t.Errorf("expected PathToPrefix(%s) = %s, got %s", tc.Path, tc.Escaped, got) + } + } +} + +func TestPrefixToPath(t *testing.T) { + for _, tc := range escapeTests { + got, err := PrefixToPath(tc.Escaped) + if err != nil { + t.Errorf("expected PrefixToPath(%s) err = nil, got %v", tc.Escaped, err) + } + if got != tc.Path { + t.Errorf("expected PrefixToPath(%s) = %s, got %s", tc.Escaped, tc.Path, got) + } + } +} + +func TestPrefixToPathError(t *testing.T) { + tests := []string{ + "foo%", + "foo%1", + "foo%%12", + "foo%1g", + } + for _, tc := range tests { + _, err := PrefixToPath(tc) + if err == nil { + t.Errorf("expected PrefixToPath(%s) err != nil, got nil", tc) + } + } +} + +func TestRuntimePackageList(t *testing.T) { + // Test that all packages imported by the runtime are marked as runtime + // packages. + testenv.MustHaveGoBuild(t) + goCmd, err := testenv.GoTool() + if err != nil { + t.Fatal(err) + } + pkgList, err := exec.Command(goCmd, "list", "-deps", "runtime").Output() + if err != nil { + if err, ok := err.(*exec.ExitError); ok { + t.Log(string(err.Stderr)) + } + t.Fatal(err) + } + for _, pkg := range strings.Split(strings.TrimRight(string(pkgList), "\n"), "\n") { + if pkg == "unsafe" { + continue + } + if !LookupPkgSpecial(pkg).Runtime { + t.Errorf("package %s is imported by runtime, but not marked Runtime", pkg) + } + } +} diff --git a/src/cmd/internal/objabi/pkgspecial.go b/src/cmd/internal/objabi/pkgspecial.go new file mode 100644 index 0000000..6df95f3 --- /dev/null +++ b/src/cmd/internal/objabi/pkgspecial.go @@ -0,0 +1,123 @@ +// Copyright 2023 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 objabi + +import "sync" + +// PkgSpecial indicates special build properties of a given runtime-related +// package. +type PkgSpecial struct { + // Runtime indicates that this package is "runtime" or imported by + // "runtime". This has several effects (which maybe should be split out): + // + // - Implicit allocation is disallowed. + // + // - Various runtime pragmas are enabled. + // + // - Optimizations are always enabled. + // + // This should be set for runtime and all packages it imports, and may be + // set for additional packages. + Runtime bool + + // NoInstrument indicates this package should not receive sanitizer + // instrumentation. In many of these, instrumentation could cause infinite + // recursion. This is all runtime packages, plus those that support the + // sanitizers. + NoInstrument bool + + // NoRaceFunc indicates functions in this package should not get + // racefuncenter/racefuncexit instrumentation Memory accesses in these + // packages are either uninteresting or will cause false positives. + NoRaceFunc bool + + // AllowAsmABI indicates that assembly in this package is allowed to use ABI + // selectors in symbol names. Generally this is needed for packages that + // interact closely with the runtime package or have performance-critical + // assembly. + AllowAsmABI bool +} + +var runtimePkgs = []string{ + "runtime", + + "runtime/internal/atomic", + "runtime/internal/math", + "runtime/internal/sys", + "runtime/internal/syscall", + + "internal/abi", + "internal/bytealg", + "internal/chacha8rand", + "internal/coverage/rtcov", + "internal/cpu", + "internal/goarch", + "internal/godebugs", + "internal/goexperiment", + "internal/goos", +} + +// extraNoInstrumentPkgs is the set of packages in addition to runtimePkgs that +// should have NoInstrument set. +var extraNoInstrumentPkgs = []string{ + "runtime/race", + "runtime/msan", + "runtime/asan", + // We omit bytealg even though it's imported by runtime because it also + // backs a lot of package bytes. Currently we don't have a way to omit race + // instrumentation when used from the runtime while keeping race + // instrumentation when used from user code. Somehow this doesn't seem to + // cause problems, though we may be skating on thin ice. See #61204. + "-internal/bytealg", +} + +var noRaceFuncPkgs = []string{"sync", "sync/atomic"} + +var allowAsmABIPkgs = []string{ + "runtime", + "reflect", + "syscall", + "internal/bytealg", + "internal/chacha8rand", + "runtime/internal/syscall", + "runtime/internal/startlinetest", +} + +var ( + pkgSpecials map[string]PkgSpecial + pkgSpecialsOnce sync.Once +) + +// LookupPkgSpecial returns special build properties for the given package path. +func LookupPkgSpecial(pkgPath string) PkgSpecial { + pkgSpecialsOnce.Do(func() { + // Construct pkgSpecials from various package lists. This lets us use + // more flexible logic, while keeping the final map simple, and avoids + // the init-time cost of a map. + pkgSpecials = make(map[string]PkgSpecial) + set := func(elt string, f func(*PkgSpecial)) { + s := pkgSpecials[elt] + f(&s) + pkgSpecials[elt] = s + } + for _, pkg := range runtimePkgs { + set(pkg, func(ps *PkgSpecial) { ps.Runtime = true; ps.NoInstrument = true }) + } + for _, pkg := range extraNoInstrumentPkgs { + if pkg[0] == '-' { + set(pkg[1:], func(ps *PkgSpecial) { ps.NoInstrument = false }) + } else { + set(pkg, func(ps *PkgSpecial) { ps.NoInstrument = true }) + } + } + for _, pkg := range noRaceFuncPkgs { + set(pkg, func(ps *PkgSpecial) { ps.NoRaceFunc = true }) + } + for _, pkg := range allowAsmABIPkgs { + set(pkg, func(ps *PkgSpecial) { ps.AllowAsmABI = true }) + } + }) + return pkgSpecials[pkgPath] +} diff --git a/src/cmd/internal/objabi/reloctype.go b/src/cmd/internal/objabi/reloctype.go new file mode 100644 index 0000000..5442984 --- /dev/null +++ b/src/cmd/internal/objabi/reloctype.go @@ -0,0 +1,429 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package objabi + +type RelocType int16 + +//go:generate stringer -type=RelocType +const ( + R_ADDR RelocType = 1 + iota + // R_ADDRPOWER relocates a pair of "D-form" instructions (instructions with 16-bit + // immediates in the low half of the instruction word), usually addis followed by + // another add or a load, inserting the "high adjusted" 16 bits of the address of + // the referenced symbol into the immediate field of the first instruction and the + // low 16 bits into that of the second instruction. + R_ADDRPOWER + // R_ADDRARM64 relocates an adrp, add pair to compute the address of the + // referenced symbol. + R_ADDRARM64 + // R_ADDRMIPS (only used on mips/mips64) resolves to the low 16 bits of an external + // address, by encoding it into the instruction. + R_ADDRMIPS + // R_ADDROFF resolves to a 32-bit offset from the beginning of the section + // holding the data being relocated to the referenced symbol. + R_ADDROFF + R_SIZE + R_CALL + R_CALLARM + R_CALLARM64 + R_CALLIND + R_CALLPOWER + // R_CALLMIPS (only used on mips64) resolves to non-PC-relative target address + // of a CALL (JAL) instruction, by encoding the address into the instruction. + R_CALLMIPS + R_CONST + R_PCREL + // R_TLS_LE, used on 386, amd64, and ARM, resolves to the offset of the + // thread-local symbol from the thread local base and is used to implement the + // "local exec" model for tls access (r.Sym is not set on intel platforms but is + // set to a TLS symbol -- runtime.tlsg -- in the linker when externally linking). + R_TLS_LE + // R_TLS_IE, used 386, amd64, and ARM resolves to the PC-relative offset to a GOT + // slot containing the offset from the thread-local symbol from the thread local + // base and is used to implemented the "initial exec" model for tls access (r.Sym + // is not set on intel platforms but is set to a TLS symbol -- runtime.tlsg -- in + // the linker when externally linking). + R_TLS_IE + R_GOTOFF + R_PLT0 + R_PLT1 + R_PLT2 + R_USEFIELD + // R_USETYPE resolves to an *rtype, but no relocation is created. The + // linker uses this as a signal that the pointed-to type information + // should be linked into the final binary, even if there are no other + // direct references. (This is used for types reachable by reflection.) + R_USETYPE + // R_USEIFACE marks a type is converted to an interface in the function this + // relocation is applied to. The target is a type descriptor or an itab + // (in the latter case it refers to the concrete type contained in the itab). + // This is a marker relocation (0-sized), for the linker's reachabililty + // analysis. + R_USEIFACE + // R_USEIFACEMETHOD marks an interface method that is used in the function + // this relocation is applied to. The target is an interface type descriptor. + // The addend is the offset of the method in the type descriptor. + // This is a marker relocation (0-sized), for the linker's reachabililty + // analysis. + R_USEIFACEMETHOD + // R_USENAMEDMETHOD marks that methods with a specific name must not be eliminated. + // The target is a symbol containing the name of a method called via a generic + // interface or looked up via MethodByName("F"). + R_USENAMEDMETHOD + // R_METHODOFF resolves to a 32-bit offset from the beginning of the section + // holding the data being relocated to the referenced symbol. + // It is a variant of R_ADDROFF used when linking from the uncommonType of a + // *rtype, and may be set to zero by the linker if it determines the method + // text is unreachable by the linked program. + R_METHODOFF + // R_KEEP tells the linker to keep the referred-to symbol in the final binary + // if the symbol containing the R_KEEP relocation is in the final binary. + R_KEEP + R_POWER_TOC + R_GOTPCREL + // R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address + // of a JMP instruction, by encoding the address into the instruction. + // The stack nosplit check ignores this since it is not a function call. + R_JMPMIPS + + // R_DWARFSECREF resolves to the offset of the symbol from its section. + // Target of relocation must be size 4 (in current implementation). + R_DWARFSECREF + + // R_DWARFFILEREF resolves to an index into the DWARF .debug_line + // file table for the specified file symbol. Must be applied to an + // attribute of form DW_FORM_data4. + R_DWARFFILEREF + + // Platform dependent relocations. Architectures with fixed width instructions + // have the inherent issue that a 32-bit (or 64-bit!) displacement cannot be + // stuffed into a 32-bit instruction, so an address needs to be spread across + // several instructions, and in turn this requires a sequence of relocations, each + // updating a part of an instruction. This leads to relocation codes that are + // inherently processor specific. + + // Arm64. + + // Set a MOV[NZ] immediate field to bits [15:0] of the offset from the thread + // local base to the thread local variable defined by the referenced (thread + // local) symbol. Error if the offset does not fit into 16 bits. + R_ARM64_TLS_LE + + // Relocates an ADRP; LD64 instruction sequence to load the offset between + // the thread local base and the thread local variable defined by the + // referenced (thread local) symbol from the GOT. + R_ARM64_TLS_IE + + // R_ARM64_GOTPCREL relocates an adrp, ld64 pair to compute the address of the GOT + // slot of the referenced symbol. + R_ARM64_GOTPCREL + + // R_ARM64_GOT resolves a GOT-relative instruction sequence, usually an adrp + // followed by another ld instruction. + R_ARM64_GOT + + // R_ARM64_PCREL resolves a PC-relative addresses instruction sequence, usually an + // adrp followed by another add instruction. + R_ARM64_PCREL + + // R_ARM64_PCREL_LDST8 resolves a PC-relative addresses instruction sequence, usually an + // adrp followed by a LD8 or ST8 instruction. + R_ARM64_PCREL_LDST8 + + // R_ARM64_PCREL_LDST16 resolves a PC-relative addresses instruction sequence, usually an + // adrp followed by a LD16 or ST16 instruction. + R_ARM64_PCREL_LDST16 + + // R_ARM64_PCREL_LDST32 resolves a PC-relative addresses instruction sequence, usually an + // adrp followed by a LD32 or ST32 instruction. + R_ARM64_PCREL_LDST32 + + // R_ARM64_PCREL_LDST64 resolves a PC-relative addresses instruction sequence, usually an + // adrp followed by a LD64 or ST64 instruction. + R_ARM64_PCREL_LDST64 + + // R_ARM64_LDST8 sets a LD/ST immediate value to bits [11:0] of a local address. + R_ARM64_LDST8 + + // R_ARM64_LDST16 sets a LD/ST immediate value to bits [11:1] of a local address. + R_ARM64_LDST16 + + // R_ARM64_LDST32 sets a LD/ST immediate value to bits [11:2] of a local address. + R_ARM64_LDST32 + + // R_ARM64_LDST64 sets a LD/ST immediate value to bits [11:3] of a local address. + R_ARM64_LDST64 + + // R_ARM64_LDST128 sets a LD/ST immediate value to bits [11:4] of a local address. + R_ARM64_LDST128 + + // PPC64. + + // R_POWER_TLS_LE is used to implement the "local exec" model for tls + // access. It resolves to the offset of the thread-local symbol from the + // thread pointer (R13) and is split against a pair of instructions to + // support a 32 bit displacement. + R_POWER_TLS_LE + + // R_POWER_TLS_IE is used to implement the "initial exec" model for tls access. It + // relocates a D-form, DS-form instruction sequence like R_ADDRPOWER_DS. It + // inserts to the offset of GOT slot for the thread-local symbol from the TOC (the + // GOT slot is filled by the dynamic linker with the offset of the thread-local + // symbol from the thread pointer (R13)). + R_POWER_TLS_IE + + // R_POWER_TLS marks an X-form instruction such as "ADD R3,R13,R4" as completing + // a sequence of GOT-relative relocations to compute a TLS address. This can be + // used by the system linker to to rewrite the GOT-relative TLS relocation into a + // simpler thread-pointer relative relocation. See table 3.26 and 3.28 in the + // ppc64 elfv2 1.4 ABI on this transformation. Likewise, the second argument + // (usually called RB in X-form instructions) is assumed to be R13. + R_POWER_TLS + + // R_POWER_TLS_IE_PCREL34 is similar to R_POWER_TLS_IE, but marks a single MOVD + // which has been assembled as a single prefixed load doubleword without using the + // TOC. + R_POWER_TLS_IE_PCREL34 + + // R_POWER_TLS_LE_TPREL34 is similar to R_POWER_TLS_LE, but computes an offset from + // the thread pointer in one prefixed instruction. + R_POWER_TLS_LE_TPREL34 + + // R_ADDRPOWER_DS is similar to R_ADDRPOWER above, but assumes the second + // instruction is a "DS-form" instruction, which has an immediate field occupying + // bits [15:2] of the instruction word. Bits [15:2] of the address of the + // relocated symbol are inserted into this field; it is an error if the last two + // bits of the address are not 0. + R_ADDRPOWER_DS + + // R_ADDRPOWER_GOT relocates a D-form + DS-form instruction sequence by inserting + // a relative displacement of referenced symbol's GOT entry to the TOC pointer. + R_ADDRPOWER_GOT + + // R_ADDRPOWER_GOT_PCREL34 is identical to R_ADDRPOWER_GOT, but uses a PC relative + // sequence to generate a GOT symbol addresses. + R_ADDRPOWER_GOT_PCREL34 + + // R_ADDRPOWER_PCREL relocates two D-form instructions like R_ADDRPOWER, but + // inserts the displacement from the place being relocated to the address of the + // relocated symbol instead of just its address. + R_ADDRPOWER_PCREL + + // R_ADDRPOWER_TOCREL relocates two D-form instructions like R_ADDRPOWER, but + // inserts the offset from the TOC to the address of the relocated symbol + // rather than the symbol's address. + R_ADDRPOWER_TOCREL + + // R_ADDRPOWER_TOCREL_DS relocates a D-form, DS-form instruction sequence like + // R_ADDRPOWER_DS but inserts the offset from the TOC to the address of the + // relocated symbol rather than the symbol's address. + R_ADDRPOWER_TOCREL_DS + + // R_ADDRPOWER_D34 relocates a single prefixed D-form load/store operation. All + // prefixed forms are D form. The high 18 bits are stored in the prefix, + // and the low 16 are stored in the suffix. The address is absolute. + R_ADDRPOWER_D34 + + // R_ADDRPOWER_PCREL34 relates a single prefixed D-form load/store/add operation. + // All prefixed forms are D form. The resulting address is relative to the + // PC. It is a signed 34 bit offset. + R_ADDRPOWER_PCREL34 + + // RISC-V. + + // R_RISCV_JAL resolves a 20 bit offset for a J-type instruction. + R_RISCV_JAL + + // R_RISCV_JAL_TRAMP is the same as R_RISCV_JAL but denotes the use of a + // trampoline, which we may be able to avoid during relocation. These are + // only used by the linker and are not emitted by the compiler or assembler. + R_RISCV_JAL_TRAMP + + // R_RISCV_CALL resolves a 32 bit PC-relative address for an AUIPC + JALR + // instruction pair. + R_RISCV_CALL + + // R_RISCV_PCREL_ITYPE resolves a 32 bit PC-relative address for an + // AUIPC + I-type instruction pair. + R_RISCV_PCREL_ITYPE + + // R_RISCV_PCREL_STYPE resolves a 32 bit PC-relative address for an + // AUIPC + S-type instruction pair. + R_RISCV_PCREL_STYPE + + // R_RISCV_TLS_IE resolves a 32 bit TLS initial-exec address for an + // AUIPC + I-type instruction pair. + R_RISCV_TLS_IE + + // R_RISCV_TLS_LE resolves a 32 bit TLS local-exec address for a + // LUI + I-type instruction sequence. + R_RISCV_TLS_LE + + // R_RISCV_GOT_HI20 resolves the high 20 bits of a 32-bit PC-relative GOT + // address. + R_RISCV_GOT_HI20 + + // R_RISCV_PCREL_HI20 resolves the high 20 bits of a 32-bit PC-relative + // address. + R_RISCV_PCREL_HI20 + + // R_RISCV_PCREL_LO12_I resolves the low 12 bits of a 32-bit PC-relative + // address using an I-type instruction. + R_RISCV_PCREL_LO12_I + + // R_RISCV_PCREL_LO12_S resolves the low 12 bits of a 32-bit PC-relative + // address using an S-type instruction. + R_RISCV_PCREL_LO12_S + + // R_RISCV_BRANCH resolves a 12-bit PC-relative branch offset. + R_RISCV_BRANCH + + // R_RISCV_RVC_BRANCH resolves an 8-bit PC-relative offset for a CB-type + // instruction. + R_RISCV_RVC_BRANCH + + // R_RISCV_RVC_JUMP resolves an 11-bit PC-relative offset for a CJ-type + // instruction. + R_RISCV_RVC_JUMP + + // R_PCRELDBL relocates s390x 2-byte aligned PC-relative addresses. + // TODO(mundaym): remove once variants can be serialized - see issue 14218. + R_PCRELDBL + + // Loong64. + + // R_ADDRLOONG64 resolves to the low 12 bits of an external address, by encoding + // it into the instruction. + R_ADDRLOONG64 + + // R_ADDRLOONG64U resolves to the sign-adjusted "upper" 20 bits (bit 5-24) of an + // external address, by encoding it into the instruction. + R_ADDRLOONG64U + + // R_ADDRLOONG64TLS resolves to the low 12 bits of a TLS address (offset from + // thread pointer), by encoding it into the instruction. + R_ADDRLOONG64TLS + + // R_ADDRLOONG64TLSU resolves to the high 20 bits of a TLS address (offset from + // thread pointer), by encoding it into the instruction. + R_ADDRLOONG64TLSU + + // R_CALLLOONG64 resolves to non-PC-relative target address of a CALL (BL/JIRL) + // instruction, by encoding the address into the instruction. + R_CALLLOONG64 + + // R_LOONG64_TLS_IE_PCREL_HI and R_LOONG64_TLS_IE_LO relocates a pcalau12i, ld.d + // pair to compute the address of the GOT slot of the tls symbol. + R_LOONG64_TLS_IE_PCREL_HI + R_LOONG64_TLS_IE_LO + + // R_LOONG64_GOT_HI and R_LOONG64_GOT_LO resolves a GOT-relative instruction sequence, + // usually an pcalau12i followed by another ld or addi instruction. + R_LOONG64_GOT_HI + R_LOONG64_GOT_LO + + // R_JMPLOONG64 resolves to non-PC-relative target address of a JMP instruction, + // by encoding the address into the instruction. + R_JMPLOONG64 + + // R_ADDRMIPSU (only used on mips/mips64) resolves to the sign-adjusted "upper" 16 + // bits (bit 16-31) of an external address, by encoding it into the instruction. + R_ADDRMIPSU + // R_ADDRMIPSTLS (only used on mips64) resolves to the low 16 bits of a TLS + // address (offset from thread pointer), by encoding it into the instruction. + R_ADDRMIPSTLS + + // R_ADDRCUOFF resolves to a pointer-sized offset from the start of the + // symbol's DWARF compile unit. + R_ADDRCUOFF + + // R_WASMIMPORT resolves to the index of the WebAssembly function import. + R_WASMIMPORT + + // R_XCOFFREF (only used on aix/ppc64) prevents garbage collection by ld + // of a symbol. This isn't a real relocation, it can be placed in anywhere + // in a symbol and target any symbols. + R_XCOFFREF + + // R_PEIMAGEOFF resolves to a 32-bit offset from the start address of where + // the executable file is mapped in memory. + R_PEIMAGEOFF + + // R_INITORDER specifies an ordering edge between two inittask records. + // (From one p..inittask record to another one.) + // This relocation does not apply any changes to the actual data, it is + // just used in the linker to order the inittask records appropriately. + R_INITORDER + + // R_WEAK marks the relocation as a weak reference. + // A weak relocation does not make the symbol it refers to reachable, + // and is only honored by the linker if the symbol is in some other way + // reachable. + R_WEAK = -1 << 15 + + R_WEAKADDR = R_WEAK | R_ADDR + R_WEAKADDROFF = R_WEAK | R_ADDROFF +) + +// IsDirectCall reports whether r is a relocation for a direct call. +// A direct call is a CALL instruction that takes the target address +// as an immediate. The address is embedded into the instruction(s), possibly +// with limited width. An indirect call is a CALL instruction that takes +// the target address in register or memory. +func (r RelocType) IsDirectCall() bool { + switch r { + case R_CALL, R_CALLARM, R_CALLARM64, R_CALLLOONG64, R_CALLMIPS, R_CALLPOWER, + R_RISCV_CALL, R_RISCV_JAL, R_RISCV_JAL_TRAMP: + return true + } + return false +} + +// IsDirectJump reports whether r is a relocation for a direct jump. +// A direct jump is a JMP instruction that takes the target address +// as an immediate. The address is embedded into the instruction, possibly +// with limited width. An indirect jump is a JMP instruction that takes +// the target address in register or memory. +func (r RelocType) IsDirectJump() bool { + switch r { + case R_JMPMIPS: + return true + case R_JMPLOONG64: + return true + } + return false +} + +// IsDirectCallOrJump reports whether r is a relocation for a direct +// call or a direct jump. +func (r RelocType) IsDirectCallOrJump() bool { + return r.IsDirectCall() || r.IsDirectJump() +} diff --git a/src/cmd/internal/objabi/reloctype_string.go b/src/cmd/internal/objabi/reloctype_string.go new file mode 100644 index 0000000..c8923c0 --- /dev/null +++ b/src/cmd/internal/objabi/reloctype_string.go @@ -0,0 +1,114 @@ +// Code generated by "stringer -type=RelocType"; DO NOT EDIT. + +package objabi + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[R_ADDR-1] + _ = x[R_ADDRPOWER-2] + _ = x[R_ADDRARM64-3] + _ = x[R_ADDRMIPS-4] + _ = x[R_ADDROFF-5] + _ = x[R_SIZE-6] + _ = x[R_CALL-7] + _ = x[R_CALLARM-8] + _ = x[R_CALLARM64-9] + _ = x[R_CALLIND-10] + _ = x[R_CALLPOWER-11] + _ = x[R_CALLMIPS-12] + _ = x[R_CONST-13] + _ = x[R_PCREL-14] + _ = x[R_TLS_LE-15] + _ = x[R_TLS_IE-16] + _ = x[R_GOTOFF-17] + _ = x[R_PLT0-18] + _ = x[R_PLT1-19] + _ = x[R_PLT2-20] + _ = x[R_USEFIELD-21] + _ = x[R_USETYPE-22] + _ = x[R_USEIFACE-23] + _ = x[R_USEIFACEMETHOD-24] + _ = x[R_USENAMEDMETHOD-25] + _ = x[R_METHODOFF-26] + _ = x[R_KEEP-27] + _ = x[R_POWER_TOC-28] + _ = x[R_GOTPCREL-29] + _ = x[R_JMPMIPS-30] + _ = x[R_DWARFSECREF-31] + _ = x[R_DWARFFILEREF-32] + _ = x[R_ARM64_TLS_LE-33] + _ = x[R_ARM64_TLS_IE-34] + _ = x[R_ARM64_GOTPCREL-35] + _ = x[R_ARM64_GOT-36] + _ = x[R_ARM64_PCREL-37] + _ = x[R_ARM64_PCREL_LDST8-38] + _ = x[R_ARM64_PCREL_LDST16-39] + _ = x[R_ARM64_PCREL_LDST32-40] + _ = x[R_ARM64_PCREL_LDST64-41] + _ = x[R_ARM64_LDST8-42] + _ = x[R_ARM64_LDST16-43] + _ = x[R_ARM64_LDST32-44] + _ = x[R_ARM64_LDST64-45] + _ = x[R_ARM64_LDST128-46] + _ = x[R_POWER_TLS_LE-47] + _ = x[R_POWER_TLS_IE-48] + _ = x[R_POWER_TLS-49] + _ = x[R_POWER_TLS_IE_PCREL34-50] + _ = x[R_POWER_TLS_LE_TPREL34-51] + _ = x[R_ADDRPOWER_DS-52] + _ = x[R_ADDRPOWER_GOT-53] + _ = x[R_ADDRPOWER_GOT_PCREL34-54] + _ = x[R_ADDRPOWER_PCREL-55] + _ = x[R_ADDRPOWER_TOCREL-56] + _ = x[R_ADDRPOWER_TOCREL_DS-57] + _ = x[R_ADDRPOWER_D34-58] + _ = x[R_ADDRPOWER_PCREL34-59] + _ = x[R_RISCV_JAL-60] + _ = x[R_RISCV_JAL_TRAMP-61] + _ = x[R_RISCV_CALL-62] + _ = x[R_RISCV_PCREL_ITYPE-63] + _ = x[R_RISCV_PCREL_STYPE-64] + _ = x[R_RISCV_TLS_IE-65] + _ = x[R_RISCV_TLS_LE-66] + _ = x[R_RISCV_GOT_HI20-67] + _ = x[R_RISCV_PCREL_HI20-68] + _ = x[R_RISCV_PCREL_LO12_I-69] + _ = x[R_RISCV_PCREL_LO12_S-70] + _ = x[R_RISCV_BRANCH-71] + _ = x[R_RISCV_RVC_BRANCH-72] + _ = x[R_RISCV_RVC_JUMP-73] + _ = x[R_PCRELDBL-74] + _ = x[R_ADDRLOONG64-75] + _ = x[R_ADDRLOONG64U-76] + _ = x[R_ADDRLOONG64TLS-77] + _ = x[R_ADDRLOONG64TLSU-78] + _ = x[R_CALLLOONG64-79] + _ = x[R_LOONG64_TLS_IE_PCREL_HI-80] + _ = x[R_LOONG64_TLS_IE_LO-81] + _ = x[R_LOONG64_GOT_HI-82] + _ = x[R_LOONG64_GOT_LO-83] + _ = x[R_JMPLOONG64-84] + _ = x[R_ADDRMIPSU-85] + _ = x[R_ADDRMIPSTLS-86] + _ = x[R_ADDRCUOFF-87] + _ = x[R_WASMIMPORT-88] + _ = x[R_XCOFFREF-89] + _ = x[R_PEIMAGEOFF-90] + _ = x[R_INITORDER-91] +} + +const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USENAMEDMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_PCREL_LDST8R_ARM64_PCREL_LDST16R_ARM64_PCREL_LDST32R_ARM64_PCREL_LDST64R_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_POWER_TLS_IE_PCREL34R_POWER_TLS_LE_TPREL34R_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_GOT_PCREL34R_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_ADDRPOWER_D34R_ADDRPOWER_PCREL34R_RISCV_JALR_RISCV_JAL_TRAMPR_RISCV_CALLR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IER_RISCV_TLS_LER_RISCV_GOT_HI20R_RISCV_PCREL_HI20R_RISCV_PCREL_LO12_IR_RISCV_PCREL_LO12_SR_RISCV_BRANCHR_RISCV_RVC_BRANCHR_RISCV_RVC_JUMPR_PCRELDBLR_ADDRLOONG64R_ADDRLOONG64UR_ADDRLOONG64TLSR_ADDRLOONG64TLSUR_CALLLOONG64R_LOONG64_TLS_IE_PCREL_HIR_LOONG64_TLS_IE_LOR_LOONG64_GOT_HIR_LOONG64_GOT_LOR_JMPLOONG64R_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREFR_PEIMAGEOFFR_INITORDER" + +var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 226, 237, 243, 254, 264, 273, 286, 300, 314, 328, 344, 355, 368, 387, 407, 427, 447, 460, 474, 488, 502, 517, 531, 545, 556, 578, 600, 614, 629, 652, 669, 687, 708, 723, 742, 753, 770, 782, 801, 820, 834, 848, 864, 882, 902, 922, 936, 954, 970, 980, 993, 1007, 1023, 1040, 1053, 1078, 1097, 1113, 1129, 1141, 1152, 1165, 1176, 1188, 1198, 1210, 1221} + +func (i RelocType) String() string { + i -= 1 + if i < 0 || i >= RelocType(len(_RelocType_index)-1) { + return "RelocType(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _RelocType_name[_RelocType_index[i]:_RelocType_index[i+1]] +} diff --git a/src/cmd/internal/objabi/stack.go b/src/cmd/internal/objabi/stack.go new file mode 100644 index 0000000..7c7ff4e --- /dev/null +++ b/src/cmd/internal/objabi/stack.go @@ -0,0 +1,32 @@ +// Copyright 2011 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 objabi + +import ( + "internal/abi" + "internal/buildcfg" +) + +func StackNosplit(race bool) int { + // This arithmetic must match that in runtime/stack.go:stackNosplit. + return abi.StackNosplitBase * stackGuardMultiplier(race) +} + +// stackGuardMultiplier returns a multiplier to apply to the default +// stack guard size. Larger multipliers are used for non-optimized +// builds that have larger stack frames or for specific targets. +func stackGuardMultiplier(race bool) int { + // This arithmetic must match that in runtime/internal/sys/consts.go:StackGuardMultiplier. + n := 1 + // On AIX, a larger stack is needed for syscalls. + if buildcfg.GOOS == "aix" { + n += 1 + } + // The race build also needs more stack. + if race { + n += 1 + } + return n +} diff --git a/src/cmd/internal/objabi/symkind.go b/src/cmd/internal/objabi/symkind.go new file mode 100644 index 0000000..bafc51b --- /dev/null +++ b/src/cmd/internal/objabi/symkind.go @@ -0,0 +1,77 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package objabi + +// A SymKind describes the kind of memory represented by a symbol. +type SymKind uint8 + +// Defined SymKind values. +// These are used to index into cmd/link/internal/sym/AbiSymKindToSymKind +// +// TODO(rsc): Give idiomatic Go names. +// +//go:generate stringer -type=SymKind +const ( + // An otherwise invalid zero value for the type + Sxxx SymKind = iota + // Executable instructions + STEXT + // Read only static data + SRODATA + // Static data that does not contain any pointers + SNOPTRDATA + // Static data + SDATA + // Statically data that is initially all 0s + SBSS + // Statically data that is initially all 0s and does not contain pointers + SNOPTRBSS + // Thread-local data that is initially all 0s + STLSBSS + // Debugging data + SDWARFCUINFO + SDWARFCONST + SDWARFFCN + SDWARFABSFCN + SDWARFTYPE + SDWARFVAR + SDWARFRANGE + SDWARFLOC + SDWARFLINES + // Coverage instrumentation counter for libfuzzer. + SLIBFUZZER_8BIT_COUNTER + // Coverage instrumentation counter, aux variable for cmd/cover + SCOVERAGE_COUNTER + SCOVERAGE_AUXVAR + + SSEHUNWINDINFO + // Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values. +) diff --git a/src/cmd/internal/objabi/symkind_string.go b/src/cmd/internal/objabi/symkind_string.go new file mode 100644 index 0000000..3f2ad43 --- /dev/null +++ b/src/cmd/internal/objabi/symkind_string.go @@ -0,0 +1,43 @@ +// Code generated by "stringer -type=SymKind"; DO NOT EDIT. + +package objabi + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Sxxx-0] + _ = x[STEXT-1] + _ = x[SRODATA-2] + _ = x[SNOPTRDATA-3] + _ = x[SDATA-4] + _ = x[SBSS-5] + _ = x[SNOPTRBSS-6] + _ = x[STLSBSS-7] + _ = x[SDWARFCUINFO-8] + _ = x[SDWARFCONST-9] + _ = x[SDWARFFCN-10] + _ = x[SDWARFABSFCN-11] + _ = x[SDWARFTYPE-12] + _ = x[SDWARFVAR-13] + _ = x[SDWARFRANGE-14] + _ = x[SDWARFLOC-15] + _ = x[SDWARFLINES-16] + _ = x[SLIBFUZZER_8BIT_COUNTER-17] + _ = x[SCOVERAGE_COUNTER-18] + _ = x[SCOVERAGE_AUXVAR-19] + _ = x[SSEHUNWINDINFO-20] +} + +const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSLIBFUZZER_8BIT_COUNTERSCOVERAGE_COUNTERSCOVERAGE_AUXVARSSEHUNWINDINFO" + +var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 63, 74, 83, 95, 105, 114, 125, 134, 145, 168, 185, 201, 215} + +func (i SymKind) String() string { + if i >= SymKind(len(_SymKind_index)-1) { + return "SymKind(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _SymKind_name[_SymKind_index[i]:_SymKind_index[i+1]] +} diff --git a/src/cmd/internal/objabi/typekind.go b/src/cmd/internal/objabi/typekind.go new file mode 100644 index 0000000..990ff18 --- /dev/null +++ b/src/cmd/internal/objabi/typekind.go @@ -0,0 +1,40 @@ +// Copyright 2012 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 objabi + +// Must match runtime and reflect. +// Included by cmd/gc. + +const ( + KindBool = 1 + iota + KindInt + KindInt8 + KindInt16 + KindInt32 + KindInt64 + KindUint + KindUint8 + KindUint16 + KindUint32 + KindUint64 + KindUintptr + KindFloat32 + KindFloat64 + KindComplex64 + KindComplex128 + KindArray + KindChan + KindFunc + KindInterface + KindMap + KindPtr + KindSlice + KindString + KindStruct + KindUnsafePointer + KindDirectIface = 1 << 5 + KindGCProg = 1 << 6 + KindMask = (1 << 5) - 1 +) diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go new file mode 100644 index 0000000..a3e1242 --- /dev/null +++ b/src/cmd/internal/objabi/util.go @@ -0,0 +1,33 @@ +// Copyright 2015 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 objabi + +import ( + "fmt" + "strings" + + "internal/buildcfg" +) + +const ( + ElfRelocOffset = 256 + MachoRelocOffset = 2048 // reserve enough space for ELF relocations + GlobalDictPrefix = ".dict" // prefix for names of global dictionaries +) + +// HeaderString returns the toolchain configuration string written in +// Go object headers. This string ensures we don't attempt to import +// or link object files that are incompatible with each other. This +// string always starts with "go object ". +func HeaderString() string { + archExtra := "" + if k, v := buildcfg.GOGOARCH(); k != "" && v != "" { + archExtra = " " + k + "=" + v + } + return fmt.Sprintf("go object %s %s %s%s X:%s\n", + buildcfg.GOOS, buildcfg.GOARCH, + buildcfg.Version, archExtra, + strings.Join(buildcfg.Experiment.Enabled(), ",")) +} |