vtable 中未对齐的地址
Unaligned address in vtable
我不知道在这里问这样的问题是否合适,所以如果需要请重定向我。
我正在研究嵌入式 MCU (STM32L5),遇到一些 C++ 代码因硬故障而崩溃的问题。经过进一步调查,我确定原因是分支到未对齐(即没有字对齐)的内存地址。经过更多挖掘,我发现这是在虚拟方法的分支上发生的。而且,据我所知,vtable 本身中的地址看起来是“无效的”(即,不是字对齐的)。这里有一些图片来说明我所看到的。
单步执行这三行(0x080349ce 到 0x080349d2)时,寄存器值如下:
- R2 = 0x20005484 -> 指向我的抽象对象的指针地址
- R3 = 0x805a398 -> 指向 class 的 vtable 内的指针(参见“内存详细信息”中突出显示的条目)
- R3 = 0x8032f15 -> 从内存中的 vtable 读取的未对齐地址(参见“内存”中突出显示的条目)
我的问题是,我应该责怪编译器做了一些不应该做的事情,还是我的代码有什么问题会导致这种类型的问题?我正在使用 -Og 优化编译代码。
编辑
实际对象(我正在调用其虚拟方法)驻留在 lambda 中的堆栈中,如下所示:
[this, event, args...](){
Event<ARGS...> e(event, args...);
dispatch(&e);
}
其中 Event<>
是一个模板化的 class,它继承自我的抽象基础 class AbstractEvent
并实现了虚拟方法 clone()
.
dispatch() 函数接受一个 const AbstractEvent*
并在最终到达上面的汇编代码(即下面的 defer
方法)之前调用更多的方法。因此,在我尝试调用虚拟方法时,该对象应该仍在堆栈上。
bool defer(const AbstractEvent* e) {
if ((e == nullptr) || (_deferQueue.full())) {
return false;
} else {
AbstractEvent* clone = e->clone();
_deferQueue.push(clone);
return true;
}
作为参考,AbstractEvent和Event<>的实现如下:
class AbstractEvent {
public:
AbstractEvent(int index) : _index(index) { }
virtual ~AbstractEvent() = default;
int event() const { return _index; }
virtual AbstractEvent* clone() const = 0;
private:
int _index;
};
template <typename... ARGS>
class Event : public AbstractEvent {
public:
Event(int index, const ARGS&... args) :
AbstractEvent(index),
_values(args...)
{
}
Event(const Event& other) = default;
AbstractEvent* clone() const override { return new Event(*this); }
template <unsigned int INDEX = 0>
inline const auto& value() const {
return std::get<INDEX>(_values);
}
private:
std::tuple<ARGS...> _values;
};
感谢@11c 指出我们关于“thumb”的说明。事实证明,我的问题实际上并不在我认为的位置,因此不是编译器问题。我认为是未对齐的地址实际上是处理器的正常行为。
参考如下:
我不知道在这里问这样的问题是否合适,所以如果需要请重定向我。
我正在研究嵌入式 MCU (STM32L5),遇到一些 C++ 代码因硬故障而崩溃的问题。经过进一步调查,我确定原因是分支到未对齐(即没有字对齐)的内存地址。经过更多挖掘,我发现这是在虚拟方法的分支上发生的。而且,据我所知,vtable 本身中的地址看起来是“无效的”(即,不是字对齐的)。这里有一些图片来说明我所看到的。
单步执行这三行(0x080349ce 到 0x080349d2)时,寄存器值如下:
- R2 = 0x20005484 -> 指向我的抽象对象的指针地址
- R3 = 0x805a398 -> 指向 class 的 vtable 内的指针(参见“内存详细信息”中突出显示的条目)
- R3 = 0x8032f15 -> 从内存中的 vtable 读取的未对齐地址(参见“内存”中突出显示的条目)
我的问题是,我应该责怪编译器做了一些不应该做的事情,还是我的代码有什么问题会导致这种类型的问题?我正在使用 -Og 优化编译代码。
编辑
实际对象(我正在调用其虚拟方法)驻留在 lambda 中的堆栈中,如下所示:
[this, event, args...](){
Event<ARGS...> e(event, args...);
dispatch(&e);
}
其中 Event<>
是一个模板化的 class,它继承自我的抽象基础 class AbstractEvent
并实现了虚拟方法 clone()
.
dispatch() 函数接受一个 const AbstractEvent*
并在最终到达上面的汇编代码(即下面的 defer
方法)之前调用更多的方法。因此,在我尝试调用虚拟方法时,该对象应该仍在堆栈上。
bool defer(const AbstractEvent* e) {
if ((e == nullptr) || (_deferQueue.full())) {
return false;
} else {
AbstractEvent* clone = e->clone();
_deferQueue.push(clone);
return true;
}
作为参考,AbstractEvent和Event<>的实现如下:
class AbstractEvent {
public:
AbstractEvent(int index) : _index(index) { }
virtual ~AbstractEvent() = default;
int event() const { return _index; }
virtual AbstractEvent* clone() const = 0;
private:
int _index;
};
template <typename... ARGS>
class Event : public AbstractEvent {
public:
Event(int index, const ARGS&... args) :
AbstractEvent(index),
_values(args...)
{
}
Event(const Event& other) = default;
AbstractEvent* clone() const override { return new Event(*this); }
template <unsigned int INDEX = 0>
inline const auto& value() const {
return std::get<INDEX>(_values);
}
private:
std::tuple<ARGS...> _values;
};
感谢@11c 指出我们关于“thumb”的说明。事实证明,我的问题实际上并不在我认为的位置,因此不是编译器问题。我认为是未对齐的地址实际上是处理器的正常行为。
参考如下: