If --gcc-toolchain is specified, its value overrides the cmake variable GCC_INSTALL_PREFIX.
When the value is non-empty: the value is appended to the --prefix list and is used to detect GCC installations.
The GCC installation is used to provide include directories/library directories and some startup files (e.g. crtbegin).
Problem 1.
–prefix(-B) does more than --gcc-toolchain: clang::driver::Driver::GetProgramPath basically searches for $prefix/$triple-$file and $prefix$file,
where $prefix is taken from the list of --prefix(-B). --gcc-toolchain does not participate in the search.
The result is that ‘ld’ and ‘as’ may come from the system (more precisely, sysroot):
cd clang/test/Driver
Make sure Inputs/opensuse_42.2_aarch64_tree/usr/bin/ld exists.
clang -target aarch64-suse-linux --gcc-toolchain=Inputs/opensuse_42.2_aarch64_tree/usr ‘-###’ gcc-toolchain.cpp -v
I have ld in my /usr/local/bin and it takes precedence over /usr/bin/ld
“/usr/local/bin/ld” “-EL” “–eh-frame-hdr” “-m” “aarch64linux” “-dynamic-linker” “/lib/ld-linux-aarch64.so.1” “-o” “a.out” “Inputs/opensuse_42.2_aarch64_tree/usr/lib64/gcc/aarch64-suse-linux/4.8/…/…/…/…/lib64/crt1.o” “Inputs/opensuse_42.2_aarch64_tree/usr/lib64/gcc/aarch64-suse-linux/4.8/…/…/…/…/lib64/crti.o” “Inputs/opensuse_42.2_aarch64_tree/usr/lib64/gcc/aarch64-suse-linux/4.8/crtbegin.o” “-LInputs/opensuse_42.2_aarch64_tree/usr/lib64/gcc/aarch64-suse-linux/4.8” “-LInputs/opensuse_42.2_aarch64_tree/usr/lib64/gcc/aarch64-suse-linux/4.8/…/…/…/…/lib64” “-L/lib/aarch64-linux-gnu” “-L/lib/…/lib64” “-L/usr/lib/aarch64-linux-gnu” “-L/usr/lib/…/lib64” “-LInputs/opensuse_42.2_aarch64_tree/usr/lib64/gcc/aarch64-suse-linux/4.8/…/…/…” “-L/tmp/RelA/bin/…/lib” “-L/lib” “-L/usr/lib” “/tmp/gcc-toolchain-f87f08.o” “-lgcc” “–as-needed” “-lgcc_s” “–no-as-needed” “-lc” “-lgcc” “–as-needed” “-lgcc_s” “–no-as-needed” “Inputs/opensuse_42.2_aarch64_tree/usr/lib64/gcc/aarch64-suse-linux/4.8/crtend.o” “Inputs/opensuse_42.2_aarch64_tree/usr/lib64/gcc/aarch64-suse-linux/4.8/…/…/…/…/lib64/crtn.o”
The -L and crt* files are indeed from Inputs/opensuse_42.2_aarch64_tree/usr, but ld (and as if -fno-integrated-as) is from the system.
On many Linux distributions you can normally assume that the system ld and as only support the host architecture.
This means --gcc-toolchain can only be used to specify a GCC installation with the same architecture.
–prefix can make as and ld paths correct, but: if another --prefix is needed, why do we use --gcc-toolchain?
I have sent a patch to document the current state: https://reviews.llvm.org/D97902
Problem 2.
Non-empty --gcc-toolchain has one nicer property: it suppresses GCC installation detection in the sysroot.
Now let me alter the command a bit:
“/usr/lib/gcc-cross/aarch64-linux-gnu/10/…/…/…/…/aarch64-linux-gnu/bin/ld” “-EL” “–eh-frame-hdr” “-m” “aarch64linux” “-dynamic-linker” “/lib/ld-linux-aarch64.so.1” “-o” “a.out” “/usr/lib/gcc-cross/aarch64-linux-gnu/10/…/…/…
/…/aarch64-linux-gnu/lib/crt1.o” “/usr/lib/gcc-cross/aarch64-linux-gnu/10/…/…/…/…/aarch64-linux-gnu/lib/crti.o” “/usr/lib/gcc-cross/aarch64-linux-gnu/10/crtbegin.o” “-L/usr/lib/gcc-cross/aarch64-linux-gnu/10” “-L/usr/lib/gcc-cross/aarch64-linux-gnu/10/…/…/…/aarch64-linux-gnu” “-L/usr/lib/gcc-cross/aarch64-linux-gnu/10/…/…/…/…/lib64” “-L/lib/aarch64-linux-gnu” “-L/lib/…/lib64” “-L/usr/lib/aarch64-linux-gnu” “-L/usr/lib/…/lib64” “-L/usr/lib/aarch64-linux-gnu/…/…/lib64” “-L/usr/lib/gcc-cross/aarch64-linux-gnu/10/…/…/…/…/aarch64-linux-gnu/lib” “-L/usr/lib/gcc-cross/aarch64-linux-gnu/10/…/…/…” “-L/tmp/RelA/bin/…/lib” “-L/lib” “-L/usr/lib” “/tmp/gcc-toolchain-b491cd.o” “-lgcc” “–as-needed” “-lgcc_s” “–no-as-needed” “-lc” “-lgcc” “–as-needed” “-lgcc_s” “–no-as-needed” “/usr/lib/gcc-cross/aarch64-linux-gnu/10/crtend.o” “/usr/lib/gcc-cross/aarch64-linux-gnu/10/…/…/…/…/aarch64-linux-gnu/lib/crtn.o”
I have installed an aarch64 cross gcc. Because its version is larger than the version of the GCC installation under --prefix, the system gcc-cross/aarch64-linux-gnu takes precedence.
This behavior looks a bit unfortunate. Should we let the first --prefix win and drop future --prefix and default system installations?
[1]:
The logic is around https://github.com/llvm/llvm-project/blob/main/clang/lib/Driver/ToolChains/Gnu.cpp#L1910
Prefixes = --prefix/-B list (only the directory subset is effective)
StringRef GCCToolchainDir = --gcc-toolchain= or CMake variable GCC_INSTALL_PREFIX
if (GCCToolchainDir != “”) {
Prefixes.push_back(std::string(GCCToolchainDir));
} else {
if (!D.SysRoot.empty()) {
Prefixes.push_back(D.SysRoot);
// Add D.SysRoot+"/usr" to Prefixes. Some distributions add more directories.
AddDefaultGCCPrefixes(TargetTriple, Prefixes, D.SysRoot);
}
// D.InstalledDir is the directory of the clang executable, e.g. /usr/bin
Prefixes.push_back(D.InstalledDir + “/…”);
if (D.SysRoot.empty())
AddDefaultGCCPrefixes(TargetTriple, Prefixes, D.SysRoot);
}
// Gentoo / ChromeOS specific logic.
// I will move this block in https://reviews.llvm.org/D97894
if (GCCToolchainDir == “” || GCCToolchainDir == D.SysRoot + “/usr”) {
…
}
// Loop over the various components which exist and select the best GCC
// installation available. GCC installs are ranked by version number.
Version = GCCVersion::Parse(“0.0.0”);
for (const std::string &Prefix : Prefixes) {
auto &VFS = D.getVFS();
if (!VFS.exists(Prefix))
continue;
// CandidateLibDirs is a subset of {/lib64, /lib32, /lib}.
for (StringRef Suffix : CandidateLibDirs) {
const std::string LibDir = Prefix + Suffix.str();
if (!VFS.exists(LibDir))
continue;
bool GCCDirExists = VFS.exists(LibDir + “/gcc”);
bool GCCCrossDirExists = VFS.exists(LibDir + “/gcc-cross”);
// Precise match. Detect $Prefix/lib/$–target
ScanLibDirForGCCTriple(TargetTriple, Args, LibDir, TargetTriple.str(),
false, GCCDirExists, GCCCrossDirExists);
// Usually empty.
for (StringRef Candidate : ExtraTripleAliases) // Try these first.
ScanLibDirForGCCTriple(TargetTriple, Args, LibDir, Candidate, false,
GCCDirExists, GCCCrossDirExists);
// CandidateTripleAliases is a set with “x86_64-linux-gnu”, “x86_64-unknown-linux-gnu”, …
// This loop detects directories like $Prefix/lib/x86_64-linux-gnu.
for (StringRef Candidate : CandidateTripleAliases)
ScanLibDirForGCCTriple(TargetTriple, Args, LibDir, Candidate, false,
GCCDirExists, GCCCrossDirExists);
}
for (StringRef Suffix : CandidateBiarchLibDirs) {
const std::string LibDir = Prefix + Suffix.str();
if (!VFS.exists(LibDir))
continue;
bool GCCDirExists = VFS.exists(LibDir + “/gcc”);
bool GCCCrossDirExists = VFS.exists(LibDir + “/gcc-cross”);
for (StringRef Candidate : CandidateBiarchTripleAliases)
ScanLibDirForGCCTriple(TargetTriple, Args, LibDir, Candidate, true,
GCCDirExists, GCCCrossDirExists);
}
}