【问题标题】:Best way to implement a callgate?实现呼叫门的最佳方法?
【发布时间】:2022-06-10 19:51:14
【问题描述】:

我正在使用firewall 为 stm32 编程。要打开防火墙,必须使用呼叫门。或多或少通常的方法是编写一个带有可变参数的函数,并使用 switch 语句中的第一个参数在不同的函数之间切换。

int function1_outside(int arg1)
{
    int status;
    // disable interrupts
    status = CallGate(ID1, arg1);
    // enable interrupts
    return status;
}

int CallGate(FunctionID eID, ...)
{
    va_list arguments;
    int status;

    /* Initializing arguments to store all values */
    va_start(arguments, PrimaskParam);

    switch (eID)
    {
        case ID1:
        {
            int arg1;
            /* Retrieve argument(s) */
            arg1 = va_arg(arguments, int);
            // Call the real function
            status = function1_inside(arg1);
            break;
        }
        case ID2:
        {
            // do more ...
            break;
        }
    }

    /* Clean up arguments list*/
    va_end(arguments);

    return status;
}

int function1_inside(int arg1)
{
    int result;
    // do stuff
    result = arg1 + 1;
    return result;
}

这种方法的最大问题是保持所有部分同步。
有没有减少需要保持同步的部分的好方法?

【问题讨论】:

  • 从我幼稚的角度来看,您可以使用函数指针作为第一个参数,使用 void 指针作为第二个参数,允许您使用指向函数的指针来选择函数并允许您将参数的结构作为第二个参数传递,不是吗?这将最大限度地减少要保持同步的部分。
  • 我已经想到了类似的方向。一个问题是获取函数指针。带有调用门的部分是单独编译的。您必须提前通过调用门或在激活防火墙之前传输函数指针。但是您还必须使用指针维护结构。
  • 另外一件事是如何从一个带有 va_list 的函数转换为多个普通函数?
  • CallGate 必须是可变参数吗?否则,您可以将其声明为void CallGate(void (*function)(void*), void* data),从而接受要调用的函数和函数的参数。至于获取函数指针,你可以在外面做extern void function_inside(void*); CallGate(&function_inside, (void*)function_parameters);
  • 我建议保留开关以本地化功能,这样您只需在头文件中进行简单枚举即可在 2 个编译单元之间共享它们。对于参数,在标题中将其声明为:extern void CallGate(int code, ...);。然后将您的子函数声明为:function1_inside(va_list ap); 其中ap 是指向通过va_start(ap, code); 获得的下一个参数的指针。然后在子函数中,您可以检索特定于该函数的所有参数。

标签: c embedded stm32


【解决方案1】:

代码不如callgate 代码的正确放置重要。

它必须从受保护内存段中的第 2 个字开始执行。您需要在链接描述文件中定义它,然后将函数放在正确的段中(以字节为单位的位置 4)。从那里调用的所有函数都必须位于该内存段中(即放置在保护区地址空间中)。所以基本上 - 没有库(除非您修改链接器脚本以将特定目标文件中的代码放入受保护的地址空间)。

【讨论】:

    【解决方案2】:

    我会做一个函数指针数组,并通过void* 参数传递任何参数。使用stdarg 会让人困惑,最好从头开始做一个指针。

    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    
    enum callgate_func_u {
        CALLGATE_FUNC_THAT_PRINTS_INT,
        CALLGATE_OTHER_FUNC,
        CALLGATE_FUNC_LEN,
    };
    
    int function1_inside(int id, void *arg) {
        return printf("%d", *(int*)arg);
    }
    int function2_inside(int id, void *arg) {
        return printf("%lf", *(float*)arg);
    }
    
    /// Represents a function to be called.
    /// @param id The ID used to call the function.
    /// @param arg A pointer to __any__ user context.
    typedef int callgate_func_t(int id, void *arg);
    static callgate_func_t *const callgate_functions[CALLGATE_FUNC_LEN] = {
        [CALLGATE_FUNC_THAT_PRINTS_INT] = function1_inside,
        [CALLGATE_OTHER_FUNC] = function2_inside,
    };
    
    int callgate(enum callgate_func_u id, void *param) {
        static_assert(sizeof(callgate_functions)/sizeof(*callgate_functions) == CALLGATE_FUNC_LEN, "");
        assert(id < CALLGATE_FUNC_LEN);
        callgate_func_t *const func = callgate_functions[id];
        assert(func != NULL);
        return func(id, param);
    }
    
    int function1_outside(int arg1) {
        int status;
        // disable interrupts
        status = callgate(CALLGATE_FUNC_THAT_PRINTS_INT, &arg1);
        // enable interrupts
        return status;
    }
    
    int main() {
        printf("5 = ");
        function1_outside(5);
        printf("\n");
    }
    

    总的来说,您可能对ioctlfcntl 等系统调用感兴趣。他们基本上在做同样的事情,在内核端有一个很大的switch,它根据 id 选择适当的操作。

    【讨论】:

    • 内部带有指针的数组似乎也是一个不错的解决方案。需要注意的一件事 printf 需要与硬件交互。调用防火墙外的函数会关闭防火墙,并在返回时触发防火墙重置系统。这就是为什么应该禁用中断或需要防火墙内部中断向量表的原因。
    • 太棒了! ?这对我来说很重要吗?所以把它包裹在callgate函数里面。
    • 答案基本上没有意义或意义不大。 DV-TED。 @KamilCuk 在回答问题之前阅读了 STM32 Lx 参考手册。
    猜你喜欢
    • 1970-01-01
    • 2021-08-22
    • 2015-01-18
    • 1970-01-01
    • 2012-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-27
    相关资源
    最近更新 更多