G ++ генерирует логически неверный ассемблерный код

Сегодня я использую g ++ для компиляции файла c ++, но, похоже, проблема с g ++ заключается в том, что когда переменная типа char сравнивается с любым числом типов char с битом 7, установленным в 1, g ++ всегда будет предполагать, что это false, что делает работу неправильно. Более конкретно, код C ++ выглядит так:

  // test.cpp
__asm__(".code16gcc \n\t");
int equals0(char i)
{
return i==0x80;
}
int equals1(char i)
{
return i==0x10;
}
int equals2(int i)
{
return i==0x80;
}

Обратите внимание, что есть предложение преамбулы «.code16gcc», которое я использовал для генерации кодов реального режима.

Чтобы быть более конкретным, моя версия g ++ — «g ++ (GCC) 6.4.0» на Cygwin.

Теперь скомпилируйте этот файл в код сборки, используя:
g++ -S -o test.s test.cpp -m32

И полученный файл:

     // some trival information is omitted
.code16gcc

.text
...
__Z7equals0c://func equals0
pushl   %ebp
movl    %esp, %ebp
subl    $4, %esp
movl    8(%ebp), %eax
movb    %al, -4(%ebp)  //no comparison with 0x80
movl    $0, %eax   //always returns 0
leave
ret
...
__Z7equals1c://func equals1
pushl   %ebp
movl    %esp, %ebp
subl    $4, %esp
movl    8(%ebp), %eax
movb    %al, -4(%ebp)
cmpb    $16, -4(%ebp)  // a comparison with 0x10
sete    %al
movzbl  %al, %eax    // returns value depend on the result of comparison
leave
ret
...
__Z7equals2i://func equals2
pushl   %ebp
movl    %esp, %ebp
cmpl    $128, 8(%ebp) //also a comparison
sete    %al
movzbl  %al, %eax
popl    %ebp
ret

Обратите внимание, что функция equals0 всегда будет возвращать 0 (сохраненный в eax), но функция equals1 будет возвращать правильный результат, основанный на сгенерированном файле сборки.

Также обратите внимание, что единственная разница между функцией equals0 и equals1 — это константа, используемая для сравнения, 0x80 для equals0 и 0x10 для equals1. Исходя из этого, мы можем сказать, что g ++ сгенерировал логически неверный код.

Кто-нибудь знает почему и объяснит это?

1

Решение

0x80 (int 128) находится вне диапазона подписанного символа ([-128;127]).

поэтому подписанный символ не может быть равен 0x80

8

Другие решения

Обратите внимание, что есть предложение преамбулы «.code16gcc», которое я использовал для генерации кодов реального режима.

Неправильно. Ваш __asm__(".code16gcc \n\t"); просто излучающий некоторая директива ассемблера (которая сделала бы ваш ассемблер переключить режим). Но вы хотите изменить поведение вашего компилятора GCC (например, вы ожидаете, что GCC будет испускать 16-битный код ассемблера реального режима).

AFAIK, ты не можешь сделать это легко. Возможно, вы могли бы найти вариант GCC (возможно, некоторый кросс-компилятор) для генерации кода реального режима, а затем вам нужно использовать этот вариант в вашем модуле перевода C ++. Вы мечтаете о некоторых -m16realmode флаг для GCC, который не существует. И GCC -m16 Режим (очень запутанно названный!) все еще испускает 32-битный код:

-m16 вариант такой же, как -m32за исключением того, что он выводит .code16gcc директива ассемблера в начале вывода сборки, чтобы двоичный файл мог работать в 16-битном режиме.

(на самом деле, двоичный не могу работать в 16-битном режиме, если вы не предоставлять и позвонить некоторым дополнительный переключение кода на 32 бита; вам, вероятно, нужно написать этот код режима переключения на ассемблере)

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

Если вы кодируете игрушечную операционную систему (и вы должны кодировать ее как минимум в 32-битном режиме), посмотрите на OSDEV wiki для подсказок.

Теперь скомпилируйте этот файл в код сборки, используя: g++ -S -o test.s test.cpp -m32

Это все еще использует 32-битный режим (не 16-битное наследие) для всего источника (и ошибочно вставляет неправильно .code16gcc директива ассемблера внутри него). Кстати, вам лучше попросить некоторые оптимизации. Поэтому я рекомендую компилировать с g++ -m32 -Wall -O -fverbose-asm -S -o test.s test.cpp,

Кстати, вы смущены подписью char который фиксированный в пределах -m32 архитектура x86 (так char это signed char на x86; на PowerPC это может быть unsigned char). Я не рекомендую перекомпилировать с -funsigned-char но если вы делаете это, вы меняете ABI и вам нужно перекомпилировать все, включая вашу стандартную библиотеку C и стандартную библиотеку C ++.

2

По вопросам рекламы [email protected]