]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.8] cmd/link: make mach-o dwarf segment properly aligned
authorRuss Cox <rsc@golang.org>
Thu, 30 Mar 2017 00:53:32 +0000 (20:53 -0400)
committerAustin Clements <austin@google.com>
Wed, 5 Apr 2017 16:58:33 +0000 (16:58 +0000)
Without this, the load fails during kernel exec, which results in the
mysterious and completely uninformative "Killed: 9" error.

It appears that the stars (or at least the inputs) were properly aligned
with earlier versions of Xcode so that this happened accidentally.
Make it happen on purpose.

Gregory Man bisected the breakage to this change in LLVM,
which fits the theory nicely:
https://github.com/llvm-mirror/llvm/commit/9a41e59c

Fixes #19734.

Change-Id: Ice67a09af2de29d3c0d5e3fcde6a769580897c95
Reviewed-on: https://go-review.googlesource.com/39603
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

misc/cgo/testcshared/test.bash
src/cmd/link/internal/ld/macho_combine_dwarf.go

index 052ee0e758987a9f55c9a8e2c0f86b0a280c19ac..0315fb07f577644b6d53db6b940e04c8eb455482 100755 (executable)
@@ -179,6 +179,13 @@ if test "$output" != "PASS"; then
     status=1
 fi
 
+if test "$libext" = "dylib"; then
+       # make sure dylibs are well-formed
+       if ! otool -l libgo*.dylib >/dev/null; then
+               status=1
+       fi
+fi
+
 if test $status = 0; then
     echo "ok"
 fi
index dcc371ec058a94ded8d6a60fc5141202ca9a52bd..8c6c4a86acfecc55b6cc34c2ab649914ad9e0b58 100644 (file)
@@ -17,6 +17,7 @@ import (
 
 var realdwarf, linkseg *macho.Segment
 var dwarfstart, linkstart int64
+var dwarfaddr, linkaddr int64
 var linkoffset uint32
 
 const (
@@ -41,8 +42,7 @@ const (
        LC_DYLIB_CODE_SIGN_DRS  = 0x2B
        LC_ENCRYPTION_INFO_64   = 0x2C
 
-       dwarfMinAlign = 6  // 64 = 1 << 6
-       pageAlign     = 12 // 4096 = 1 << 12
+       pageAlign = 12 // 4096 = 1 << 12
 )
 
 type loadCmd struct {
@@ -157,16 +157,13 @@ func machoCombineDwarf(inexe, dsym, outexe string) error {
        }
 
        // Now copy the dwarf data into the output.
-       maxalign := uint32(dwarfMinAlign) //
-       for _, sect := range dwarfm.Sections {
-               if sect.Align > maxalign {
-                       maxalign = sect.Align
-               }
-       }
-       dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, maxalign)
+       // Kernel requires all loaded segments to be page-aligned in the file,
+       // even though we mark this one as being 0 bytes of virtual address space.
+       dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, pageAlign)
        if _, err = outf.Seek(dwarfstart, 0); err != nil {
                return err
        }
+       dwarfaddr = int64((linkseg.Addr + linkseg.Memsz + 1<<pageAlign - 1) &^ (1<<pageAlign - 1))
 
        if _, err = dwarff.Seek(int64(realdwarf.Offset), 0); err != nil {
                return err
@@ -277,10 +274,10 @@ func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error {
                return err
        }
        // There shouldn't be any sections, but just to make sure...
-       return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset))
+       return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset), 0)
 }
 
-func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, delta uint64) error {
+func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, deltaOffset, deltaAddr uint64) error {
        iseg := reflect.Indirect(seg)
        nsect := iseg.FieldByName("Nsect").Uint()
        if nsect == 0 {
@@ -291,16 +288,20 @@ func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, delta uint64)
        isect := reflect.Indirect(sect)
        offsetField := isect.FieldByName("Offset")
        reloffField := isect.FieldByName("Reloff")
+       addrField := isect.FieldByName("Addr")
        sectSize := int64(isect.Type().Size())
        for i := uint64(0); i < nsect; i++ {
                if err := r.ReadAt(sectOffset, sect.Interface()); err != nil {
                        return err
                }
                if offsetField.Uint() != 0 {
-                       offsetField.SetUint(offsetField.Uint() + delta)
+                       offsetField.SetUint(offsetField.Uint() + deltaOffset)
                }
                if reloffField.Uint() != 0 {
-                       reloffField.SetUint(reloffField.Uint() + delta)
+                       reloffField.SetUint(reloffField.Uint() + deltaOffset)
+               }
+               if addrField.Uint() != 0 {
+                       addrField.SetUint(addrField.Uint() + deltaAddr)
                }
                if err := r.WriteAt(sectOffset, sect.Interface()); err != nil {
                        return err
@@ -327,15 +328,30 @@ func machoUpdateDwarfHeader(r *loadCmdReader) error {
        if err := r.ReadAt(0, seg); err != nil {
                return err
        }
-       segValue := reflect.ValueOf(seg)
-       offset := reflect.Indirect(segValue).FieldByName("Offset")
+       segv := reflect.ValueOf(seg).Elem()
+
+       segv.FieldByName("Offset").SetUint(uint64(dwarfstart))
+       segv.FieldByName("Addr").SetUint(uint64(dwarfaddr))
+
+       deltaOffset := uint64(dwarfstart) - realdwarf.Offset
+       deltaAddr := uint64(dwarfaddr) - realdwarf.Addr
+
+       // If we set Memsz to 0 (and might as well set Addr too),
+       // then the xnu kernel will bail out halfway through load_segment
+       // and not apply further sanity checks that we might fail in the future.
+       // We don't need the DWARF information actually available in memory.
+       // But if we do this for buildmode=c-shared then the user-space
+       // dynamic loader complains about memsz < filesz. Sigh.
+       if Buildmode != BuildmodeCShared {
+               segv.FieldByName("Addr").SetUint(0)
+               segv.FieldByName("Memsz").SetUint(0)
+               deltaAddr = 0
+       }
 
-       delta := uint64(dwarfstart) - realdwarf.Offset
-       offset.SetUint(offset.Uint() + delta)
        if err := r.WriteAt(0, seg); err != nil {
                return err
        }
-       return machoUpdateSections(*r, segValue, reflect.ValueOf(sect), delta)
+       return machoUpdateSections(*r, segv, reflect.ValueOf(sect), deltaOffset, deltaAddr)
 }
 
 func machoUpdateLoadCommand(r loadCmdReader, cmd interface{}, fields ...string) error {