1*9bb1b549SSpandan Das# Copyright 2014 The Bazel Authors. All rights reserved. 2*9bb1b549SSpandan Das# 3*9bb1b549SSpandan Das# Licensed under the Apache License, Version 2.0 (the "License"); 4*9bb1b549SSpandan Das# you may not use this file except in compliance with the License. 5*9bb1b549SSpandan Das# You may obtain a copy of the License at 6*9bb1b549SSpandan Das# 7*9bb1b549SSpandan Das# http://www.apache.org/licenses/LICENSE-2.0 8*9bb1b549SSpandan Das# 9*9bb1b549SSpandan Das# Unless required by applicable law or agreed to in writing, software 10*9bb1b549SSpandan Das# distributed under the License is distributed on an "AS IS" BASIS, 11*9bb1b549SSpandan Das# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9bb1b549SSpandan Das# See the License for the specific language governing permissions and 13*9bb1b549SSpandan Das# limitations under the License. 14*9bb1b549SSpandan Das 15*9bb1b549SSpandan Dasload( 16*9bb1b549SSpandan Das "//go/private:common.bzl", 17*9bb1b549SSpandan Das "as_set", 18*9bb1b549SSpandan Das "count_group_matches", 19*9bb1b549SSpandan Das "has_shared_lib_extension", 20*9bb1b549SSpandan Das) 21*9bb1b549SSpandan Dasload( 22*9bb1b549SSpandan Das "//go/private:mode.bzl", 23*9bb1b549SSpandan Das "LINKMODE_NORMAL", 24*9bb1b549SSpandan Das "LINKMODE_PLUGIN", 25*9bb1b549SSpandan Das "extld_from_cc_toolchain", 26*9bb1b549SSpandan Das "extldflags_from_cc_toolchain", 27*9bb1b549SSpandan Das) 28*9bb1b549SSpandan Dasload( 29*9bb1b549SSpandan Das "//go/private:rpath.bzl", 30*9bb1b549SSpandan Das "rpath", 31*9bb1b549SSpandan Das) 32*9bb1b549SSpandan Dasload( 33*9bb1b549SSpandan Das "@bazel_skylib//lib:collections.bzl", 34*9bb1b549SSpandan Das "collections", 35*9bb1b549SSpandan Das) 36*9bb1b549SSpandan Das 37*9bb1b549SSpandan Dasdef _format_archive(d): 38*9bb1b549SSpandan Das return "{}={}={}".format(d.label, d.importmap, d.file.path) 39*9bb1b549SSpandan Das 40*9bb1b549SSpandan Dasdef _transitive_archives_without_test_archives(archive, test_archives): 41*9bb1b549SSpandan Das # Build the set of transitive dependencies. Currently, we tolerate multiple 42*9bb1b549SSpandan Das # archives with the same importmap (though this will be an error in the 43*9bb1b549SSpandan Das # future), but there is a special case which is difficult to avoid: 44*9bb1b549SSpandan Das # If a go_test has internal and external archives, and the external test 45*9bb1b549SSpandan Das # transitively depends on the library under test, we need to exclude the 46*9bb1b549SSpandan Das # library under test and use the internal test archive instead. 47*9bb1b549SSpandan Das deps = depset(transitive = [d.transitive for d in archive.direct]) 48*9bb1b549SSpandan Das result = {} 49*9bb1b549SSpandan Das 50*9bb1b549SSpandan Das # Unfortunately, Starlark doesn't support set() 51*9bb1b549SSpandan Das test_imports = {} 52*9bb1b549SSpandan Das for t in test_archives: 53*9bb1b549SSpandan Das test_imports[t.importmap] = True 54*9bb1b549SSpandan Das for d in deps.to_list(): 55*9bb1b549SSpandan Das if d.importmap in test_imports: 56*9bb1b549SSpandan Das continue 57*9bb1b549SSpandan Das if d.importmap in result: 58*9bb1b549SSpandan Das print("Multiple copies of {} passed to the linker. Ignoring {} in favor of {}".format(d.importmap, d.file.path, result[d.importmap].file.path)) 59*9bb1b549SSpandan Das continue 60*9bb1b549SSpandan Das result[d.importmap] = d 61*9bb1b549SSpandan Das return result.values() 62*9bb1b549SSpandan Das 63*9bb1b549SSpandan Dasdef emit_link( 64*9bb1b549SSpandan Das go, 65*9bb1b549SSpandan Das archive = None, 66*9bb1b549SSpandan Das test_archives = [], 67*9bb1b549SSpandan Das executable = None, 68*9bb1b549SSpandan Das gc_linkopts = [], 69*9bb1b549SSpandan Das version_file = None, 70*9bb1b549SSpandan Das info_file = None): 71*9bb1b549SSpandan Das """See go/toolchains.rst#link for full documentation.""" 72*9bb1b549SSpandan Das 73*9bb1b549SSpandan Das if archive == None: 74*9bb1b549SSpandan Das fail("archive is a required parameter") 75*9bb1b549SSpandan Das if executable == None: 76*9bb1b549SSpandan Das fail("executable is a required parameter") 77*9bb1b549SSpandan Das 78*9bb1b549SSpandan Das # Exclude -lstdc++ from link options. We don't want to link against it 79*9bb1b549SSpandan Das # unless we actually have some C++ code. _cgo_codegen will include it 80*9bb1b549SSpandan Das # in archives via CGO_LDFLAGS if it's needed. 81*9bb1b549SSpandan Das extldflags = [f for f in extldflags_from_cc_toolchain(go) if f not in ("-lstdc++", "-lc++")] 82*9bb1b549SSpandan Das 83*9bb1b549SSpandan Das if go.coverage_enabled: 84*9bb1b549SSpandan Das extldflags.append("--coverage") 85*9bb1b549SSpandan Das gc_linkopts = list(gc_linkopts) 86*9bb1b549SSpandan Das gc_linkopts.extend(go.mode.gc_linkopts) 87*9bb1b549SSpandan Das gc_linkopts, extldflags = _extract_extldflags(gc_linkopts, extldflags) 88*9bb1b549SSpandan Das builder_args = go.builder_args(go, "link") 89*9bb1b549SSpandan Das tool_args = go.tool_args(go) 90*9bb1b549SSpandan Das 91*9bb1b549SSpandan Das # Add in any mode specific behaviours 92*9bb1b549SSpandan Das if go.mode.race: 93*9bb1b549SSpandan Das tool_args.add("-race") 94*9bb1b549SSpandan Das if go.mode.msan: 95*9bb1b549SSpandan Das tool_args.add("-msan") 96*9bb1b549SSpandan Das 97*9bb1b549SSpandan Das if go.mode.pure: 98*9bb1b549SSpandan Das tool_args.add("-linkmode", "internal") 99*9bb1b549SSpandan Das else: 100*9bb1b549SSpandan Das extld = extld_from_cc_toolchain(go) 101*9bb1b549SSpandan Das tool_args.add_all(extld) 102*9bb1b549SSpandan Das if extld and (go.mode.static or 103*9bb1b549SSpandan Das go.mode.race or 104*9bb1b549SSpandan Das go.mode.link != LINKMODE_NORMAL or 105*9bb1b549SSpandan Das go.mode.goos == "windows" and go.mode.msan): 106*9bb1b549SSpandan Das # Force external linking for the following conditions: 107*9bb1b549SSpandan Das # * Mode is static but not pure: -static must be passed to the C 108*9bb1b549SSpandan Das # linker if the binary contains cgo code. See #2168, #2216. 109*9bb1b549SSpandan Das # * Non-normal build mode: may not be strictly necessary, especially 110*9bb1b549SSpandan Das # for modes like "pie". 111*9bb1b549SSpandan Das # * Race or msan build for Windows: Go linker has pairwise 112*9bb1b549SSpandan Das # incompatibilities with mingw, and we get link errors in race mode. 113*9bb1b549SSpandan Das # Using the C linker avoids that. Race and msan always require a 114*9bb1b549SSpandan Das # a C toolchain. See #2614. 115*9bb1b549SSpandan Das # * Linux race builds: we get linker errors during build with Go's 116*9bb1b549SSpandan Das # internal linker. For example, when using zig cc v0.10 117*9bb1b549SSpandan Das # (clang-15.0.3): 118*9bb1b549SSpandan Das # 119*9bb1b549SSpandan Das # runtime/cgo(.text): relocation target memset not defined 120*9bb1b549SSpandan Das tool_args.add("-linkmode", "external") 121*9bb1b549SSpandan Das 122*9bb1b549SSpandan Das if go.mode.static: 123*9bb1b549SSpandan Das extldflags.append("-static") 124*9bb1b549SSpandan Das if go.mode.link != LINKMODE_NORMAL: 125*9bb1b549SSpandan Das builder_args.add("-buildmode", go.mode.link) 126*9bb1b549SSpandan Das if go.mode.link == LINKMODE_PLUGIN: 127*9bb1b549SSpandan Das tool_args.add("-pluginpath", archive.data.importpath) 128*9bb1b549SSpandan Das 129*9bb1b549SSpandan Das arcs = _transitive_archives_without_test_archives(archive, test_archives) 130*9bb1b549SSpandan Das arcs.extend(test_archives) 131*9bb1b549SSpandan Das if (go.coverage_enabled and go.coverdata and 132*9bb1b549SSpandan Das not any([arc.importmap == go.coverdata.data.importmap for arc in arcs])): 133*9bb1b549SSpandan Das arcs.append(go.coverdata.data) 134*9bb1b549SSpandan Das builder_args.add_all(arcs, before_each = "-arc", map_each = _format_archive) 135*9bb1b549SSpandan Das builder_args.add("-package_list", go.package_list) 136*9bb1b549SSpandan Das 137*9bb1b549SSpandan Das # Build a list of rpaths for dynamic libraries we need to find. 138*9bb1b549SSpandan Das # rpaths are relative paths from the binary to directories where libraries 139*9bb1b549SSpandan Das # are stored. Binaries that require these will only work when installed in 140*9bb1b549SSpandan Das # the bazel execroot. Most binaries are only dynamically linked against 141*9bb1b549SSpandan Das # system libraries though. 142*9bb1b549SSpandan Das cgo_rpaths = sorted(collections.uniq([ 143*9bb1b549SSpandan Das f 144*9bb1b549SSpandan Das for d in archive.cgo_deps.to_list() 145*9bb1b549SSpandan Das if has_shared_lib_extension(d.basename) 146*9bb1b549SSpandan Das for f in rpath.flags(go, d, executable = executable) 147*9bb1b549SSpandan Das ])) 148*9bb1b549SSpandan Das extldflags.extend(cgo_rpaths) 149*9bb1b549SSpandan Das 150*9bb1b549SSpandan Das # Process x_defs, and record whether stamping is used. 151*9bb1b549SSpandan Das stamp_x_defs_volatile = False 152*9bb1b549SSpandan Das stamp_x_defs_stable = False 153*9bb1b549SSpandan Das for k, v in archive.x_defs.items(): 154*9bb1b549SSpandan Das builder_args.add("-X", "%s=%s" % (k, v)) 155*9bb1b549SSpandan Das if go.stamp: 156*9bb1b549SSpandan Das stable_vars_count = (count_group_matches(v, "{STABLE_", "}") + 157*9bb1b549SSpandan Das v.count("{BUILD_EMBED_LABEL}") + 158*9bb1b549SSpandan Das v.count("{BUILD_USER}") + 159*9bb1b549SSpandan Das v.count("{BUILD_HOST}")) 160*9bb1b549SSpandan Das if stable_vars_count > 0: 161*9bb1b549SSpandan Das stamp_x_defs_stable = True 162*9bb1b549SSpandan Das if count_group_matches(v, "{", "}") != stable_vars_count: 163*9bb1b549SSpandan Das stamp_x_defs_volatile = True 164*9bb1b549SSpandan Das 165*9bb1b549SSpandan Das # Stamping support 166*9bb1b549SSpandan Das stamp_inputs = [] 167*9bb1b549SSpandan Das if stamp_x_defs_stable: 168*9bb1b549SSpandan Das stamp_inputs.append(info_file) 169*9bb1b549SSpandan Das if stamp_x_defs_volatile: 170*9bb1b549SSpandan Das stamp_inputs.append(version_file) 171*9bb1b549SSpandan Das if stamp_inputs: 172*9bb1b549SSpandan Das builder_args.add_all(stamp_inputs, before_each = "-stamp") 173*9bb1b549SSpandan Das 174*9bb1b549SSpandan Das builder_args.add("-o", executable) 175*9bb1b549SSpandan Das builder_args.add("-main", archive.data.file) 176*9bb1b549SSpandan Das builder_args.add("-p", archive.data.importmap) 177*9bb1b549SSpandan Das tool_args.add_all(gc_linkopts) 178*9bb1b549SSpandan Das tool_args.add_all(go.toolchain.flags.link) 179*9bb1b549SSpandan Das 180*9bb1b549SSpandan Das # Do not remove, somehow this is needed when building for darwin/arm only. 181*9bb1b549SSpandan Das tool_args.add("-buildid=redacted") 182*9bb1b549SSpandan Das if go.mode.strip: 183*9bb1b549SSpandan Das tool_args.add("-s", "-w") 184*9bb1b549SSpandan Das tool_args.add_joined("-extldflags", extldflags, join_with = " ") 185*9bb1b549SSpandan Das 186*9bb1b549SSpandan Das conflict_err = _check_conflicts(arcs) 187*9bb1b549SSpandan Das if conflict_err: 188*9bb1b549SSpandan Das # Report package conflict errors in execution instead of analysis. 189*9bb1b549SSpandan Das # We could call fail() with this message, but Bazel prints a stack 190*9bb1b549SSpandan Das # that doesn't give useful information. 191*9bb1b549SSpandan Das builder_args.add("-conflict_err", conflict_err) 192*9bb1b549SSpandan Das 193*9bb1b549SSpandan Das inputs_direct = stamp_inputs + [go.sdk.package_list] 194*9bb1b549SSpandan Das if go.coverage_enabled and go.coverdata: 195*9bb1b549SSpandan Das inputs_direct.append(go.coverdata.data.file) 196*9bb1b549SSpandan Das inputs_transitive = [ 197*9bb1b549SSpandan Das archive.libs, 198*9bb1b549SSpandan Das archive.cgo_deps, 199*9bb1b549SSpandan Das as_set(go.crosstool), 200*9bb1b549SSpandan Das as_set(go.sdk.tools), 201*9bb1b549SSpandan Das as_set(go.stdlib.libs), 202*9bb1b549SSpandan Das ] 203*9bb1b549SSpandan Das inputs = depset(direct = inputs_direct, transitive = inputs_transitive) 204*9bb1b549SSpandan Das 205*9bb1b549SSpandan Das go.actions.run( 206*9bb1b549SSpandan Das inputs = inputs, 207*9bb1b549SSpandan Das outputs = [executable], 208*9bb1b549SSpandan Das mnemonic = "GoLink", 209*9bb1b549SSpandan Das executable = go.toolchain._builder, 210*9bb1b549SSpandan Das arguments = [builder_args, "--", tool_args], 211*9bb1b549SSpandan Das env = go.env, 212*9bb1b549SSpandan Das ) 213*9bb1b549SSpandan Das 214*9bb1b549SSpandan Dasdef _extract_extldflags(gc_linkopts, extldflags): 215*9bb1b549SSpandan Das """Extracts -extldflags from gc_linkopts and combines them into a single list. 216*9bb1b549SSpandan Das 217*9bb1b549SSpandan Das Args: 218*9bb1b549SSpandan Das gc_linkopts: a list of flags passed in through the gc_linkopts attributes. 219*9bb1b549SSpandan Das ctx.expand_make_variables should have already been applied. -extldflags 220*9bb1b549SSpandan Das may appear multiple times in this list. 221*9bb1b549SSpandan Das extldflags: a list of flags to be passed to the external linker. 222*9bb1b549SSpandan Das 223*9bb1b549SSpandan Das Return: 224*9bb1b549SSpandan Das A tuple containing the filtered gc_linkopts with external flags removed, 225*9bb1b549SSpandan Das and a combined list of external flags. Each string in the returned 226*9bb1b549SSpandan Das extldflags list may contain multiple flags, separated by whitespace. 227*9bb1b549SSpandan Das """ 228*9bb1b549SSpandan Das filtered_gc_linkopts = [] 229*9bb1b549SSpandan Das is_extldflags = False 230*9bb1b549SSpandan Das for opt in gc_linkopts: 231*9bb1b549SSpandan Das if is_extldflags: 232*9bb1b549SSpandan Das is_extldflags = False 233*9bb1b549SSpandan Das extldflags.append(opt) 234*9bb1b549SSpandan Das elif opt == "-extldflags": 235*9bb1b549SSpandan Das is_extldflags = True 236*9bb1b549SSpandan Das else: 237*9bb1b549SSpandan Das filtered_gc_linkopts.append(opt) 238*9bb1b549SSpandan Das return filtered_gc_linkopts, extldflags 239*9bb1b549SSpandan Das 240*9bb1b549SSpandan Dasdef _check_conflicts(arcs): 241*9bb1b549SSpandan Das importmap_to_label = {} 242*9bb1b549SSpandan Das for arc in arcs: 243*9bb1b549SSpandan Das if arc.importmap in importmap_to_label: 244*9bb1b549SSpandan Das return """package conflict error: {}: multiple copies of package passed to linker: 245*9bb1b549SSpandan Das {} 246*9bb1b549SSpandan Das {} 247*9bb1b549SSpandan DasSet "importmap" to different paths or use 'bazel cquery' to ensure only one 248*9bb1b549SSpandan Daspackage with this path is linked.""".format( 249*9bb1b549SSpandan Das arc.importmap, 250*9bb1b549SSpandan Das importmap_to_label[arc.importmap], 251*9bb1b549SSpandan Das arc.label, 252*9bb1b549SSpandan Das ) 253*9bb1b549SSpandan Das importmap_to_label[arc.importmap] = arc.label 254*9bb1b549SSpandan Das for arc in arcs: 255*9bb1b549SSpandan Das for dep_importmap, dep_label in zip(arc._dep_importmaps, arc._dep_labels): 256*9bb1b549SSpandan Das if dep_importmap not in importmap_to_label: 257*9bb1b549SSpandan Das return "package conflict error: {}: package needed by {} was not passed to linker".format( 258*9bb1b549SSpandan Das dep_importmap, 259*9bb1b549SSpandan Das arc.label, 260*9bb1b549SSpandan Das ) 261*9bb1b549SSpandan Das if importmap_to_label[dep_importmap] != dep_label: 262*9bb1b549SSpandan Das err = """package conflict error: {}: package imports {} 263*9bb1b549SSpandan Das was compiled with: {} 264*9bb1b549SSpandan Das but was linked with: {}""".format( 265*9bb1b549SSpandan Das arc.importmap, 266*9bb1b549SSpandan Das dep_importmap, 267*9bb1b549SSpandan Das dep_label, 268*9bb1b549SSpandan Das importmap_to_label[dep_importmap], 269*9bb1b549SSpandan Das ) 270*9bb1b549SSpandan Das if importmap_to_label[dep_importmap].name.endswith("_test"): 271*9bb1b549SSpandan Das err += """ 272*9bb1b549SSpandan DasThis sometimes happens when an external test (package ending with _test) 273*9bb1b549SSpandan Dasimports a package that imports the library being tested. This is not supported.""" 274*9bb1b549SSpandan Das err += "\nSee https://github.com/bazelbuild/rules_go/issues/1877." 275*9bb1b549SSpandan Das return err 276*9bb1b549SSpandan Das return None 277