У меня действительно странный сбой в моем коде (с g ++ версии 7.3):
При компиляции с -o0 все работает нормально. Но при компиляции с -O1 / O2 / O3 я получаю ошибку segfault. Работая в GDB, похоже, что сразу после возврата из функции по какой-то причине происходит вызов _unwind_resume, что приводит к ошибкам. Я не видел ни одного вызова _unwind_resume в том же месте в моем коде с кодом, скомпилированным с -O0.
Но есть еще:
Я попытался запустить его с помощью valgrind, чтобы узнать, получу ли я какую-либо полезную информацию. Он сообщает о «нераспознанной инструкции по адресу», но никаких проблем с памятью.
Я пытался использовать разные флаги оптимизации по отдельности, чтобы увидеть, есть ли конкретная оптимизация, которая вызывает это. Я использовал все оптимизации, перечисленные g ++ -Q -O1 -help = optimizer. Без падений. Я попытался использовать все соответствующие флаги -f вместе. По-прежнему без сбоев.
Я попытался скомпилировать clang ++ вместо g ++ с любым уровнем оптимизации. Опять же, работает нормально, без сбоев.
Я полагаю, что стек вызовов каким-то образом поврежден оптимизированным кодом в g ++, но я понятия не имею, почему или что с этим делать. Есть идеи как подойти?
Примечание: к сожалению, я не могу поделиться кодом, и он слишком большой для того, чтобы делиться им здесь в любом случае.
Редактировать: мне удалось уменьшить код, который вызывает ошибку. Вот репродуктор:
#include <iostream>
#include <vector>
using namespace std;
class Expr;
typedef std::vector<Expr*> ExprVec;
enum class Ops {
Search
};
enum class ExprKind {
Const,
Function,
Unknown
};
namespace Values {
class Value
{
public:
virtual ~Value() {};
};
class String : public Value
{
public:
String(const string& v): _val(v) {}
String(const char* s): _val(s) {}
~String() {
}
static const string name;
private:
string _val;
};
const string String::name = "String";
} // Values
class Expr
{
public:
Expr() {}
Expr(const ExprVec& args)
: _args(args) {}
virtual ~Expr() {
for (auto arg: _args) {
delete arg;
}
};
virtual Values::Value* run() = 0;
protected:
ExprVec _args;
};
template<class ValType>
class ConstExpr: public Expr
{
public:
ConstExpr() {}
ConstExpr(ValType* val)
: _val(val) {}
~ConstExpr() {
delete _val;
}
virtual Values::Value* run() {
return _val;
}
private:
ValType* _val;
};
typedef ConstExpr<Values::String> StringExpr;
// This is a template for all expression nodes. Instantiate for each expression
template <ExprKind Kind, Ops Name, class RetVal, class Arg1>
class ExprT: public Expr
{
public:
ExprT() {}
ExprT(const ExprVec& args)
: Expr(args) {}
virtual Values::Value* run() {
Arg1* arg1= dynamic_cast<Arg1*>(_args[0]->run());
return run(arg1);
}
RetVal* run(Arg1* arg1);
};
typedef ExprT<ExprKind::Function, Ops::Search, Values::String, Values::String> Search;
template<>
Values::String* Search::run(Values::String* arg1)
{
}
int main()
{
Values::String* str = new Values::String("blahbla");
Expr* se = new StringExpr(str);
ExprVec args({se});
Expr* search = new Search(args);
search->run();
cout<<"Done\n";
return 0;
}
Edit2: Я только что понял, что это не настоящий репродуктор. Search :: run здесь не возвращает значения, а в исходном коде возвращает значение. Я постараюсь создать лучший репродуктор.
Обновление:
Я наконец-то понял это ... приведенный выше пример не сможет воспроизвести конкретную ошибку, потому что для этого требуются как минимум два исходных файла и один заголовочный файл. Проблема заключалась в том, что у меня была некоторая функция с неспециализированными определениями шаблонов в заголовочном файле и полностью специализированными экземплярами этих функций в одном из исходных файлов.
Я не объявлял специализации во включаемом файле, который был источником проблемы ... Я понял это только когда попытался скомпилировать его в Windows (вместо Linux), и получил ошибки компоновки из-за нескольких экземпляры тех же функций.