@@ -21,13 +21,14 @@ import (
21
21
"github.com/cilium/ebpf/cmd/bpf2go/gen"
22
22
)
23
23
24
- const helpText = `Usage: %[1]s [options] <ident> <source file > [-- <C flags>]
24
+ const helpText = `Usage: %[1]s [options] <ident> <source files... > [-- <C flags>]
25
25
26
26
ident is used as the stem of all generated Go types and functions, and
27
27
must be a valid Go identifier.
28
28
29
- source is a single C file that is compiled using the specified compiler
30
- (usually some version of clang).
29
+ source files are C files that are compiled using the specified compiler
30
+ (usually some version of clang) and linked together into a single
31
+ BPF program.
31
32
32
33
You can pass options to the compiler by appending them after a '--' argument
33
34
or by supplying -cflags. Flags passed as arguments take precedence
@@ -60,8 +61,8 @@ func run(stdout io.Writer, args []string) (err error) {
60
61
type bpf2go struct {
61
62
stdout io.Writer
62
63
verbose bool
63
- // Absolute path to a .c file .
64
- sourceFile string
64
+ // Absolute paths to .c files .
65
+ sourceFiles [] string
65
66
// Absolute path to a directory where .go are written
66
67
outputDir string
67
68
// Alternative output stem. If empty, identStem is used.
@@ -186,13 +187,18 @@ func newB2G(stdout io.Writer, args []string) (*bpf2go, error) {
186
187
187
188
b2g .identStem = args [0 ]
188
189
189
- sourceFile , err := filepath .Abs (args [1 ])
190
- if err != nil {
191
- return nil , err
190
+ sourceFiles := args [1 :]
191
+ b2g .sourceFiles = make ([]string , len (sourceFiles ))
192
+ for i , source := range sourceFiles {
193
+ absPath , err := filepath .Abs (source )
194
+ if err != nil {
195
+ return nil , fmt .Errorf ("convert source file to absolute path: %w" , err )
196
+ }
197
+ b2g .sourceFiles [i ] = absPath
192
198
}
193
- b2g .sourceFile = sourceFile
194
199
195
200
if b2g .makeBase != "" {
201
+ var err error
196
202
b2g .makeBase , err = filepath .Abs (b2g .makeBase )
197
203
if err != nil {
198
204
return nil , err
@@ -298,10 +304,13 @@ func getBool(key string, defaultVal bool) bool {
298
304
}
299
305
300
306
func (b2g * bpf2go ) convertAll () (err error ) {
301
- if _ , err := os .Stat (b2g .sourceFile ); os .IsNotExist (err ) {
302
- return fmt .Errorf ("file %s doesn't exist" , b2g .sourceFile )
303
- } else if err != nil {
304
- return err
307
+ // Check all source files exist
308
+ for _ , source := range b2g .sourceFiles {
309
+ if _ , err := os .Stat (source ); os .IsNotExist (err ) {
310
+ return fmt .Errorf ("file %s doesn't exist" , source )
311
+ } else if err != nil {
312
+ return err
313
+ }
305
314
}
306
315
307
316
if ! b2g .disableStripping {
@@ -320,6 +329,65 @@ func (b2g *bpf2go) convertAll() (err error) {
320
329
return nil
321
330
}
322
331
332
+ // compileOne compiles a single source file and returns the temporary object file name
333
+ // and any dependencies found during compilation.
334
+ func (b2g * bpf2go ) compileOne (tgt gen.Target , cwd , source , objFileName , outputStem string ) (tmpObjFileName string , deps []dependency , err error ) {
335
+ var depInput * os.File
336
+ cFlags := slices .Clone (b2g .cFlags )
337
+ if b2g .makeBase != "" {
338
+ depInput , err = os .CreateTemp ("" , "bpf2go" )
339
+ if err != nil {
340
+ return "" , nil , err
341
+ }
342
+ defer depInput .Close ()
343
+ defer os .Remove (depInput .Name ())
344
+
345
+ cFlags = append (cFlags ,
346
+ // Output dependency information.
347
+ "-MD" ,
348
+ // Create phony targets so that deleting a dependency doesn't
349
+ // break the build.
350
+ "-MP" ,
351
+ // Write it to temporary file
352
+ "-MF" + depInput .Name (),
353
+ )
354
+ }
355
+
356
+ // Compile to final object file name first
357
+ err = gen .Compile (gen.CompileArgs {
358
+ CC : b2g .cc ,
359
+ Strip : b2g .strip ,
360
+ DisableStripping : b2g .disableStripping ,
361
+ Flags : cFlags ,
362
+ Target : tgt ,
363
+ Workdir : cwd ,
364
+ Source : source ,
365
+ Dest : objFileName ,
366
+ })
367
+ if err != nil {
368
+ return "" , nil , fmt .Errorf ("compile %s: %w" , source , err )
369
+ }
370
+
371
+ // Move the compiled object to a temporary file
372
+ tmpObjFileName = filepath .Join (filepath .Dir (objFileName ), fmt .Sprintf ("%s_%s_%s.o" ,
373
+ outputStem ,
374
+ filepath .Base (source ),
375
+ tgt .Suffix ()))
376
+ if err := os .Rename (objFileName , tmpObjFileName ); err != nil {
377
+ return "" , nil , fmt .Errorf ("move object file: %w" , err )
378
+ }
379
+
380
+ // Parse dependencies if enabled
381
+ if b2g .makeBase != "" {
382
+ deps , err = parseDependencies (cwd , depInput )
383
+ if err != nil {
384
+ return "" , nil , fmt .Errorf ("parse dependencies for %s: %w" , source , err )
385
+ }
386
+ }
387
+
388
+ return tmpObjFileName , deps , nil
389
+ }
390
+
323
391
func (b2g * bpf2go ) convert (tgt gen.Target , goarches gen.GoArches ) (err error ) {
324
392
removeOnError := func (f * os.File ) {
325
393
if err != nil {
@@ -341,6 +409,7 @@ func (b2g *bpf2go) convert(tgt gen.Target, goarches gen.GoArches) (err error) {
341
409
}
342
410
343
411
objFileName := filepath .Join (absOutPath , stem + ".o" )
412
+ goFileName := filepath .Join (absOutPath , stem + ".go" )
344
413
345
414
cwd , err := os .Getwd ()
346
415
if err != nil {
@@ -354,39 +423,43 @@ func (b2g *bpf2go) convert(tgt gen.Target, goarches gen.GoArches) (err error) {
354
423
return fmt .Errorf ("remove obsolete output: %w" , err )
355
424
}
356
425
357
- var depInput * os.File
358
- cFlags := slices .Clone (b2g .cFlags )
359
- if b2g .makeBase != "" {
360
- depInput , err = os .CreateTemp ("" , "bpf2go" )
426
+ // Compile each source file
427
+ var allDeps []dependency
428
+ var tmpObjFileNames []string
429
+ for _ , source := range b2g .sourceFiles {
430
+ tmpObjFileName , deps , err := b2g .compileOne (tgt , cwd , source , objFileName , outputStem )
361
431
if err != nil {
362
432
return err
363
433
}
364
- defer depInput .Close ()
365
- defer os .Remove (depInput .Name ())
434
+ tmpObjFileNames = append (tmpObjFileNames , tmpObjFileName )
366
435
367
- cFlags = append (cFlags ,
368
- // Output dependency information.
369
- "-MD" ,
370
- // Create phony targets so that deleting a dependency doesn't
371
- // break the build.
372
- "-MP" ,
373
- // Write it to temporary file
374
- "-MF" + depInput .Name (),
375
- )
436
+ if len (deps ) > 0 {
437
+ // There is always at least a dependency for the main file.
438
+ deps [0 ].file = goFileName
439
+ allDeps = append (allDeps , deps ... )
440
+ }
376
441
}
377
442
378
- err = gen .Compile (gen.CompileArgs {
379
- CC : b2g .cc ,
380
- Strip : b2g .strip ,
381
- DisableStripping : b2g .disableStripping ,
382
- Flags : cFlags ,
383
- Target : tgt ,
384
- Workdir : cwd ,
385
- Source : b2g .sourceFile ,
386
- Dest : objFileName ,
387
- })
388
- if err != nil {
389
- return fmt .Errorf ("compile: %w" , err )
443
+ // If we have multiple object files, link them together
444
+ if len (tmpObjFileNames ) > 1 {
445
+ cmd := exec .Command ("bpftool" , "gen" , "object" , objFileName )
446
+ cmd .Args = append (cmd .Args , tmpObjFileNames ... )
447
+ cmd .Stderr = os .Stderr
448
+ if err := cmd .Run (); err != nil {
449
+ return fmt .Errorf ("link object files: %w" , err )
450
+ }
451
+ } else {
452
+ // Single file, just rename it back to the final name
453
+ if err := os .Rename (tmpObjFileNames [0 ], objFileName ); err != nil {
454
+ return fmt .Errorf ("rename object file: %w" , err )
455
+ }
456
+ }
457
+
458
+ // Clean up temporary object files
459
+ for _ , tmpObj := range tmpObjFileNames {
460
+ if err := os .Remove (tmpObj ); err != nil && ! os .IsNotExist (err ) {
461
+ return fmt .Errorf ("remove temporary object file: %w" , err )
462
+ }
390
463
}
391
464
392
465
if b2g .disableStripping {
@@ -428,7 +501,6 @@ func (b2g *bpf2go) convert(tgt gen.Target, goarches gen.GoArches) (err error) {
428
501
}
429
502
430
503
// Write out generated go
431
- goFileName := filepath .Join (absOutPath , stem + ".go" )
432
504
goFile , err := os .Create (goFileName )
433
505
if err != nil {
434
506
return err
@@ -456,9 +528,10 @@ func (b2g *bpf2go) convert(tgt gen.Target, goarches gen.GoArches) (err error) {
456
528
return
457
529
}
458
530
459
- deps , err := parseDependencies (cwd , depInput )
460
- if err != nil {
461
- return fmt .Errorf ("can't read dependency information: %s" , err )
531
+ // Merge dependencies if we have multiple source files
532
+ var finalDeps []dependency
533
+ if len (allDeps ) > 0 {
534
+ finalDeps = mergeDependencies (allDeps )
462
535
}
463
536
464
537
depFileName := goFileName + ".d"
@@ -468,9 +541,7 @@ func (b2g *bpf2go) convert(tgt gen.Target, goarches gen.GoArches) (err error) {
468
541
}
469
542
defer depOutput .Close ()
470
543
471
- // There is always at least a dependency for the main file.
472
- deps [0 ].file = goFileName
473
- if err := adjustDependencies (depOutput , b2g .makeBase , deps ); err != nil {
544
+ if err := adjustDependencies (depOutput , b2g .makeBase , finalDeps ); err != nil {
474
545
return fmt .Errorf ("can't adjust dependency information: %s" , err )
475
546
}
476
547
0 commit comments