我有一个 x86-64 程序,仅当从 gdb 调试器运行时才能正常工作
我在 x86 汇编程序中编写了 malloc 的原始版本作为练习。该代码使用链表来跟踪分配的内存块。我决定添加一个函数来遍历列表并打印出每个块的元数据,并遇到了这个奇怪的问题。当我使用 gdb 运行代码时,它可以正常工作,但是当我不使用 gdb 直接运行时,它就不能正常工作。当我将 sbrk 返回的地址打印为十六进制字符串时,只有从 gdb 运行时才能正确打印。如果在没有 gdb 的情况下重复运行,则每次运行都会打印不同的数字。我已将代码缩减到说明问题所需的最少代码。我已经尝试了所有我能想到的方法来找出问题所在。我确信我的 itoh 和 printstring 函数工作正常。我尝试过链接 c 库并使用 put,但效果相同。我尝试将所有寄存器初始化为零。我查找了因调用 sbrk 而更改的任何寄存器,并在调用过程中保存和恢复了它们。没有任何效果。这是说明问题的代码:
global _start,itoh,printstring
section .rodata
TRUE equ 1
FALSE equ 0
NULL equ 0
LF equ 10
sys_brk equ 12
exit_ok equ 0
sys_exit equ 60
sys_write equ 1
stdout equ 1
section .data
current_brk dq 1
linefeed db LF, NULL
msg1 db 'Test should print 0x403000 from constant: ', NULL
msg2 db 'Test should print 0x403000 from sys_brk return: ', NULL
number db '--------------------', NULL
section .text
_start: mov rdi, msg1
call printstring
mov rdi, 0x403000
mov rsi, number
mov rdx, TRUE
call itoh
mov rdi, number
call printstring
mov rax, sys_brk
syscall
mov [current_brk], rax
mov rdi, msg2
call printstring
mov rdi, [current_brk]
mov rsi, number
mov rdx, TRUE
call itoh
mov rdi, number
call printstring
.exit: mov rax, sys_exit
mov rdi, exit_ok
syscall
;
; itoh - rdi intger to convert
; - rsi address of string to return result
; - rdx if true add a newline to string
; return nothing
itoh: push rcx
push rax
xor r10, r10 ; r10 counts the digits pushed onto stack
mov r9, rdx ; save newline flag in r9
mov rax, rdi ; rax is bottom half of dividend
mov rcx, 16 ; rcx is divisor
.div: xor rdx, rdx ; zero rdx, top half of 128 bit dividend
div rcx ; divide rdx:rax by rcx
push rdx ; rdx is remainder
inc r10 ; increment digit counter
cmp rax, 0 ; is quotient zero?
jne .div ; no - keep dividimg by 16 and pushing remainder
.pop: mov byte[rsi], "0"
inc rsi
mov byte[rsi], "x"
inc rsi
.p0: pop r11 ; get a digit from stack
cmp r11, 10
jl .p1
sub r11, 10
add r11, "a"
jmp .p2
.p1: add r11, "0" ; convert to ascii char
.p2: mov byte[rsi],r11b ; copy ascii digit to string buffer
dec r10 ; decrement digit count
inc rsi ; point rsi to next char position
cmp r10, 0 ; is digit counter 0
jne .p0 ; no, go get another digit from stack
cmp r9, 0
je .exit
mov byte[rsi], LF
inc rsi
.exit: mov byte[rsi], NULL ; terminate string
pop rax
pop rcx
ret
;
; printstring - rdi is address of string
; return nothing
printstring:
push rcx ; sys_write modifies rcx
push rax ; sys_write modifies rax
xor rdx, rdx ; zero rdx, char count
mov rsi, rdi ; use rsi to index into string
.countloop:
cmp byte [rsi],NULL ; end of string?
je .countdone ; yes, finished counting
inc rdx ; no, count++
inc rsi ; point to next char
jmp .countloop
.countdone:
cmp rdx, 0 ; were there any characters?
je .printdone ; no - exit
mov rax, sys_write ; write system call
mov rsi, rdi ; address of string
mov rdi, stdout ; write to stdout
syscall ; number of bytes to write is in rdx
.printdone:
pop rax
pop rcx
ret
yasm -felf64 -gdwarf2 test.asm
ld -g -otest test.o
gdb test
Type "apropos word" to search for commands related to "word"...
Reading symbols from test...
[?2004h(gdb) run
[?2004l
Starting program: /home/david/asm/test
Test should print 0x403000 from constant: 0x403000
Test should print 0x403000 from sys_brk return: 0x403000
[Inferior 1 (process 28325) exited normally]
[?2004h[?2004l
[?2004h(gdb) q
[?2004l
./test
Test should print 0x403000 from constant: 0x403000
Test should print 0x403000 from sys_brk return: 0x14cf000
I have written a primitive version of malloc in x86 assembler as an exercise. The code uses a linked list to keep track of allocated memory blocks. I decided to add a function to walk the list and print out the meta data for each block and encountered this weird problem. When I run the code using gdb it works properly but when run directly without gdb it does not. When I print out an address returned by sbrk as a hex string it only prints correctly if run from gdb. If run repeatedly without gdb it prints a different number each run. I have cut the code down to the minimum needed to illustrate the problem. I have tried everything I can think of to find the problem. I'm sure that my itoh and printstring funcions are working correctly. I have tried linking with the c library and using puts but it does the same. I tried initializing all registers to zero. I have looked for any registers altered by the call to sbrk and saved and restored them across the call. Nothing has worked. Here is the code that illustrates the problem:
global _start,itoh,printstring
section .rodata
TRUE equ 1
FALSE equ 0
NULL equ 0
LF equ 10
sys_brk equ 12
exit_ok equ 0
sys_exit equ 60
sys_write equ 1
stdout equ 1
section .data
current_brk dq 1
linefeed db LF, NULL
msg1 db 'Test should print 0x403000 from constant: ', NULL
msg2 db 'Test should print 0x403000 from sys_brk return: ', NULL
number db '--------------------', NULL
section .text
_start: mov rdi, msg1
call printstring
mov rdi, 0x403000
mov rsi, number
mov rdx, TRUE
call itoh
mov rdi, number
call printstring
mov rax, sys_brk
syscall
mov [current_brk], rax
mov rdi, msg2
call printstring
mov rdi, [current_brk]
mov rsi, number
mov rdx, TRUE
call itoh
mov rdi, number
call printstring
.exit: mov rax, sys_exit
mov rdi, exit_ok
syscall
;
; itoh - rdi intger to convert
; - rsi address of string to return result
; - rdx if true add a newline to string
; return nothing
itoh: push rcx
push rax
xor r10, r10 ; r10 counts the digits pushed onto stack
mov r9, rdx ; save newline flag in r9
mov rax, rdi ; rax is bottom half of dividend
mov rcx, 16 ; rcx is divisor
.div: xor rdx, rdx ; zero rdx, top half of 128 bit dividend
div rcx ; divide rdx:rax by rcx
push rdx ; rdx is remainder
inc r10 ; increment digit counter
cmp rax, 0 ; is quotient zero?
jne .div ; no - keep dividimg by 16 and pushing remainder
.pop: mov byte[rsi], "0"
inc rsi
mov byte[rsi], "x"
inc rsi
.p0: pop r11 ; get a digit from stack
cmp r11, 10
jl .p1
sub r11, 10
add r11, "a"
jmp .p2
.p1: add r11, "0" ; convert to ascii char
.p2: mov byte[rsi],r11b ; copy ascii digit to string buffer
dec r10 ; decrement digit count
inc rsi ; point rsi to next char position
cmp r10, 0 ; is digit counter 0
jne .p0 ; no, go get another digit from stack
cmp r9, 0
je .exit
mov byte[rsi], LF
inc rsi
.exit: mov byte[rsi], NULL ; terminate string
pop rax
pop rcx
ret
;
; printstring - rdi is address of string
; return nothing
printstring:
push rcx ; sys_write modifies rcx
push rax ; sys_write modifies rax
xor rdx, rdx ; zero rdx, char count
mov rsi, rdi ; use rsi to index into string
.countloop:
cmp byte [rsi],NULL ; end of string?
je .countdone ; yes, finished counting
inc rdx ; no, count++
inc rsi ; point to next char
jmp .countloop
.countdone:
cmp rdx, 0 ; were there any characters?
je .printdone ; no - exit
mov rax, sys_write ; write system call
mov rsi, rdi ; address of string
mov rdi, stdout ; write to stdout
syscall ; number of bytes to write is in rdx
.printdone:
pop rax
pop rcx
ret
yasm -felf64 -gdwarf2 test.asm
ld -g -otest test.o
gdb test
Type "apropos word" to search for commands related to "word"...
Reading symbols from test...
[?2004h(gdb) run
[?2004l
Starting program: /home/david/asm/test
Test should print 0x403000 from constant: 0x403000
Test should print 0x403000 from sys_brk return: 0x403000
[Inferior 1 (process 28325) exited normally]
[?2004h[?2004l
[?2004h(gdb) q
[?2004l
./test
Test should print 0x403000 from constant: 0x403000
Test should print 0x403000 from sys_brk return: 0x14cf000
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论