"linker sets" 在 hybrid clang Windows 驱动程序上

"linker sets" on hybrid clang Windows driver

好吧,这里的情况非常特殊,所以有点恶心,答案可能是否定的。 使用 clang(因为它是 Unix 源代码)将项目编译为 .libs,但为“/driver”链接 MSVC++ 以生成内核组件。

正在寻找一种方法来处理 Linux MODULE_PARAM() ,他们可以在其中定义 static int tunable; 并使其可针对内核进行更改。可能会被写入注册表项,这似乎是 Windows 与 sysctl、kstat 或 /proc

等效的方式

这可以通过“链接器集”轻松处理,使用 SET_ENTRY(tunable) 然后 SET_FOREACH() 遍历它们。

在使它们工作时遇到一些问题,我怀疑是因为与 MSVC++ 的链接,所以我可能无法使其工作。不过也许你们可以想个办法。

使用:

#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));
    }
}

编译 (clang) 似乎没问题,但唉,链接 (MSVC++) 说:

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

进行如下更改:

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

Windows 上的 static 将从 .obj 文件中删除它。删除 static 并使用:

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

获取 .obj 文件中的所有预期定义,包括 __set_testset_sym_settest1__start_set_testset.

但它不起作用,因为它似乎根本没有创建链接器部分 set_testset,并且提到的符号是“随机”在旁边,而不是预期的 start, settest1, settest2, stop .

在查看 #pragma section(".CRT$XCU",read,write) 向 CRT 的 initterm 添加构造函数的示例时,我遇到了这个片段:

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 ));

这似乎是 Windows 会做“linker-sets”的方式,而且它有效。

如果我将部分名称从 set_testset 调整为 Windows 名称(如 .mine$ 并确保 start/stop 是“a”并且分别为“z”。

我假设“m”只是任何字符,只要它在“a”和“z”之间,就能得到顺序。

.mime$m 的设置是否应该在 #pragma 行之前?我还没有查看 #pragma section read 行的作用(但没有它也能工作)。