| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package buildinfo |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io" |
| "testing" |
| ) |
| |
| type byteExe struct { |
| b []byte |
| } |
| |
| func (x *byteExe) DataReader(addr uint64) (io.ReaderAt, error) { |
| if addr >= uint64(len(x.b)) { |
| return nil, fmt.Errorf("ReadData(%d) out of bounds of %d-byte slice", addr, len(x.b)) |
| } |
| return bytes.NewReader(x.b[addr:]), nil |
| } |
| |
| func (x *byteExe) DataStart() (uint64, uint64) { |
| return 0, uint64(len(x.b)) |
| } |
| |
| func TestSearchMagic(t *testing.T) { |
| tests := []struct { |
| name string |
| data []byte |
| want uint64 |
| wantErr error |
| }{ |
| { |
| name: "beginning", |
| data: func() []byte { |
| b := make([]byte, buildInfoHeaderSize) |
| copy(b, buildInfoMagic) |
| return b |
| }(), |
| want: 0, |
| }, |
| { |
| name: "offset", |
| data: func() []byte { |
| b := make([]byte, 512) |
| copy(b[4*buildInfoAlign:], buildInfoMagic) |
| return b |
| }(), |
| want: 4 * buildInfoAlign, |
| }, |
| { |
| name: "second_chunk", |
| data: func() []byte { |
| b := make([]byte, 4*searchChunkSize) |
| copy(b[searchChunkSize+4*buildInfoAlign:], buildInfoMagic) |
| return b |
| }(), |
| want: searchChunkSize + 4*buildInfoAlign, |
| }, |
| { |
| name: "second_chunk_short", |
| data: func() []byte { |
| // Magic is 64-bytes into the second chunk, |
| // which is short; only exactly long enough to |
| // hold the header. |
| b := make([]byte, searchChunkSize+4*buildInfoAlign+buildInfoHeaderSize) |
| copy(b[searchChunkSize+4*buildInfoAlign:], buildInfoMagic) |
| return b |
| }(), |
| want: searchChunkSize + 4*buildInfoAlign, |
| }, |
| { |
| name: "missing", |
| data: func() []byte { |
| b := make([]byte, buildInfoHeaderSize) |
| return b |
| }(), |
| wantErr: errNotGoExe, |
| }, |
| { |
| name: "too_short", |
| data: func() []byte { |
| // There needs to be space for the entire |
| // header, not just the magic. |
| b := make([]byte, len(buildInfoMagic)) |
| copy(b, buildInfoMagic) |
| return b |
| }(), |
| wantErr: errNotGoExe, |
| }, |
| { |
| name: "misaligned", |
| data: func() []byte { |
| b := make([]byte, 512) |
| copy(b[7:], buildInfoMagic) |
| return b |
| }(), |
| wantErr: errNotGoExe, |
| }, |
| { |
| name: "misaligned_across_chunk", |
| data: func() []byte { |
| // Magic crosses chunk boundary. By definition, |
| // it has to be misaligned. |
| b := make([]byte, 2*searchChunkSize) |
| copy(b[searchChunkSize-8:], buildInfoMagic) |
| return b |
| }(), |
| wantErr: errNotGoExe, |
| }, |
| { |
| name: "header_across_chunk", |
| data: func() []byte { |
| // The magic is aligned within the first chunk, |
| // but the rest of the 32-byte header crosses |
| // the chunk boundary. |
| b := make([]byte, 2*searchChunkSize) |
| copy(b[searchChunkSize-buildInfoAlign:], buildInfoMagic) |
| return b |
| }(), |
| want: searchChunkSize - buildInfoAlign, |
| }, |
| } |
| for _, tc := range tests { |
| t.Run(tc.name, func(t *testing.T) { |
| x := &byteExe{tc.data} |
| dataAddr, dataSize := x.DataStart() |
| addr, err := searchMagic(x, dataAddr, dataSize) |
| if tc.wantErr == nil { |
| if err != nil { |
| t.Errorf("searchMagic got err %v want nil", err) |
| } |
| if addr != tc.want { |
| t.Errorf("searchMagic got addr %d want %d", addr, tc.want) |
| } |
| } else { |
| if err != tc.wantErr { |
| t.Errorf("searchMagic got err %v want %v", err, tc.wantErr) |
| } |
| } |
| }) |
| } |
| } |