【问题标题】:Segmentation Fault on an assembly x64 linux tictactoe程序集x64 linux tictactoe上的分段错误
【发布时间】:2018-04-19 09:48:08
【问题描述】:

我从 16 位 msdos 改编了一个 tictactoe,以在 64 位的 linux 上工作。

section .bss
game_position_pointer resb 9
key resb 1

section .data
new_line db 10
nl_size equ $-new_line

game_draw db "_|_|_", 10 
          db "_|_|_", 10
          db "_|_|_", 10, 0
gd_size equ $-game_draw

win_flag db 0

player db "0", 0
p_size equ $-player

game_over_message db "FIM DE JOGO AMIGOS", 10, 0
gom_size equ $-game_over_message

game_start_message db "JOGO DA VELHA"
gsm_size equ $-game_start_message

player_message db "JOGADOR ", 0
pm_size equ $-player_message

win_message db " GANHOU!", 0
wm_size equ $-win_message

type_message db "ENTRE COM UMA POSICAO NO TABULEIRO: ", 0
tm_size equ $-type_message

clear_screen_ASCII_escape db 27,"[H",27,"[2J"      ; <ESC> [H  <ESC>  [2J
cs_size equ $-clear_screen_ASCII_escape

section .text 
global _start 

_start:
call set_game_pos_pointer

main_loop:
call clear_screen

mov rsi, game_start_message
mov rdx, gsm_size
call print

mov rsi, new_line
mov rdx, nl_size
call print

mov rsi, player_message
mov rdx, pm_size
call print

mov rsi, player
mov rdx, p_size
call print

mov rsi, new_line
mov rdx, nl_size
call print

mov rsi, game_draw
mov rdx, gd_size
call print

mov rsi, new_line
mov rdx, nl_size
call print

mov rsi, type_message
mov rdx, tm_size
call print

call read_keyboard               ; Vamos ler a posição que o usuário vai passar

mov al, [key]
sub al, 49                       ; 49 equivale a "1" em ASCII, eu faço essa subtração porque eu quero converter ASCII para inteiro, ao mesmo tempo que faço subtraio de 1 o valor inteiro


call update_draw

call check

cmp byte[win_flag], 1
je game_over

call change_player

jmp main_loop

change_player:

mov rsi, player
xor byte[rsi], 1  ; Tipo um xor swap :)

ret

print:
mov rax, 1
mov rdi, 1
syscall
ret

read_keyboard:
mov rax, 0
mov rdi, 0
mov rsi, key 
mov rdx, 1
syscall

ret

clear_screen:
mov rsi, clear_screen_ASCII_escape
mov rdx, cs_size
call print
ret

set_game_pos_pointer:
mov rsi, game_draw
mov rbx, game_position_pointer

mov rcx, 9

loop_1:
    mov [rbx], rsi
    add rsi, 2

    inc rbx
    loop loop_1

ret

update_draw:
lea rbx, [game_position_pointer + rax]

mov rsi, player

cmp byte[rsi], "0"
je draw_x

cmp byte[rsi], "1"
je draw_o

draw_x:
    mov cl, "x"
    jmp update

draw_o:
    mov cl, "o"
    jmp update

update:

    mov [rbx], cl


ret

check:
call check_line
ret

check_line:

mov rcx, 0

check_line_loop:
    cmp rcx, 0
    je first_line

    cmp rcx, 1
    je second_line

    cmp rcx, 2
    je third_line

    call check_column
    ret

    first_line:
        mov rsi, 0
        jmp do_check_line

    second_line:
        mov rsi, 3
        jmp do_check_line

    third_line:
        mov rsi, 6
        jmp do_check_line

    do_check_line:
        inc rcx

        lea rbx, [game_position_pointer + rsi]
        mov al, [rbx]
        cmp al, "_"
        je check_line_loop

        inc rsi
        lea rbx, [game_position_pointer + rsi]
        cmp al, [rbx]
        jne check_line_loop

        inc rsi
        lea rbx, [game_position_pointer + rsi]
        cmp al, [rbx]
        jne check_line_loop

    mov byte[win_flag], 1
    ret

check_column:
mov rcx, 0

check_colum_loop:
    cmp rcx, 0
    je first_column

    cmp rcx, 1
    je second_column

    cmp rcx, 2
    je third_column

    call check_diagonal
    ret

    first_column:
        mov rsi, 0
        jmp do_check_column

    second_column:
        mov rsi, 1
        jmp do_check_column

    third_column:
        mov rsi, 2
        jmp do_check_column

    do_check_column:
        inc rcx

        lea rbx, [game_position_pointer + rsi]
        mov al, [rbx]
        cmp al, "_"
        je check_colum_loop

        add rsi, 3
        lea rbx, [game_position_pointer + rsi]
        cmp al, [rbx]
        jne check_colum_loop

        add rsi, 3
        lea rbx, [game_position_pointer + rsi]
        cmp al, [rbx]
        jne check_colum_loop

        mov byte[win_flag], 1
        ret

check_diagonal:
mov rcx, 0

check_diagonal_loop:
    cmp rcx, 0
    je first_diagonal

    cmp rcx, 1
    je second_diagonal

    ret

first_diagonal:
    mov rsi, 0
    mov rdx, 4          ; tamanho do pulo que vamos dar para o meio da diagonal 
    jmp do_check_diagonal

second_diagonal:
    mov rsi, 2
    mov rdx, 2
    jmp do_check_diagonal

do_check_diagonal:
    inc rcx

    lea rbx, [game_position_pointer + rsi]
    mov al, [rbx]
    cmp al, "_"
    je check_diagonal_loop

    add rsi, rdx
    lea rbx, [game_position_pointer + rsi]
    cmp al, [rbx]
    jne check_diagonal_loop

    add rsi, rdx
    lea rbx, [game_position_pointer + rsi]
    cmp al, [rbx]
    jne check_diagonal_loop

mov byte[win_flag], 1
ret

game_over:
call clear_screen

mov rsi, game_start_message
mov rdx, gsm_size
call print

mov rsi, new_line
mov rdx, nl_size
call print

mov rsi, game_draw
mov rdx, gd_size
call print

mov rsi, new_line
mov rdx, nl_size
call print

mov rsi, game_over_message
mov rdx, gom_size
call print

mov rsi, player_message
mov rdx, pm_size
call print

mov rsi, player
mov rdx, p_size
call print

mov rsi, win_message
mov rdx, wm_size
call print

jmp fim

fim:
mov rax, 60
mov rdi, 0
syscall

但是标签 update_draw 的这一行存在分段错误:

mov [rbx], cl

我只是无法理解我做错了什么。任何建议或帮助将不胜感激

编辑: 我已经编辑了添加建议的代码,奇怪的是,我发现 change_player 标签没有改变任何东西,实际上看起来我的整个代码没有设法修改部分数据的变量。例如,

change_player:
    mov si, player
    xor byte[si], 1  ; Tipo um xor swap :)
    ret

不改变变量 player,值始终为“0”

【问题讨论】:

  • 段错误在哪条指令上?使用gdb 找出答案。这是一个几乎没有任何 cmets 的大量代码转储,与 minimal reproducible example 相反。 idownvotedbecau.se/nodebuggingidownvotedbecau.se/toomuchcodeidownvotedbecau.se/unreadablecode
  • 哦,代码后面有文字,我是在修复您的格式后才看到的。此时rbx 中的值是什么(使用调试器检查。mov bl, byte[game_position_pointer + rax] 写入 RBX 的低字节,因此这看起来很可疑,除非在到达使用它作为指针的指令之前将 RBX 设置为有效指针,或者,如果您要索引 256 字节对齐的内容,并且部分寄存器写入是进行数组索引的一种 hacky 方式。
  • mov si, [player] 是替换 si 中先前值的负载。您的意思是用mov [player], si 存储吗? x86-64 有很多寄存器;您可能根本不需要内存,例如xor r15d, 1。下次遇到困难时,使用调试器单步检查寄存器。
  • 其实我忘了编辑这最后一部分,现在看看。
  • xor 正在正常工作,我在这些变量中写入的任何内容实际上都没有被写入。再比如update_draw标签,mov [rbx], cl这行好像没什么效果。

标签: assembly segmentation-fault x86-64 tic-tac-toe


【解决方案1】:

有些地方看起来很可疑,尤其是

mov bl, byte[game_position_pointer + rax]

mov rbx, [game_position_pointer + rsi]
mov al, [rbx]

bl 覆盖了 rbx 的最低 8 位,因此该操作看起来像是将字节从“rax”中的板位置移动到 bl。 Rbx 随后用作指针(即您的错误指令)。

在第二个例子中,看起来意图是将棋盘位置的地址加载到 rbx 中,然后获取该位置的值。那看起来像

lea rbx, [game_position_pointer + rsi]
mov al, [rbx]

或者更简洁的

mov al, game_position_pointer[rsi]

抱歉,如果我弄乱了语法,我不熟悉你的汇编风格。 GNU 提供了一种更精确的语法。

【讨论】:

  • 再次,masm 语法是一团糟,但您似乎将播放器用作指针而不是标签。我很惊讶你没有得到很多 segv。
  • 当我说乱七八糟的时候,gas 的“粗略”语法的好处是您可以明确区分加载地址和值;而 DOS 汇编掩盖了这一点。
  • 我在这里测试,发现sys_read在我键入位置后正在读取“\n”,所以它跳过了下一个循环循环并返回“0”,让印象播放器变量从未改变。哈哈。
  • 尽管如此,问题依然存在,在game_position_pointer中,函数set_game_pos_pointer应该将棋盘的每个方格与一个位置相关联,当调用update_draw时,指针工作正常,但是字符串game_draw不受影响。
【解决方案2】:

在:

mov rbx, [game_position_pointer + rsi]

mov al,[rbx]

您使用位置[game_position_pointer + rsi] 中的值作为导致分段错误的指针,您需要使用game_position_pointer + rsi 位置本身

**我不知道它是否适用于您的游戏盒,或者您可以使用

mov al ,byte[game_position_pointer + rsi]

【讨论】:

  • 我已经对代码进行了一些更改,例如 lea rbx, [game_position_pointer + rsi]
  • 感谢您的回复!
  • game_position_pointer 数组的内容是 9 个地址,所以我使用 location [game_position_pointer + rsi] 中的值作为指针。但导致分段 f。我不知道如何取消引用地址。
猜你喜欢
  • 1970-01-01
  • 2020-05-25
  • 2017-11-04
  • 1970-01-01
  • 2016-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多