Skip to content

Commit f95957d

Browse files
committed
cmd/bpf2go: fix s390x target
The current format of output filenames for a specific GOARCH is <stem>_<clang target>_<linux target>.go The s390x GOARCH has a linux target of "s390", which leads to a file name like foo_bpfeb_s390.go The problem is that "_s390" is interpreted as an additional build constraint by the Go toolchain. In effect, the will only be included in the build if GOARCH is s390 and s390x which is of course not possible. Fix this by changing the output filename format and add some code which removes files with the old naming schema. There is a small risk that this might in the future delete files that we haven't written, but the chances of that seem low enough. Extend the bpf2go tests to ensure that we've generated the right build tags for arm64, amd64 and s390x. Signed-off-by: Lorenz Bauer <lmb@isovalent.com>
1 parent 4ad6b8c commit f95957d

File tree

5 files changed

+119
-49
lines changed

5 files changed

+119
-49
lines changed

cmd/bpf2go/compile_test.go

+10-16
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212
const minimalSocketFilter = `__attribute__((section("socket"), used)) int main() { return 0; }`
1313

1414
func TestCompile(t *testing.T) {
15-
dir := mustWriteTempFile(t, "test.c", minimalSocketFilter)
15+
dir := t.TempDir()
16+
mustWriteFile(t, dir, "test.c", minimalSocketFilter)
1617

1718
var dep bytes.Buffer
1819
err := compile(compileArgs{
@@ -46,7 +47,8 @@ func TestCompile(t *testing.T) {
4647

4748
func TestReproducibleCompile(t *testing.T) {
4849
clangBin := clangBin(t)
49-
dir := mustWriteTempFile(t, "test.c", minimalSocketFilter)
50+
dir := t.TempDir()
51+
mustWriteFile(t, dir, "test.c", minimalSocketFilter)
5052

5153
err := compile(compileArgs{
5254
cc: clangBin,
@@ -84,7 +86,8 @@ func TestReproducibleCompile(t *testing.T) {
8486
}
8587

8688
func TestTriggerMissingTarget(t *testing.T) {
87-
dir := mustWriteTempFile(t, "test.c", `_Pragma(__BPF_TARGET_MISSING);`)
89+
dir := t.TempDir()
90+
mustWriteFile(t, dir, "test.c", `_Pragma(__BPF_TARGET_MISSING);`)
8891

8992
err := compile(compileArgs{
9093
cc: clangBin(t),
@@ -148,19 +151,10 @@ nothing:
148151
}
149152
}
150153

151-
func mustWriteTempFile(t *testing.T, name, contents string) string {
152-
t.Helper()
153-
154-
tmp, err := os.MkdirTemp("", "bpf2go")
155-
if err != nil {
156-
t.Fatal(err)
157-
}
158-
t.Cleanup(func() { os.RemoveAll(tmp) })
159-
160-
tmpFile := filepath.Join(tmp, name)
154+
func mustWriteFile(tb testing.TB, dir, name, contents string) {
155+
tb.Helper()
156+
tmpFile := filepath.Join(dir, name)
161157
if err := os.WriteFile(tmpFile, []byte(contents), 0660); err != nil {
162-
t.Fatal(err)
158+
tb.Fatal(err)
163159
}
164-
165-
return tmp
166160
}

cmd/bpf2go/main.go

+38-1
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,17 @@ func (b2g *bpf2go) convert(tgt target, goarches []goarch) (err error) {
307307
if outputStem == "" {
308308
outputStem = strings.ToLower(b2g.identStem)
309309
}
310+
311+
// The output filename must not match any of the following patterns:
312+
//
313+
// *_GOOS
314+
// *_GOARCH
315+
// *_GOOS_GOARCH
316+
//
317+
// Otherwise it is interpreted as a build constraint by the Go toolchain.
310318
stem := fmt.Sprintf("%s_%s", outputStem, tgt.clang)
311319
if tgt.linux != "" {
312-
stem = fmt.Sprintf("%s_%s_%s", outputStem, tgt.clang, tgt.linux)
320+
stem = fmt.Sprintf("%s_%s_%s", outputStem, tgt.linux, tgt.clang)
313321
}
314322

315323
objFileName := filepath.Join(b2g.outputDir, stem+".o")
@@ -333,6 +341,10 @@ func (b2g *bpf2go) convert(tgt target, goarches []goarch) (err error) {
333341
cFlags = append(cFlags, "-D__TARGET_ARCH_"+tgt.linux)
334342
}
335343

344+
if err := b2g.removeOldOutputFiles(outputStem, tgt); err != nil {
345+
return fmt.Errorf("remove obsolete output: %w", err)
346+
}
347+
336348
var dep bytes.Buffer
337349
err = compile(compileArgs{
338350
cc: b2g.cc,
@@ -415,6 +427,31 @@ func (b2g *bpf2go) convert(tgt target, goarches []goarch) (err error) {
415427
return nil
416428
}
417429

430+
// removeOldOutputFiles removes output files generated by an old naming scheme.
431+
//
432+
// In the old scheme some linux targets were interpreted as build constraints
433+
// by the go toolchain.
434+
func (b2g *bpf2go) removeOldOutputFiles(outputStem string, tgt target) error {
435+
if tgt.linux == "" {
436+
return nil
437+
}
438+
439+
stem := fmt.Sprintf("%s_%s_%s", outputStem, tgt.clang, tgt.linux)
440+
for _, ext := range []string{".o", ".go"} {
441+
filename := filepath.Join(b2g.outputDir, stem+ext)
442+
443+
if err := os.Remove(filename); errors.Is(err, os.ErrNotExist) {
444+
continue
445+
} else if err != nil {
446+
return err
447+
}
448+
449+
fmt.Fprintln(b2g.stdout, "Removed obsolete", filename)
450+
}
451+
452+
return nil
453+
}
454+
418455
type target struct {
419456
// Clang arch string, used to define the clang -target flag, as per
420457
// "clang -print-targets".

cmd/bpf2go/main_test.go

+70-31
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,22 @@ import (
1919

2020
func TestRun(t *testing.T) {
2121
clangBin := clangBin(t)
22-
dir := mustWriteTempFile(t, "test.c", minimalSocketFilter)
22+
dir := t.TempDir()
23+
mustWriteFile(t, dir, "test.c", minimalSocketFilter)
2324

24-
cwd, err := os.Getwd()
25-
if err != nil {
26-
t.Fatal(err)
27-
}
25+
modRoot, err := filepath.Abs("../..")
26+
qt.Assert(t, qt.IsNil(err))
2827

29-
modRoot := filepath.Clean(filepath.Join(cwd, "../.."))
3028
if _, err := os.Stat(filepath.Join(modRoot, "go.mod")); os.IsNotExist(err) {
3129
t.Fatal("No go.mod file in", modRoot)
3230
}
3331

34-
tmpDir, err := os.MkdirTemp("", "bpf2go-module-*")
35-
if err != nil {
36-
t.Fatal(err)
37-
}
38-
defer os.RemoveAll(tmpDir)
39-
32+
modDir := t.TempDir()
4033
execInModule := func(name string, args ...string) {
4134
t.Helper()
4235

4336
cmd := exec.Command(name, args...)
44-
cmd.Dir = tmpDir
37+
cmd.Dir = modDir
4538
if out, err := cmd.CombinedOutput(); err != nil {
4639
if out := string(out); out != "" {
4740
t.Log(out)
@@ -62,8 +55,15 @@ func TestRun(t *testing.T) {
6255
fmt.Sprintf("-replace=%s=%s", module, modRoot),
6356
)
6457

65-
err = run(io.Discard, "foo", tmpDir, []string{
58+
goarches := []string{
59+
"amd64", // little-endian
60+
"arm64",
61+
"s390x", // big-endian
62+
}
63+
64+
err = run(io.Discard, "main", modDir, []string{
6665
"-cc", clangBin,
66+
"-target", strings.Join(goarches, ","),
6767
"bar",
6868
filepath.Join(dir, "test.c"),
6969
})
@@ -72,18 +72,24 @@ func TestRun(t *testing.T) {
7272
t.Fatal("Can't run:", err)
7373
}
7474

75-
for _, arch := range []string{
76-
"amd64", // little-endian
77-
"s390x", // big-endian
78-
} {
75+
mustWriteFile(t, modDir, "main.go",
76+
`
77+
package main
78+
79+
func main() {
80+
var obj barObjects
81+
println(obj.Main)
82+
}`)
83+
84+
for _, arch := range goarches {
7985
t.Run(arch, func(t *testing.T) {
80-
goBin := exec.Command("go", "build", "-mod=mod")
81-
goBin.Dir = tmpDir
82-
goBin.Env = append(os.Environ(),
86+
goBuild := exec.Command("go", "build", "-mod=mod", "-o", "/dev/null")
87+
goBuild.Dir = modDir
88+
goBuild.Env = append(os.Environ(),
8389
"GOOS=linux",
8490
"GOARCH="+arch,
8591
)
86-
out, err := goBin.CombinedOutput()
92+
out, err := goBuild.CombinedOutput()
8793
if err != nil {
8894
if out := string(out); out != "" {
8995
t.Log(out)
@@ -112,7 +118,8 @@ func TestErrorMentionsEnvVar(t *testing.T) {
112118
}
113119

114120
func TestDisableStripping(t *testing.T) {
115-
dir := mustWriteTempFile(t, "test.c", minimalSocketFilter)
121+
dir := t.TempDir()
122+
mustWriteFile(t, dir, "test.c", minimalSocketFilter)
116123

117124
err := run(io.Discard, "foo", dir, []string{
118125
"-cc", clangBin(t),
@@ -213,7 +220,7 @@ func TestCollectTargetsErrors(t *testing.T) {
213220
target string
214221
}{
215222
{"unknown", "frood"},
216-
{"no linux target", "mips64p32le"},
223+
{"no linux target", "mipsle"},
217224
}
218225

219226
for _, test := range tests {
@@ -228,7 +235,8 @@ func TestCollectTargetsErrors(t *testing.T) {
228235
}
229236

230237
func TestConvertGOARCH(t *testing.T) {
231-
tmp := mustWriteTempFile(t, "test.c",
238+
tmp := t.TempDir()
239+
mustWriteFile(t, tmp, "test.c",
232240
`
233241
#ifndef __TARGET_ARCH_x86
234242
#error __TARGET_ARCH_x86 is not defined
@@ -432,22 +440,41 @@ func TestParseArgs(t *testing.T) {
432440
}
433441

434442
func TestGoarches(t *testing.T) {
435-
exe, err := exec.LookPath("go")
436-
if errors.Is(err, exec.ErrNotFound) {
437-
t.Skip("go binary is not in PATH")
438-
}
439-
qt.Assert(t, qt.IsNil(err))
443+
exe := goBin(t)
440444

441445
for goarch := range targetByGoArch {
442446
t.Run(string(goarch), func(t *testing.T) {
443447
goEnv := exec.Command(exe, "env")
444-
goEnv.Env = []string{"GOOS=linux", "GOARCH=" + string(goarch)}
448+
goEnv.Env = []string{"GOROOT=/", "GOOS=linux", "GOARCH=" + string(goarch)}
445449
output, err := goEnv.CombinedOutput()
446450
qt.Assert(t, qt.IsNil(err), qt.Commentf("go output is:\n%s", string(output)))
447451
})
448452
}
449453
}
450454

455+
func TestClangTargets(t *testing.T) {
456+
exe := goBin(t)
457+
458+
clangTargets := map[string]struct{}{}
459+
for _, tgt := range targetByGoArch {
460+
clangTargets[tgt.clang] = struct{}{}
461+
}
462+
463+
for target := range clangTargets {
464+
for _, env := range []string{"GOOS", "GOARCH"} {
465+
env += "=" + target
466+
t.Run(env, func(t *testing.T) {
467+
goEnv := exec.Command(exe, "env")
468+
goEnv.Env = []string{"GOROOT=/", env}
469+
output, err := goEnv.CombinedOutput()
470+
t.Log("go output is:", string(output))
471+
qt.Assert(t, qt.IsNotNil(err), qt.Commentf("No clang target should be a valid build constraint"))
472+
})
473+
}
474+
475+
}
476+
}
477+
451478
func clangBin(t *testing.T) string {
452479
t.Helper()
453480

@@ -465,3 +492,15 @@ func clangBin(t *testing.T) string {
465492
t.Log("Testing against", clang)
466493
return clang
467494
}
495+
496+
func goBin(t *testing.T) string {
497+
t.Helper()
498+
499+
exe, err := exec.LookPath("go")
500+
if errors.Is(err, exec.ErrNotFound) {
501+
t.Skip("go binary is not in PATH")
502+
}
503+
qt.Assert(t, qt.IsNil(err))
504+
505+
return exe
506+
}

examples/uretprobe/bpf_bpfel_x86.go renamed to examples/uretprobe/bpf_x86_bpfel.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)