def*_*nst 5 c assembly gcc inline-assembly
我知道使用内联汇编解决以下问题是个坏主意.我目前正在学习内联汇编作为linux内核类的一部分,这是该类的一个赋值的一部分.
下面的开头是一段几乎正确的代码片段,而不是段错误.它是一个函数,它将src从索引处开始并在索引处s_idx结束(排他地)的子字符串复制e_idx到dest仅使用内联汇编的预分配.
static inline char *asm_sub_str(char *dest, char *src, int s_idx, int e_idx) {
asm("addq %q2, %%rsi;" /* Add start index to src (ptrs are 64-bit) */
"subl %k2, %%ecx;" /* Get length of substr as e - s (int is 32-bit) */
"cld;" /* Clear direction bit (force increment) */
"rep movsb;" /* Move %ecx bytes of str at %esi into str at %edi */
: /* No Ouputs */
: "S" (src), "D" (dest), "g" (s_idx), "c" (e_idx)
: "cc", "memory"
);
return dest;
}
Run Code Online (Sandbox Code Playgroud)
此代码的问题是第二个输入参数的约束.使用gccs默认优化编译时-ggdb,会生成以下程序集:
Dump of assembler code for function asm_sub_str:
0x00000000004008e6 <+0>: push %rbp
0x00000000004008e7 <+1>: mov %rsp,%rbp
0x00000000004008ea <+4>: mov %rdi,-0x8(%rbp)
0x00000000004008ee <+8>: mov %rsi,-0x10(%rbp)
0x00000000004008f2 <+12>: mov %edx,-0x14(%rbp)
0x00000000004008f5 <+15>: mov %ecx,-0x18(%rbp)
0x00000000004008f8 <+18>: mov -0x10(%rbp),%rax
0x00000000004008fc <+22>: mov -0x8(%rbp),%rdx
0x0000000000400900 <+26>: mov -0x18(%rbp),%ecx
0x0000000000400903 <+29>: mov %rax,%rsi
0x0000000000400906 <+32>: mov %rdx,%rdi
0x0000000000400909 <+35>: add -0x14(%rbp),%rsi
0x000000000040090d <+39>: sub -0x14(%rbp),%ecx
0x0000000000400910 <+42>: cld
0x0000000000400911 <+43>: rep movsb %ds:(%rsi),%es:(%rdi)
0x0000000000400913 <+45>: mov -0x8(%rbp),%rax
0x0000000000400917 <+49>: pop %rbp
0x0000000000400918 <+50>: retq
Run Code Online (Sandbox Code Playgroud)
这与将第二个输入参数的约束设置为"m"而不是使用时生成的程序集相同,这使"g"我相信编译器正在有效地选择"m"约束.在使用gdb单步执行这些指令时,我发现有问题的指令+35会将起始偏移索引添加s_idx到src指针中%rsi.问题当然是s_idx只有32位,而静态上该位置的64位整数的高4字节不一定是0.在我的机器上,它实际上是非零的并且导致添加混淆上层其中4个字节%rsi导致指令中的段错误+43.
当然,解决上述是改变参数的约束2来"r"所以它被放置在其自己的64位寄存器,其中前4个字节被正确地归零,收工.相反,我的问题是为什么gcc解析"g"约束"m"而不是"r"在这种情况下表达式"%q2"指示参数的值2将被用作64位值?
我不太了解gcc如何解析内联汇编,而且我知道在汇编中没有真正的输入感,但我认为gcc可以识别当它被用作64位值时的有效隐式s_idx转换long在第一个内联指令中.FWIW,如果我明确更改"g" (s_idx)为"g" ((long) s_idx),则gcc会将"g"约束解析为,"r"因为(long) s_idx它是临时值.我认为gcc也可以隐含地做到这一点?