在 gcc 中访问内联汇编中的字符串的地址
Posted
技术标签:
【中文标题】在 gcc 中访问内联汇编中的字符串的地址【英文标题】:Accessing address of a string in inline assembly in gcc 【发布时间】:2014-11-05 16:07:05 【问题描述】:我已经编写了下面的汇编代码来将字符串从小写转换为大写,它不能完全工作,因为我无法访问我正在转换的字符串的地址。这段代码为什么不起作用?
#include<stdio.h>
int convert(char *str)
char *ptr;
__asm__ __volatile__ ( "movl (%1),%%ebx;"
"subl $1,%%ebx;"
"movl %%ebx,%0;"
"REPEAT: addl $1,%%ebx;"
"testl %%ebx,%%ebx;"
"je END;"
"movzbl 0(%%ebx),%%ecx;"
"cmpl $97, %%ecx;"
"jb END;"
"cmpl $122,%%ecx;"
"ja END;"
"subb $32,0(%%ebx);"
"jmp REPEAT;"
"END: movl %%ebx,(%0);"
:"=r" (ptr)
:"r" (str)
);
printf("converted string =%s\n", str);
int main()
int i;
char str[] = "convert";
i = convert(str);
return 0;
【问题讨论】:
您的问题是什么?请提出问题。 @FUZxxi:我无法访问字符串的地址,上面的代码不起作用! 您在什么时候分配给ptr
或str
?
str 是我的输入,所以是 %1,我希望 str 的地址在 ebx 中,最终输出在 ptr 中,基本上 ptr 只是为了输出目的。我想在 str 本身中将字符更改为大写。
那是因为你在哪里编译 amd64,而不是 i386。在 amd64 上,指针的大小为 64 位。当然,movl %rdi,%ebx
不是有效指令。您可能希望使用 cc -m32
编译 i386。
【参考方案1】:
接受的答案中的代码似乎有一些问题:
正如所写,此代码无法编译(当只有 1 个参数时它引用 %1),并且在第 4 个 asm 行缺少终止符。 此代码无法正确处理“aBc”等字符串。 此代码不使用“内存”破坏器,即使它修改了内存。 此代码(仍然)修改未破坏的寄存器 (ebx)。 不适用于 x64。不如这样:
char *convert(char *str)
char *res = str;
char temp;
__asm__ __volatile__ (
"dec %[str]\n"
"REPEAT:\n\t"
"inc %[str]\n\t"
"movb (%[str]), %[temp]\n\t" /* Read the next char */
"testb %[temp], %[temp]\n\t"
"jz END\n\t" /* Is the char null */
"cmpb $97, %[temp]\n\t" /* >= 'a'? */
"jb REPEAT\n\t"
"cmpb $122, %[temp]\n\t" /* <= 'z'? */
"ja REPEAT\n\t"
"subb $32, %[temp]\n\t" /* Perform lowercase */
"mov %[temp], (%[str])\n\t" /* Write back to memory */
"jmp REPEAT\n"
"END:\n"
: [str] "+r" (str), [temp] "=r" (temp)
: /* no inputs */
: "memory"
);
/* Note that at this point, str points to the null.
str - res is the length. */
return res;
这段代码:
使用更少的寄存器(2 对 4)。 通过使用“=r”(temp),我们让编译器选择最佳寄存器用于暂存,而不是强制使用特定寄存器。 只读取内存一次(而不是两次)。 返回一个指向字符串的指针(而不是什么都不返回?)。 IMO,使用 %[temp] 和 %[src] 比 %1 更容易阅读。 使用\n\t
(而不是;
)使gcc -S
的输出更易于阅读。
此代码修改了 str(这就是它被列为“+r”的原因)。
或者如果你真的想花哨,把代码写在'c'中,然后使用gcc -O2 -S
查看输出。
【讨论】:
【参考方案2】:这是我的解决方案与上面的略有不同,感谢 FUZxxi 指出。我应该说检索程序集在很大程度上有帮助,它可能很难理解,但它给你带来了实际问题。如果有人想了解我想要实现的目标,我已经写了足够多的 cmets。
/* code to convert from lower case to upper case */
int convert(char *str)
__asm__ __volatile__ ( "movl %1,%%ebx;" // Get the address of str
"subl $1,%%ebx;"
"REPEAT: addl $1,%%ebx;"
"movl 0(%%ebx),%%edx" // Move the contents to edx
"movzbl %%dl,%%ecx;" // moving last character to ecx
"testl %%ecx,%%ecx;" // compare if it's null
"je END;"
"cmpl $97, %%ecx;"
"jb END;"
"cmpl $122,%%ecx;"
"ja END;"
"subb $32,(%%ebx);" // if its lower case, subtract 32
"jmp REPEAT;"
"END:;"
: // No output specified
:"r" (str) //input
:"ecx","edx" //clobbers
);
printf("converted string =%s\n", str);
如果您使用“gcc -m32”选项编译,如果您正在为 amd64 编译,上述代码应该可以工作。我遇到了一个问题“致命错误:sys/cdefs.h:没有这样的文件或目录”
解决方法:安装这个包:libc6-dev-i386
【讨论】:
这个解决方案不是一个高效的解决方案,如果您想快速完成(主要是我们想要的),请使用矢量指令。以上是关于在 gcc 中访问内联汇编中的字符串的地址的主要内容,如果未能解决你的问题,请参考以下文章