Страницы

Поиск по вопросам

воскресенье, 5 января 2020 г.

Не получается изменить переменную из ассемблерной вставки

#cpp #ассемблер #clang #clang++


https://wandbox.org/permlink/fNyh3Nj0LroooFFq

#include 
#include 

using namespace std;

char *doSmth(char *s)
{
  volatile register char *p asm("rax") = s;

  __asm__
  ("\
    .intel_syntax noprefix   \n\
    inc rax                  \n\
    inc byte ptr [rax]       \n\
  ");

  return (char*)p;
}

int main()
{
  char s[] = "abcXYZ";

  cout << s << ' ' << doSmth(s) << endl;

  return 0;
}


Выводится


  abcXYZ accXYZ


т. е. инкремент символа происходит верно, а инкремент указателя тоже как бы происходит
(изменён верный символ), но только внутри ассемблерной вставки и не отражается на переменной,
используемой в дальнейшем коде.

А хотелось бы получить строку с ожидаемо отрезанным первым символом:


  abcXYZ ccXYZ


Как исправить?
    


Ответы

Ответ 1



Компилятор же не знает, что вы там внутри переменную p поменяли, и берёт значение не из регистра, а из переменной s (её-то вы не меняли, а там записано то же самое). Это один из вариантов, на самом деле их до чёрта. Такая ситуация называется UB (undefined behavior). Чтобы компилятор не занимался самодеятельностью, надо рассказать ему какие переменные изменялись. Например, так: char *doSmth(char *s) { __asm__ volatile ( "inc %0\n" "incb (%0)\n" : "+r"(s) : : "memory"); return s; } Здесь "+r" (s) означает, что переменная s должна быть доступна для чтения и записи как регистр, а volatile и "memory" означают, что в процессе могли измениться посторонние данные (т.е. не являющиеся ни входными параметрами, ни выходными). Подробнее о синтаксисе можно прочитать здесь: https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

Ответ 2



У меня результат зависит от включённой оптимизации. Работает, как предполагалось, при --- testold.cpp 2019-10-30 12:25:49.539828727 +0100 +++ test.cpp 2019-10-30 12:24:11.419439287 +0100 @@ -9,7 +9,6 @@ __asm__ ("\ - .intel_syntax noprefix \n\ inc rax \n\ "); и компиляции с g++ -masm=intel test.cpp. Если не указывать -masm=intel, то не ассемблируется. Если собирать с -O1 или выше, то переменная не изменяется. user@host:~/path$ ./a.out 1234567 234567 user@host:~/path$ Debian, amd64, g++ 6.3.0

Комментариев нет:

Отправить комментарий