|  | // 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) | 
|  | } |