]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link/internal/ld: introduce -funcalign=N option
authorAleksey Markin <alexanius@gmail.com>
Wed, 26 Mar 2025 15:47:15 +0000 (18:47 +0300)
committerCherry Mui <cherryyz@google.com>
Thu, 3 Apr 2025 19:02:03 +0000 (12:02 -0700)
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>
src/cmd/link/doc.go
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/ld/main.go
src/cmd/link/link_test.go

index 840f4b04edd1f47dd28a701165a3059a7b9d6bb6..b620219e9615dc8b665c7dc89d9d64b9b154a2b6 100644 (file)
@@ -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
index ca394700cfe34f57a392a11cc6762123ea2a754f..b3e1ac457dc0f5187a9ca2d0352dc6b1f96c6d25 100644 (file)
@@ -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
index b114ca2a3d4115a0767e166a8839ccac60856584..7f22b6ba1c5bf00c8d5211af4d365cc27402fa25 100644 (file)
@@ -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 := ""
index 377dcd6c856fa6dfa5bb1739ba62c7ee64568420..6a684890be0a03806184d1a7b3bc8353b880883c 100644 (file)
@@ -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
 
index cd2f9e3953ca5aa509a6cbd56f9cc1c157428354..53c4ee77fed4b492656400f1befa467da8b78ece 100644 (file)
@@ -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"