- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
更新:我创建了一个更多的 M,但仍然是重现崩溃的 CVE。摘要:删除了 Base
类中对 Bool* bools_
字段的所有使用(但它仍然必须定义,否则不会发生崩溃)。还从 Base
及其后代中删除了 Base::Initialize()
和虚方法 Rule
。附上新的 MCVE。
我已经设法为此代码创建了一个 MCVE 并将其发布在下方。
一些描述性细节:代码使用虚基类和派生类,某些实例化的派生类具有调用从“基”类继承的非虚方法的构造函数(实际上是派生类,但在继承层次结构比我所说的“派生”类)来初始化“基”类数据。该方法调用在派生类中重写的虚方法。我意识到这是一件危险的事情,但根据我对 C++ 的(可能有限的)理解,它似乎应该起作用,因为派生类构造函数的主体在设置“基”类虚拟表之前不会执行.在任何情况下,段错误都不会在调用“基”类的初始化方法期间发生。
段错误发生在“基”类构造函数中,并且仅当构造函数的主体为空时。如果我向构造函数添加调试行以在到达该点时打印出来,则调试行被打印出来并且代码正常运行。我的猜测是,出于某种原因,编译器正在优化应该在执行“基”类的构造函数主体之前发生的初始化,包括 vtable 的设置。
如主题行所述,此代码在使用 Apple 的 g++ 或 g++ 7.2.0 进行编译且未进行优化时运行良好,并且在使用 g++ 7.2.0 甚至 -O3 进行编译时也运行良好。它仅在使用 Apple 的 g++ LLVM 实现编译 -O2
或 -O3
时出现段错误。该编译器的 g++ --version
的输出是:
% /usr/bin/g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin17.3.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Content
紧随其后的是 MCVE。
#include <iostream>
using namespace std;
class OriginalBaseClass {
public:
OriginalBaseClass(long double data1 = 1, long int data2 = 1) : data1_(data1), data2_(data2) { cout << "In OriginalBaseClass constructor\n"; }
private:
long double data1_;
long int data2_;
};
class Base : public virtual OriginalBaseClass {
public:
Base(long int data1 = 0, long int data2 = 0) : data1_(data1), data2_(data2) { cout << "In Base constructor\n"; }
virtual ~Base();
private:
bool* bools_;
long int data1_;
long int data2_;
};
Base::~Base()
{
cout << "In Base destructor\n";
}
class Derived_A : public virtual Base {
public:
Derived_A() { cout << "In Derived_A constructor\n"; }
};
class Derived_B : public Derived_A {
public:
Derived_B() : OriginalBaseClass(), Base(4, 1), Derived_A() { cout << "In Derived_B constructor\n"; }
};
int main()
{
Derived_B Derb;
}
错误报告链接:https://bugreport.apple.com/web/
引用编号 36382481
最佳答案
这看起来像是 Clang 中的错误,由无效生成未对齐的 SSE 存储引起。以下是基于您的代码的最小示例:
struct alignas(16) Base1 { };
struct Base2 : virtual Base1 {
__attribute__((noinline)) Base2() : data1_(0), data2_(0) { }
long dummy_, data1_, data2_;
};
struct Base3 : virtual Base2 { };
int main() { Base3 obj; }
这是由 Clang 生成的布局(GCC 使用相同的布局):
*** Dumping AST Record Layout
0 | struct Base1 (empty)
| [sizeof=16, dsize=16, align=16,
| nvsize=16, nvalign=16]
*** Dumping AST Record Layout
0 | struct Base2
0 | (Base2 vtable pointer)
8 | long dummy_
16 | long data1_
24 | long data2_
0 | struct Base1 (virtual base) (empty)
| [sizeof=32, dsize=32, align=16,
| nvsize=32, nvalign=8]
*** Dumping AST Record Layout
0 | struct Base3
0 | (Base3 vtable pointer)
0 | struct Base1 (virtual base) (empty)
8 | struct Base2 (virtual base)
8 | (Base2 vtable pointer)
16 | long dummy_
24 | long data1_
32 | long data2_
| [sizeof=48, dsize=40, align=16,
| nvsize=8, nvalign=8]
我们可以看到Base3
和Base1
合并了,所以他们共享地址。 Base2
由 Base3
实例化,之后以 8 字节 偏移放置,将 Base2
实例对齐在 8字节数,即使 alignof(Base2)
为 16。这仍然是正确的行为,因为这是 Base2 中所有成员字段之间的最大对齐方式
。继承自虚基类Base1
的对齐不需要保留,因为Base1
由派生类Base3
实例化,负责对齐 >Base1
正确。
问题出在 Clang 生成的代码上:
mov rbx,rdi ; rdi contains this pointer
...
xorps xmm0,xmm0
movaps XMMWORD PTR [rbx+0x10],xmm0
Clang 决定使用一条需要 16 字节对齐的 movaps
指令同时初始化 data1_
和 data2_
,但是 Base2
实例仅 8 字节对齐,导致段错误。
看起来 Clang 假设它可以使用 16 字节对齐存储,因为 alignof(Base2)
是 16,但对于具有虚拟基类的类,这种假设是错误的。
如果您需要临时解决方案,您可以使用 -mno-sse
标志禁用 SSE 指令。请注意,这可能会影响性能。
Itanium ABI 文档可以在这里找到:https://refspecs.linuxfoundation.org/cxxabi-1.75.html
它明确提到nvalign:
nvalign(O): the non-virtual alignment of an object, which is the alignment of O without virtual bases.
然后是关于如何分配的解释:
Allocation of Members Other Than Virtual Bases
If D is not an empty base class or D is a data member: Start at offset dsize(C), incremented if necessary for alignment to nvalign(D) for base classes or to align(D) for data members. Place D at this offset unless doing so would result in two components (direct or indirect) of the same type having the same offset. If such a component type conflict occurs, increment the candidate offset by nvalign(D) for base classes or by align(D) for data members and try again, repeating until success occurs (which will occur no later than sizeof(C) rounded up to the required alignment).
看起来 Clang 和 GCC 都支持 Itanium ABI,使用非虚拟对齐正确对齐 Base2
。我们还可以在上面的记录布局转储中看到这一点。
您可以使用 -fsanitize=undefined
(GCC 和 Clang)编译您的程序,以在运行时获取此误报警告消息:
main.cpp:29:5: runtime error: constructor call on misaligned address 0x7ffd3b895dd8 for type 'Base2', which requires 16 byte alignment
0x7ffd3b895dd8: note: pointer points here
e9 55 00 00 ea c6 2e 02 9b 7f 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 f8 97 95 34
因此目前存在三个错误。我已经报告了所有这些:
关于使用 Apple 的 LLVM 编译器编译 -O 时出现 C++ 代码段错误,但使用 g++ -7.2.0 时则不会,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48158288/
我目前正在尝试让 g++ 工作,并查看 http://gcc.gnu.org/install/build.html ,我似乎找不到它在哪里说如何“执行编译器的 3 阶段 bootstrap ”。我在哪
James Powell 在他对即将举行的演示文稿的简短描述中说,他自豪地发明了最粗糙的 Python 单行代码之一: (None for g in g if (yield from g) and F
请告诉我我的证明是否正确 We have a connected graph, and specific vertex u in V(G). Suppose we compute the dfs tr
下面的test2和test3结果是不同的。 我对此感到困惑,因为它看起来像相同的逻辑,并且与linux bash ||逻辑不同。 $data = @( [PSCustomObject]@{St
我试图找到一个明确的 G 代码语法规范,而不是单个 G 代码的含义,我无处不在的规范,我的意思是详细的语法规范,目的是编写解析器。 我编写解析器没有问题,我只是在寻找语法规范,例如。我知道您不必总是为
我写了这个 mixin,但它循环了很多时间。你能帮我优化我的代码吗?或者你能建议一些其他的东西来获得想要的结果吗? dfgdfgsdfgsdf 最佳答案 希望这就是您要找的。 $spaces: (4,
默认情况下,g++ 似乎会省略未使用的类内定义方法的代码。示例 from my previous question : struct Foo { void bar() {} void baz(
是否可以将文件内容通过管道传送到 g++编译程序? 我想这样做是因为我想使用数据库中的文件而不是磁盘上的物理文件。可以通过我制作的 API 轻松检索文件内容。 例如,我想做这样的事情: g++ con
如何profile c++代码获取每行代码的调用次数和消耗时间,就像profile工具一样在 Matlab 中呢? 我尝试使用-fprofile-arcs之类的东西,但它只生成代码覆盖率报告,其中可以
如何在几行代码上禁用所有警告。可以使用 GCC 诊断功能禁用特定警告,但是否有针对所有警告的标志。我尝试了这个方法,但不起作用 #pragma GCC diagnostic push #pragma
我有一个链接到 opencv 2.2 的可执行文件。但是,我删除了 opencv 2.2 并安装了 opencv 2.3。 问题是,有没有办法在不重新编译整个源代码的情况下将这个可执行文件链接到新的共
在编译带有一些标志的以下文件时,是否可以让 g++ 显示错误? #include using namespace std; int main() { int arr[ 2 ]; cout
在学习 Haskell 时,我遇到了一个挑战,要找到两个函数 f 和 g,例如 f g 和 f 。 g 是等价的(并且是总计,因此像 f = undefined 或 f = (.) f 这样的东西不算
根据我的理解,Theta 位于 Big O 和 Omega 之间,但我看到了这个声明,但我无法理解为什么交集会出现在这里。我能否对 Θ(g(n)) = O(g(n)) ∩ Ω(g(n)) 获得数学和分
我需要为这个递归函数编写一个迭代函数。 int funcRec(int n){ if(n>1) { return 2*funcRec(n - 1) + 3*funcRec(n
我在 github repository 上有代码示例并在 travis-ci 上创建了一个构建便于复制。 最小的、完整的和可验证的例子 可能不是最小的,但我相信它足够小 它使用 boost.inte
编辑:我们将调用箭头 p纯如果存在这样的函数f即:p = arr f . 我试图更好地掌握 Haskell 中的 Arrows,我想弄清楚什么时候 f >>> (g &&& h) = (f >>> g
我有两个(或更多)函数定义为: val functionM: String => Option[Int] = s => Some(s.length) val functionM2: Int => Op
好像是的。任何直观或严肃的证据都值得赞赏。 最佳答案 没有。 我认为您的问题等同于:给定函数 f 和 g,f 是 O(g) 或 g 是 O(f) 是否总是正确的?这在 SE Computer Scie
如果我设法证明 f(n) = o(g(n))(小 o),那么这两个函数的总和 f( n) + g(n) 应该被“更大”的函数 g(n) 紧紧束缚。 然而,我在证明这一点时遇到了一些麻烦。 最佳答案 以
我是一名优秀的程序员,十分优秀!