Порядок байтов. Big-endian и little-endian

Последнее обновление: 19.01.2024

При работе с данных следует учитывать порядок байтов. В настоящее время распростраенны два порядка байтов: big-endian и little-endian. Порядок big-endian предполагает расположение байтов от старшего к младшему. Порядок little-endian предполагает расположение байтов от младшего к старшему. Например, возьмем шестнадцатеричное число 0x01234567. Это число состоит из четырех байтов. В архитектурах с big-endian байты этого числа будут расположены следующим образом:

Адрес0x00000x00010x00020x0003
Значение01234567

То есть сначала идет самый старший байт.

В архитектурах с little-endian байты этого числа будут расположены в обратном порядке:

Адрес0x00000x00010x00020x0003
Значение67452301

То есть сначала идет самый младший байт.

Архитектура Intel x86-64 для расположения байтов применяет порядок little-endian. Почему это важно? Возьмем следующую программу под Linux:

global _start           

section .data
number dd 0x01234567    ; 4 байт

section .text 
_start:           
    movzx rdi, byte [number]    ; получаем 1-й байт
    mov rax, 60         
    syscall             

В данном случае в секции данных определено 4-байтное число 0x01234567. Старший байт числа равен "0x01", а самый младший байт числа - "0x67" (103 в десятичной системе). В программе мы получаем первый байт в регистр rdi. Запустим программу:

root@Eugene:~/asm# nasm -f elf64 hello.asm -o hello.o
root@Eugene:~/asm# ld -o hello hello.o
root@Eugene:~/asm# ./hello
root@Eugene:~/asm# echo $?
103
root@Eugene:~/asm#

Мы видим, что самый первый байт равен 103 или 0x67. То есть самый первый байт - это самый младший байт. Соответственно, чтобы получить самый старший байт (четвертый байт), нам надо прибавить смещение в 3 байта:

movzx rdi, byte [number+2]

Но возьмем другую программу (также на Linux):

global _start           

section .data
numbers db 0x01, 0x23, 0x45, 0x67    ; 4 байт

section .text 
_start:           
    movzx rdi, byte [numbers]    ; получаем 1-й байт
    mov rax, 60         
    syscall 

Здесь также в секции данных определены 4 байта, но эти байты представляют массив. Данные массива в памяти расположены в порядке его определения, то есть сначала идет первый байт массива, потом второй и т.д. В итоге в регистр RDI будет помещено число 0х01:

root@Eugene:~/asm# nasm -f elf64 hello.asm -o hello.o
root@Eugene:~/asm# ld -o hello hello.o
root@Eugene:~/asm# ./hello
root@Eugene:~/asm# echo $?
1
root@Eugene:~/asm#

Возьмем более сложный пример:

global _start           

section .data
numbers dw 0x0123, 0x4567    ; 4 байта - каждое число по  2 байта

section .text 
_start:           
    movzx rdi, byte [numbers]    ; получаем 1-й байт
    mov rax, 60         
    syscall            

Опять у нас в секции данных 4 байта, но теперь это массив из двубайтовых чисел (слов). Первое число равно 0x0123. Но в соответствии с little-endian опять же сначала будет располагаться младший байт числа, а потом старший:

Адрес0x00000x0001
Значение2301

Соответственно в регистр RDI будет помещен младший байт числа - 0x23 (число 35 в десятичной системе):

root@Eugene:~/asm# nasm -f elf64 hello.asm -o hello.o
root@Eugene:~/asm# ld -o hello hello.o
root@Eugene:~/asm# ./hello
root@Eugene:~/asm# echo $?
35
root@Eugene:~/asm#

Возьмем четвертую ситуацию:

global _start           

section .data
number db "01234567"    ; 8 байт

section .text 
_start:           
    movzx rdi, byte [number]    ; получаем 1-й байт
    mov rax, 60         
    syscall             

Теперь число представлено строкой ASCII. Каждый символ в строке ASCII занимает 1 байт, соответственно строка number занимает 8 байт. Но в плане размещения данных строка символов фактически эквивалентна массиву - в данном случае массиву байт. То есть сначала идет байт (числовой код), который представляет символ "0", далее идет байт символа "1" и так далее. Поэтому в регистре RDI окажется символ 48 - числовой код символа "0":

root@Eugene:~/asm# nasm -f elf64 hello.asm -o hello.o
root@Eugene:~/asm# ld -o hello hello.o
root@Eugene:~/asm# ./hello
root@Eugene:~/asm# echo $?
48
root@Eugene:~/asm#
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850