I addressed and accounted for this in my comment. All three of GNU grep, ugrep and ripgrep can dynamically link libc and PCRE2.
ripgrep doesn't bundle any compression code or terminal databases. ripgrep does bundle a significant number of Unicode tables.
How it would work, is the process would tell the OS what libraries it expects to be loaded and at what address, and it would just mmap that piece of shared readonly memory. This mapping step happens for shared libraries anyways, but the symbol resolution is dynamic.
I imagine this would also harden the application somewhat, as there are less dynamic dispatches for hackers to exploit.
It seems that what you are suggesting is using position dependent code, which has performance benefits, especially on 32-bit x86, but it seems that we are moving away from it, one reason being that modern hardware supports position independent code better, and also for security.
Having fixed addresses means that hackers know exactly where functions are, making their life easier for writing their shellcodes. As a result, a common security feature we tend to see more and more is ASLR, where code is relocated at random addresses, even for statically-linked code, which is the exact opposite of what you are suggesting.
What you are suggesting could protect against things like DLL injection, which is something I consider more of a feature than a bug. For me, the small improvement in security is not worth it, especially since it would be incompatible with ASLR. Some of these benefits could be achieved by hardening the dynamic linker without changing the executables and libraries themselves.
Not sure what you're suggesting with position dependent code. All libraries would still be position independent, but libfoo would be loaded at 0x5000000 in executable A and 0x6000000 in executable B.
For ASLR, you'd have the exact same performance and security characteristics as statically linked code. Either you could go position dependent and forgo ASLR, or go position independent and randomly shift your base address, in which case the loaded libs would need to account for that (for example if ASLR decides to load your process at 0x10 then libfoo would be loaded at 0x5000010).
Also I don't see any reason why you couldn't combine this with dynamic linking, and static linking in the same process.
Anyways, I still took it into account when calling ripgrep "bloated". Using ldd, I counted 3.6 MB of dependencies for ripgrep and 7.1 MB for ugrep. Which coincidentally result in about 8 MB for both ugrep and ripgrep. But ugrep accounts for the entire libstdc++ and other libraries, which includes code that ugrep doesn't need (such as compression), so I would have expected ugrep to be smaller. GNU grep has 2.5 MB of dependencies btw: 1.9MB for libc and 0.6MB for libpcre2.
And to make things clear, I don't put ugrep in the lightweight category either. C++ (modern C++ in particular) suffers from some of the same problems as Rust: lots of code generation leading to bloat and slow compile times, but (as you pointed out) it tends to play along better with dynamic libraries with a C interface.
I don't know how much a size-optimized grep with the same features as ripgrep would take. 4 MB looks like a lot, but sometimes bloat come from unexpected places. For example, some compression algorithms may include predefined dictionaries, coloring may involve terminal databases, and Unicode support my involve databases too.