]> Cypherpunks repositories - gostls13.git/commitdiff
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)
committerRuss Cox <rsc@golang.org>
Thu, 30 Mar 2017 12:29:18 +0000 (12:29 +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/38854
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
misc/cgo/testcshared/test.bash
src/cmd/link/internal/ld/macho_combine_dwarf.go

index 4ff4db446c565318fd644db20e1ee700f8655f15..315a0d4036739a0880906a6d392d5f53b1b89da5 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 {