Результат функции

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

Нередко функции выполняют некоторые вычисления и генерируют некоторый результат этих вычислений. Вполне вероятно, что мы захотим передать этот результат во внешний код. Во многих языках программирования высокого уровня есть механизм возвращения результата функции - обычно для этого применяется оператор return. В ассемблере такого механизма нет. Наиболее удобным местом для возврата результатов функции в архитектуре x86-64 являются регистры.

Как правило, результат функции помещается в регистр RAX (или его подрегистры EAX, AX, AL), хотя в реальности, если мы сами пишем всю программу на ассемблере, мы можем использовать для этой цели любой регистр общего назначения. Однако на регистр RAX завязано много соглашений. Так, именно в RAX большинство языков высокого уровня помещают результат функции. А согласно интерфейсам System ABI и Microsoft Windows ABI прицелочисленный результат помещается в регистр RAX.

Если нужно вернуть результат, размер которого превышает 64 бита (например, 128 бит или 256 бит) и соответственно не помещается в RAX или в другой 64-разрядный регистр общего назначения, то можно разделить результат на части и вернуть эти части в двух или более регистрах. Часто можно увидеть, как функции возвращают 128-битные значения в паре регистров RDX:RAX. .

Если надо вернуть большой объект, например, массив из 1000 элементов, то, очевидно, регистры не подходят. И в этом случае можно вместо значения возвратить его адрес (который занимает 8 байт).

Простейший пример возвращения результата в программе на Linux:

global _start

section .text
_start:
    ; устанавливаем параметры для функции sum
    mov rdi, 5
    mov rsi, 6
    call sum            ; после вызова в RAX - результат сложения
    mov rdi, rax     ; помещаем результат в RDI
    mov rax, 60
    syscall 

; Функция sum выполняет сложение двух чисел
; RDI - первое число
; RSI - второе число
; RAX - результат сложения
sum:
    mov rax, rdi
    add rax, rsi
    ret

Здесь функция sum принимает через регистры RDI и RSI два параметра - два числа, складывает их и помещает результат в регистр RAX. После вызова функции sum мы можем использовать полученный результат в регистре RAX каким-нибудь образом, например, здесь копируем его в регистр RDI.

Аналогичная программа для Windows:

global _start

section .text
_start:
    ; устанавливаем параметры для функции sum
    mov rcx, 5
    mov rdx, 6
    call sum            ; после вызова в RAX - результат сложения
    ret

; Функция sum выполняет сложение двух чисел
; RCX - первое число
; RDX - второе число
; RAX - результат сложения
sum:
    mov rax, rcx
    add rax, rdx
    ret

Другой пример найдем в программе на Linux сумму чисел массива и поместим результат в RAX:

global _start

section .data
nums dq 10, 20, 30, 15, 15
count equ ($-nums)/numSize    ; количество элементов
numSize equ 8   ; размер каждого элемента

section .text
_start:
    ; устанавливаем параметры для функции sum
    mov rdi, nums                  ; в RDI адрес массива
    mov rsi, count     ; в RSI - количество элементов массива
    call sum            ; после вызова в RAX - результат сложения
    mov rdi, rax     ; помещаем результат в RDI
    mov rax, 60
    syscall 

; Функция sum выполняет сложение чисел массива
; RDI - адрес массива
; RSI - размер массива
; RAX - результат сложения
sum:
    xor rax, rax     ; обнуляем RAX для хранения результата
    jmp while_condition ; переходим к проверке условия
while_begin:
    dec rsi     ; уменьшаем счетчик количества элементов массива
    add rax, [rdi + rsi * numSize]    ; складываем значение по адресу [rdi + rsi*numSize]
while_condition:
    cmp rsi, 0   ; сравниваем количество обработанных элементов 
    jne while_begin ; если не все элементы обработали, переходим обратно
    ret

Здесь функция sum принимает адрес массива и количество элементов в массиве. В цикле проходит пор всем элементам массива, используя косвенную адресацию и счетчик элементов - регистр RDI и помещает в регистр RAX сумму элементов массива.

Аналогичный пример для Windows:

global _start

section .data
nums dq 10, 20, 30, 15, 15
count equ ($-nums)/numSize    ; количество элементов
numSize equ 8   ; размер каждого элемента

section .text
_start:
    ; устанавливаем параметры для функции sum
    mov rcx, nums       ; в RCX адрес массива
    mov rdx, count      ; в RDX - количество элементов массива
    call sum            ; после вызова в RAX - результат сложения
    ret

; Функция sum выполняет сложение чисел массива
; RCX - адрес массива
; RDX - размер массива
; RAX - результат сложения
sum:
    xor rax, rax     ; обнуляем RAX для хранения результата
    jmp while_condition ; переходим к проверке условия
while_begin:
    dec rdx     ; уменьшаем счетчик количества элементов массива
    add rax, [rcx + rdx * numSize]    ; складываем значение по адресу [rcx + rdx*numSize]
while_condition:
    cmp rdx, 0   ; сравниваем количество обработанных элементов 
    jne while_begin ; если не все элементы обработали, переходим обратно
    ret

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850