Доступ к адресу строки во встроенной сборке в 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;

  }

person goal4321    schedule 05.11.2014    source источник
comment
Какой у Вас вопрос? Пожалуйста, задайте вопрос.   -  person fuz    schedule 05.11.2014
comment
@FUZxxi: я не могу получить доступ к адресу строки, и приведенный выше код не работает!   -  person goal4321    schedule 05.11.2014
comment
В какой момент вы назначаете ptr или str?   -  person fuz    schedule 05.11.2014
comment
str - это мой ввод, поэтому это% 1, я хочу, чтобы адрес str был в ebx, а окончательный вывод - в ptr, в основном ptr предназначен только для вывода. Я хочу изменить символы на верхние в самой строке.   -  person goal4321    schedule 05.11.2014
comment
Я бы посоветовал вам написать необходимую логику в файле сборки самостоятельно, прежде чем интегрировать ее как встроенную сборку. Я не совсем уверен, что вы пытаетесь архивировать с помощью кода. Почему вы перемещаете *str в %ebx в первой инструкции? Почему не str?   -  person fuz    schedule 05.11.2014
comment
При этом у меня возникла ошибка компилятора, изначально мне нравился movl %1,%%ebx; ошибка компилятора: Ошибка: неподдерживаемая инструкция `mov'   -  person goal4321    schedule 05.11.2014
comment
Это потому, что вы компилируете для amd64, а не для i386. На amd64 указатели имеют размер 64 бита. Конечно, movl %rdi,%ebx недопустимая инструкция. Вместо этого вы можете скомпилировать для i386 с cc -m32.   -  person fuz    schedule 05.11.2014
comment
Кроме того, получение ошибки компилятора не означает, что вы должны случайным образом использовать косвенные ссылки. Сначала извлеките сборку с cc -S и попытайтесь понять, какую сборку генерирует компилятор.   -  person fuz    schedule 05.11.2014
comment
Давайте продолжим обсуждение в чате.   -  person goal4321    schedule 05.11.2014
comment
В дополнение к комментариям выше, позвольте мне указать, что вы изменяете регистры ebx и ecx, не сообщая об этом компилятору. Если вы используете их как временные регистры, перечислите их в разделе clobbers. Кроме того, поскольку вы изменяете содержимое str, вы должны использовать затирание памяти. Если вы планируете работать со встроенным ассемблером gcc, вам следует ознакомиться с их документацией по этому поводу: gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html   -  person David Wohlferd    schedule 05.11.2014
comment
@David: Спасибо, что предоставили документ.   -  person goal4321    schedule 06.11.2014


Ответы (2)


Код в принятом ответе, похоже, имеет несколько проблем:

  • Как написано, этот код не компилируется (он ссылается на %1, когда есть только 1 параметр), и в нем отсутствует терминатор в 4-й строке asm.
  • Этот код неправильно обрабатывает такие строки, как «aBc».
  • Этот код не использует затирание «памяти», даже если он изменяет память.
  • Этот код (все еще) изменяет регистр, который не затирается (ebx).
  • Не работает на х64.

Как насчет чего-то более похожего на это:

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, чтобы увидеть вывод.

person David Wohlferd    schedule 08.11.2014

Вот мое решение, немного отличающееся от приведенного выше. Спасибо 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);
}

Приведенный выше код должен работать, если вы компилируете с использованием параметра «gcc -m32», если вы компилируете для amd64. Я столкнулся с проблемой при
"фатальной ошибке: sys/cdefs.h: нет такого файла или каталога"

решение: установите этот пакет: libc6-dev-i386

person goal4321    schedule 06.11.2014
comment
Как бы то ни было, это решение не является эффективным. Если вы хотите сделать это быстро (в основном это то, что мы хотим), пожалуйста, используйте векторные инструкции. - person goal4321; 28.11.2014