1
2
3
4
5 package buildid
6
7 import (
8 "bytes"
9 "cmd/internal/codesign"
10 imacho "cmd/internal/macho"
11 "crypto/sha256"
12 "debug/elf"
13 "debug/macho"
14 "fmt"
15 "io"
16 )
17
18
19
20
21
22
23 func FindAndHash(r io.Reader, id string, bufSize int) (matches []int64, hash [32]byte, err error) {
24 if bufSize == 0 {
25 bufSize = 31 * 1024
26 }
27 if len(id) == 0 {
28 return nil, [32]byte{}, fmt.Errorf("buildid.FindAndHash: no id specified")
29 }
30 if len(id) > bufSize {
31 return nil, [32]byte{}, fmt.Errorf("buildid.FindAndHash: buffer too small")
32 }
33 zeros := make([]byte, len(id))
34 idBytes := []byte(id)
35
36 r0 := r
37
38
39
40
41 r = excludeMachoCodeSignature(r)
42
43
44
45
46 r = excludeHostBuildID(r, r0)
47
48
49
50
51
52
53
54
55 tiny := (len(id) + 127) &^ 127
56 buf := make([]byte, tiny+bufSize)
57 h := sha256.New()
58 start := tiny
59 for offset := int64(0); ; {
60
61
62
63 n, err := io.ReadFull(r, buf[tiny:])
64 if err != io.ErrUnexpectedEOF && err != io.EOF && err != nil {
65 return nil, [32]byte{}, err
66 }
67
68
69 for {
70 i := bytes.Index(buf[start:tiny+n], idBytes)
71 if i < 0 {
72 break
73 }
74 matches = append(matches, offset+int64(start+i-tiny))
75 h.Write(buf[start : start+i])
76 h.Write(zeros)
77 start += i + len(id)
78 }
79 if n < bufSize {
80
81 h.Write(buf[start : tiny+n])
82 break
83 }
84
85
86
87
88 if start < len(buf)-tiny {
89 h.Write(buf[start : len(buf)-tiny])
90 start = len(buf) - tiny
91 }
92
93
94 copy(buf[0:], buf[bufSize:])
95 start -= bufSize
96 offset += int64(bufSize)
97 }
98 h.Sum(hash[:0])
99 return matches, hash, nil
100 }
101
102 func Rewrite(w io.WriterAt, pos []int64, id string) error {
103 b := []byte(id)
104 for _, p := range pos {
105 if _, err := w.WriteAt(b, p); err != nil {
106 return err
107 }
108 }
109
110
111 if f, cmd, ok := findMachoCodeSignature(w); ok {
112 if codesign.Size(int64(cmd.Dataoff), "a.out") == int64(cmd.Datasize) {
113
114
115
116 text := f.Segment("__TEXT")
117 cs := make([]byte, cmd.Datasize)
118 codesign.Sign(cs, w.(io.Reader), "a.out", int64(cmd.Dataoff), int64(text.Offset), int64(text.Filesz), f.Type == macho.TypeExec)
119 if _, err := w.WriteAt(cs, int64(cmd.Dataoff)); err != nil {
120 return err
121 }
122 }
123 }
124
125 return nil
126 }
127
128 func excludeMachoCodeSignature(r io.Reader) io.Reader {
129 _, cmd, ok := findMachoCodeSignature(r)
130 if !ok {
131 return r
132 }
133 return &excludedReader{r, 0, int64(cmd.Dataoff), int64(cmd.Dataoff + cmd.Datasize)}
134 }
135
136 func excludeHostBuildID(r, r0 io.Reader) io.Reader {
137 off, sz, ok := findHostBuildID(r0)
138 if !ok {
139 return r
140 }
141 return &excludedReader{r, 0, off, off + sz}
142 }
143
144
145
146
147 type excludedReader struct {
148 r io.Reader
149 off int64
150 start, end int64
151 }
152
153 func (r *excludedReader) Read(p []byte) (int, error) {
154 n, err := r.r.Read(p)
155 if n > 0 && r.off+int64(n) > r.start && r.off < r.end {
156 cstart := r.start - r.off
157 if cstart < 0 {
158 cstart = 0
159 }
160 cend := r.end - r.off
161 if cend > int64(n) {
162 cend = int64(n)
163 }
164 zeros := make([]byte, cend-cstart)
165 copy(p[cstart:cend], zeros)
166 }
167 r.off += int64(n)
168 return n, err
169 }
170
171 func findMachoCodeSignature(r any) (*macho.File, codesign.CodeSigCmd, bool) {
172 ra, ok := r.(io.ReaderAt)
173 if !ok {
174 return nil, codesign.CodeSigCmd{}, false
175 }
176 f, err := macho.NewFile(ra)
177 if err != nil {
178 return nil, codesign.CodeSigCmd{}, false
179 }
180 cmd, ok := codesign.FindCodeSigCmd(f)
181 return f, cmd, ok
182 }
183
184 func findHostBuildID(r io.Reader) (offset int64, size int64, ok bool) {
185 ra, ok := r.(io.ReaderAt)
186 if !ok {
187 return 0, 0, false
188 }
189
190 ef, err := elf.NewFile(ra)
191 if err == nil {
192
193 sect := ef.Section(".note.gnu.build-id")
194 if sect == nil {
195 return 0, 0, false
196 }
197
198 return int64(sect.Offset + 16), int64(sect.Size - 16), true
199 }
200
201 mf, err := macho.NewFile(ra)
202 if err != nil {
203 return 0, 0, false
204 }
205
206
207 reader := imacho.NewLoadCmdReader(io.NewSectionReader(ra, 0, 1<<63-1), mf.ByteOrder, imacho.FileHeaderSize(mf))
208 for i := uint32(0); i < mf.Ncmd; i++ {
209 cmd, err := reader.Next()
210 if err != nil {
211 break
212 }
213 if cmd.Cmd == imacho.LC_UUID {
214
215
216 return int64(reader.Offset() + 8), int64(cmd.Len - 8), true
217 }
218 }
219 return 0, 0, false
220 }
221
View as plain text