https://chacker.pl/
Do tej pory wszystkie przykłady wykorzystywały typ danych Int o dowolnej precyzji z Z3. Jeśli użyjesz typów danych o dowolnej precyzji do modelowania pliku binarnego, wynik może nie odzwierciedlać rzeczywistości, ponieważ pliki binarne operują na liczbach całkowitych o stałej szerokości, które oferują jedynie ograniczoną precyzję. Dlatego Z3 oferuje również wektory bitowe, które są liczbami całkowitymi o stałej szerokości, idealnie nadającymi się do użycia w obliczeniach symbolicznych. Do manipulowania wektorami bitowymi używa się dedykowanych operatorów, takich jak bvadd, bvsub i bvmul, zamiast zwykłych operatorów liczb całkowitych, takich jak +, − i ×. Tabela 1 przedstawia przegląd najczęściej używanych operatorów wektorów bitowych. Zobaczysz ich wiele, jeśli przyjrzysz się ograniczeniom i wyrażeniom symbolicznym, które silniki Symbex, takie jak Triton, przekazują do solvera ograniczeń. Co więcej, znajomość tych operatorów przydaje się podczas tworzenia własnych narzędzi symbex. Omówmy, jak w praktyce używać operatorów wymienionych w tabeli 1. Z3 pozwala na tworzenie wektorów bitowych o dowolnej szerokości bitowej. Istnieje kilka sposobów, aby to osiągnąć, jak widać w pierwszej części tabeli 1 (1). Po pierwsze, można utworzyć 4-bitową stałą wektora bitowego zawierającą bity 1101, używając notacji #b1101. Podobnie, notacja #xda tworzy 8-bitowy wektor bitowy zawierający wartość 0xda. Jak widać, w przypadku stałych binarnych lub szesnastkowych Z3 automatycznie wnioskuje o minimalnym rozmiarze, jaki musi mieć wektor bitowy. Aby zadeklarować stałe dziesiętne, należy jawnie określić zarówno wartość wektora bitowego, jak i jego szerokość. Na przykład notacja (_ bv10 32) tworzy 32-bitowy wektor bitowy zawierający wartość 10. Można również deklarować stałe wektory bitowe o nieokreślonej wartości, używając notacji (declare-const x (_ BitVec 32)), gdzie x jest nazwą stałej, a 32 jest jej szerokością bitową.


Z3 obsługuje również arytmetyczne operatory wektorów bitowych, aby odzwierciedlać wszystkie operacje prymitywne obsługiwane w językach takich jak C/C++ i zestawach instrukcji takich jak x86 (2). Na przykład polecenie Z3 (assert (= y (bvadd x #x10))) potwierdza, że wektor bitowy y musi być równy wektorowi bitowemu x + 0x10. Dla wielu operacji Z3 obejmuje zarówno warianty ze znakiem, jak i bez znaku. Na przykład, (bvsdiv x y) wykonuje dzielenie ze znakiem x/y, podczas gdy (bvudiv x y) wykonuje dzielenie bez znaku. Należy również zauważyć, że Z3 wymaga, aby oba operandy w arytmetycznej operacji wektorowej miały tę samą szerokość bitową. W kolumnie „Przykład” w Tabeli 12-1 wymieniłem przykłady wszystkich typowych operacji wektorowych bitowych Z3. Średniki oznaczają komentarze pokazujące odpowiednik w C/C++ lub wynik arytmetyczny operacji Z3. Oprócz operatorów arytmetycznych, Z3 implementuje również popularne operatory bitowe, takie jak OR (odpowiednik | z języka C), AND (&), XOR (ˆ) i NOT (~) (3). Implementuje również porównania, takie jak = do sprawdzania równości między wektorami bitowymi, bvult do wykonywania porównania „mniejszego niż” bez znaku itd. (4). Obsługiwane porównania są bardzo podobne do tych obsługiwanych przez skoki warunkowe x86 i są szczególnie przydatne w połączeniu z operatorem ite z Z3. Na przykład, (ite (bvsge x y) 22 44) zwraca 22, jeśli x >= y, lub 44 w przeciwnym razie. Można również łączyć dwa wektory bitowe lub wyodrębniać ich część (5). Jest to przydatne, gdy trzeba wyrównać rozmiar dwóch wektorów bitowych, aby umożliwić wykonanie określonej operacji lub gdy interesuje nas tylko część wektora bitowego. Teraz, gdy znasz już operatory bitowe Z3, przyjrzyjmy się praktycznemu przykładowi wykorzystującemu te operatory.