| |
| |
| |
|
|
| package buildid |
|
|
| import ( |
| "bytes" |
| "cmd/internal/codesign" |
| imacho "cmd/internal/macho" |
| "crypto/sha256" |
| "debug/elf" |
| "debug/macho" |
| "fmt" |
| "io" |
| ) |
|
|
| |
| |
| |
| |
| |
| func FindAndHash(r io.Reader, id string, bufSize int) (matches []int64, hash [32]byte, err error) { |
| if bufSize == 0 { |
| bufSize = 31 * 1024 |
| } |
| if len(id) == 0 { |
| return nil, [32]byte{}, fmt.Errorf("buildid.FindAndHash: no id specified") |
| } |
| if len(id) > bufSize { |
| return nil, [32]byte{}, fmt.Errorf("buildid.FindAndHash: buffer too small") |
| } |
| zeros := make([]byte, len(id)) |
| idBytes := []byte(id) |
|
|
| r0 := r |
|
|
| |
| |
| |
| r = excludeMachoCodeSignature(r) |
|
|
| |
| |
| |
| r = excludeHostBuildID(r, r0) |
|
|
| |
| |
| |
| |
| |
| |
| |
| tiny := (len(id) + 127) &^ 127 |
| buf := make([]byte, tiny+bufSize) |
| h := sha256.New() |
| start := tiny |
| for offset := int64(0); ; { |
| |
| |
| |
| n, err := io.ReadFull(r, buf[tiny:]) |
| if err != io.ErrUnexpectedEOF && err != io.EOF && err != nil { |
| return nil, [32]byte{}, err |
| } |
|
|
| |
| for { |
| i := bytes.Index(buf[start:tiny+n], idBytes) |
| if i < 0 { |
| break |
| } |
| matches = append(matches, offset+int64(start+i-tiny)) |
| h.Write(buf[start : start+i]) |
| h.Write(zeros) |
| start += i + len(id) |
| } |
| if n < bufSize { |
| |
| h.Write(buf[start : tiny+n]) |
| break |
| } |
|
|
| |
| |
| |
| if start < len(buf)-tiny { |
| h.Write(buf[start : len(buf)-tiny]) |
| start = len(buf) - tiny |
| } |
|
|
| |
| copy(buf[0:], buf[bufSize:]) |
| start -= bufSize |
| offset += int64(bufSize) |
| } |
| h.Sum(hash[:0]) |
| return matches, hash, nil |
| } |
|
|
| func Rewrite(w io.WriterAt, pos []int64, id string) error { |
| b := []byte(id) |
| for _, p := range pos { |
| if _, err := w.WriteAt(b, p); err != nil { |
| return err |
| } |
| } |
|
|
| |
| if f, cmd, ok := findMachoCodeSignature(w); ok { |
| if codesign.Size(int64(cmd.Dataoff), "a.out") == int64(cmd.Datasize) { |
| |
| |
| |
| 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)} |
| } |
|
|
| func excludeHostBuildID(r, r0 io.Reader) io.Reader { |
| off, sz, ok := findHostBuildID(r0) |
| if !ok { |
| return r |
| } |
| return &excludedReader{r, 0, off, off + sz} |
| } |
|
|
| |
| |
| |
| type excludedReader struct { |
| r io.Reader |
| off int64 |
| start, end int64 |
| } |
|
|
| 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 any) (*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 |
| } |
|
|
| func findHostBuildID(r io.Reader) (offset int64, size int64, ok bool) { |
| ra, ok := r.(io.ReaderAt) |
| if !ok { |
| return 0, 0, false |
| } |
|
|
| ef, err := elf.NewFile(ra) |
| if err == nil { |
| |
| sect := ef.Section(".note.gnu.build-id") |
| if sect == nil { |
| return 0, 0, false |
| } |
| |
| return int64(sect.Offset + 16), int64(sect.Size - 16), true |
| } |
|
|
| mf, err := macho.NewFile(ra) |
| if err != nil { |
| return 0, 0, false |
| } |
|
|
| |
| reader := imacho.NewLoadCmdReader(io.NewSectionReader(ra, 0, 1<<63-1), mf.ByteOrder, imacho.FileHeaderSize(mf)) |
| for i := uint32(0); i < mf.Ncmd; i++ { |
| cmd, err := reader.Next() |
| if err != nil { |
| break |
| } |
| if cmd.Cmd == imacho.LC_UUID { |
| |
| |
| return reader.Offset() + 8, int64(cmd.Len - 8), true |
| } |
| } |
| return 0, 0, false |
| } |
|
|