github.com/kubeshark/ebpf@v0.9.2/cmd/bpf2go/compile.go (about) 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 }