]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.link] cmd: support large function alignment
authorCherry Zhang <cherryyz@google.com>
Thu, 2 Apr 2020 16:48:13 +0000 (12:48 -0400)
committerCherry Zhang <cherryyz@google.com>
Thu, 2 Apr 2020 17:24:05 +0000 (17:24 +0000)
This ports CL 226997 to the dev.link branch.
- The assembler part and old object file writing are unchanged.
- Changes to cmd/link are applied to cmd/oldlink.
- Add alignment field to new object files for the new linker.

Change-Id: Id00f323ae5bdd86b2709a702ee28bcaa9ba962f8
Reviewed-on: https://go-review.googlesource.com/c/go/+/227025
Reviewed-by: Than McIntosh <thanm@google.com>
12 files changed:
src/cmd/internal/goobj/read.go
src/cmd/internal/goobj2/objfile.go
src/cmd/internal/obj/arm64/asm7.go
src/cmd/internal/obj/arm64/asm_test.go
src/cmd/internal/obj/link.go
src/cmd/internal/obj/objfile.go
src/cmd/internal/obj/objfile2.go
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/loader/loader.go
src/cmd/link/link_test.go
src/cmd/oldlink/internal/ld/data.go
src/cmd/oldlink/internal/objfile/objfile.go

index 027c77e72570e5d5bb4b962e4c87f856335a80c8..44e619cabbf9586b42bd5b4d9e6686f73e0e89f1 100644 (file)
@@ -96,6 +96,7 @@ type Var struct {
 type Func struct {
        Args     int64      // size in bytes of argument frame: inputs and outputs
        Frame    int64      // size in bytes of local variable frame
+       Align    uint32     // alignment requirement in bytes for the address of the function
        Leaf     bool       // function omits save of link register (ARM)
        NoSplit  bool       // function omits stack split prologue
        TopFrame bool       // function is the top of the call stack
@@ -591,6 +592,7 @@ func (r *objReader) parseObject(prefix []byte) error {
                        s.Func = f
                        f.Args = r.readInt()
                        f.Frame = r.readInt()
+                       f.Align = uint32(r.readInt())
                        flags := r.readInt()
                        f.Leaf = flags&(1<<0) != 0
                        f.TopFrame = flags&(1<<4) != 0
index c7b508cc5eefeb8787de9097a30fe16f5cb458f7..52544bf77386628cae00608298a0ca3e1e841ffa 100644 (file)
@@ -186,11 +186,12 @@ func (h *Header) Size() int {
 
 // Symbol definition.
 type Sym struct {
-       Name string
-       ABI  uint16
-       Type uint8
-       Flag uint8
-       Siz  uint32
+       Name  string
+       ABI   uint16
+       Type  uint8
+       Flag  uint8
+       Siz   uint32
+       Align uint32
 }
 
 const SymABIstatic = ^uint16(0)
@@ -216,9 +217,10 @@ func (s *Sym) Write(w *Writer) {
        w.Uint8(s.Type)
        w.Uint8(s.Flag)
        w.Uint32(s.Siz)
+       w.Uint32(s.Align)
 }
 
-const SymSize = stringRefSize + 2 + 1 + 1 + 4
+const SymSize = stringRefSize + 2 + 1 + 1 + 4 + 4
 
 type Sym2 [SymSize]byte
 
@@ -228,10 +230,11 @@ func (s *Sym2) Name(r *Reader) string {
        return r.StringAt(off, len)
 }
 
-func (s *Sym2) ABI() uint16 { return binary.LittleEndian.Uint16(s[8:]) }
-func (s *Sym2) Type() uint8 { return s[10] }
-func (s *Sym2) Flag() uint8 { return s[11] }
-func (s *Sym2) Siz() uint32 { return binary.LittleEndian.Uint32(s[12:]) }
+func (s *Sym2) ABI() uint16   { return binary.LittleEndian.Uint16(s[8:]) }
+func (s *Sym2) Type() uint8   { return s[10] }
+func (s *Sym2) Flag() uint8   { return s[11] }
+func (s *Sym2) Siz() uint32   { return binary.LittleEndian.Uint32(s[12:]) }
+func (s *Sym2) Align() uint32 { return binary.LittleEndian.Uint32(s[16:]) }
 
 func (s *Sym2) Dupok() bool         { return s.Flag()&SymFlagDupok != 0 }
 func (s *Sym2) Local() bool         { return s.Flag()&SymFlagLocal != 0 }
index e8b092a2a84d5288c0f2b9d02ed6958c48d76466..8e5b598084936e8ec46abaf2d86a63eb405a7f2c 100644 (file)
@@ -886,25 +886,10 @@ const OP_NOOP = 0xd503201f
 
 // align code to a certain length by padding bytes.
 func pcAlignPadLength(pc int64, alignedValue int64, ctxt *obj.Link) int {
-       switch alignedValue {
-       case 8:
-               if pc%8 == 4 {
-                       return 4
-               }
-       case 16:
-               switch pc % 16 {
-               case 4:
-                       return 12
-               case 8:
-                       return 8
-               case 12:
-                       return 4
-               }
-       default:
-               ctxt.Diag("Unexpected alignment: %d for PCALIGN directive\n", alignedValue)
+       if !((alignedValue&(alignedValue-1) == 0) && 8 <= alignedValue && alignedValue <= 2048) {
+               ctxt.Diag("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d\n", alignedValue)
        }
-
-       return 0
+       return int(-pc & (alignedValue - 1))
 }
 
 func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
@@ -940,8 +925,12 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                if m == 0 {
                        switch p.As {
                        case obj.APCALIGN:
-                               a := p.From.Offset
-                               m = pcAlignPadLength(pc, a, ctxt)
+                               alignedValue := p.From.Offset
+                               m = pcAlignPadLength(pc, alignedValue, ctxt)
+                               // Update the current text symbol alignment value.
+                               if int32(alignedValue) > cursym.Func.Align {
+                                       cursym.Func.Align = int32(alignedValue)
+                               }
                                break
                        case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
                                continue
@@ -1017,8 +1006,8 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                        if m == 0 {
                                switch p.As {
                                case obj.APCALIGN:
-                                       a := p.From.Offset
-                                       m = pcAlignPadLength(pc, a, ctxt)
+                                       alignedValue := p.From.Offset
+                                       m = pcAlignPadLength(pc, alignedValue, ctxt)
                                        break
                                case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
                                        continue
index 1691828739b6e782f5e9050b656933f2e70afb05..9efdb0217f4dbc86733343516743596363bf2130 100644 (file)
@@ -18,7 +18,9 @@ import (
 
 // TestLarge generates a very large file to verify that large
 // program builds successfully, in particular, too-far
-// conditional branches are fixed.
+// conditional branches are fixed, and also verify that the
+// instruction's pc can be correctly aligned even when branches
+// need to be fixed.
 func TestLarge(t *testing.T) {
        if testing.Short() {
                t.Skip("Skip in short mode")
@@ -41,10 +43,27 @@ func TestLarge(t *testing.T) {
                t.Fatalf("can't write output: %v\n", err)
        }
 
-       // build generated file
-       cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
+       pattern := `0x0080\s00128\s\(.*\)\tMOVD\t\$3,\sR3`
+
+       // assemble generated file
+       cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
        cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
        out, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Errorf("Assemble failed: %v, output: %s", err, out)
+       }
+       matched, err := regexp.MatchString(pattern, string(out))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !matched {
+               t.Errorf("The alignment is not correct: %t, output:%s\n", matched, out)
+       }
+
+       // build generated file
+       cmd = exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
+       cmd.Env = append(os.Environ(), "GOARCH=arm64", "GOOS=linux")
+       out, err = cmd.CombinedOutput()
        if err != nil {
                t.Errorf("Build failed: %v, output: %s", err, out)
        }
@@ -56,6 +75,8 @@ func gen(buf *bytes.Buffer) {
        fmt.Fprintln(buf, "TBZ $5, R0, label")
        fmt.Fprintln(buf, "CBZ R0, label")
        fmt.Fprintln(buf, "BEQ label")
+       fmt.Fprintln(buf, "PCALIGN $128")
+       fmt.Fprintln(buf, "MOVD $3, R3")
        for i := 0; i < 1<<19; i++ {
                fmt.Fprintln(buf, "MOVD R0, R1")
        }
index 9dd821d532a84b5f26c28ec17d142b339a6e6503..ac3621bf741c5f23071e42ecacf735ecb65ba93e 100644 (file)
@@ -398,6 +398,7 @@ type LSym struct {
 type FuncInfo struct {
        Args     int32
        Locals   int32
+       Align    int32
        Text     *Prog
        Autot    map[*LSym]struct{}
        Pcln     Pcln
index 3e97c614b886de70baa7765b0c72aba42bd132cd..cb6b7090669639b2c47c7e41c923960c132756c5 100644 (file)
@@ -350,6 +350,7 @@ func (w *objWriter) writeSym(s *LSym) {
 
        w.writeInt(int64(s.Func.Args))
        w.writeInt(int64(s.Func.Locals))
+       w.writeInt(int64(s.Func.Align))
        w.writeBool(s.NoSplit())
        flags = int64(0)
        if s.Leaf() {
index 95f920eef5618e438ae2099e85d62b7265f01a84..6261924d0d2b835ac99f68cf24fd8017df00283d 100644 (file)
@@ -244,12 +244,17 @@ func (w *writer) Sym(s *LSym) {
        if strings.HasPrefix(name, "gofile..") {
                name = filepath.ToSlash(name)
        }
+       var align uint32
+       if s.Func != nil {
+               align = uint32(s.Func.Align)
+       }
        o := goobj2.Sym{
-               Name: name,
-               ABI:  abi,
-               Type: uint8(s.Type),
-               Flag: flag,
-               Siz:  uint32(s.Size),
+               Name:  name,
+               ABI:   abi,
+               Type:  uint8(s.Type),
+               Flag:  flag,
+               Siz:   uint32(s.Size),
+               Align: align,
        }
        o.Write(w.Writer)
 }
index bff29fb568ff185948af876b72424c1a5c08a231..7c4b08a8051cbd09bb0a0f9059dc5bbf5c7c09a4 100644 (file)
@@ -2256,6 +2256,10 @@ func assignAddress(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint6
                funcsize = uint64(s.Size)
        }
 
+       if sect.Align < s.Align {
+               sect.Align = s.Align
+       }
+
        // On ppc64x a text section should not be larger than 2^26 bytes due to the size of
        // call target offset field in the bl instruction.  Splitting into smaller text
        // sections smaller than this limit allows the GNU linker to modify the long calls
index 5f128749ab19aca7f671162f5187b388a376120c..7cc846a19e34a10b9c58a8aabea59512c24f6a46 100644 (file)
@@ -1616,6 +1616,9 @@ func (l *Loader) preloadSyms(r *oReader, kind int) {
                        strings.HasPrefix(name, "runtime.gcbits.") {
                        l.SetAttrNotInSymbolTable(gi, true)
                }
+               if a := osym.Align(); a != 0 {
+                       l.SetSymAlign(gi, int32(a))
+               }
        }
 }
 
index 5e19cb5de1858edfb0c75ce861f1b9c72f45ad7c..ed2d3f44953b2c6db61f82f90197a0c210d7c607 100644 (file)
@@ -471,3 +471,73 @@ func TestOldLink(t *testing.T) {
                t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
        }
 }
+
+const testFuncAlignSrc = `
+package main
+import (
+       "fmt"
+       "reflect"
+)
+func alignPc()
+
+func main() {
+       addr := reflect.ValueOf(alignPc).Pointer()
+       if (addr % 512) != 0 {
+               fmt.Printf("expected 512 bytes alignment, got %v\n", addr)
+       } else {
+               fmt.Printf("PASS")
+       }
+}
+`
+
+const testFuncAlignAsmSrc = `
+#include "textflag.h"
+
+TEXT   ·alignPc(SB),NOSPLIT, $0-0
+       MOVD    $2, R0
+       PCALIGN $512
+       MOVD    $3, R1
+       RET
+`
+
+// TestFuncAlign verifies that the address of a function can be aligned
+// with a specfic value on arm64.
+func TestFuncAlign(t *testing.T) {
+       if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" {
+               t.Skip("skipping on non-linux/arm64 platform")
+       }
+       testenv.MustHaveGoBuild(t)
+
+       tmpdir, err := ioutil.TempDir("", "TestFuncAlign")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       src := filepath.Join(tmpdir, "falign.go")
+       err = ioutil.WriteFile(src, []byte(testFuncAlignSrc), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+       src = filepath.Join(tmpdir, "falign.s")
+       err = ioutil.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // Build and run with old object file format.
+       cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "falign")
+       cmd.Dir = tmpdir
+       out, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Errorf("build failed: %v", err)
+       }
+       cmd = exec.Command(tmpdir + "/falign")
+       out, err = cmd.CombinedOutput()
+       if err != nil {
+               t.Errorf("failed to run with err %v, output: %s", err, out)
+       }
+       if string(out) != "PASS" {
+               t.Errorf("unexpected output: %s\n", out)
+       }
+}
index 3c78896e454461d2d7284ef37c33c7967d592e98..13f412ccd85baf782d33b47711528772c95b52f6 100644 (file)
@@ -2119,6 +2119,10 @@ func assignAddress(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint6
                funcsize = uint64(s.Size)
        }
 
+       if sect.Align < s.Align {
+               sect.Align = s.Align
+       }
+
        // On ppc64x a text section should not be larger than 2^26 bytes due to the size of
        // call target offset field in the bl instruction.  Splitting into smaller text
        // sections smaller than this limit allows the GNU linker to modify the long calls
index 7be433ad40278a9d1a6c3e5f87aee122170b3249..3a59f6a6249c2d8df3b85cfa135deb1802ee3298 100644 (file)
@@ -316,6 +316,7 @@ overwrite:
 
                pc.Args = r.readInt32()
                pc.Locals = r.readInt32()
+               s.Align = r.readInt32()
                if r.readUint8() != 0 {
                        s.Attr |= sym.AttrNoSplit
                }