Использование прерывания 0x80 на 64-битном Linux [дубликат]

этот вопрос уже есть ответ здесь:

у меня есть простая 64-разрядная программа сборки, которая предназначена для печати "O" и " K " с последующей новой строкой. Однако "K" никогда не печатается. Одной из целей программы является выведите значение в нижних битах регистра rax как букву ASCII. Программа специально для 64-битного Linux, написана в образовательных целях, поэтому нет необходимости использовать системные вызовы C-стиля.

Я подозреваю, что проблема либо лежит с mov QWORD [rsp], rax или mov rcx, rsp.

В настоящее время программа выводит только "O", за которым следует новая строка.

как можно изменить программу, чтобы она использовала значение в rax, а затем напечатала "K", чтобы полный вывод "ОК", за которым следует новая строка?

bits 64

section .data

o:  db "O"      ; 'O'
nl: dq 10       ; newline

section .text

;--- function main ---
global main         ; make label available to the linker
global _start       ; make label available to the linker
_start:             ; starting point of the program
main:               ; name of the function

;--- call interrupt 0x80 ---
mov rax, 4          ; function call: 4
mov rbx, 1          ; parameter #1 is 1
mov rcx, o          ; parameter #2 is &o
mov rdx, 1          ; parameter #3 is length of string
int 0x80            ; perform the call

;--- rax = 'K' ---
mov rax, 75         ; rax = 75

;--- call interrupt 0x80 ---
sub rsp, 8          ; make some space for storing rax on the stack
mov QWORD [rsp], rax        ; move rax to a memory location on the stack
mov rax, 4          ; function call: 4
mov rbx, 1          ; parameter #1 is 1
mov rcx, rsp            ; parameter #2 is rsp
mov rdx, 1          ; parameter #3 is length of string
int 0x80            ; perform the call
add rsp, 8          ; move the stack pointer back

;--- call interrupt 0x80 ---
mov rax, 4          ; function call: 4
mov rbx, 1          ; parameter #1 is 1
mov rcx, nl         ; parameter #2 is nl
mov rdx, 1          ; parameter #3 is length of string
int 0x80            ; perform the call

;--- exit program ---
mov rax, 1          ; function call: 1
xor rbx, rbx            ; return code 0
int 0x80            ; exit program

1 ответов


очевидно, вы пишете 64-разрядную программу и используете инструкцию "int 0x80". однако "int 0x80" работает только в 32-разрядных программах.

адрес стека находится в диапазоне, недоступном для 32-разрядных программ. Поэтому вполне вероятно, что системные вызовы в стиле "int 0x80" не позволяют получить доступ к этой области памяти.

для решения этой проблемы существует два варианта:

  • Compile как 32-битное приложение (используйте 32-бит регистры типа EAX вместо 64-битных регистров типа RAX). Когда вы связываетесь без использования каких-либо общих библиотек, 32-битные программы отлично работают на 64-битном Linux.
  • использовать "системный вызов"в стиле системных вызовов, а не ООО "int 0x80, в"стиле системных вызовов. Использование этих сильно отличается от"int 0x80" -стиля!

32-разрядный код:

mov eax,4    ; In "int 0x80" style 4 means: write
mov ebx,1    ; ... and the first arg. is stored in ebx
mov ecx,esp  ; ... and the second arg. is stored in ecx
mov edx,1    ; ... and the third arg. is stored in edx
int 0x80

64-битного кода:

mov rax,1    ; In "syscall" style 1 means: write
mov rdi,1    ; ... and the first arg. is stored in rdi (not rbx)
mov rsi,rsp  ; ... and the second arg. is stored in rsi (not rcx)
mov rdx,1    ; ... and the third arg. is stored in rdx
syscall

--- редактировать ---

фон информация:

"int 0x80" предназначен для 32-разрядных программ. При вызове из 64-разрядной программы он ведет себя так же, как если бы он был вызван из 32-разрядной программы (используя 32-разрядное соглашение о вызове).

Это также означает, что параметры для "int 0x80" будут переданы в 32-битные регистры и старшие 32 бита 64-разрядных регистров игнорируются.

(Я только что проверил это на Ubuntu 16.10, 64 немного.)

это, однако, означает, что вы можете получить доступ только к памяти ниже 2^32 (или даже ниже 2^31) при использовании "int 0x80", потому что вы не можете передать адрес выше 2^32 в 32-разрядном регистре.

Если данные для записи расположены по адресу ниже 2^31, вы можете использовать "int 0x80" для записи данных. Если он расположен выше 2^32, вы не можете. Стек (RSP), скорее всего, расположен выше 2^32, поэтому вы не можете записывать данные в стек с помощью "int 0x80".

потому что это очень вероятно, ваша программа будет использовать память выше 2^32 я написал: "int 0x80 не работает с 64-битными программами."