簡體   English   中英

在gcc中內聯匯編中訪問字符串的地址

[英]Accessing address of a string in inline assembly in gcc

我已經編寫了以下匯編代碼,將字符串從小寫轉換為大寫,因為我無法訪問要轉換的字符串的地址,因此無法完全正常工作。 此代碼不起作用,為什么?

  #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指出。 我應該說,檢索程序集在很大程度上會有所幫助,雖然很難理解,但卻為您帶來了實際的問題。 如果有人想了解我要實現的目標,我已經寫了足夠多的評論。

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

如果使用amd64進行編譯,則以上代碼在使用“ gcc -m32”選項進行編譯時應該可以工作。 我這樣做時遇到問題
“嚴重錯誤:sys / cdefs.h:沒有這樣的文件或目錄”

解決方案:安裝此軟件包:libc6-dev-i386

接受的答案中的代碼似乎有一些問題:

  • 按照編寫的方式,此代碼無法編譯(當只有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 vs 4)。
  • 通過使用“ = r”(臨時),我們讓編譯器選擇最佳的寄存器用於暫存,而不是強制使用特定的寄存器。
  • 僅讀取一次內存(而不是兩次)。
  • 返回指向字符串的指針(而不是什么都不返回?)。
  • 使用%[temp]和%[src]的IMO比%1更容易閱讀。
  • 使用\\n\\t (而不是; )可以使gcc -S的輸出更易於閱讀。
  • 此代碼修改了str(這就是為什么將其列為“ + r”的原因)。

或者,如果您真的想花哨的話,請在'c'中編寫代碼,然后使用gcc -O2 -S查看輸出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM