// Copyright (C) 2018 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package bpf import ( "fmt" "io" "path/filepath" "runtime" "strings" "android/soong/android" "android/soong/bazel" "android/soong/bazel/cquery" "android/soong/cc" "github.com/google/blueprint" "github.com/google/blueprint/proptools" ) func init() { registerBpfBuildComponents(android.InitRegistrationContext) pctx.Import("android/soong/cc/config") pctx.StaticVariable("relPwd", cc.PwdPrefix()) } var ( pctx = android.NewPackageContext("android/soong/bpf") ccRule = pctx.AndroidRemoteStaticRule("ccRule", android.RemoteRuleSupports{Goma: true}, blueprint.RuleParams{ Depfile: "${out}.d", Deps: blueprint.DepsGCC, Command: "$relPwd $ccCmd --target=bpf -c $cFlags -MD -MF ${out}.d -o $out $in", CommandDeps: []string{"$ccCmd"}, }, "ccCmd", "cFlags") stripRule = pctx.AndroidStaticRule("stripRule", blueprint.RuleParams{ Command: `$stripCmd --strip-unneeded --remove-section=.rel.BTF ` + `--remove-section=.rel.BTF.ext --remove-section=.BTF.ext $in -o $out`, CommandDeps: []string{"$stripCmd"}, }, "stripCmd") ) func registerBpfBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("bpf", BpfFactory) } var PrepareForTestWithBpf = android.FixtureRegisterWithContext(registerBpfBuildComponents) // BpfModule interface is used by the apex package to gather information from a bpf module. type BpfModule interface { android.Module OutputFiles(tag string) (android.Paths, error) // Returns the sub install directory if the bpf module is included by apex. SubDir() string } type BpfProperties struct { // source paths to the files. Srcs []string `android:"path"` // additional cflags that should be used to build the bpf variant of // the C/C++ module. Cflags []string // directories (relative to the root of the source tree) that will // be added to the include paths using -I. Include_dirs []string // optional subdirectory under which this module is installed into. Sub_dir string // if set to true, generate BTF debug info for maps & programs. Btf *bool Vendor *bool VendorInternal bool `blueprint:"mutated"` } type bpf struct { android.ModuleBase android.BazelModuleBase properties BpfProperties objs android.Paths } var _ android.ImageInterface = (*bpf)(nil) func (bpf *bpf) ImageMutatorBegin(ctx android.BaseModuleContext) {} func (bpf *bpf) CoreVariantNeeded(ctx android.BaseModuleContext) bool { return !proptools.Bool(bpf.properties.Vendor) } func (bpf *bpf) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false } func (bpf *bpf) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false } func (bpf *bpf) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false } func (bpf *bpf) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { return false } func (bpf *bpf) ExtraImageVariations(ctx android.BaseModuleContext) []string { if proptools.Bool(bpf.properties.Vendor) { return []string{"vendor"} } return nil } func (bpf *bpf) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) { bpf.properties.VendorInternal = variation == "vendor" } func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) { cflags := []string{ "-nostdlibinc", // Make paths in deps files relative "-no-canonical-prefixes", "-O2", "-isystem bionic/libc/include", "-isystem bionic/libc/kernel/uapi", // The architecture doesn't matter here, but asm/types.h is included by linux/types.h. "-isystem bionic/libc/kernel/uapi/asm-arm64", "-isystem bionic/libc/kernel/android/uapi", "-I packages/modules/Connectivity/staticlibs/native/bpf_headers/include/bpf", // TODO(b/149785767): only give access to specific file with AID_* constants "-I system/core/libcutils/include", "-I " + ctx.ModuleDir(), } for _, dir := range android.PathsForSource(ctx, bpf.properties.Include_dirs) { cflags = append(cflags, "-I "+dir.String()) } cflags = append(cflags, bpf.properties.Cflags...) if proptools.Bool(bpf.properties.Btf) { cflags = append(cflags, "-g") if runtime.GOOS != "darwin" { cflags = append(cflags, "-fdebug-prefix-map=/proc/self/cwd=") } } srcs := android.PathsForModuleSrc(ctx, bpf.properties.Srcs) for _, src := range srcs { if strings.ContainsRune(filepath.Base(src.String()), '_') { ctx.ModuleErrorf("invalid character '_' in source name") } obj := android.ObjPathWithExt(ctx, "unstripped", src, "o") ctx.Build(pctx, android.BuildParams{ Rule: ccRule, Input: src, Output: obj, Args: map[string]string{ "cFlags": strings.Join(cflags, " "), "ccCmd": "${config.ClangBin}/clang", }, }) if proptools.Bool(bpf.properties.Btf) { objStripped := android.ObjPathWithExt(ctx, "", src, "o") ctx.Build(pctx, android.BuildParams{ Rule: stripRule, Input: obj, Output: objStripped, Args: map[string]string{ "stripCmd": "${config.ClangBin}/llvm-strip", }, }) bpf.objs = append(bpf.objs, objStripped.WithoutRel()) } else { bpf.objs = append(bpf.objs, obj.WithoutRel()) } } } func (bpf *bpf) AndroidMk() android.AndroidMkData { return android.AndroidMkData{ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { var names []string fmt.Fprintln(w) fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) fmt.Fprintln(w) var localModulePath string if bpf.properties.VendorInternal { localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/bpf" } else { localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf" } if len(bpf.properties.Sub_dir) > 0 { localModulePath += "/" + bpf.properties.Sub_dir } for _, obj := range bpf.objs { objName := name + "_" + obj.Base() names = append(names, objName) fmt.Fprintln(w, "include $(CLEAR_VARS)", " # bpf.bpf.obj") fmt.Fprintln(w, "LOCAL_MODULE := ", objName) data.Entries.WriteLicenseVariables(w) fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String()) fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", obj.Base()) fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") fmt.Fprintln(w, localModulePath) fmt.Fprintln(w, "include $(BUILD_PREBUILT)") fmt.Fprintln(w) } fmt.Fprintln(w, "include $(CLEAR_VARS)", " # bpf.bpf") fmt.Fprintln(w, "LOCAL_MODULE := ", name) data.Entries.WriteLicenseVariables(w) android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", names) fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)") }, } } var _ android.MixedBuildBuildable = (*bpf)(nil) func (bpf *bpf) IsMixedBuildSupported(ctx android.BaseModuleContext) bool { return true } func (bpf *bpf) QueueBazelCall(ctx android.BaseModuleContext) { bazelCtx := ctx.Config().BazelContext bazelCtx.QueueBazelRequest( bpf.GetBazelLabel(ctx, bpf), cquery.GetOutputFiles, android.GetConfigKey(ctx)) } func (bpf *bpf) ProcessBazelQueryResponse(ctx android.ModuleContext) { bazelCtx := ctx.Config().BazelContext objPaths, err := bazelCtx.GetOutputFiles(bpf.GetBazelLabel(ctx, bpf), android.GetConfigKey(ctx)) if err != nil { ctx.ModuleErrorf(err.Error()) return } bazelOuts := android.Paths{} for _, p := range objPaths { bazelOuts = append(bazelOuts, android.PathForBazelOut(ctx, p)) } bpf.objs = bazelOuts } // Implements OutputFileFileProducer interface so that the obj output can be used in the data property // of other modules. func (bpf *bpf) OutputFiles(tag string) (android.Paths, error) { switch tag { case "": return bpf.objs, nil default: return nil, fmt.Errorf("unsupported module reference tag %q", tag) } } func (bpf *bpf) SubDir() string { return bpf.properties.Sub_dir } var _ android.OutputFileProducer = (*bpf)(nil) func BpfFactory() android.Module { module := &bpf{} module.AddProperties(&module.properties) android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitBazelModule(module) return module } type bazelBpfAttributes struct { Srcs bazel.LabelListAttribute Copts bazel.StringListAttribute Absolute_includes bazel.StringListAttribute Btf *bool // TODO(b/249528391): Add support for sub_dir } // bpf bp2build converter func (b *bpf) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) { if ctx.ModuleType() != "bpf" { return } srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, b.properties.Srcs)) copts := bazel.MakeStringListAttribute(b.properties.Cflags) absolute_includes := bazel.MakeStringListAttribute(b.properties.Include_dirs) btf := b.properties.Btf attrs := bazelBpfAttributes{ Srcs: srcs, Copts: copts, Absolute_includes: absolute_includes, Btf: btf, } props := bazel.BazelTargetModuleProperties{ Rule_class: "bpf", Bzl_load_location: "//build/bazel/rules/bpf:bpf.bzl", } ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: b.Name()}, &attrs) }