在gcc中访问内联汇编中的字符串地址

时间:2014-11-05 16:07:06

标签: c gcc inline-assembly uppercase att

我编写了下面的汇编代码,用于将字符串从小写字母转换为大写字母,但由于我无法访问我正在转换的字符串的地址,因此它无法正常工作。这段代码不起作用的原因?

  #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;

  }

2 个答案:

答案 0 :(得分:0)

这是我的解决方案与上述略有不同,感谢FUZxxi指出。我应该说检索程序集在很大程度上有帮助,它可能很难理解但它会给你带来实际问题。如果有人想了解我想要实现的目标,我已经写了足够的评论。

/* 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);
}

如果使用&#34; gcc -m32&#34;进行编译,则上述代码应该有效。选项,如果您正在编译amd64。我遇到了这样的问题&#34;致命错误:sys / cdefs.h:没有这样的文件或目录&#34;

解决方案:安装此软件包:libc6-dev-i386

答案 1 :(得分:0)

接受的答案中的代码似乎有一些问题:

  • 如上所述,此代码无法编译(当只有1个参数时它引用%1),并且它在第4个asm行中缺少终结符。
  • 此代码无法正确处理“aBc”等字符串。
  • 此代码不使用“memory”clobber,即使它修改了内存。
  • 此代码(仍然)修改未被破坏的寄存器(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查看输出。