Is the C++ filesystem object file installed via LLVM's APT repository missing symbols?

Hello,

I have downloaded LLVM and Clang 8 from the APT repository (deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main). When I compile and link

#include

namespace fs = std::filesystem;

int main()

{

fs::path wd = fs::current_path();

return 0;

}

with clang-8 -std=c++17 -stdlib=libc++ -lc++ -lc++abi -lc++fs test.cpp -v

I get the output:

clang version 8.0.0-svn356034-1~exp1~20190313094121.53 (branches/release_80)

Target: x86_64-pc-linux-gnu

Thread model: posix

InstalledDir: /usr/bin

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/5

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/6

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/6.5.0

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/7

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/7.4.0

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/8

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5.5.0

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.5.0

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.4.0

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8

Selected GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0

Candidate multilib: .;@m64

Selected multilib: .;@m64

“/usr/lib/llvm-8/bin/clang” -cc1 -triple x86_64-pc-linux-gnu -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name test.cpp -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -v -resource-dir /usr/lib/llvm-8/lib/clang/8.0.0 -internal-isystem /usr/lib/llvm-8/bin/…/include/c++/v1 -internal-isystem /usr/include/clang/8.0.0/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-8/lib/clang/8.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/ -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -o /tmp/test-b088e9.o -x c++ test.cpp -faddrsig

clang -cc1 version 8.0.0 based upon LLVM 8.0.0 default target x86_64-pc-linux-gnu

ignoring nonexistent directory “/include”

ignoring duplicate directory “/usr/include/clang/8.0.0/include”

#include “…” search starts here:

#include <…> search starts here:

/usr/lib/llvm-8/bin/…/include/c++/v1

/usr/include/clang/8.0.0/include

/usr/local/include

/usr/include/x86_64-linux-gnu

/usr/include

End of search list.

“/usr/bin/ld” --hash-style=both --build-id --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu/crt1.o /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu/crti.o /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/crtbegin.o -L/usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0 -L/usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu -L/lib/x86_64-linux-gnu -L/lib/…/lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/… -L/usr/lib/llvm-8/bin/…/lib -L/lib -L/usr/lib -lc++ -lc++abi -lc++fs /tmp/test-b088e9.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/crtend.o /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu/crtn.o

/tmp/test-b088e9.o: In function `std::__1::__fs::filesystem::current_path()’:

test.cpp:(.text._ZNSt3__14__fs10filesystem12current_pathEv[_ZNSt3__14__fs10filesystem12current_pathEv]+0x14): undefined reference to `std::__1::__fs::filesystem::__current_path(std::__1::error_code*)’

clang: error: linker command failed with exit code 1 (use -v to see invocation)

I ran:

nm -C /usr/lib/llvm-8/lib/libc++fs.a | fgrep ‘std::__1::__fs::filesystem::__current_path’

and got:

0000000000002540 T std::__1::__fs::filesystem::__current_path(std::__1::error_code*)

0000000000002880 T std::__1::__fs::filesystem::__current_path(std::__1::__fs::filesystem::path const&, std::__1::error_code*)

It seems that the current_path functions, as defined in the libc++fs.a file that I installed, do not match the overloads as documented at https://en.cppreference.com/w/cpp/filesystem/current_path or specified in the filesystem header file of libc++. According to http://libcxx.llvm.org/ts1z_status.html, as of June 17, 2016, the filesystem library is complete. For comparison, the APT repository was last updated on March 27, 2019.

I do not know if perhaps I made a mistake in linking the code or installing the library. https://libcxx.llvm.org/ directed me to ask here if I’m not sure whether I found a bug.

Is this an issue with the files I downloaded from the APT repository, or is it my own mistake?

Thanks,

TAH

Hello,

I have downloaded LLVM and Clang 8 from the APT repository (deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main). When I compile and link

#include

namespace fs = std::filesystem;

int main()

{

fs::path wd = fs::current_path();

return 0;

}

with clang-8 -std=c++17 -stdlib=libc++ -lc++ -lc++abi -lc++fs test.cpp -v

I get the output:

clang version 8.0.0-svn356034-1~exp1~20190313094121.53 (branches/release_80)

Target: x86_64-pc-linux-gnu

Thread model: posix

InstalledDir: /usr/bin

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/5

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/6

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/6.5.0

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/7

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/7.4.0

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/8

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5.5.0

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.5.0

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.4.0

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8

Selected GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0

Candidate multilib: .;@m64

Selected multilib: .;@m64

“/usr/lib/llvm-8/bin/clang” -cc1 -triple x86_64-pc-linux-gnu -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name test.cpp -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -v -resource-dir /usr/lib/llvm-8/lib/clang/8.0.0 -internal-isystem /usr/lib/llvm-8/bin/…/include/c++/v1 -internal-isystem /usr/include/clang/8.0.0/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-8/lib/clang/8.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/ -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -o /tmp/test-b088e9.o -x c++ test.cpp -faddrsig

clang -cc1 version 8.0.0 based upon LLVM 8.0.0 default target x86_64-pc-linux-gnu

ignoring nonexistent directory “/include”

ignoring duplicate directory “/usr/include/clang/8.0.0/include”

#include “…” search starts here:

#include <…> search starts here:

/usr/lib/llvm-8/bin/…/include/c++/v1

/usr/include/clang/8.0.0/include

/usr/local/include

/usr/include/x86_64-linux-gnu

/usr/include

End of search list.

“/usr/bin/ld” --hash-style=both --build-id --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu/crt1.o /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu/crti.o /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/crtbegin.o -L/usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0 -L/usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu -L/lib/x86_64-linux-gnu -L/lib/…/lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/… -L/usr/lib/llvm-8/bin/…/lib -L/lib -L/usr/lib -lc++ -lc++abi -lc++fs /tmp/test-b088e9.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/crtend.o /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu/crtn.o

/tmp/test-b088e9.o: In function `std::__1::__fs::filesystem::current_path()’:

test.cpp:(.text._ZNSt3__14__fs10filesystem12current_pathEv[_ZNSt3__14__fs10filesystem12current_pathEv]+0x14): undefined reference to `std::__1::__fs::filesystem::__current_path(std::__1::error_code*)’

clang: error: linker command failed with exit code 1 (use -v to see invocation)

I ran:

nm -C /usr/lib/llvm-8/lib/libc++fs.a | fgrep ‘std::__1::__fs::filesystem::__current_path’

and got:

0000000000002540 T std::__1::__fs::filesystem::__current_path(std::__1::error_code*)

0000000000002880 T std::__1::__fs::filesystem::__current_path(std::__1::__fs::filesystem::path const&, std::__1::error_code*)

It seems that the current_path functions, as defined in the libc++fs.a file that I installed, do not match the overloads as documented at https://en.cppreference.com/w/cpp/filesystem/current_path or specified in the filesystem header file of libc++. According to http://libcxx.llvm.org/ts1z_status.html, as of June 17, 2016, the filesystem library is complete. For comparison, the APT repository was last updated on March 27, 2019.

I do not know if perhaps I made a mistake in linking the code or installing the library. https://libcxx.llvm.org/ directed me to ask here if I’m not sure whether I found a bug.

Is this an issue with the files I downloaded from the APT repository, or is it my own mistake?

Your invocation of Clang puts -lc++fs before the source file that depends on it.
You need to put the source file before the libraries. Ex:

clang-8 -std=c++17 -stdlib=libc++ test.cpp -lc++fs -v

Isn’t that something that Clang should handle?

Louis

To clarify, what I meant is that from a very naive point of view, the order in which you provide -l's on the command line shouldn’t matter – it should work as long as you provide the right set of -l's. I’m puzzled every time a tool (e.g. ld) behaves differently based on the order of command line arguments.

Maybe there’s a good reason for this, though. Maybe the cfe-dev folks can explain?

Louis

Getting the right order requires evaluating the dependencies. Clang has to rely on the underlying linker to do this, so the logic can’t be in Clang itself.

On Darwin, ld64 does a pre-scan to build a hash of symbols so that it can handle out-of-order libraries. Probably some other linkers do the same, but many just don’t support that. If you have cycles, you need to either list libraries twice or otherwise use command-line arguments to get the linker to rescan the list.

Hello,

I have downloaded LLVM and Clang 8 from the APT repository (deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main). When I compile and link

#include

namespace fs = std::filesystem;

int main()

{

fs::path wd = fs::current_path();

return 0;

}

with clang-8 -std=c++17 -stdlib=libc++ -lc++ -lc++abi -lc++fs test.cpp -v

I get the output:

clang version 8.0.0-svn356034-1~exp1~20190313094121.53 (branches/release_80)

Target: x86_64-pc-linux-gnu

Thread model: posix

InstalledDir: /usr/bin

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/5

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/6

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/6.5.0

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/7

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/7.4.0

Found candidate GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/8

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/5.5.0

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/6.5.0

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.4.0

Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8

Selected GCC installation: /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0

Candidate multilib: .;@m64

Selected multilib: .;@m64

“/usr/lib/llvm-8/bin/clang” -cc1 -triple x86_64-pc-linux-gnu -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name test.cpp -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -v -resource-dir /usr/lib/llvm-8/lib/clang/8.0.0 -internal-isystem /usr/lib/llvm-8/bin/…/include/c++/v1 -internal-isystem /usr/include/clang/8.0.0/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-8/lib/clang/8.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/ -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fcxx-exceptions -fexceptions -fdiagnostics-show-option -o /tmp/test-b088e9.o -x c++ test.cpp -faddrsig

clang -cc1 version 8.0.0 based upon LLVM 8.0.0 default target x86_64-pc-linux-gnu

ignoring nonexistent directory “/include”

ignoring duplicate directory “/usr/include/clang/8.0.0/include”

#include “…” search starts here:

#include <…> search starts here:

/usr/lib/llvm-8/bin/…/include/c++/v1

/usr/include/clang/8.0.0/include

/usr/local/include

/usr/include/x86_64-linux-gnu

/usr/include

End of search list.

“/usr/bin/ld” --hash-style=both --build-id --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu/crt1.o /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu/crti.o /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/crtbegin.o -L/usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0 -L/usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu -L/lib/x86_64-linux-gnu -L/lib/…/lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/… -L/usr/lib/llvm-8/bin/…/lib -L/lib -L/usr/lib -lc++ -lc++abi -lc++fs /tmp/test-b088e9.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/crtend.o /usr/bin/…/lib/gcc/x86_64-linux-gnu/5.5.0/…/…/…/x86_64-linux-gnu/crtn.o

/tmp/test-b088e9.o: In function `std::__1::__fs::filesystem::current_path()’:

test.cpp:(.text._ZNSt3__14__fs10filesystem12current_pathEv[_ZNSt3__14__fs10filesystem12current_pathEv]+0x14): undefined reference to `std::__1::__fs::filesystem::__current_path(std::__1::error_code*)’

clang: error: linker command failed with exit code 1 (use -v to see invocation)

I ran:

nm -C /usr/lib/llvm-8/lib/libc++fs.a | fgrep ‘std::__1::__fs::filesystem::__current_path’

and got:

0000000000002540 T std::__1::__fs::filesystem::__current_path(std::__1::error_code*)

0000000000002880 T std::__1::__fs::filesystem::__current_path(std::__1::__fs::filesystem::path const&, std::__1::error_code*)

It seems that the current_path functions, as defined in the libc++fs.a file that I installed, do not match the overloads as documented at https://en.cppreference.com/w/cpp/filesystem/current_path or specified in the filesystem header file of libc++. According to http://libcxx.llvm.org/ts1z_status.html, as of June 17, 2016, the filesystem library is complete. For comparison, the APT repository was last updated on March 27, 2019.

I do not know if perhaps I made a mistake in linking the code or installing the library. https://libcxx.llvm.org/ directed me to ask here if I’m not sure whether I found a bug.

Is this an issue with the files I downloaded from the APT repository, or is it my own mistake?

Your invocation of Clang puts -lc++fs before the source file that depends on it.
You need to put the source file before the libraries. Ex:

clang-8 -std=c++17 -stdlib=libc++ test.cpp -lc++fs -v

Isn’t that something that Clang should handle?

To clarify, what I meant is that from a very naive point of view, the order in which you provide -l's on the command line shouldn’t matter – it should work as long as you provide the right set of -l's. I’m puzzled every time a tool (e.g. ld) behaves differently based on the order of command line arguments.

Maybe there’s a good reason for this, though. Maybe the cfe-dev folks can explain?

Getting the right order requires evaluating the dependencies. Clang has to rely on the underlying linker to do this, so the logic can’t be in Clang itself.

Right, in that case what I’m puzzled about is that some linkers are dependent on the order.

On Darwin, ld64 does a pre-scan to build a hash of symbols so that it can handle out-of-order libraries.

Ah, nice, that makes sense.

Thanks for the explanation,

Louis

Traditional Unix linkers only pull symbols from archives (static libraries) if an undefined reference to that symbol has already been seen by the time the static library is encountered on the command line. In other words, if libfoo.a defines foo, and bar.o has an undefined reference to foo, ordering them on the link line as bar.o libfoo.a will work, but libfoo.a bar.o won’t, since the undefined reference to foo appears after the static library defining it. This is the reason why static libraries are sometimes repeated on the command line or --start-group --end-group exist.

This is specific to traditional Unix linkers. LLD for ELF chooses is agnostic to the order of libraries; see “Efficient archive file handling” on http://lld.llvm.org/NewLLD.html. ld64 and Microsoft’s linker are also agnostic to the ordering (though Microsoft’s linker has its own peculiarities related to library symbol lookup, which again aren’t emulated by LLD for COFF; see https://docs.microsoft.com/en-us/cpp/build/reference/link-input-files?view=vs-2019, specifically “Libraries are searched in command line order as well, with the following caveat: Symbols that are unresolved when bringing in an object file from a library are searched for in that library first, and then the following libraries from the command line and /DEFAULTLIB (Specify Default Library) directives, and then to any libraries at the beginning of the command line.”).

This is the intended design of GNU ld. Some linkers follow this behavior, some don’t. e.g. IIRC lld chose to have an implicit grouping (like ld’s start-group/end-group), which comes w/pros and cons. Pros: like you said, you needn’t concern yourself with the order of the libs. If you are consuming several different libraries, one could see the perspective that from up here at the executable’s perspective, you don’t care how the linker does symbol resolution and one could imagine that it might not even matter that it takes more time for it to go and search the ones that appear earlier on the command line. Cons: increased link time, cyclical dependencies among libraries are easy to introduce now because the linker won’t alert you to the problem.

Relevant details from the ld.bfd manpage:

–start-group archives --end-group

The specified archives are searched repeatedly until no new undefined references are created. Normally, an archive is searched only once in the order that it is specified on the command line. If a symbol in that archive is needed to resolve an undefined symbol referred to by an object in an archive that appears later on the command line, the linker would not be able to resolve that reference. By grouping the archives, they all be searched repeatedly until all possible references are resolved.

Using this option has a significant performance cost. It is best to use it only when there are unavoidable circular references between two or more archives.

To clarify, what I meant is that from a very naive point of view, the order in which you provide -l's on the command line shouldn’t matter – it should work as long as you provide the right set of -l's. I’m puzzled every time a tool (e.g. ld) behaves differently based on the order of command line arguments.

Maybe there’s a good reason for this, though. Maybe the cfe-dev folks can explain?

Louis

LLD claims that their design choice here actually improves link time: see “Efficient archive file handling” on http://lld.llvm.org/NewLLD.html. Agreed on the circular dependence con.

This is the intended design of GNU ld. Some linkers follow this behavior, some don’t. e.g. IIRC lld chose to have an implicit grouping (like ld’s start-group/end-group), which comes w/pros and cons. Pros: like you said, you needn’t concern yourself with the order of the libs. If you are consuming several different libraries, one could see the perspective that from up here at the executable’s perspective, you don’t care how the linker does symbol resolution and one could imagine that it might not even matter that it takes more time for it to go and search the ones that appear earlier on the command line. Cons: increased link time, cyclical dependencies among libraries are easy to introduce now because the linker won’t alert you to the problem.

This doesn’t really have an impact on link times in lld (note that lld is faster than both gnu ld and gold). This is because lld just adds all symbols to its internal symbol table, but unresolved archive symbols get added as lazy symbols that don’t show up in the output unless they are actually referenced. It’s slower in other linkers because they do another full iteration over the symbol table every time an archive is visited. The biggest issue I saw with this is that you actually have different semantics for what gets linked, but it turned out that every case where this broke the link was really a bug in the program being linked, often duplicate definitions of stuff.

  • Michael Spencer