Expected behavior
make succeeds when the distribution's global CFLAGS target an x86-64 level
that includes AVX2 (-march=x86-64-v3, or -march=native on any AVX2 CPU).
These are the stock compiler flags on CachyOS, Clear Linux, ALHP and any
source-based or -march-tuned distribution, and they are passed to the build
through TC_EXTRA_CFLAGS.
Observed behavior
The build aborts while compiling the Argon2 SSE2 translation unit:
Compiling opt_sse2.c
../Crypto/Argon2/src/opt_sse2.c: In function 'fill_block':
../Crypto/Argon2/src/opt_sse2.c:61:9: error: implicit declaration of function 'BLAKE2_ROUND'; did you mean 'BLAKE2_ROUND_1'? [-Wimplicit-function-declaration]
61 | BLAKE2_ROUND(state[8 * i + 0], state[8 * i + 1], state[8 * i + 2],
| ^~~~~~~~~~~~
| BLAKE2_ROUND_1
make[1]: *** [.../Build/Include/Makefile.inc:20: ../Crypto/Argon2/src/opt_sse2.o] Error 1
This has been an error rather than a warning since GCC 14, which promoted
-Wimplicit-function-declaration to an error by default. On older compilers the
same code would have produced an unresolved symbol at link time, so the SSE2
path was never actually usable under these flags.
Root cause
Crypto/Argon2/src/blake2/blamka-round-opt.h selects its implementation from
the predefined ISA macros:
| condition |
what the header defines |
!__AVX512F__ && !__AVX2__ |
BLAKE2_ROUND on __m128i (line 170) |
__AVX2__ |
only BLAKE2_ROUND_1 / BLAKE2_ROUND_2 on __m256i (lines 303, 316) |
__AVX512F__ |
BLAKE2_ROUND, BLAKE2_ROUND_1, BLAKE2_ROUND_2 on __m512i |
opt_sse2.c is the 128-bit implementation and its fill_block() needs the
__m128i BLAKE2_ROUND. It is compiled by the generic %.o: %.c rule with the
unmodified global $(CFLAGS), so when those carry AVX2 the header hands it the
__m256i branch instead and the macro it needs is never defined.
opt_avx2.c does not have this problem: Volume/Volume.make:111 routes it to
OBJSAVX2 with the .oavx2 suffix, and the %.oavx2 rule at
Build/Include/Makefile.inc:43-45 appends -mavx2 explicitly, so that TU always
gets the ISA level it was written for regardless of the global flags. opt_sse2.c has no corresponding rule pinning
it to the SSE2 baseline; it is simply assumed that the global flags never
exceed SSE2.
Worth noting that this is specific to the VeraCrypt integration rather than a
defect in Argon2 itself. Upstream P-H-C/phc-winner-argon2 ships a single
opt.c and has no runtime dispatch, so there the header's macro-driven
selection is unambiguous: the one optimized TU is compiled at whatever ISA level
the build targets. Splitting it into opt_sse2.c + opt_avx2.c with the
dispatcher in ref.c created the requirement that each TU be compiled at a
fixed, different ISA level, and only one of the two got an explicit rule.
Steps to reproduce
A single compiler invocation is enough; no packaging or distro tooling involved.
From src/ of a clean 1.26.29 checkout:
# succeeds
gcc -march=x86-64 -DARGON2_NO_THREADS -I. -I./Crypto -I./Crypto/Argon2/include \
-c Crypto/Argon2/src/opt_sse2.c -o /tmp/ok.o
# fails with the error above
gcc -march=x86-64-v3 -DARGON2_NO_THREADS -I. -I./Crypto -I./Crypto/Argon2/include \
-c Crypto/Argon2/src/opt_sse2.c -o /tmp/fail.o
Equivalently, a full build: make TC_EXTRA_CFLAGS="-march=x86-64-v3".
Suggested fix
Give opt_sse2.c an explicit baseline the same way opt_avx2.c gets an
explicit -mavx2. The smallest form is a target-specific variable in
Volume/Volume.make, next to the existing entry on line 137:
OBJS += ../Crypto/Argon2/src/opt_sse2.o
+
+# Select the __m128i branch of blamka-round-opt.h regardless of the -march level
+# in the global CFLAGS. Appended after $(TC_EXTRA_CFLAGS), so it wins.
+../Crypto/Argon2/src/opt_sse2.o: CFLAGS += -mno-avx2 -mno-avx512f
This works because Makefile:588 places $(TC_EXTRA_CFLAGS) at the end of
CFLAGS, and the target-specific += appends after that, so the later flag
wins. A .osse2 suffix rule alongside the existing .oavx2 / .ossse3 /
.oaesni rules in Build/Include/Makefile.inc would be more consistent with
the surrounding style, at the cost of also threading a new OBJSSSE2 variable
through the clean target, the archive rule and the dependency include.
I have been running 1.26.29 built with the patch above on -march=native
(AVX2, no AVX512). veracrypt --text --test passes, and an Argon2 volume
created, dismounted, remounted and byte-compared round-trips correctly.
Your Environment
VeraCrypt version: 1.26.29 (also reproduces on current master; both files
are byte-identical to the tag)
Operating system and version: CachyOS (Arch-based), Linux 7.2.2
System type: 64-bit
Compiler: GCC 16.2.1
Relevant flags: CFLAGS="-march=native -O3 -flto=auto" from
/etc/makepkg.conf; the CPU has AVX2 but not AVX512.
Expected behavior
makesucceeds when the distribution's globalCFLAGStarget an x86-64 levelthat includes AVX2 (
-march=x86-64-v3, or-march=nativeon any AVX2 CPU).These are the stock compiler flags on CachyOS, Clear Linux, ALHP and any
source-based or
-march-tuned distribution, and they are passed to the buildthrough
TC_EXTRA_CFLAGS.Observed behavior
The build aborts while compiling the Argon2 SSE2 translation unit:
This has been an error rather than a warning since GCC 14, which promoted
-Wimplicit-function-declarationto an error by default. On older compilers thesame code would have produced an unresolved symbol at link time, so the SSE2
path was never actually usable under these flags.
Root cause
Crypto/Argon2/src/blake2/blamka-round-opt.hselects its implementation fromthe predefined ISA macros:
!__AVX512F__ && !__AVX2__BLAKE2_ROUNDon__m128i(line 170)__AVX2__BLAKE2_ROUND_1/BLAKE2_ROUND_2on__m256i(lines 303, 316)__AVX512F__BLAKE2_ROUND,BLAKE2_ROUND_1,BLAKE2_ROUND_2on__m512iopt_sse2.cis the 128-bit implementation and itsfill_block()needs the__m128iBLAKE2_ROUND. It is compiled by the generic%.o: %.crule with theunmodified global
$(CFLAGS), so when those carry AVX2 the header hands it the__m256ibranch instead and the macro it needs is never defined.opt_avx2.cdoes not have this problem:Volume/Volume.make:111routes it toOBJSAVX2with the.oavx2suffix, and the%.oavx2rule atBuild/Include/Makefile.inc:43-45appends-mavx2explicitly, so that TU alwaysgets the ISA level it was written for regardless of the global flags.
opt_sse2.chas no corresponding rule pinningit to the SSE2 baseline; it is simply assumed that the global flags never
exceed SSE2.
Worth noting that this is specific to the VeraCrypt integration rather than a
defect in Argon2 itself. Upstream
P-H-C/phc-winner-argon2ships a singleopt.cand has no runtime dispatch, so there the header's macro-drivenselection is unambiguous: the one optimized TU is compiled at whatever ISA level
the build targets. Splitting it into
opt_sse2.c+opt_avx2.cwith thedispatcher in
ref.ccreated the requirement that each TU be compiled at afixed, different ISA level, and only one of the two got an explicit rule.
Steps to reproduce
A single compiler invocation is enough; no packaging or distro tooling involved.
From
src/of a clean 1.26.29 checkout:Equivalently, a full build:
make TC_EXTRA_CFLAGS="-march=x86-64-v3".Suggested fix
Give
opt_sse2.can explicit baseline the same wayopt_avx2.cgets anexplicit
-mavx2. The smallest form is a target-specific variable inVolume/Volume.make, next to the existing entry on line 137:This works because
Makefile:588places$(TC_EXTRA_CFLAGS)at the end ofCFLAGS, and the target-specific+=appends after that, so the later flagwins. A
.osse2suffix rule alongside the existing.oavx2/.ossse3/.oaesnirules inBuild/Include/Makefile.incwould be more consistent withthe surrounding style, at the cost of also threading a new
OBJSSSE2variablethrough the
cleantarget, the archive rule and the dependency include.I have been running 1.26.29 built with the patch above on
-march=native(AVX2, no AVX512).
veracrypt --text --testpasses, and an Argon2 volumecreated, dismounted, remounted and byte-compared round-trips correctly.
Your Environment
VeraCrypt version: 1.26.29 (also reproduces on current
master; both filesare byte-identical to the tag)
Operating system and version: CachyOS (Arch-based), Linux 7.2.2
System type: 64-bit
Compiler: GCC 16.2.1
Relevant flags:
CFLAGS="-march=native -O3 -flto=auto"from/etc/makepkg.conf; the CPU has AVX2 but not AVX512.