+++ date = "2023-03-28T23:59:00+08:00" draft = false tags = [] title = "Set Up a Test Harness with Mount Namespace" authors = ["xry111"] +++ I unintentionally introduced GCC [PR109293] when I was fixing [PR91085]. The issue is trivial to fix, but I wanted to test it in an environment where `memmem()` is indeed unavailable. However it's not easy to find one (the PR mentions MinGW but I've never succeeded to build GCC on MinGW). Yes, as a LFS editor I can hack Glibc code to hide `memmem()` from the API and ABI and build a system without it. But this approach seems too overkill. But I'm also an OJ developer (who spends a large amount of time attempting to hide things from the contestants :) so I soon came up with an idea. [PR109293]: https://gcc.gnu.org/PR109293 [PR91085]: https://gcc.gnu.org/PR91085 ## Hide `memmem()` from `string.h` It's easy: just comment `memmem()` out. Then we need to inject `string.h` into `/usr/include`. We can just back theoriginal one up and install the modified header there, but this is obviously dangerous and will be unacceptable in a real productive environment. So we can create a new mount namespace and bind mount the header instead: ```text $ unshare -m -r # mount --bind ~/no-memmem/string.h /usr/include/string.h ``` Note that to use a separate mount namespace (`-m`), a separate user namespace is also needed (`-r`) and the current user will be mapped as `root` in the new user namespace. The bind mount is isolated in the separate namespace, so it won't affect any applications running on the system, but the programs started by the shell spawned by `unshare`. ## Hide `memmem()` from `libc.so` It's not so easy. Initially I tried: ```text # objcopy /usr/lib/libc.so.6 -N memmem libc.so.6 # mount --bind libc.so.6 /usr/lib/libc.so.6 ``` in the separate namespace. But it did not work. The `objcopy` command stripped `memmem()` from the `.symtab` section, so `memmem()` did not show up in `objdump -t` output; however it was still in `.dynsym` section, so it still showed up in `objdump -T` and it's enough for the linker to find the symbol from a shared library. So I created a `no-memmem.a` file with: ``` # cat no-memmem.c extern void __dont_use_memmem_no_exist(); void memmem(void) { __dont_use_memmem_no_exist(); } # cc no-memmem.c -c # ar cr no-memmem.a no-memmem.o ``` If we add `no-memmem.a` into the `gcc` command, anything using `memmem` will fail to link like: ```text /usr/bin/ld: no-memmem.a(no-memmem.o): in function `memmem': no-memmem.c:(.text+0xa): undefined reference to `__dont_use_memmem_no_exist' ``` Then how to inject `no-memmem.a` for everything using `libc.so`? Well, `libc.so` is a linker script: ```text /* GNU ld script Use the shared library, but some functions are only in the static library, so try that secondarily. */ OUTPUT_FORMAT(elf64-x86-64) GROUP ( /usr/lib/libc.so.6 /usr/lib/libc_nonshared.a AS_NEEDED ( /usr/lib/ld-linux-x86-64.so.2 ) ) ``` So we just need to copy `libc.so`, adding the path to `no-memmem.a` before `/usr/lib/libc.so.6` into the copy, and bind mount the copy onto `/usr/lib/libc.so`. ## Test result `fixincl` succeeded to link in the namespace with `memmem()` hidden, and the `memmem()` implementation in `libiberty` is linked into the `fixincl` executable as expected. Now the patch is OK to go!