...

Source file src/github.com/cilium/ebpf/cmd/bpf2go/compile.go

Documentation: github.com/cilium/ebpf/cmd/bpf2go

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"strings"
    13  )
    14  
    15  type compileArgs struct {
    16  	// Which compiler to use
    17  	cc     string
    18  	cFlags []string
    19  	// Absolute working directory
    20  	dir string
    21  	// Absolute input file name
    22  	source string
    23  	// Absolute output file name
    24  	dest string
    25  	// Target to compile for, defaults to "bpf".
    26  	target string
    27  	// Depfile will be written here if depName is not empty
    28  	dep io.Writer
    29  }
    30  
    31  func compile(args compileArgs) error {
    32  	// Default cflags that can be overridden by args.cFlags
    33  	overrideFlags := []string{
    34  		// Code needs to be optimized, otherwise the verifier will often fail
    35  		// to understand it.
    36  		"-O2",
    37  		// Clang defaults to mcpu=probe which checks the kernel that we are
    38  		// compiling on. This isn't appropriate for ahead of time
    39  		// compiled code so force the most compatible version.
    40  		"-mcpu=v1",
    41  	}
    42  
    43  	cmd := exec.Command(args.cc, append(overrideFlags, args.cFlags...)...)
    44  	cmd.Stderr = os.Stderr
    45  
    46  	inputDir := filepath.Dir(args.source)
    47  	relInputDir, err := filepath.Rel(args.dir, inputDir)
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	target := args.target
    53  	if target == "" {
    54  		target = "bpf"
    55  	}
    56  
    57  	// C flags that can't be overridden.
    58  	cmd.Args = append(cmd.Args,
    59  		"-target", target,
    60  		"-c", args.source,
    61  		"-o", args.dest,
    62  		// Don't include clang version
    63  		"-fno-ident",
    64  		// Don't output inputDir into debug info
    65  		"-fdebug-prefix-map="+inputDir+"="+relInputDir,
    66  		"-fdebug-compilation-dir", ".",
    67  		// We always want BTF to be generated, so enforce debug symbols
    68  		"-g",
    69  		fmt.Sprintf("-D__BPF_TARGET_MISSING=%q", "GCC error \"The eBPF is using target specific macros, please provide -target\""),
    70  	)
    71  	cmd.Dir = args.dir
    72  
    73  	var depRd, depWr *os.File
    74  	if args.dep != nil {
    75  		depRd, depWr, err = os.Pipe()
    76  		if err != nil {
    77  			return err
    78  		}
    79  		defer depRd.Close()
    80  		defer depWr.Close()
    81  
    82  		// This becomes /dev/fd/3
    83  		cmd.ExtraFiles = append(cmd.ExtraFiles, depWr)
    84  		cmd.Args = append(cmd.Args,
    85  			// Output dependency information.
    86  			"-MD",
    87  			// Create phony targets so that deleting a dependency doesn't
    88  			// break the build.
    89  			"-MP",
    90  			// Write it to our pipe
    91  			"-MF/dev/fd/3",
    92  		)
    93  	}
    94  
    95  	if err := cmd.Start(); err != nil {
    96  		return fmt.Errorf("can't execute %s: %s", args.cc, err)
    97  	}
    98  
    99  	if depRd != nil {
   100  		// Close our copy of the write end so that Copy will terminate
   101  		// when cc exits.
   102  		depWr.Close()
   103  		if _, err := io.Copy(args.dep, depRd); err != nil {
   104  			return fmt.Errorf("error writing depfile: %w", err)
   105  		}
   106  	}
   107  
   108  	if err := cmd.Wait(); err != nil {
   109  		return fmt.Errorf("%s: %s", args.cc, err)
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  func adjustDependencies(baseDir string, deps []dependency) ([]byte, error) {
   116  	var buf bytes.Buffer
   117  	for _, dep := range deps {
   118  		relativeFile, err := filepath.Rel(baseDir, dep.file)
   119  		if err != nil {
   120  			return nil, err
   121  		}
   122  
   123  		if len(dep.prerequisites) == 0 {
   124  			_, err := fmt.Fprintf(&buf, "%s:\n\n", relativeFile)
   125  			if err != nil {
   126  				return nil, err
   127  			}
   128  			continue
   129  		}
   130  
   131  		var prereqs []string
   132  		for _, prereq := range dep.prerequisites {
   133  			relativePrereq, err := filepath.Rel(baseDir, prereq)
   134  			if err != nil {
   135  				return nil, err
   136  			}
   137  
   138  			prereqs = append(prereqs, relativePrereq)
   139  		}
   140  
   141  		_, err = fmt.Fprintf(&buf, "%s: \\\n %s\n\n", relativeFile, strings.Join(prereqs, " \\\n "))
   142  		if err != nil {
   143  			return nil, err
   144  		}
   145  	}
   146  	return buf.Bytes(), nil
   147  }
   148  
   149  type dependency struct {
   150  	file          string
   151  	prerequisites []string
   152  }
   153  
   154  func parseDependencies(baseDir string, in io.Reader) ([]dependency, error) {
   155  	abs := func(path string) string {
   156  		if filepath.IsAbs(path) {
   157  			return path
   158  		}
   159  		return filepath.Join(baseDir, path)
   160  	}
   161  
   162  	scanner := bufio.NewScanner(in)
   163  	var line strings.Builder
   164  	var deps []dependency
   165  	for scanner.Scan() {
   166  		buf := scanner.Bytes()
   167  		if line.Len()+len(buf) > 1024*1024 {
   168  			return nil, errors.New("line too long")
   169  		}
   170  
   171  		if bytes.HasSuffix(buf, []byte{'\\'}) {
   172  			line.Write(buf[:len(buf)-1])
   173  			continue
   174  		}
   175  
   176  		line.Write(buf)
   177  		if line.Len() == 0 {
   178  			// Skip empty lines
   179  			continue
   180  		}
   181  
   182  		parts := strings.SplitN(line.String(), ":", 2)
   183  		if len(parts) < 2 {
   184  			return nil, fmt.Errorf("invalid line without ':'")
   185  		}
   186  
   187  		// NB: This doesn't handle filenames with spaces in them.
   188  		// It seems like make doesn't do that either, so oh well.
   189  		var prereqs []string
   190  		for _, prereq := range strings.Fields(parts[1]) {
   191  			prereqs = append(prereqs, abs(prereq))
   192  		}
   193  
   194  		deps = append(deps, dependency{
   195  			abs(string(parts[0])),
   196  			prereqs,
   197  		})
   198  		line.Reset()
   199  	}
   200  	if err := scanner.Err(); err != nil {
   201  		return nil, err
   202  	}
   203  
   204  	// There is always at least a dependency for the main file.
   205  	if len(deps) == 0 {
   206  		return nil, fmt.Errorf("empty dependency file")
   207  	}
   208  	return deps, nil
   209  }
   210  
   211  // strip DWARF debug info from file by executing exe.
   212  func strip(exe, file string) error {
   213  	cmd := exec.Command(exe, "-g", file)
   214  	cmd.Stderr = os.Stderr
   215  	if err := cmd.Run(); err != nil {
   216  		return fmt.Errorf("%s: %s", exe, err)
   217  	}
   218  	return nil
   219  }
   220  

View as plain text