Байт новой строки 0Ah игнорируется программой печати системного вызова x86_64 - PullRequest
2 голосов
/ 12 марта 2019

Я следовал простому руководству по созданию программы NASM x86_64, которая использует определенные функции для печати переменных с добавлением новой строки в конце. sprintLF вызывает sprint, которая, в свою очередь, печатает все, что находится в rax, установив соответствующий системный вызов. При возврате sprintLF обновляет rax с помощью 0Ah кода новой строки, который затем помещает в стек и переназначает rax на адрес стека 0Ah, прежде чем снова вызывать sprint с кодом новой строки для записи в stdout. Ниже всего кода, который я отлаживал в sprint в gdb, который показывает, что во всех правильных регистрах хранятся значения, связанные с системным вызовом 4, и я затрудняюсь понять, почему строки переменных успешно печатаются, а переводы строк - нет.

код вызова

 ;; Hello World Program (Externam file include)                                                                                                                                                      
    ;; Compile with: nasm -f elf64 helloworld-if.asm                                                                                                                                                   
    ;; Link with ld helloworld-if.o -o helloworld-if                                                                                                                                                  
    ;; Run with ./helloworld-inc                                                                                                                                                                        

    %include        'function.asm' ; include our external file                                                                                                                                          

    SECTION .data
    msg1    db      'Hello, brave new world!', 0h           ;our first message string add null terminating byte                                                                                         
    msg2    db      'This is how we recycle in NASM.', 0h   ; our second message string add null terminating byte                                                                                       

    SECTION .text
    global _start

_start:

    mov     rax, msg1       ; mov the address of our first message string into RAX                                                                                                                      
    call    sprintLF                ; call our string printing function                                                                                                                                 

    mov     rax, msg2       ; move the address of our second message string into RAX                                                                                                                    
    call    sprintLF                ; call our string printing function                                                                                                                                 

    call    quit            ; call our quit function   

ПОЛЕЗНЫЕ ФУНКЦИИ

; -------------------------------------------------------------------------------------------------------------------                                                                                       
; int slen(String message)                                                                                                                                                                                  
; String length calculation function                                                                                                                                                                        


slen:                           ; this is our first function declaration                                                                                                                                    

    push    rbx             ; push the value in RBX onto the stack to preserve it while we use RBX in this function                                                                                     
    mov     rbx, rax        ; move this address in RAX into RBX ( Both point to the same segment in memory)                                                                                             

nextchar:

    cmp     byte [rax], 0   ; this is the same as lesson 3                                                                                                                                              
    jz      finished
    inc     rax
    jmp     nextchar

finished:

    sub     rax, rbx
    pop     rbx             ; pop the value on the stack back into RBX                                                                                                                                  
    ret                     ; return to where the function was called                                                                                                                                   


    ;; ---------------------------------------------------------------------------------------------------------                                                                                        
    ;; void sprint(String message)                                                                                                                                                                      
    ;; String printing function                                                                                                                                                                         
sprint:

    push    rdx
    push    rcx
    push    rbx
    push    rax
    call    slen

    mov     rdx, rax
    pop     rax

    mov     rcx, rax
    mov     rbx, 1
    mov     rax, 4
    int     80h

    pop     rbx
    pop     rcx
    pop     rdx
    ret

    ;; ----------------------------------------------------------------------------------------------------------                                                                                       
    ;; void sprintLF(String message)                                                                                                                                                                    
    ;; String printing with line feed function                                                                                                                                                          

sprintLF:
    call    sprint

    push    rax             ; push rax onto the stack to preserve it while         we use the rax register in this function                                                                                     
    mov     rax, 0Ah        ; push 0Ah into rax, 0Ah is the ascii         character for a linefeed                                                                                                              
    push    rax             ; push the linefeede onto the stack so we     can get the address                                                                                                               
    mov     rax, rsp        ; move the address of the current stack     pointer into rax for sprint -> because write requires a memory address     
    call    sprint          ; call our sprint function                                                                                                                                                  
    pop     rax             ; restore out linefeed character from the     stack                                                                                                                             
    pop     rax             ; return to our program                                                                                                                                                     
    ret

        ;; -----------------------------------------------------------------------------------------------------------                                                                                      
    ;; void exit()                                                                                                                                                                                      
    ;; Exit program restore resources                                                                                                                                                                   
quit:

    mov     rbx, 0
    mov     rax, 1
    int     80h
    ret

Команды, используемые для выполнения кода и вывода, следующие:

nasm -f elf64 helloworld-if.asm
ld helloworld-if.o -o hellworld-if
./hellworld-if

Hello, brave new world!This is how we recycle in NASM.

В другой программе, где я пытаюсь распечатать аргументы после помещения их в стек, происходит то же самое, поэтому я могу только догадываться, что системный вызов не любит брать его значение из стека, но я новичок в сборке, и это меня смущает .

1 Ответ

1 голос
/ 12 марта 2019

Вы пытались преобразовать 32-битный код Linux, который использует int0x80, в 64-битный код. Хотя это может работать во многих случаях, это не работает для всего. int 0x80 - это 32-битный интерфейс системных вызовов, но с совместимостью IA32, встроенной в ядро ​​Linux (по умолчанию для большинства дистрибутивов), вы по-прежнему можете использовать int 0x80. Уловка в том, что только младшие 32-битные регистры распознаются, когда ядро ​​обрабатывает ваш int 0x80 запрос.

Код в вашем первом вопросе не показал никаких проблем, но этот код не работает. Причина в том, что указатель стека в RSP обычно является адресом, к которому нельзя обратиться с 32-разрядным значением. Когда вы делаете mov rax,rsp, полное 64-битное значение RSP перемещается в RAX, но вызов sprint int 0x80 будет видеть только нижние 32-битные значения RAX (регистр EAX).

Способ обойти это - использовать 64-битный интерфейс syscall. К сожалению, номера системных вызовов и передаваемые параметры регистров изменились. Блог Райана Чепмена содержит красивую таблицу 64-битных syscall номеров системных вызовов и их параметров.

Номер и параметры системного вызова sys_write из таблицы:

enter image description here

На основе этой информации вы можете преобразовать sprint в интерфейс syscall, выполнив следующее:

sprint:
    push    r11                ; R11 and RCX are clobbered by syscall as well
    push    rcx
    push    rdx
    push    rsi
    push    rdi
    push    rax
    call    slen

    mov     rdx, rax           ; RDX = number of characters to print
    pop     rax

    mov     rsi, rax           ; RSI = address of characters to print
    mov     rdi, 1             ; RDI = file descriptor (1=STDOUT)
    mov     rax, 1             ; System call number 1 = sys_write
    syscall                    ; 64-bit system call (rather than int 0x80)

    pop     rdi
    pop     rsi
    pop     rdx
    pop     rcx
    pop     r11
    ret

Это довольно неэффективно, и его можно убрать. Я представляю это таким образом, чтобы вы могли понять изменения с точки зрения вашего исходного кода. Я прокомментировал строки актуальности.

Примечание : Вы должны действительно преобразовать все int 0x80 вызовы в syscall, используя таблицу Райана Чепмена в качестве руководства. Я оставляю это как упражнение для ОП.

...