Невозможно получить один символ для печати в Linux NASM - PullRequest
2 голосов
/ 20 мая 2019

Я пытаюсь создать программу, которая принимает некоторые входные данные, находит странные позиции в строке и печатает соответствующий символ, поэтому вы вводите 'somewords', а она печатает 'oeod'.
В итоге я сделал цикл, который выполняет итерациизатем строка делит счетчик на 2 и печатает символ в позиции счетчика, если остаток не равен 0.

Вместо одного символа он ничего не печатает.

Полный код:

SECTION .bss
inp: resb 255

SECTION .data
msg db "Enter the string: ", 0h

SECTION .text
global _start

_start:
    mov    eax, msg
    call   stprint 

    mov    edx, 255  ; take user input 
    mov    ecx, inp 
    mov    ebx, 0 
    mov    eax, 3 
    int    80h 

    call   findodd

    mov    ebx, 0
    mov    eax, 1
    int    80h

findodd:
    push   eax
    push   ecx
    push   edx
    push   esi
    push   ebx

    mov    ecx, 0     ; counter
    mov    esi, 2     ; divider

.iterstring:  
    mov    eax, inp           ; move input to eax
    cmp    byte [eax+ecx], 0  ; check for end of the string in position
    je     .finish            ; if equal, finish
    inc    ecx  

    push   eax
    mov    eax, ecx   ; move counter to eax 
    xor    edx, edx   ; divide it by 2
    idiv   esi  
    pop    eax
    cmp    edx, 0     ; check the remainder
    jnz    .printchar ; print character if != 0
    jmp    .iterstring

.printchar:  
    push   eax
    push   ebx
    movzx  ebx, byte [eax+ecx] ; move single byte to ebx

    push   ecx
    mov    ecx, ebx  ; move ebx to print
    mov    edx, 1    ; print the character
    mov    ebx, 1
    mov    eax, 4
    int    80h

    pop    ecx
    pop    eax
    pop    ebx
    jmp    .iterstring  

.finish:  
    pop    eax  
    pop    ecx   
    pop    edx
    pop    esi
    pop    ebx
    ret  

; print string function (taken from tutorial)
; if I try to print single character with it I get SEGFAULT
stprint:
    push    edx
    push    ecx
    push    ebx
    push    eax
    call    stlen

    mov     edx, eax
    pop     eax

    mov     ecx, eax
    mov     ebx, 1
    mov     eax, 4
    int     80h

    pop     ebx
    pop     ecx
    pop     edx
    ret

stlen:
    push    ebx
    mov     ebx, eax

nextch:
    cmp     byte [eax], 0
    jz      finish
    inc     eax
    jmp     nextch

finish:
    sub     eax, ebx
    pop     ebx
    ret

Я безуспешно пытался использовать bl, al и cl.Я также попытался сделать некоторые проверки.Например, выведите счетчик в .iterstring:

nasm -f elf lr3.asm && ld -m elf_i386 lr3.o -o lr3 && ./lr3
Enter the string: test
1
2
3
4
5

Таким образом, похоже, что итерация работает нормально.

Больше всего мне повезло с ответом на аналогичный вопрос ( Как напечатать символ в Linux x86 NASM? ) с такими изменениями в коде:

.printchar:
  push   eax
  push   ebx
  push   esi
  mov    eax, inp
  movzx  ebx, byte [eax+ecx]
  mov    esi, ecx ; see below

  push   ecx
  push   ebx
  mov    ecx, esp
  mov    edx, 1    ; print the character
  mov    ebx, 1
  mov    eax, 4
  int    80h

  pop    ecx
  pop    ebx
  pop    eax
  pop    ebx
  mov    ecx, esi  ; without this it just prints 1 character and ends
  pop    esi       ; so ecx is not restored with pop for some reason?
  jmp    .iterstring

Но он печатает все, кроме первых символов:

nasm -f elf lr3.asm && ld -m elf_i386 lr3.o -o lr3 && ./lr3
Enter the string: somewords
mewords        

Я застрял и не могу понять, в чем мои ошибки.

Редактировать, окончательный код:

findodd:
    push   eax
    push   ecx
    push   edx
    push   esi
    push   ebx
    mov    esi, 0     ; counter
    mov    eax, inp

.iterstring:
    inc    esi
    cmp    byte [eax+esi], 0
    jz     .finish
    test   esi,1
    jz     .iterstring

    movzx   ecx, byte [eax+esi]
    push    ecx
    mov     ecx, esp
    mov     edx, 1
    mov     ebx, 1
    push    eax
    mov     eax, 4
    int     80h
    pop     eax
    pop     ecx
    jmp     .iterstring

.finish:
    pop    eax
    pop    ecx
    pop    edx
    pop    esi
    pop    ebx
    ret

Теперь все работает как задумано:

nasm -f elf lr3.asm && ld -m elf_i386 lr3.o -o lr3 && ./lr3
Enter the string: somewords
oeod

Мне пришлось убрать больше инструкций push-pop, а также пришлось переместить счетчик в esi, потому что регистры не нажималисьвсегда восстанавливал их значение в стеке, что странно для меня.
Когда я пытался перейти на ecx-адрес byte [eax+ecx], это работало, но когда я менял его на byte [eax+1], это вызывало segfault, потому что восстановление eax после popперерыв.Когда я нажал ecx, чтобы распечатать сообщение, а затем снова отправил его обратно, это привело к segfault, и gdb показал, что внутри ecx был мусорный код после извлечения.
С текущим кодом все работает нормально.

1 Ответ

3 голосов
/ 20 мая 2019

Эта строка неверна:

mov    ecx, ebx  ; move ebx to print

write (int 80h / eax=4) ожидает, что ecx будет содержать адрес данных для записи (см. thisтаблица ).Но вы передаете сами данные.

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

Как примечание: проверка четных / нечетных чисел неоправданно сложна.Это может быть упрощено до:

test ecx,1      ; set EFLAGS based on ecx AND 1
jnz .printchar
...