只写指针类型

时间:2013-04-17 00:12:12

标签: c++ c pointers readonly const-correctness

我正在为嵌入式系统编写软件。

我们使用指针访问FPGA器件的寄存器 一些寄存器是只读的,而另一些寄存器是只写的。

只读寄存器在读取时会产生未定义的值。

我想定义一个指针类型,允许编译器在从只读寄存器(a.k.a. dereferencing)读取值时检测。

只能使用C语言语法创建只写指针吗? (我们正在使用C开发第一个原型,但是在第二代转向C ++。)

如何在C ++中创建高效的只写指针? (请记住,这不是跟踪动态内存中的项目,而是访问硬件地址。)

此代码用于安全性和质量最受关注的嵌入式系统。

6 个答案:

答案 0 :(得分:60)

我可能会为每个人写一个小的包装类:

template <class T>
class read_only {
    T volatile *addr;
public:
    read_only(int address) : addr((T *)address) {}
    operator T() volatile const { return *addr; }
};

template <class T>
class write_only { 
    T volatile *addr;
public:
    write_only(int address) : addr ((T *)address) {}

    // chaining not allowed since it's write only.
    void operator=(T const &t) volatile { *addr = t; } 
};

至少假设你的系统有一个合理的编译器,我希望这两个都得到优化,所以生成的代码与使用原始指针无法区分。用法:

read_only<unsigned char> x(0x1234);
write_only<unsigned char> y(0x1235);

y = x + 1;         // No problem

x = y;             // won't compile

答案 1 :(得分:8)

我会使用结构组合来覆盖寄存器和一对函数来处理它们。

fpga_register.h中,您会有类似

的内容
#define FPGA_READ = 1; 
#define FPGA_WRITE = 2;
typedef struct register_t {
    char permissions;
} FPGARegister;

FPGARegister* fpga_init(void* address, char permissions);

int fpga_write(FPGARegister* register, void* value);

int fpga_read(FPGARegister* register, void* value);

使用xor中的READ和WRITE来表达权限。

fpga_register.c中,您将定义一个新结构

typedef struct register_t2 {
    char permissions;
    void * address;
} FPGARegisterReal;

以便您返回指向它的指针,而不是FPGARegister上指向fpga_init的指针。

然后,在fpga_readfpga_write上检查权限和

  • 如果允许operetion,则将FPGARegister从参数转回FPGARegisterReal,执行所需操作(设置或读取值)并返回成功代码
  • 如果不允许操作,只需返回错误代码

这样,包括头文件在内的任何人都无法访问FPGARegisterReal结构,因此无法直接访问寄存器地址。显然,人们可以破解它,但我很确定这种故意的黑客行为不是你真正的担忧。

答案 2 :(得分:8)

我使用过很多硬件,其中一些有“只读”或“只写”寄存器(或者不同的功能取决于你是否读取或写入寄存器,当有人决定时这会很有趣做“reg | = 4;”而不是记住它应该具有的值,设置第2位并写入新值,就像你应该的那样。没有什么比试图调试有随机位出现的硬件并从寄存器中消失的那样你不能read !;)到目前为止,我还没有看到任何实际阻止从只写寄存器读取或写入只读寄存器的尝试。

顺便说一句,我是否说过具有“只写”的寄存器是一个非常糟糕的主意,因为你无法读回来检查软件是否正确设置了寄存器,这使调试变得非常困难 - 而且编写驱动程序的人不喜欢通过两行VHDL或Verilog代码调试可以变得非常简单的难题。

如果您对寄存器布局有一定的控制权,我建议您将“只读”寄存器放在4KB对齐的地址,将“writeonly”寄存器放在另一个4KB对齐的地址中[超过4KB就可以了]。然后,您可以编程硬件的内存控制器以防止访问。

或者,如果正在读取不应该读取的寄存器,或者写入不应写入的寄存器,则让硬件产生中断。我认为硬件确实会产生其他用途的中断?

使用各种C ++解决方案提出的其他建议很好,但它并没有真正阻止那些有意直接使用寄存器的人,所以如果它真的是一个安全问题(而不是“让它变得尴尬”),那么你应该有硬件来防止滥用硬件。

答案 3 :(得分:7)

在C中,您可以使用指向不完整类型的指针来阻止所有解除引用:


/* writeonly.h */
typedef struct writeonly *wo_ptr_t;

/* writeonly.c */
#include "writeonly.h"

struct writeonly {
  int value 
};

/*...*/

   FOO_REGISTER->value = 42;

/* someother.c */
#include "writeonly.h"

/*...*/

   int x = FOO_REGISTER->value; /* error: deref'ing pointer to incomplete type */

只有writeonly.c,或者通常任何具有定义struct writeonly的代码都可以取消引用指针。当然,该代码也可能意外地读取该值,但至少所有其他代码都被阻止将所有指针一起解除引用,同时能够传递这些指针并将它们存储在变量,数组和结构中。

writeonly.[ch]可以提供写入值的功能。

答案 4 :(得分:6)

我认为在C中没有优雅的方式。但我确实看到了这样做的方法:

#define DEREF_PTR(type, ptr) type ptr; \
typedef char ptr ## _DEREF_PTR;

#define NO_DEREF_PTR(type, ptr) type ptr; \

#define DEREFERENCE(ptr) \
*ptr; \
{ptr ## _DEREF_PTR \
attempt_to_dereference_pointer_ ## ptr;}

int main(int argc, char *argv[]) {
    DEREF_PTR(int*, x)
    NO_DEREF_PTR(int*, y);

    DEREFERENCE(x);
    DEREFERENCE(y); // will throw an error
}

这样可以为您提供静态错误检查。当然,使用这种方法,你必须出去修改所有指针声明才能使用宏,这可能不是很有趣。

修改 如评论中所述。

#define READABLE_PTR(type, ptr) type ptr; \
typedef char ptr ## _READABLE_PTR;

#define NON_READABLE_PTR(type, ptr) type ptr; \

#define GET(ptr) \
*ptr; \
{ptr ## _READABLE_PTR \
attempt_to_dereference_non_readable_pointer_ ## ptr;}

#define SET(ptr, value) \
*ptr = value;


int main(int argc, char *argv[]) {
    READABLE_PTR(int*, x)
    NON_READABLE_PTR(int*, y);

    SET(x, 1);
    SET(y, 1);

    int foo = GET(x);
    int bar = GET(y); // error
}

答案 5 :(得分:0)

Dan Saks有一个Youtube演示文稿,我找不到,他在哪里介绍了嵌入式设备的只写寄存器。

幸运的是,他在https://www.embedded.com/how-to-enforce-write-only-access/

上写了一篇文章,该文章更易于搜索。

这是文章中的代码,已针对C ++ 11更新

class write_only_T{
public:
    write_only_T(){}
    write_only_T(T const& v) : m(v){}
    write_only_T(T&& v) : m(std::move(v)){}
    write_only_T& operator=(T const& v){
        m = v;
        return *this;
    }
    write_only_T& operator=(T&& v){
        m = std::move(v);
        return *this;
    }
    write_only_T(write_only_T const&) = delete;
    write_only_T(write_only_T&&) = delete;
    write_only_T& operator=(write_only_T const&) = delete;
    write_only_T& operator=(write_only_T&&) = delete;
private:
    T m;
};

如果您使用此指针,我认为您不需要特殊的指针类型,因为只写是值的属性,但是我可以想象一个跳过值类型的综合指针类型。 可能您需要引入只写引用类型,依此类推。