[RFC][landed] Implement getauxval (original) (raw)

What is getauxval?

Detailed documentation is at getauxval(3) - Linux manual page. It is a routine to get basic information about the current platform, such as page size, cache size, hardware capabilities, and so on.

Why is it important, anyway?

As stated in the above section, the function is critical for getting essential low-level information from the system. Say you want to test allocations based on page size, you probably want to use sysconf with _SC_PAGESIZE. That function call, however, is based on getauxval.

As for other applications, getauxval is important when users want to dispatch their functions based on hardware information.

How is getauxval implemented, normally?

The Linux ABI on most platforms (all platforms that are currently supported) specifies that auxv is passed onto the initial process stack after argv and environ (separated by NULL values). Therefore, in other libcs, the startup routine scans the initial stack and stores a pointer to auxv in a global variable. Subsequent calls to getauxval will then lookup values from auxv.

A side note: When should we populate auxv?

We should consider the situation where getauxval is called early in the startup stage. getauxval can be used in ifuncs’ dispatchers. Therefore, in the startup routine, auxv should be populated right after TLS initialization, and before calling other initialization functions.

What makes it complicated in LLVM’s libc?

LLVM’s libc supports both overlay mode and fullbuild mode. That is, user can use symbols from LLVM’s libc while the library is not loaded by our own startup routines.

So, what are other ways of getting auxv if we are in overlay mode?

There are several ways, each having their own limitations:

What is the design, then?

struct AuxEntry {
  long id, value;
};
long getauxval(long id) {
    if (app.auxv_ptr is not null) {
        return search(id, app.auxv_ptr);
    }
    static AuxEntry * auxv;
    static OnceKey once_key;
    once (&once_key, [] {
        auxv = mmap(..); // mmap with a fixed size
        if (auxv) {
            first try syscall(SYS_prctl, ...);
            then try read the whole /proc/self/auxv into auxv;
            if both fails, then clean up memory;
            otherwise register a cleanup hook with atexit.
        }
    });
    if (auxv) {
       return search(id, auxv);
    } else {
       read /proc/self/auxv entry by entry to find id
    }
    return AUX_NULL; // no found
}

What else needs to be done?

The above function needs to check app.auxv_ptr in overlay mode. Thus, app should always be defined even in overlay mode. Another choice is to use a weak symbol.