LearnGO/go/pkg/mod/github.com/cilium/ebpf@v0.11.0/cmd/bpf2go/compile.go
2024-09-19 21:38:24 -04:00

211 lines
4.7 KiB
Go

package main
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
)
type compileArgs struct {
// Which compiler to use
cc string
cFlags []string
// Absolute working directory
dir string
// Absolute input file name
source string
// Absolute output file name
dest string
// Target to compile for, defaults to "bpf".
target string
// Depfile will be written here if depName is not empty
dep io.Writer
}
func compile(args compileArgs) error {
// Default cflags that can be overridden by args.cFlags
overrideFlags := []string{
// Code needs to be optimized, otherwise the verifier will often fail
// to understand it.
"-O2",
// Clang defaults to mcpu=probe which checks the kernel that we are
// compiling on. This isn't appropriate for ahead of time
// compiled code so force the most compatible version.
"-mcpu=v1",
}
cmd := exec.Command(args.cc, append(overrideFlags, args.cFlags...)...)
cmd.Stderr = os.Stderr
inputDir := filepath.Dir(args.source)
relInputDir, err := filepath.Rel(args.dir, inputDir)
if err != nil {
return err
}
target := args.target
if target == "" {
target = "bpf"
}
// C flags that can't be overridden.
cmd.Args = append(cmd.Args,
"-target", target,
"-c", args.source,
"-o", args.dest,
// Don't include clang version
"-fno-ident",
// Don't output inputDir into debug info
"-fdebug-prefix-map="+inputDir+"="+relInputDir,
"-fdebug-compilation-dir", ".",
// We always want BTF to be generated, so enforce debug symbols
"-g",
fmt.Sprintf("-D__BPF_TARGET_MISSING=%q", "GCC error \"The eBPF is using target specific macros, please provide -target that is not bpf, bpfel or bpfeb\""),
)
cmd.Dir = args.dir
var depFile *os.File
if args.dep != nil {
depFile, err = os.CreateTemp("", "bpf2go")
if err != nil {
return err
}
defer depFile.Close()
defer os.Remove(depFile.Name())
cmd.Args = append(cmd.Args,
// Output dependency information.
"-MD",
// Create phony targets so that deleting a dependency doesn't
// break the build.
"-MP",
// Write it to temporary file
"-MF"+depFile.Name(),
)
}
if err := cmd.Run(); err != nil {
return fmt.Errorf("can't execute %s: %s", args.cc, err)
}
if depFile != nil {
if _, err := io.Copy(args.dep, depFile); err != nil {
return fmt.Errorf("error writing depfile: %w", err)
}
}
return nil
}
func adjustDependencies(baseDir string, deps []dependency) ([]byte, error) {
var buf bytes.Buffer
for _, dep := range deps {
relativeFile, err := filepath.Rel(baseDir, dep.file)
if err != nil {
return nil, err
}
if len(dep.prerequisites) == 0 {
_, err := fmt.Fprintf(&buf, "%s:\n\n", relativeFile)
if err != nil {
return nil, err
}
continue
}
var prereqs []string
for _, prereq := range dep.prerequisites {
relativePrereq, err := filepath.Rel(baseDir, prereq)
if err != nil {
return nil, err
}
prereqs = append(prereqs, relativePrereq)
}
_, err = fmt.Fprintf(&buf, "%s: \\\n %s\n\n", relativeFile, strings.Join(prereqs, " \\\n "))
if err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
type dependency struct {
file string
prerequisites []string
}
func parseDependencies(baseDir string, in io.Reader) ([]dependency, error) {
abs := func(path string) string {
if filepath.IsAbs(path) {
return path
}
return filepath.Join(baseDir, path)
}
scanner := bufio.NewScanner(in)
var line strings.Builder
var deps []dependency
for scanner.Scan() {
buf := scanner.Bytes()
if line.Len()+len(buf) > 1024*1024 {
return nil, errors.New("line too long")
}
if bytes.HasSuffix(buf, []byte{'\\'}) {
line.Write(buf[:len(buf)-1])
continue
}
line.Write(buf)
if line.Len() == 0 {
// Skip empty lines
continue
}
parts := strings.SplitN(line.String(), ":", 2)
if len(parts) < 2 {
return nil, fmt.Errorf("invalid line without ':'")
}
// NB: This doesn't handle filenames with spaces in them.
// It seems like make doesn't do that either, so oh well.
var prereqs []string
for _, prereq := range strings.Fields(parts[1]) {
prereqs = append(prereqs, abs(prereq))
}
deps = append(deps, dependency{
abs(string(parts[0])),
prereqs,
})
line.Reset()
}
if err := scanner.Err(); err != nil {
return nil, err
}
// There is always at least a dependency for the main file.
if len(deps) == 0 {
return nil, fmt.Errorf("empty dependency file")
}
return deps, nil
}
// strip DWARF debug info from file by executing exe.
func strip(exe, file string) error {
cmd := exec.Command(exe, "-g", file)
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("%s: %s", exe, err)
}
return nil
}