Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,63 @@ jobs:
- name: Test
run: go test -v -race ./...

- name: Test TinyGo build-info fallback
run: go test -tags=tinygo -run TestInspectReaderWithoutGoMetadata ./binary

- name: Check debug/buildinfo stays out of the TinyGo build
if: runner.os == 'Linux'
run: |
if go list -tags=tinygo -deps . ./binary | grep -qx debug/buildinfo; then
echo "debug/buildinfo reached the tinygo build; keep it behind //go:build !tinygo" >&2
exit 1
fi

- name: Build WebAssembly library
if: runner.os == 'Linux'
run: GOOS=js GOARCH=wasm go build . ./binary

- name: Build WASI library
if: runner.os == 'Linux'
run: GOOS=wasip1 GOARCH=wasm go build . ./binary

tinygo:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod

- name: Set up Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
package-manager-cache: false

- name: Install TinyGo and Wasmtime
working-directory: ${{ runner.temp }}
run: |
curl --fail --location --silent --show-error --output tinygo.tar.gz https://github.com/tinygo-org/tinygo/releases/download/v0.42.0/tinygo0.42.0.linux-amd64.tar.gz
echo 'b87688fa2e19cee7d813cad7fd7dadb71dff3198e47125aba66ba4af5e490438 tinygo.tar.gz' | sha256sum --check
tar -xzf tinygo.tar.gz
echo "$RUNNER_TEMP/tinygo/bin" >> "$GITHUB_PATH"
curl --fail --location --silent --show-error --output wasmtime.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v44.0.1/wasmtime-v44.0.1-x86_64-linux.tar.xz
echo 'afd58715f105e3a7f454169daed22168c5736ec5f225fb04c4ac62c54c9508a3 wasmtime.tar.xz' | sha256sum --check
tar -xJf wasmtime.tar.xz
echo "$RUNNER_TEMP/wasmtime-v44.0.1-x86_64-linux" >> "$GITHUB_PATH"

- name: Test TinyGo WebAssembly binary inspection
run: tinygo test -target=wasm -run '^TestInspectReaderWithoutGoMetadata$' -v ./binary

- name: Test TinyGo WASI binary inspection
run: tinygo test -target=wasip1 -run '^TestInspectReaderWithoutGoMetadata$' -v ./binary

lint:
runs-on: ubuntu-latest
steps:
Expand Down
29 changes: 5 additions & 24 deletions binary/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package binary

import (
"debug/buildinfo"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -39,8 +38,8 @@ type Object struct {
// is present.
Producer []string `json:"producer,omitempty"`

// Go is set when the object was produced by the Go toolchain and
// carries embedded build metadata.
// Go is set when the object carries Go build metadata. It is unavailable
// under TinyGo.
Go *GoBuild `json:"go,omitempty"`

// Static lists libraries that appear to be statically linked into the
Expand Down Expand Up @@ -132,9 +131,9 @@ func InspectReader(r io.ReaderAt, size int64) (*Object, error) {
// The fat Mach-O path reads build info from its first slice because
// debug/buildinfo cannot open fat containers itself.
if obj.Go == nil {
if bi, err := buildinfo.Read(sr); err == nil {
obj.Go = goBuildFrom(bi)
obj.Producer = append(obj.Producer, bi.GoVersion)
if bi, err := readGoBuild(sr); err == nil {
obj.Go = bi
obj.Producer = append(obj.Producer, bi.Version)
}
}

Expand All @@ -159,21 +158,3 @@ func dispatch(r *io.SectionReader, size int64, head [4]byte) (*Object, []byte, e
return nil, nil, ErrUnrecognized
}
}

func goBuildFrom(bi *buildinfo.BuildInfo) *GoBuild {
g := &GoBuild{
Version: bi.GoVersion,
Path: bi.Path,
}
if bi.Main.Path != "" {
g.Main = bi.Main.Path + "@" + bi.Main.Version
}
for _, dep := range bi.Deps {
if dep.Replace != nil {
g.Deps = append(g.Deps, dep.Path+" => "+dep.Replace.Path+"@"+dep.Replace.Version)
} else {
g.Deps = append(g.Deps, dep.Path+"@"+dep.Version)
}
}
return g
}
34 changes: 34 additions & 0 deletions binary/buildinfo_std.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//go:build !tinygo

package binary

import (
"debug/buildinfo"
"io"
)

func readGoBuild(r io.ReaderAt) (*GoBuild, error) {
bi, err := buildinfo.Read(r)
if err != nil {
return nil, err
}
return goBuildFrom(bi), nil
}

func goBuildFrom(bi *buildinfo.BuildInfo) *GoBuild {
g := &GoBuild{
Version: bi.GoVersion,
Path: bi.Path,
}
if bi.Main.Path != "" {
g.Main = bi.Main.Path + "@" + bi.Main.Version
}
for _, dep := range bi.Deps {
if dep.Replace != nil {
g.Deps = append(g.Deps, dep.Path+" => "+dep.Replace.Path+"@"+dep.Replace.Version)
} else {
g.Deps = append(g.Deps, dep.Path+"@"+dep.Version)
}
}
return g
}
13 changes: 13 additions & 0 deletions binary/buildinfo_tinygo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build tinygo

package binary

import (
"errors"
"fmt"
"io"
)

func readGoBuild(r io.ReaderAt) (*GoBuild, error) {
return nil, fmt.Errorf("brief/binary: Go buildinfo inspection not available under tinygo: %w", errors.ErrUnsupported)
}
11 changes: 5 additions & 6 deletions binary/macho.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package binary

import (
"bytes"
"debug/buildinfo"
"debug/macho"
stdbin "encoding/binary"
"errors"
Expand Down Expand Up @@ -110,13 +109,13 @@ func inspectMachOFat(r io.ReaderAt, size int64, head [4]byte) (*Object, []byte,
rodata.Write(sliceROData)
}

if bi, err := buildinfo.Read(sr); err == nil {
if bi, err := readGoBuild(sr); err == nil {
if obj.Go == nil {
obj.Go = goBuildFrom(bi)
obj.Go = bi
}
if !producerSeen[bi.GoVersion] {
producerSeen[bi.GoVersion] = true
obj.Producer = append(obj.Producer, bi.GoVersion)
if !producerSeen[bi.Version] {
producerSeen[bi.Version] = true
obj.Producer = append(obj.Producer, bi.Version)
}
}
}
Expand Down
62 changes: 62 additions & 0 deletions binary/reader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package binary_test

import (
"bytes"
"debug/elf"
"debug/macho"
"encoding/binary"
"testing"

briefbinary "github.com/git-pkgs/brief/binary"
)

func TestInspectReaderWithoutGoMetadata(t *testing.T) {
var elfData bytes.Buffer
elfHeader := elf.Header64{
Ident: [elf.EI_NIDENT]byte{0x7f, 'E', 'L', 'F', byte(elf.ELFCLASS64), byte(elf.ELFDATA2LSB), byte(elf.EV_CURRENT)},
Type: uint16(elf.ET_REL), Machine: uint16(elf.EM_X86_64), Version: uint32(elf.EV_CURRENT),
Ehsize: 64,
}
if err := binary.Write(&elfData, binary.LittleEndian, elfHeader); err != nil {
t.Fatal(err)
}

var machData bytes.Buffer
machHeader := macho.FileHeader{Magic: macho.Magic64, Cpu: macho.CpuArm64, Type: macho.TypeObj}
if err := binary.Write(&machData, binary.LittleEndian, machHeader); err != nil {
t.Fatal(err)
}
if err := binary.Write(&machData, binary.LittleEndian, uint32(0)); err != nil {
t.Fatal(err)
}

var fatData bytes.Buffer
fatHeader := []uint32{macho.MagicFat, 1, uint32(macho.CpuArm64), 0, 28, uint32(machData.Len()), 0}
if err := binary.Write(&fatData, binary.BigEndian, fatHeader); err != nil {
t.Fatal(err)
}
fatData.Write(machData.Bytes())

for _, tt := range []struct {
format string
arch string
data []byte
}{
{"elf", "amd64", elfData.Bytes()},
{"mach-o", "arm64", machData.Bytes()},
{"mach-o-universal", "arm64", fatData.Bytes()},
} {
t.Run(tt.format, func(t *testing.T) {
obj, err := briefbinary.InspectReader(bytes.NewReader(tt.data), int64(len(tt.data)))
if err != nil {
t.Fatal(err)
}
if obj.Format != tt.format || obj.Arch != tt.arch {
t.Fatalf("object = %+v, want %s/%s", obj, tt.format, tt.arch)
}
if obj.Go != nil || len(obj.Producer) != 0 {
t.Fatalf("unexpected Go metadata: Go=%+v, Producer=%v", obj.Go, obj.Producer)
}
})
}
}
Loading