1# Copyright (C) 2021 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//rules:common_settings.bzl", "BuildSettingInfo")
16load(":cc_constants.bzl", "constants")
17load(
18    ":cc_library_common.bzl",
19    "get_includes_paths",
20    "is_external_directory",
21    "parse_sdk_version",
22    "system_dynamic_deps_defaults",
23)
24load(":composed_transitions.bzl", "drop_sanitizer_transition")
25load(":stl.bzl", "stl_info_from_attr")
26
27# "cc_object" module copts, taken from build/soong/cc/object.go
28_CC_OBJECT_COPTS = ["-fno-addrsig"]
29
30# partialLd module link opts, taken from build/soong/cc/builder.go
31# https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/builder.go;l=87;drc=f2be52c4dcc2e3d743318e106633e61de0ad2afd
32_CC_OBJECT_LINKOPTS = [
33    "-fuse-ld=lld",
34    "-nostdlib",
35    "-no-pie",
36    "-Wl,-r",
37]
38
39CcObjectInfo = provider(fields = [
40    # The merged compilation outputs for this cc_object and its transitive
41    # dependencies.
42    "objects",
43])
44
45def split_srcs_hdrs(files):
46    headers = []
47    non_headers_as = []
48    non_headers_c = []
49    for f in files:
50        if f.extension in constants.hdr_exts:
51            headers.append(f)
52        elif f.extension in constants.as_src_exts:
53            non_headers_as.append(f)
54        else:
55            non_headers_c.append(f)
56    return non_headers_c, non_headers_as, headers
57
58def _objcopy_noaddrsig(ctx, noaddrsig_output, linking_output, cc_toolchain):
59    output_file = ctx.actions.declare_file(noaddrsig_output)
60
61    feature_configuration = cc_common.configure_features(
62        ctx = ctx,
63        cc_toolchain = cc_toolchain,
64        requested_features = ctx.features,
65        unsupported_features = ctx.disabled_features,
66    )
67
68    objcopy_path = cc_common.get_tool_for_action(
69        feature_configuration = feature_configuration,
70        action_name = "objcopy",
71    )
72    command_line = cc_common.get_memory_inefficient_command_line(
73        feature_configuration = feature_configuration,
74        action_name = "objcopy",
75        variables = cc_common.empty_variables(),
76    )
77
78    args = ctx.actions.args()
79    args.add_all(command_line)
80    args.add("--remove-section=.llvm_addrsig")
81    args.add(linking_output.executable)
82    args.add(output_file)
83
84    ctx.actions.run(
85        executable = objcopy_path,
86        arguments = [args],
87        inputs = depset(
88            direct = [linking_output.executable],
89            transitive = [
90                cc_toolchain.all_files,
91            ],
92        ),
93        outputs = [output_file],
94        mnemonic = "CppObjcopyNoAddrsig",
95    )
96
97    return output_file
98
99def _cc_object_impl(ctx):
100    cc_toolchain = ctx.toolchains["//prebuilts/clang/host/linux-x86:nocrt_toolchain"].cc
101
102    extra_features = []
103
104    extra_disabled_features = [
105        "disable_pack_relocations",
106        "dynamic_executable",
107        "dynamic_linker",
108        "linker_flags",
109        "no_undefined_symbols",
110        "pack_dynamic_relocations",
111        "strip_debug_symbols",
112        # TODO(cparsons): Look into disabling this feature for nocrt toolchain?
113        "use_libcrt",
114    ]
115    if is_external_directory(ctx.label.package):
116        extra_disabled_features.append("non_external_compiler_flags")
117        extra_features.append("external_compiler_flags")
118    else:
119        extra_features.append("non_external_compiler_flags")
120        extra_disabled_features.append("external_compiler_flags")
121
122    apex_min_sdk_version = ctx.attr._apex_min_sdk_version[BuildSettingInfo].value
123    if ctx.attr.crt and apex_min_sdk_version:
124        extra_disabled_features.append("sdk_version_default")
125        extra_features += parse_sdk_version(apex_min_sdk_version)
126    elif ctx.attr.min_sdk_version:
127        extra_disabled_features.append("sdk_version_default")
128        extra_features += parse_sdk_version(ctx.attr.min_sdk_version)
129
130    # Disable coverage for cc object because we link cc objects below and
131    # clang will link extra lib behind the scene to support profiling if coverage
132    # is enabled, so the symbols of the extra lib will be loaded into the generated
133    # object file. When later we link a shared library that depends on more than
134    # one such cc objects it will fail due to the duplicated symbols problem.
135    extra_disabled_features.append("coverage")
136
137    feature_configuration = cc_common.configure_features(
138        ctx = ctx,
139        cc_toolchain = cc_toolchain,
140        requested_features = ctx.features + extra_features,
141        unsupported_features = ctx.disabled_features + extra_disabled_features,
142    )
143
144    compilation_contexts = []
145    deps_objects = []
146    for obj in ctx.attr.objs:
147        compilation_contexts.append(obj[CcInfo].compilation_context)
148        deps_objects.append(obj[CcObjectInfo].objects)
149    for includes_dep in ctx.attr.includes_deps:
150        compilation_contexts.append(includes_dep[CcInfo].compilation_context)
151
152    product_variables = ctx.attr._android_product_variables[platform_common.TemplateVariableInfo]
153    asflags = [ctx.expand_make_variables("asflags", flag, product_variables.variables) for flag in ctx.attr.asflags]
154
155    srcs_c, srcs_as, private_hdrs = split_srcs_hdrs(ctx.files.srcs + ctx.files.srcs_as)
156
157    (compilation_context, compilation_outputs_c) = cc_common.compile(
158        name = ctx.label.name,
159        actions = ctx.actions,
160        feature_configuration = feature_configuration,
161        cc_toolchain = cc_toolchain,
162        srcs = srcs_c,
163        includes = get_includes_paths(ctx, ctx.attr.local_includes) + get_includes_paths(ctx, ctx.attr.absolute_includes, package_relative = False),
164        public_hdrs = ctx.files.hdrs,
165        private_hdrs = private_hdrs,
166        user_compile_flags = ctx.attr.copts,
167        compilation_contexts = compilation_contexts,
168    )
169
170    (compilation_context, compilation_outputs_as) = cc_common.compile(
171        name = ctx.label.name,
172        actions = ctx.actions,
173        feature_configuration = feature_configuration,
174        cc_toolchain = cc_toolchain,
175        srcs = srcs_as,
176        includes = get_includes_paths(ctx, ctx.attr.local_includes) + get_includes_paths(ctx, ctx.attr.absolute_includes, package_relative = False),
177        public_hdrs = ctx.files.hdrs,
178        private_hdrs = private_hdrs,
179        user_compile_flags = ctx.attr.copts + asflags,
180        compilation_contexts = compilation_contexts,
181    )
182
183    # do not propagate includes
184    compilation_context = cc_common.create_compilation_context(
185        headers = compilation_context.headers,
186        defines = compilation_context.defines,
187        local_defines = compilation_context.local_defines,
188    )
189
190    objects_to_link = cc_common.merge_compilation_outputs(compilation_outputs = deps_objects + [compilation_outputs_c, compilation_outputs_as])
191
192    user_link_flags = [] + ctx.attr.linkopts
193    user_link_flags.extend(_CC_OBJECT_LINKOPTS)
194    additional_inputs = []
195
196    if ctx.attr.linker_script != None:
197        linker_script = ctx.files.linker_script[0]
198        user_link_flags.append("-Wl,-T," + linker_script.path)
199        additional_inputs.append(linker_script)
200
201    # partially link if there are multiple object files
202    if len(objects_to_link.objects) + len(objects_to_link.pic_objects) > 1:
203        linking_output = cc_common.link(
204            name = ctx.label.name + ".addrsig.o",
205            actions = ctx.actions,
206            feature_configuration = feature_configuration,
207            cc_toolchain = cc_toolchain,
208            user_link_flags = user_link_flags,
209            compilation_outputs = objects_to_link,
210            additional_inputs = additional_inputs,
211        )
212
213        noaddrsig_output = _objcopy_noaddrsig(ctx, ctx.label.name + ".o", linking_output, cc_toolchain)
214
215        files = depset([noaddrsig_output])
216    else:
217        files = depset(objects_to_link.objects + objects_to_link.pic_objects)
218
219    return [
220        DefaultInfo(files = files),
221        CcInfo(compilation_context = compilation_context),
222        CcObjectInfo(objects = objects_to_link),
223    ]
224
225_cc_object = rule(
226    implementation = _cc_object_impl,
227    cfg = drop_sanitizer_transition,
228    attrs = {
229        "srcs": attr.label_list(allow_files = constants.all_dot_exts),
230        "srcs_as": attr.label_list(allow_files = constants.all_dot_exts),
231        "hdrs": attr.label_list(allow_files = constants.hdr_dot_exts),
232        "absolute_includes": attr.string_list(),
233        "local_includes": attr.string_list(),
234        "copts": attr.string_list(),
235        "asflags": attr.string_list(),
236        "linkopts": attr.string_list(),
237        "objs": attr.label_list(
238            providers = [CcInfo, CcObjectInfo],
239        ),
240        "includes_deps": attr.label_list(providers = [CcInfo]),
241        "linker_script": attr.label(allow_single_file = True),
242        "sdk_version": attr.string(),
243        "min_sdk_version": attr.string(),
244        "crt": attr.bool(default = False),
245        "package_name": attr.string(
246            mandatory = True,
247            doc = "Just the path to the target package. Used by transitions.",
248        ),
249        "_android_product_variables": attr.label(
250            default = Label("//build/bazel/product_config:product_variables_for_attributes"),
251            providers = [platform_common.TemplateVariableInfo],
252        ),
253        "_apex_min_sdk_version": attr.label(
254            default = "//build/bazel/rules/apex:min_sdk_version",
255        ),
256        "_allowlist_function_transition": attr.label(
257            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
258        ),
259    },
260    toolchains = ["//prebuilts/clang/host/linux-x86:nocrt_toolchain"],
261    fragments = ["cpp"],
262)
263
264def cc_object(
265        name,
266        copts = [],
267        hdrs = [],
268        asflags = [],
269        linkopts = [],
270        srcs = [],
271        srcs_as = [],
272        objs = [],
273        deps = [],
274        native_bridge_supported = False,  # TODO: not supported yet. @unused
275        stl = "",
276        system_dynamic_deps = None,
277        sdk_version = "",
278        min_sdk_version = "",
279        **kwargs):
280    "Build macro to correspond with the cc_object Soong module."
281
282    if system_dynamic_deps == None:
283        system_dynamic_deps = system_dynamic_deps_defaults
284
285    stl_info = stl_info_from_attr(stl, False)
286    linkopts = linkopts + stl_info.linkopts
287    copts = copts + stl_info.cppflags
288
289    _cc_object(
290        name = name,
291        hdrs = hdrs,
292        asflags = asflags,
293        copts = _CC_OBJECT_COPTS + copts,
294        linkopts = linkopts,
295        # TODO(b/261996812): we shouldn't need to have both srcs and srcs_as as inputs here
296        srcs = srcs,
297        srcs_as = srcs_as,
298        objs = objs,
299        includes_deps = stl_info.static_deps + stl_info.shared_deps + system_dynamic_deps + deps,
300        sdk_version = sdk_version,
301        min_sdk_version = min_sdk_version,
302        package_name = native.package_name(),
303        **kwargs
304    )
305