aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/link
diff options
context:
space:
mode:
authorAleksey Markin <alexanius@gmail.com>2025-03-26 18:47:15 +0300
committerCherry Mui <cherryyz@google.com>2025-04-03 12:02:03 -0700
commit9302a57134a698b62576cd3b849017a02c731c98 (patch)
tree1bd3ec037ceffd3ff777c8c0779020705e038a9c /src/cmd/link
parent5b36f613565b98c4f6610aa6c1a203e294b2f5b4 (diff)
downloadgo-9302a57134a698b62576cd3b849017a02c731c98.tar.xz
cmd/link/internal/ld: introduce -funcalign=N option
This patch adds linker option -funcalign=N that allows to set alignment for function entries. This CL is based on vasiliy.leonenko@gmail.com's cl/615736. For #72130 Change-Id: I57e5c9c4c71a989533643fda63a9a79c5c897dea Reviewed-on: https://go-review.googlesource.com/c/go/+/660996 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
Diffstat (limited to 'src/cmd/link')
-rw-r--r--src/cmd/link/doc.go2
-rw-r--r--src/cmd/link/internal/ld/data.go4
-rw-r--r--src/cmd/link/internal/ld/lib.go6
-rw-r--r--src/cmd/link/internal/ld/main.go4
-rw-r--r--src/cmd/link/link_test.go83
5 files changed, 94 insertions, 5 deletions
diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go
index 840f4b04ed..b620219e96 100644
--- a/src/cmd/link/doc.go
+++ b/src/cmd/link/doc.go
@@ -83,6 +83,8 @@ Flags:
Set space-separated flags to pass to the external linker.
-f
Ignore version mismatch in the linked archives.
+ -funcalign N
+ Set function alignment to N bytes
-g
Disable Go package data checks.
-importcfg file
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index ca394700cf..b3e1ac457d 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -2658,9 +2658,7 @@ func assignAddress(ctxt *Link, sect *sym.Section, n int, s loader.Sym, va uint64
}
align := ldr.SymAlign(s)
- if align == 0 {
- align = int32(Funcalign)
- }
+ align = max(align, int32(Funcalign))
va = uint64(Rnd(int64(va), int64(align)))
if sect.Align < align {
sect.Align = align
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index b114ca2a3d..7f22b6ba1c 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -377,7 +377,11 @@ func mayberemoveoutfile() {
}
func libinit(ctxt *Link) {
- Funcalign = thearch.Funcalign
+ if *FlagFuncAlign != 0 {
+ Funcalign = *FlagFuncAlign
+ } else {
+ Funcalign = thearch.Funcalign
+ }
// add goroot to the end of the libdir list.
suffix := ""
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
index 377dcd6c85..6a684890be 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -105,6 +105,7 @@ var (
FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
FlagRound = flag.Int64("R", -1, "set address rounding `quantum`")
FlagTextAddr = flag.Int64("T", -1, "set the start address of text symbols")
+ FlagFuncAlign = flag.Int("funcalign", 0, "set function align to `N` bytes")
flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")
flagPruneWeakMap = flag.Bool("pruneweakmap", true, "prune weak mapinit refs")
flagRandLayout = flag.Int64("randlayout", 0, "randomize function layout")
@@ -251,6 +252,9 @@ func Main(arch *sys.Arch, theArch Arch) {
if *FlagRound != -1 && (*FlagRound < 4096 || !isPowerOfTwo(*FlagRound)) {
Exitf("invalid -R value 0x%x", *FlagRound)
}
+ if *FlagFuncAlign != 0 && !isPowerOfTwo(int64(*FlagFuncAlign)) {
+ Exitf("invalid -funcalign value %d", *FlagFuncAlign)
+ }
checkStrictDups = *FlagStrictDups
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index cd2f9e3953..53c4ee77fe 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -16,6 +16,7 @@ import (
"path/filepath"
"regexp"
"runtime"
+ "strconv"
"strings"
"testing"
@@ -701,7 +702,6 @@ func TestFuncAlign(t *testing.T) {
t.Fatal(err)
}
- // Build and run with old object file format.
cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "falign")
cmd.Dir = tmpdir
out, err := cmd.CombinedOutput()
@@ -718,6 +718,87 @@ func TestFuncAlign(t *testing.T) {
}
}
+const testFuncAlignOptionSrc = `
+package main
+//go:noinline
+func foo() {
+}
+//go:noinline
+func bar() {
+}
+//go:noinline
+func baz() {
+}
+func main() {
+ foo()
+ bar()
+ baz()
+}
+`
+
+// TestFuncAlignOption verifies that the -funcalign option changes the function alignment
+func TestFuncAlignOption(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ t.Parallel()
+
+ tmpdir := t.TempDir()
+
+ src := filepath.Join(tmpdir, "falign.go")
+ err := os.WriteFile(src, []byte(testFuncAlignOptionSrc), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ alignTest := func(align uint64) {
+ exeName := "falign.exe"
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-funcalign="+strconv.FormatUint(align, 10), "-o", exeName, "falign.go")
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("build failed: %v \n%s", err, out)
+ }
+ exe := filepath.Join(tmpdir, exeName)
+ cmd = testenv.Command(t, exe)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("failed to run with err %v, output: %s", err, out)
+ }
+
+ // Check function alignment
+ f, err := objfile.Open(exe)
+ if err != nil {
+ t.Fatalf("failed to open file:%v\n", err)
+ }
+ defer f.Close()
+
+ fname := map[string]bool{"_main.foo": false,
+ "_main.bar": false,
+ "_main.baz": false}
+ syms, err := f.Symbols()
+ for _, s := range syms {
+ fn := s.Name
+ if _, ok := fname[fn]; !ok {
+ fn = "_" + s.Name
+ if _, ok := fname[fn]; !ok {
+ continue
+ }
+ }
+ if s.Addr%align != 0 {
+ t.Fatalf("unaligned function: %s %x. Expected alignment: %d\n", fn, s.Addr, align)
+ }
+ fname[fn] = true
+ }
+ for k, v := range fname {
+ if !v {
+ t.Fatalf("function %s not found\n", k)
+ }
+ }
+ }
+ alignTest(16)
+ alignTest(32)
+}
+
const testTrampSrc = `
package main
import "fmt"