1313// limitations under the License.
1414package com .google .devtools .build .lib .rules .cpp ;
1515
16+ import static com .google .common .collect .ImmutableList .toImmutableList ;
17+
1618import com .google .common .base .Preconditions ;
1719import com .google .common .base .Strings ;
1820import com .google .common .collect .ImmutableList ;
@@ -40,7 +42,6 @@ public class LibrariesToLinkCollector {
4042 private final PathFragment toolchainLibrariesSolibDir ;
4143 private final CppConfiguration cppConfiguration ;
4244 private final CcToolchainProvider ccToolchainProvider ;
43- private final Artifact outputArtifact ;
4445 private final boolean isLtoIndexing ;
4546 private final PathFragment solibDir ;
4647 private final Iterable <? extends LinkerInput > linkerInputs ;
@@ -49,7 +50,8 @@ public class LibrariesToLinkCollector {
4950 private final Artifact thinltoParamFile ;
5051 private final FeatureConfiguration featureConfiguration ;
5152 private final boolean needWholeArchive ;
52- private final String rpathRoot ;
53+ private final ImmutableList <String > potentialExecRoots ;
54+ private final ImmutableList <String > rpathRoots ;
5355 private final boolean needToolchainLibrariesRpath ;
5456 private final Map <Artifact , Artifact > ltoMap ;
5557 private final RuleErrorConsumer ruleErrorConsumer ;
@@ -76,7 +78,6 @@ public LibrariesToLinkCollector(
7678 this .cppConfiguration = cppConfiguration ;
7779 this .ccToolchainProvider = toolchain ;
7880 this .toolchainLibrariesSolibDir = toolchainLibrariesSolibDir ;
79- this .outputArtifact = output ;
8081 this .solibDir = solibDir ;
8182 this .isLtoIndexing = isLtoIndexing ;
8283 this .allLtoArtifacts = allLtoArtifacts ;
@@ -106,22 +107,83 @@ public LibrariesToLinkCollector(
106107 // and the second could use $ORIGIN/../_solib_[arch]. But since this is a shared
107108 // artifact, both are symlinks to the same place, so
108109 // there's no *one* RPATH setting that fits all targets involved in the sharing.
109- rpathRoot = ccToolchainProvider .getSolibDirectory () + "/" ;
110+ potentialExecRoots = ImmutableList .of ();
111+ rpathRoots = ImmutableList .of (ccToolchainProvider .getSolibDirectory () + "/" );
110112 } else {
111- // When executed from within a runfiles directory, the binary lies under a path such as
112- // target.runfiles/some_repo/pkg/file, whereas the solib directory is located under
113- // target.runfiles/main_repo.
114- PathFragment runfilesPath = outputArtifact .getRunfilesPath ();
115- String runfilesExecRoot ;
116- if (runfilesPath .startsWith (LabelConstants .EXTERNAL_RUNFILES_PATH_PREFIX )) {
117- // runfilesPath is of the form ../some_repo/pkg/file, walk back some_repo/pkg and then
118- // descend into the main workspace.
119- runfilesExecRoot = Strings .repeat ("../" , runfilesPath .segmentCount () - 2 ) + workspaceName + "/" ;
120- } else {
121- // runfilesPath is of the form pkg/file, walk back pkg to reach the main workspace.
122- runfilesExecRoot = Strings .repeat ("../" , runfilesPath .segmentCount () - 1 );
113+ // The runtime location of the solib directory relative to the binary depends on four factors:
114+ //
115+ // * whether the binary is contained in the main repository or an external repository;
116+ // * whether the binary is executed directly or from a runfiles tree;
117+ // * whether the binary is staged as a symlink (sandboxed execution; local execution if the
118+ // binary is in the runfiles of another target) or a regular file (remote execution) - the
119+ // dynamic linker follows sandbox and runfiles symlinks into its location under the
120+ // unsandboxed execroot, which thus becomes the effective $ORIGIN;
121+ // * whether --experimental_sibling_repository_layout is enabled or not.
122+ //
123+ // The rpaths emitted into the binary thus have to cover the following cases (assuming that
124+ // the binary target is located in the pkg `pkg` and has name `file`) for the directory used
125+ // as $ORIGIN by the dynamic linker and the directory containing the solib directories:
126+ //
127+ // 1. main, direct, symlink:
128+ // $ORIGIN: $EXECROOT/pkg
129+ // solib root: $EXECROOT
130+ // 2. main, direct, regular file:
131+ // $ORIGIN: $EXECROOT/pkg
132+ // solib root: $EXECROOT/pkg/file.runfiles/main_repo
133+ // 3. main, runfiles, symlink:
134+ // $ORIGIN: $EXECROOT/pkg
135+ // solib root: $EXECROOT
136+ // 4. main, runfiles, regular file:
137+ // $ORIGIN: other_target.runfiles/main_repo/pkg
138+ // solib root: other_target.runfiles/main_repo
139+ // 5a. external, direct, symlink:
140+ // $ORIGIN: $EXECROOT/external/other_repo/pkg
141+ // solib root: $EXECROOT
142+ // 5b. external, direct, symlink, with --experimental_sibling_repository_layout:
143+ // $ORIGIN: $EXECROOT/../other_repo/pkg
144+ // solib root: $EXECROOT/../other_repo
145+ // 6a. external, direct, regular file:
146+ // $ORIGIN: $EXECROOT/external/other_repo/pkg
147+ // solib root: $EXECROOT/external/other_repo/pkg/file.runfiles/main_repo
148+ // 6b. external, direct, regular file, with --experimental_sibling_repository_layout:
149+ // $ORIGIN: $EXECROOT/../other_repo/pkg
150+ // solib root: $EXECROOT/../other_repo/pkg/file.runfiles/other_repo
151+ // 7a. external, runfiles, symlink:
152+ // $ORIGIN: $EXECROOT/external/other_repo/pkg
153+ // solib root: $EXECROOT
154+ // 7b. external, runfiles, symlink, with --experimental_sibling_repository_layout:
155+ // $ORIGIN: $EXECROOT/../other_repo/pkg
156+ // solib root: $EXECROOT/../other_repo
157+ // 8a. external, runfiles, regular file:
158+ // $ORIGIN: other_target.runfiles/some_repo/pkg
159+ // solib root: other_target.runfiles/main_repo
160+ // 8b. external, runfiles, regular file, with --experimental_sibling_repository_layout:
161+ // $ORIGIN: other_target.runfiles/some_repo/pkg
162+ // solib root: other_target.runfiles/some_repo
163+ //
164+ // Cases 1, 3, 4, 5, 7, and 8b are covered by an rpath that walks up the root relative path.
165+ // Case 8a is covered by walking up some_repo/pkg and then into main_repo.
166+ // Cases 2 and 6 are currently not covered as they would require an rpath containing the
167+ // binary filename, which may contain commas that would clash with the `-Wl` argument used to
168+ // pass the rpath to the linker.
169+ // TODO(#14600): Fix this by using `-Xlinker` instead of `-Wl`.
170+ ImmutableList .Builder <String > execRoots = ImmutableList .builder ();
171+ // Handles cases 1, 3, 4, 5, and 7.
172+ execRoots .add (Strings .repeat ("../" , output .getRootRelativePath ().segmentCount () - 1 ));
173+ if (output .getRunfilesPath ().startsWith (LabelConstants .EXTERNAL_RUNFILES_PATH_PREFIX )
174+ && output .getRoot ().isLegacy ()) {
175+ // Handles case 8a. The runfiles path is of the form ../some_repo/pkg/file and we need to
176+ // walk up some_repo/pkg and then down into main_repo.
177+ execRoots .add (
178+ Strings .repeat ("../" , output .getRunfilesPath ().segmentCount () - 2 ) + workspaceName
179+ + "/" );
123180 }
124- rpathRoot = runfilesExecRoot + ccToolchainProvider .getSolibDirectory () + "/" ;
181+
182+ potentialExecRoots = execRoots .build ();
183+ rpathRoots =
184+ potentialExecRoots .stream ()
185+ .map ((execRoot ) -> execRoot + ccToolchainProvider .getSolibDirectory () + "/" )
186+ .collect (toImmutableList ());
125187 }
126188
127189 ltoMap = generateLtoMap ();
@@ -196,10 +258,10 @@ public CollectedLibrariesToLink collectLibrariesToLink() {
196258 // directory. In other words, given blaze-bin/my/package/binary, rpathRoot would be
197259 // "../../_solib_[arch]".
198260 if (needToolchainLibrariesRpath ) {
199- runtimeLibrarySearchDirectories . add (
200- Strings . repeat ( "../" , outputArtifact . getRootRelativePath (). segmentCount () - 1 )
201- + toolchainLibrariesSolibName
202- + "/" );
261+ for ( String potentialExecRoot : potentialExecRoots ) {
262+ runtimeLibrarySearchDirectories . add (
263+ potentialExecRoot + toolchainLibrariesSolibName + "/" );
264+ }
203265 }
204266 if (isNativeDeps ) {
205267 // We also retain the $ORIGIN/ path to solibs that are in _solib_<arch>, as opposed to
@@ -231,7 +293,9 @@ public CollectedLibrariesToLink collectLibrariesToLink() {
231293 NestedSetBuilder <String > allRuntimeLibrarySearchDirectories = NestedSetBuilder .linkOrder ();
232294 // rpath ordering matters for performance; first add the one where most libraries are found.
233295 if (includeSolibDir ) {
234- allRuntimeLibrarySearchDirectories .add (rpathRoot );
296+ for (String rpathRoot : rpathRoots ) {
297+ allRuntimeLibrarySearchDirectories .add (rpathRoot );
298+ }
235299 }
236300 allRuntimeLibrarySearchDirectories .addAll (rpathRootsForExplicitSoDeps .build ());
237301 if (includeToolchainLibrariesSolibDir ) {
@@ -346,17 +410,21 @@ private void addDynamicInputLinkOptions(
346410 // When all dynamic deps are built in transitioned configurations, the default solib dir is
347411 // not created. While resolving paths, the dynamic linker stops at the first directory that
348412 // does not exist, even when followed by "../". We thus have to normalize the relative path.
349- String relativePathToRoot =
350- rpathRoot + dotdots + libDir .relativeTo (commonParent ).getPathString ();
351- String normalizedPathToRoot = PathFragment .create (relativePathToRoot ).getPathString ();
352- rpathRootsForExplicitSoDeps .add (normalizedPathToRoot );
413+ for (String rpathRoot : rpathRoots ) {
414+ String relativePathToRoot =
415+ rpathRoot + dotdots + libDir .relativeTo (commonParent ).getPathString ();
416+ String normalizedPathToRoot = PathFragment .create (relativePathToRoot ).getPathString ();
417+ rpathRootsForExplicitSoDeps .add (normalizedPathToRoot );
418+ }
353419
354420 // Unless running locally, libraries will be available under the root relative path, so we
355421 // should add that to the rpath as well.
356422 if (inputArtifact .getRootRelativePathString ().startsWith ("_solib_" )) {
357423 PathFragment artifactPathUnderSolib = inputArtifact .getRootRelativePath ().subFragment (1 );
358- rpathRootsForExplicitSoDeps .add (
359- rpathRoot + artifactPathUnderSolib .getParentDirectory ().getPathString ());
424+ for (String rpathRoot : rpathRoots ) {
425+ rpathRootsForExplicitSoDeps .add (
426+ rpathRoot + artifactPathUnderSolib .getParentDirectory ().getPathString ());
427+ }
360428 }
361429 }
362430
0 commit comments