通过预定义的静态地址访问寄存器是C ++中未定义的行为吗? [英] Is accessing registers through predefined static addresses undefined behaviour in C++?

查看:107
本文介绍了通过预定义的静态地址访问寄存器是C ++中未定义的行为吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编译要在独立环境中运行的C ++程序,我正在运行的CPU定义了一个32位外设寄存器,该寄存器可以在PERIPH_ADDRESS(正确对齐,并且不与任何其他C ++对象,堆栈等重叠).

I'm compiling a C++ program to run in a freestanding environment and the CPU I'm running on defines a 32-bit peripheral register to be available (edit: memory-mapped) at PERIPH_ADDRESS (aligned correctly, and not overlapping with any other C++ object, stack etc.).

我使用预定义的PERIPH_ADDRESS编译以下代码,然后将其与完整程序链接并运行.

I compile the following code with PERIPH_ADDRESS predefined, later link it with a full program and run it.

#include <cstdint>

struct Peripheral {
    const volatile uint32_t REG;
};

static Peripheral* const p = reinterpret_cast<Peripheral*>(PERIPH_ADDRESS);

uint32_t get_value_1() {
    return p->REG;
}

static Peripheral& q = *reinterpret_cast<Peripheral*>(PERIPH_ADDRESS);

uint32_t get_value_2() {
    return q.REG;
}

extern Peripheral r;
// the address of r is set in the linking step to PERIPH_ADDRESS

uint32_t get_value_3() {
    return r.REG;
}

任何get_value函数(直接或通过p/q)是否具有未定义的行为?如果是,我可以解决这个问题吗?

Does any of the get_value functions (either directly or through p/q) have undefined behavior? If yes, can I fix it?

我认为一个等效的问题是:任何符合条件的编译器都可以拒绝为我编译期望的程序吗?例如,一个打开了UB sanitezer的计算机.

I think an equivalent question would be: Can any conforming compiler refuse to compile the expected program for me? For example, one with UB sanitezer turned on.

我看过[ basic.stc.dynamic.safety ]和[ basic.compound#def:object_pointer_type ]但这似乎仅限制了指向动态对象的指针的有效性.我认为它不适用于此代码,因为从未假定PERIPH_ADDRESS处的对象"是动态的.我想可以肯定地说,以p表示的存储永远都不会达到其存储持续时间的尽头,可以将其视为 static .

I have looked at [basic.stc.dynamic.safety] and [basic.compound#def:object_pointer_type] but that seems to only restrict the validity of pointers to dynamic objects. I don't think it applies to this code, because the "object" at PERIPH_ADDRESS is never assumed to be dynamic. I think I can safely say that the storage denoted by p never reaches the end of its storage duration, it can be considered static.

我还查看了

I've also looked at Why does C++ disallow the creation of valid pointers from a valid address and type? and the answers given to that question. They also only refer to dynamic objects' addresses and their validity, so they do not answer my question.

我已经考虑过但无法回答自己的其他问题,可能会帮助您解决主要问题:

Other questions I've considered but couldn't answer myself that might help with the main question:

  • 我是否遇到过任何UB问题,因为该对象从未在C ++抽象机中构造过?
  • 或者我真的可以认为该对象是静态存储持续时间正确的构造"对象吗?

很明显,我更喜欢引用任何最新C ++标准的答案.

Obviously, I'd prefer answers that reference any recent C++ standard.

推荐答案

本文总结了@curiousguy @Passer By,@ Pete Backer和其他人最初发布的非常有用的答案.这主要基于标准文本(因此为language-lawyer标签),并带有其他答案提供的参考.我之所以将其作为社区Wiki,是因为答案并没有完全令人满意,但是很多人都有很好的观点.随时进行编辑.

This is summarizing the very helpful answers posted originally by @curiousguy @Passer By, @Pete Backer and others. This is mostly based on the standard text (hence the language-lawyer tag) with references provided by other answers. I made this a community wiki because none of the answers were completely satisfying but many had good points. Feel free to edit.

在最佳情况下,代码是由实现定义的,但是它可能具有未定义的行为.

实现定义的部分:

    从整数类型到指针类型的
  1. reinterpret_cast是实现定义的. [ expr.reinterpret.cast/5]

  1. reinterpret_cast from integer type to pointer type is implementation-defined. [expr.reinterpret.cast/5]

可以将整数类型或枚举类型的值显式转换为指针. 指针将转换为足够大的整数(如果实现中存在此类整数)并返回相同的指针类型,则指针将具有其原始值; 指针与整数之间的映射否则由实现定义. [注意:除非[basic.stc.dynamic.safety]中所述,否则此类转换的结果将不是安全得出的指针值. —注释]

A value of integral type or enumeration type can be explicitly converted to a pointer. A pointer converted to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type will have its original value; mappings between pointers and integers are otherwise implementation-defined. [ Note: Except as described in [basic.stc.dynamic.safety], the result of such a conversion will not be a safely-derived pointer value. — end note ]

  • 对易失性对象的访问由实现定义. [ dcl.type.cv/5]

    通过易失性glvalue进行访问的语义是实现定义的.如果尝试通过使用非易失性glvalue来访问用volatile限定类型定义的对象,行为是不确定的.

    The semantics of an access through a volatile glvalue are implementation-defined. If an attempt is made to access an object defined with a volatile-qualified type through the use of a non-volatile glvalue, the behavior is undefined.

  • 必须避免UB的部分:

    1. 指针必须指向C ++中的有效对象 抽象机器 ,否则程序具有UB.

    1. The pointers must point to a valid object in the C++ abstract machine, otherwise the program has UB.

    据我所知,如果抽象机的实现是由运行在具有所描述的内存映射寄存器的环境中的健全的,合格的编译器和链接器生成的程序,则 implementation 可以说在该位置具有C ++ uint32_t对象,并且没有UB具有任何功能. [ intro.compliance/8] 似乎允许这样做:

    As far as I can tell, if the implementation of the abstract machine is a program produced by a sane, conformant compiler and linker running in an environment that has the register memory-mapped as described, then the implementation can be said to have a C++ uint32_t object at that location, and there is no UB with any of the functions. This seems to be allowed by [intro.compliance/8]:

    一个符合标准的实现可以具有扩展(包括附加的库函数),前提是它们不会改变任何格式正确的程序的行为. [...]

    A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. [...]

    这仍然需要对[简介的自由解释. .object/1] ,因为该对象不是通过以下任何一种方式创建的:

    This still requires liberal interpretation of [intro.object/1], because the object is not created in any of the listed ways:

    在隐式更改联合的活动成员([class.union])或创建临时对象([ conv.rval],[class.temporary]).

    An object is created by a definition ([basic.def]), by a new-expression, when implicitly changing the active member of a union ([class.union]), or when a temporary object is created ([conv.rval], [class.temporary]).

    如果抽象机的实现中有一个带有消毒剂的编译器(-fsanitize=undefined-fsanitize=address),则可能不得不向编译器添加额外的信息,以说服它存在 该位置的有效对象.

    If the implementation of the abstract machine has a compiler with a sanitizer (-fsanitize=undefined, -fsanitize=address), then one might have to add extra information to the compiler to convince it that there is a valid object at that location.

    当然,ABI必须正确,但这是问题所在(正确的对齐方式和内存映射).

    Of course the ABI has to be correct, but that was implied in the question (correct alignment and memory-mapping).

    由实现定义,实现是具有 strict 还是 relaxed 指针安全性[

    It is implementation-defined whether an implementation has strict or relaxed pointer safety [basic.stc.dynamic.safety/4]. With strict pointer safety, objects with dynamic storage duration can only be accessed through a safely-derived pointer [basic.stc.dynamic.safety]. The p and &q values are not that, but the objects they refer to do not have dynamic storage duration, so this clause does not apply.

    实现可能具有宽松的指针安全性,在这种情况下,指针值的有效性不取决于它是否是安全得出的指针值. 或者,实现可能具有严格的指针安全性,在这种情况下,指向具有动态存储持续时间的对象的指针值(不是安全得出的指针值)是无效的指针值 [...] . [注意:使用无效的指针值(包括将其传递给释放函数)的效果是不确定的,请参阅[basic.stc].

    An implementation may have relaxed pointer safety, in which case the validity of a pointer value does not depend on whether it is a safely-derived pointer value. Alternatively, an implementation may have strict pointer safety, in which case a pointer value referring to an object with dynamic storage duration that is not a safely-derived pointer value is an invalid pointer value [...]. [ Note: The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined, see [basic.stc].

    实际结论似乎是需要实施定义的支持来避免UB.对于精明的编译器,生成的程序不包含UB,或者它可能具有非常依赖的UB(取决于您的外观).但是,除非明确告知消毒剂预期位置中存在正确的对象,否则消毒剂可以合理地抱怨该代码.指针的派生不应该是一个实际的问题.

    The practical conclusion seems to be that implementation-defined support is needed to avoid UB. For sane compilers, the resulting program is UB-free or it might have UB that can be very well relied on (depending on how you look at it). Sanitizers however can justifiably complain about the code unless they are explicitly told that the correct object exists in the expected location. The derivation of the pointer should not be a practical problem.

    这篇关于通过预定义的静态地址访问寄存器是C ++中未定义的行为吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆