aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/internal/buildid/rewrite.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/internal/buildid/rewrite.go')
-rw-r--r--src/cmd/internal/buildid/rewrite.go71
1 files changed, 71 insertions, 0 deletions
diff --git a/src/cmd/internal/buildid/rewrite.go b/src/cmd/internal/buildid/rewrite.go
index 5be54552a6..a7928959c4 100644
--- a/src/cmd/internal/buildid/rewrite.go
+++ b/src/cmd/internal/buildid/rewrite.go
@@ -6,7 +6,9 @@ package buildid
import (
"bytes"
+ "cmd/internal/codesign"
"crypto/sha256"
+ "debug/macho"
"fmt"
"io"
)
@@ -26,6 +28,11 @@ func FindAndHash(r io.Reader, id string, bufSize int) (matches []int64, hash [32
zeros := make([]byte, len(id))
idBytes := []byte(id)
+ // For Mach-O files, we want to exclude the code signature.
+ // The code signature contains hashes of the whole file (except the signature
+ // itself), including the buildid. So the buildid cannot contain the signature.
+ r = excludeMachoCodeSignature(r)
+
// The strategy is to read the file through buf, looking for id,
// but we need to worry about what happens if id is broken up
// and returned in parts by two different reads.
@@ -87,5 +94,69 @@ func Rewrite(w io.WriterAt, pos []int64, id string) error {
return err
}
}
+
+ // Update Mach-O code signature, if any.
+ if f, cmd, ok := findMachoCodeSignature(w); ok {
+ if codesign.Size(int64(cmd.Dataoff), "a.out") == int64(cmd.Datasize) {
+ // Update the signature if the size matches, so we don't need to
+ // fix up headers. Binaries generated by the Go linker should have
+ // the expected size. Otherwise skip.
+ text := f.Segment("__TEXT")
+ cs := make([]byte, cmd.Datasize)
+ codesign.Sign(cs, w.(io.Reader), "a.out", int64(cmd.Dataoff), int64(text.Offset), int64(text.Filesz), f.Type == macho.TypeExec)
+ if _, err := w.WriteAt(cs, int64(cmd.Dataoff)); err != nil {
+ return err
+ }
+ }
+ }
+
return nil
}
+
+func excludeMachoCodeSignature(r io.Reader) io.Reader {
+ _, cmd, ok := findMachoCodeSignature(r)
+ if !ok {
+ return r
+ }
+ return &excludedReader{r, 0, int64(cmd.Dataoff), int64(cmd.Dataoff + cmd.Datasize)}
+}
+
+// excludedReader wraps an io.Reader. Reading from it returns the bytes from
+// the underlying reader, except that when the byte offset is within the
+// range between start and end, it returns zero bytes.
+type excludedReader struct {
+ r io.Reader
+ off int64 // current offset
+ start, end int64 // the range to be excluded (read as zero)
+}
+
+func (r *excludedReader) Read(p []byte) (int, error) {
+ n, err := r.r.Read(p)
+ if n > 0 && r.off+int64(n) > r.start && r.off < r.end {
+ cstart := r.start - r.off
+ if cstart < 0 {
+ cstart = 0
+ }
+ cend := r.end - r.off
+ if cend > int64(n) {
+ cend = int64(n)
+ }
+ zeros := make([]byte, cend-cstart)
+ copy(p[cstart:cend], zeros)
+ }
+ r.off += int64(n)
+ return n, err
+}
+
+func findMachoCodeSignature(r interface{}) (*macho.File, codesign.CodeSigCmd, bool) {
+ ra, ok := r.(io.ReaderAt)
+ if !ok {
+ return nil, codesign.CodeSigCmd{}, false
+ }
+ f, err := macho.NewFile(ra)
+ if err != nil {
+ return nil, codesign.CodeSigCmd{}, false
+ }
+ cmd, ok := codesign.FindCodeSigCmd(f)
+ return f, cmd, ok
+}