Why does GCC-generated code read garbage from the stack?

Consider the following code (uses Eigen ):

#include <Eigen/Dense> #include <iostream> template<int rows, int cols, int row, class R, class Rv, int N, class... Rs> inline typename std::enable_if<sizeof...(Rs)==0>::type setRow(Eigen::Matrix<R,rows,cols>&) {} template<int rows, int cols, int row, class R, class Rv, int N=0, class... Rs> inline typename std::enable_if<sizeof...(Rs)==cols-N-1>::type setRow(Eigen::Matrix<R,rows,cols>& m, Rv val, Rs...args) { m(row,N)=val; setRow<rows,cols,row,R,Rv,N+1>(m,args...); } template<class T, int R, int C, int CUR_ROW> class MatrixConstructor { Eigen::Matrix<T,R,C> m; public: MatrixConstructor(const Eigen::Matrix<T,R,C>& m) : m(m) {} MatrixConstructor() {} template<class...Ts> typename std::enable_if<sizeof...(Ts)==C && CUR_ROW<R-1, MatrixConstructor<T,R,C,CUR_ROW+1>>::type operator()(Ts... vals) { setRow<R,C,CUR_ROW>(m,vals...); return MatrixConstructor<T,R,C,CUR_ROW+1>(m); } template<class...Ts> typename std::enable_if<sizeof...(Ts)==C && CUR_ROW==R-1, Eigen::Matrix<T,R,C>>::type operator()(Ts... vals) { setRow<R,C,CUR_ROW>(m,vals...); return m; } }; void test() { Eigen::Matrix<double,4,3> m=MatrixConstructor<double,4,3,0>()(1,2,3) (4,5,6) (7,8,9) (5,4,3); std::cout << m; } int main() { test(); } 

I will compile it with gcc-4.8 with full optimization and the ability to generate a list of assemblies. Here is the command I'm using:

 g++ minitest.cpp -I/usr/include/eigen3 -std=c++0x -O3 -march=native -S -masm=intel 

(My processor is Intel (R) Xeon (R) CPU E3-1226 v3, running on a 64-bit Linux system - I hope now -march=native makes sense to readers.)

Interestingly, some of the instructions created using this command line seem pointless and even redundant. See how the test() function starts after the stack is configured (for the full code for test() and main() see here ):

 vmovsd xmm4, QWORD PTR .LC6[rip] # 1.0 lea rsi, [rsp+96] vmovsd xmm5, QWORD PTR .LC7[rip] # 2.0 vmovsd QWORD PTR [rsp], xmm4 vmovapd xmm3, XMMWORD PTR [rsp+16] # What does it read?! vmovapd xmm1, XMMWORD PTR [rsp] # And this! vmovsd QWORD PTR [rsp+32], xmm5 vmovsd xmm0, QWORD PTR .LC8[rip] # 3.0 vmovapd XMMWORD PTR [rsp+304], xmm3 # And now even save this junk?! vmovapd XMMWORD PTR [rsp+192], xmm1 vmovapd xmm3, XMMWORD PTR [rsp+48] vmovapd xmm1, XMMWORD PTR [rsp+32] vmovsd QWORD PTR [rsp+64], xmm0 vmovsd xmm7, QWORD PTR .LC12[rip] # 7.0 vmovapd XMMWORD PTR [rsp+336], xmm3 vmovapd XMMWORD PTR [rsp+224], xmm1 vmovapd xmm3, XMMWORD PTR [rsp+80] vmovsd QWORD PTR [rsp+304], xmm7 # Even stranger β€” overwrites the junk 

I went through these spam readings in the debugger and confirmed that after them the xmm3 and xmm1 registers really have meaningless values. Looking at this reading of undefined values, I begin to suspect that my program is really trying to access some memory that should be inaccessible. But why? Did I introduce somewhere UB?

I also tried to run a program compiled with -fsanitize=address , but it worked without crashing.

+6
source share
1 answer

Your code performs the following steps:

  • Creates an uninitialized MatrixConstructor object with an uninitialized member Eigen :: Matrix
  • Sets one row of a member of Eigen :: Matrix
  • Creates a new MatrixConstructor object, whose Eigen :: Matrix element is initialized by the old MatrixConstruction object. Eigen Element :: Matrix

So, in step 3, you copy the Eigen :: Matrix object, which has only the first set of rows. The remaining values ​​of the objects are still uninitialized. Since all these are temporary objects, they are all allocated on the stack, so it is not surprising that you see garbage being read from the stack.

Note that this assumes that the Eigen :: Matrix () constructor does not initialize an object, which from a quick look at the source does not seem to work by default.

+4
source

Source: https://habr.com/ru/post/989811/


All Articles