Загрузка регистров в gcc inline сборке? (просто?) - PullRequest
0 голосов
/ 30 января 2012

Так что кажется, что это очень простая проблема для всех, кто вообще знает о сборке, но я надеялся, что кто-нибудь сможет объяснить мне, в чем разница между следующими двумя частями кода, учитывая, что один из них приводит к ошибке сегментации идругой нет, но (мне) они кажутся логически эквивалентными.

Работает нормально:

char *src1; int esi_out, eax;
__asm__
  __volatile__(
     "lodsb\n\t;"
     : "=&S" (esi_out), "=&a" (eax)
     : "0" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x\n", *src1, src1, esi_out, eax);

и печатает:

src1 w @ 0x7fffce186959, esi_out: ce18695a, eax: ce186977

Итак, я понимаю, что этот код должен загрузить значение src1 (которое является адресом) в ESI, скопировать это значение в EAX, увеличить адрес в ESI на 1 байт, а затем при выходе вывести эти значения в локальные переменные C esi_outи eax.src1 и esi_out выглядят корректно, но eax кажется выключенным.Что здесь происходит?

Второй бит кода - это то место, где мы видим сегмент, с которым я не могу разобраться:

__asm__
  __volatile__(
        "movl %%ebx, %%esi\n\t;"
        //"lodsb\n\t;"                                                                                                                  
        : "=&S" (esi_out), "=&b" (ebx), "=&a" (eax)
        : "1" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x, ebx: %x\n", 
                        *src1, src1, esi_out, eax, ebx);

С закомментированной командой lodsb онавыдает:

src1 w @ 0x7ffff093b959, esi_out: f093b959, eax: f093b959, ebx: f093b959

И если команда lodsb не закомментирована, она вызывает ошибки.На мой взгляд, загрузка значения ESI напрямую, как в первом случае выше, загрузка его в EBX и затем перемещение его в ESI должна быть эквивалентной, нет?

Чего мне не хватает?Почему значение, записанное в EAX, не совпадает?Я написал эквивалентную программу непосредственно в ассемблер и прошел через нее, используя gdb, и она отлично работает.

Любое понимание будет с благодарностью.

Ответы [ 3 ]

3 голосов
/ 31 января 2012

Судя по выводу %p в вашем printf, вы компилируете для 64 бит, но ваш ассемблерный код предполагает 32 бита.Попробуйте

__asm__
  __volatile__(
        "movl %%rbx, %%rsi\n\t;"
        "lodsb\n\t;"    
        : "=&S" (esi_out), "=&b" (ebx), "=&a" (eax)
        : "1" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x, ebx: %x\n", 
                        *src1, src1, esi_out, eax, ebx);

Вы должны также объявить esi_out и ebx как тип указателя (void* или char*) или uintptr_t.

.lodsb использует RSI в качестве адреса источника в 64-битном режиме, но вы поместили только 32 младших бита значения указателя в RSI, чтобы он не содержал действительный адрес, следовательно, произошла ошибка.Как говорит Слаг, lodsb изменяет только младшие 8 бит регистра a (al).Вы должны либо замаскировать другие биты (eax & 0xff), очистить rax (xor% rax,% rax) или объявить eax как char.

Вы также должны найти ресурс о сборке x86_64 в целом.если это для вас сюрприз.

1 голос
/ 31 января 2012

У вас есть ошибки:

Вы работаете на 64-битной архитектуре, int недостаточно велик, чтобы содержать указатель. Переменная esi_out должна иметь ширину 64 бита, или лучше просто использовать ptrdiff_t. И вы должны назвать это rsi_out; -)

Инструкция lodsb в 64-битном режиме косвенно ссылается на rsi, а не esi. В инструкции до movl %%ebx, %%esi вы устанавливаете только нижнюю половину rsi, верхняя половина неявно очищается. Измените его на movq %%rbx, %%rsi.

1 голос
/ 31 января 2012

Ваш первый вопрос - lodsb изменяет AL, но не остаток EAX. Последний байт в вашем значении EAX ce186977 равен 0x77, что является шестнадцатеричным для строчной буквы 'w'.

К сожалению, я не знаком с синтаксисом сборки GCC - когда вы запускаете свой код и перешагиваете через movl, какой регистр является местом назначения? Мне кажется, что EBX пишет ESI. Что в EBX перед вашим кодом?

...