Home > Software design >  "linker sets" on hybrid clang Windows driver
"linker sets" on hybrid clang Windows driver

Time:03-18

OK so very special situation here, so it is somewhat gross and the answer is probably no. Compiling a project with clang (as it is Unix source) into .libs, but linking with MSVC for "/driver" to make the kernel component.

Looking for a way to handle Linux MODULE_PARAM() where they can define static int tunable; and have it be changeable for the kernel. Probably to be made into Registry entries, that seems to be how Windows would do the equivalent of sysctl, or kstat, or /proc

This could easily be handled by a "linker set", using SET_ENTRY(tunable) and then SET_FOREACH() to loop through them all.

Having some issues to get them to work, I suspect because of linking with MSVC , so I might not be able to make it work. But maybe you guys can think of a way around.

Using:

#define __MAKE_SET_CONST const

#define __STRING(x) #x      /* stringify without expanding x */
#define __XSTRING(x)    __STRING(x) /* expand x, then stringify */

#define __GLOBL(sym)    __asm__(".globl " __XSTRING(sym))
#define __WEAK(sym) __asm__(".weak " __XSTRING(sym))

#define __CONCAT1(x, y) x ## y
#define __CONCAT(x, y)  __CONCAT1(x, y)

#define __used      __attribute__((__used__))
#define __section(x)    __attribute__((__section__(x)))
#define __nosanitizeaddress __attribute__((no_sanitize("address")))
#define __weak_symbol   __attribute__((__weak__))

#define __MAKE_SET_QV(set, sym, qv)         \
    __WEAK(__CONCAT(__start_set_,set));     \
    __WEAK(__CONCAT(__stop_set_,set));      \
    static void const * qv              \
    __set_##set##_sym_##sym __section("set_" #set)  \
    __nosanitizeaddress             \
    __used = &(sym)
#define __MAKE_SET(set, sym)    __MAKE_SET_QV(set, sym, __MAKE_SET_CONST)

#define TEXT_SET(set, sym)  __MAKE_SET(set, sym)
#define DATA_SET(set, sym)  __MAKE_SET(set, sym)
#define DATA_WSET(set, sym) __MAKE_SET_QV(set, sym, )
#define BSS_SET(set, sym)   __MAKE_SET(set, sym)
#define ABS_SET(set, sym)   __MAKE_SET(set, sym)
#define SET_ENTRY(set, sym) __MAKE_SET(set, sym)

static int settest1 = 58;
static int settest2 = 156;

SET_ENTRY(testset, settest1);
SET_ENTRY(testset, settest2);

#define SET_BEGIN(set)                          \
    (&__CONCAT(__start_set_,set))
#define SET_LIMIT(set)                          \
    (&__CONCAT(__stop_set_,set))

#define SET_DECLARE(set, ptype)                 \
    extern ptype __weak_symbol *__CONCAT(__start_set_,set); \
    extern ptype __weak_symbol *__CONCAT(__stop_set_,set)

#define SET_FOREACH(pvar, set)                      \
    for (pvar = SET_BEGIN(set); pvar < SET_LIMIT(set); pvar  )

void
linkersettest(void)
{
    SET_DECLARE(testset, int);

    int **ptr;
    SET_FOREACH(ptr, testset) {
    int x = **ptr;
    KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,
        "linkerset test: %d\n", x));
    }
}

Compiling (clang) seems ok, but alas, linking (MSVC ) says:

error LNK2016: absolute symbol '__start_set_testset' used as target of REL32 relocation in section 0x1

CodePudding user response:

Making changes like:

    static void const * qv              \
    __set_##set##_sym_##sym __section("set_" #set)  \

The static here on Windows will drop it from .obj file. Removing static and going with:

    __declspec(dllexport) void const * qv               \
    __set_##set##_sym_##sym __section("set_" #set)  \

gets us all the expected defines in the .obj file, including __set_testset_sym_settest1 and __start_set_testset.

But it doesn't work, in that it does not appear to make a linker section set_testset at all, and the mentioned symbols are "randomly" in side, not the expected start, settest1, settest2, stop.

While looking around examples of #pragma section(".CRT$XCU",read,write) to add constructors to CRT's initterm, I came across this snippet:

typedef void(__cdecl* PF)(void);

#pragma section(".mine$a", read)
__declspec(allocate(".mine$a")) const PF InitSegStart = (PF)1;
#pragma section(".mine$z",read)
__declspec(allocate(".mine$z")) const PF InitSegEnd = (PF)1;

__declspec(allocate(".mine$m")) const PF __set_settest1 = (PF)&settest1;
__declspec(allocate(".mine$m")) const PF __set_settest2 = (PF)&settest2;

void
linkersettest(void)
{
    const PF* x = &InitSegStart;
    DbgBreakPoint();
    for (  x; x < &InitSegEnd;   x)
    KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,
        "linkerset test: %p %p %d\n", x, *x, *(int *)*x ));

Which appears to be the way Windows would do "linker-sets", and it works.

Possibly the clang way will work, if I adjust the section name from set_testset to a Windows name like .mine$ and ensure start/stop is "a" and "z" respectively.

I assume the "m" is just any character, as long as it is between "a" and "z", to get the order.

Should the setting of .mime$m be preceded by a #pragma line? I've not looked at what #pragma section read line does, yet (but works without it).

  • Related