The following c++ code fails with misaligned assertion only when using valgrind: ``` #include "eigen-3.4.0/Eigen/Geometry" #include <iostream> int main(int argc, char** argv) { Eigen::Affine3d* test; test = new Eigen::Affine3d(); std::cout << test << std::endl; delete test; return 0; } ``` When compiled and run normally with `g++ main.cpp -m32 -std=c++17`, the code runs properly and prints the address of `test`. When run with valgrind, however, we get `a.out: eigen-3.4.0/Eigen/src/Core/DenseStorage.h:109: Eigen::internal::plain_array<T, Size, MatrixOrArrayOptions, 16>::plain_array() [with T = double; int Size = 16; int MatrixOrArrayOptions = 0]: Assertion `(internal::UIntPtr(eigen_unaligned_array_assert_workaround_gcc47(array)) & (15)) == 0 && "this assertion is explained here: " "http://eigen.tuxfamily.org/dox-devel/group__TopicUnalignedArrayAssert.html" " **** READ THIS WEB PAGE !!! ****"' failed.`. Note that this only occurs with the `m32` flag set. How to reproduce: * Download Eigen 3.4.0 (https://gitlab.com/libeigen/eigen/-/releases/3.4.0) and place in project root * Copy the c++ code to `main.cpp` * run `g++ main.cpp -m32 -std=c++17` * run `./a.out` and confirm it succeeds * run `valgrind ./a.out` and confirm it fails
Can you try with the latest Valgrind? There have been a lot of changes to aligned allocators and deallocators, mainly in Valgrind 3.22.
And I just tried with Valgrind 3.23 and git HEAD on FreeBSD 14.0 amd64 16.0.6 and it works OK.
^^^ clang++ 16.0.6
I have tried it with clang++ and it seems to work with valgrind 3.22, but g++ (13.2.0) still fails for me on valgrind 3.22
I also get the assert on Fedora 40. As a workaround you can use "--alignment=16". However, I still don't quite understand what is happening. The operator new that gets called is just plain bog standard operator new(size_t) - not the aligned overload. That means Eigen::Affine3d doesn't have any alignment specifier. Operator new just calls malloc. GNU libc has a complicated way of setting the base alignment if malloc. https://elixir.bootlin.com/glibc/glibc-2.39/source/sysdeps/generic/malloc-alignment.h #define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \ ? __alignof__ (long double) : 2 * SIZE_SZ) long double is peculiar on x86. It's 10 bytes, its sizeof is 12 but its alignof is 4. SIZE_SZ is sizof size_t, 4. 2*SIZE_SZ is not less than alignof long double (4) so MALLOC_ALIGNMENT is 2*SIZE_SZ, or 8. That is the same as what Valgrind uses. The assert fires here │ 101 template <typename T, int Size, int MatrixOrArrayOptions> │ 102 struct plain_array<T, Size, MatrixOrArrayOptions, 16> │ 103 { │ 104 EIGEN_ALIGN_TO_BOUNDARY(16) T array[Size]; │ 105 │ 106 EIGEN_DEVICE_FUNC │ 107 plain_array() │ 108 { │ > 109 EIGEN_MAKE_UNALIGNED_ARRAY_ASSERT(15); │ 110 check_static_allocation_size<T,Size>(); │ 111 } Array is using some kind of alignas specifier, asking for 16 byte alignment. Then the assert checks that it is 16 byte aligned. But array (in Valgrind) is 8 byte aligned: (gdb) p &array $8 = (double (*)[16]) 0x45df258 plain_array is wrapped in several classes. I thought that alignment propagated out from aggregate types. If I add this std::cout << "alignof Eigen::Affine3d " << alignof(Eigen::Affine3d) << '\n'; and it says alignof Eigen::Affine3d 16 which is what I expected. When I build with clang++ I see │ 121 operator new (std::size_t sz, std::align_val_t al) So that explains why clang++ is working. I need to look more at what libc is doing.
I think that I see the cause. https://elixir.bootlin.com/glibc/glibc-2.37/source/sysdeps/i386/malloc-alignment.h#L22 GNU libc has a special case for x86 bumping up the alignment to 16 bytes.
commit d66d3ab3017582ee41c459b812f695af405da44c (HEAD -> master, origin/master, origin/HEAD) Author: Paul Floyd <pjfloyd@wanadoo.fr> Date: Wed Jun 5 21:01:22 2024 +0200 Bug 487993 - Alignment error when using Eigen with Valgrind and -m32 Not tested on Solaris, but I think that x86 is more or less unused on that platform these days.