+++
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!
