C++ invalid symbol redefinition 错误
Moevis
最近在使用一份开源代码(https://github.com/exscape/AES/blob/master/aes.c)时,出现了这个错误:
error: invalid symbol redefinition
"movq %[keys], %%r15;" // keep the pointer for easy pointer arithmetic
^
<inline asm>:1:97: note: instantiated into assembly here
movq -368(%rbp), %r15;addq $160, %r15;movdqa -352(%rbp), %xmm0;pxor (%r15), %xmm0;mov $9, %ecx;__decrypt_roundloop:subq $16, %r15;aesdec (%r15), %xmm0;dec %ecx;cmp $1, %ecx;jge __d...
^
这是一份用 aes-ni 指令加速 aes 加密解密的代码,如果仅仅是用代码中的 c 版本是没问题的,一旦调用了带有 aes-ni 的方法就会出这个编译错误。
我们可以深入看细节,源代码是这样写的:
void aes_decrypt_aesni(const unsigned char *ciphertext, unsigned char *state, const unsigned char *keys) {
asm __volatile__ (
"movq %[keys], %%r15;" // keep the pointer for easy pointer arithmetic
"addq $160, %%r15;" // move the pointer to keys + 10*16
"movdqa %[plaintext], %%xmm0;" // load plaintext
"pxor (%%r15), %%xmm0;" // perform whitening
"mov $9, %%ecx;" // initialize round counter
"_decrypt_roundloop:"
"subq $16, %%r15;" // move the pointer to the "next" round key
"aesdec (%%r15), %%xmm0;" // perform AES round
"dec %%ecx;"
"cmp $1, %%ecx;"
"jge _decrypt_roundloop;" // for (i=9; i >= 1; i--)
"subq $16, %%r15;" // move the pointer one last time
"aesdeclast (%%r15), %%xmm0;" // perform the final AES round
"movdqa %%xmm0, %[state];" // move the state back to the memory address
:[state] "=m"(*state)
:[plaintext] "m"(*ciphertext), [keys] "m"(keys)
:"%xmm0", "memory", "%ecx", "cc", "%r15"
);
}
报错信息中,指明了重定义是 _decrypt_roundloop
标志多次定义,于是我在查找了搜索引擎后,在下面链接得到一些答案:https://stackoverflow.com/questions/14506151/invalid-symbol-redefinition-in-inline-asm-on-llvm
xxx:
这样的语法是用来定义 label 的,和 C/C++ 中的 label 一样,你可以用 goto label 来跳转到指定的代码逻辑。这个语法对于多层嵌套的 for 语句比较友好,因为 C/C++ 的 break 语法只能跳出当前循环,而如果你想 break 出多重循环的时候,用 goto 语法就比较方便。
在 aes.cpp 这个代码里,用了汇编来调用 aes-ni 指令,但是问题来了,由于我的代码中多次调用这个函数,导致程序多次 inline 了这段汇编代码,然后在汇编中出现了多次 _decrypt_roundloop
标签,导致重定义。这时候可以用的解决方案是用局部标签,用数字来代替字面量:
"1:"
"subq $16, %%r15;" // move the pointer to the "next" round key
"aesdec (%%r15), %%xmm0;" // perform AES round
"dec %%ecx;"
"cmp $1, %%ecx;"
"jge 1b;" // for (i=9; i >= 1; i--)
_decrypt_roundloop
被我替换成了 1:
,在下面 jge
中我替换成了 jge 1b:
,其中 1b
表示向前找 1 这个label,如果想要向后找,可以用 1f
。