1# Copyright (C) 2022 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15load("@bazel_skylib//lib:paths.bzl", "paths")
16load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
17
18def _get_clang_cmd_output(ctx):
19    copts = [
20        "--target=bpf",
21        "-nostdlibinc",
22        "-no-canonical-prefixes",
23        "-O2",
24    ]
25    copts.extend(ctx.attr.copts)
26    if ctx.attr.btf:
27        copts.append("-g")
28
29    includes = [
30        "packages/modules/Connectivity/staticlibs/native/bpf_headers/include/bpf",
31        # TODO(b/149785767): only give access to specific file with AID_* constants
32        "system/core/libcutils/include",
33        "external/musl/src/env",
34        ctx.label.package,
35    ]
36    includes.extend(ctx.attr.absolute_includes)
37
38    system_includes = [
39        "bionic/libc/include",
40        "bionic/libc/kernel/uapi",
41        # The architecture doesn't matter here, but asm/types.h is included by linux/types.h.
42        "bionic/libc/kernel/uapi/asm-arm64",
43        "bionic/libc/kernel/android/uapi",
44    ]
45
46    toolchain = find_cpp_toolchain(ctx)
47    extra_features = [
48        "dependency_file",
49        "bpf_compiler_flags",
50    ]
51    extra_disabled_features = [
52        "sdk_version_flag",
53        "pie",
54        "non_external_compiler_flags",
55        "common_compiler_flags",
56        "asm_compiler_flags",
57        "cpp_compiler_flags",
58        "c_compiler_flags",
59        "external_compiler_flags",
60        "arm_isa_arm",
61        "arm_isa_thumb",
62        "no_override_clang_global_copts",
63    ]
64    feature_configuration = cc_common.configure_features(
65        ctx = ctx,
66        cc_toolchain = toolchain,
67        requested_features = ctx.features + extra_features,
68        unsupported_features = ctx.disabled_features + extra_disabled_features,
69    )
70
71    compilation_context = []
72    dir_name = ctx.label.name
73    if ctx.attr.btf:
74        # If btf is true, intermediate dir ("unstripped") is added when
75        # clang command is executed, because ctx.actions.run used in stripped
76        # command does not allow the same input and output names.
77        # "unstripped" will be removed when strip command is executed.
78        dir_name = paths.join("unstripped", dir_name)
79    (compilation_context, compilation_outputs) = cc_common.compile(
80        name = dir_name,
81        actions = ctx.actions,
82        feature_configuration = feature_configuration,
83        cc_toolchain = toolchain,
84        srcs = ctx.files.srcs,
85        system_includes = system_includes,
86        includes = includes,
87        user_compile_flags = copts,
88        compilation_contexts = compilation_context,
89    )
90
91    return compilation_outputs.objects
92
93def _declare_stripped_cmd_output_file(ctx, src):
94    file_path = paths.join("_objs", src.basename, src.basename)
95    return ctx.actions.declare_file(file_path)
96
97def _get_stripped_cmd_output(ctx, srcs):
98    out_files = [_declare_stripped_cmd_output_file(ctx, src) for src in srcs]
99
100    args = ctx.actions.args()
101    args.add("--strip-unneeded")
102    args.add("--remove-section=.rel.BTF")
103    args.add("--remove-section=.rel.BTF.ext")
104    args.add("--remove-section=.BTF.ext")
105
106    for in_file, out_file in zip(srcs, out_files):
107        ctx.actions.run(
108            inputs = [in_file],
109            outputs = [out_file],
110            executable = ctx.executable._strip,
111            arguments = [args] + [in_file.path, "-o", out_file.path],
112        )
113
114    return out_files
115
116def _bpf_impl(ctx):
117    for src in ctx.files.srcs:
118        if "_" in src.basename:
119            fail("Invalid character '_' in source name")
120
121    clang_outfiles = _get_clang_cmd_output(ctx)
122
123    if not ctx.attr.btf:
124        return [DefaultInfo(files = depset(clang_outfiles))]
125    else:
126        stripped_outfiles = _get_stripped_cmd_output(ctx, clang_outfiles)
127        return [DefaultInfo(files = depset(stripped_outfiles))]
128
129bpf = rule(
130    implementation = _bpf_impl,
131    attrs = {
132        "srcs": attr.label_list(
133            mandatory = True,
134            allow_files = True,
135        ),
136        "copts": attr.string_list(),
137        "absolute_includes": attr.string_list(),
138        "btf": attr.bool(
139            default = True,
140            doc = "if set to true, generate BTF debug info for maps & programs",
141        ),
142        "_strip": attr.label(
143            cfg = "exec",
144            executable = True,
145            default = "//prebuilts/clang/host/linux-x86:llvm-strip",
146            allow_files = True,
147        ),
148    },
149    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
150    fragments = ["cpp"],
151)
152