В прошлом посте я рассказал, как можно детектировать signed integer overflow с помощью готовых функций. Сегодня рассмотрим, что ж за магия такая используется для таких заклинаний.
Сразу с места в карьер. То есть в ассемблер.
Есть функция
int add(int lhs, int rhs) {
int sum;
if (__builtin_add_overflow(lhs, rhs, &sum))
abort();
return sum;
}Посмотрим, во что эта штука компилируется под гцц х86.
Все немного упрощаю, но в целом картина такая:
mov %edi, %eax
add %esi, %eax
jo call_abort
ret
call_abort:
call abort
Подготавливаем регистры, делаем сложение. А далее идет инструкция
jo. Это условный прыжок. Если условие истино - прыгаем на метку call_abort, если нет - то выходим из функции.Инструкция
jo выполняет прыжок, если выставлен флаг OF в регистре EFLAGS. То есть Overflow Flag. Он выставляется в двух случаях:1️⃣ Если операция между двумя положительными числами дает отрицательное число.
2️⃣ Если сумма двух отрицательных чисел дает в результате положительное число.
Можно считать, что это два условия для переполнения знаковых чисел. Например
127 + 127 = 0111 1111 + 0111 1111 = 1111 1110 = -2 (в дополнительном коде)
Результат сложения двух положительных чисел - отрицательное число, поэтому при таком сложении выставится регист OF.
Для беззнаковых чисел тоже кстати есть похожий флаг. CF или Carry Flag. Мы говорили, что переполнение для беззнаковых - не совсем переполнение, но процессор нам и о таком событии дает знать через выставление carry флага.
Собственно, вы и сами можете детектировать переполнение подобным образом. Конечно, придется делать асемблерную вставку, но тем не менее.
Но учитывая все условия для overflow, есть более простые способы его задетектить, чисто на арифметике. Но об этом позже.
Detect problems. Stay cool.
#base #cppcore #compiler