我一直在努力教自己一些32位x86(NASM)。我试图让用户输入文件名,打开/创建文件,然后获取用户的消息并将该消息写入文件。我在GDB中完成了它并且所有系统调用都正确返回。程序运行后,文件似乎创建不正确,没有任何内容写入。我看到其他一些类似的问题,但我的代码似乎与他们的代码差不多,所以我似乎无法弄清楚到底发生了什么。
这是我的菜鸟代码:
global _start
section .data
fmsg: db "Enter Filename: ", 0
.len: equ $ - fmsg
umsg: db "Enter message: ", 0
.len: equ $ - umsg
buff: times 50 db 0 ;array for user string
.blen: equ $ - buff
fname: times 50 db 0 ;array for filename
.flen: equ $ - fname
;modes
O_RDONLY: db 0 ;read-only
O_WRONLY: db 1 ;wirte-only
O_RDWR: db 2 ;read and write
;flags
O_CREAT: dw 100o ;create file if file doesnt exists
O_TRUNC: dw 1000o ;truncate file
O_APPEND: dw 2000o ;append to file
section .bss
fd: resd 1 ;file descriptor
bret: resd 1 ;read buffer return value
fret: resd 1 ;read filename return value
tmp: resd 1 ;temp 4 byte variable
section .text
_start:
fprompt: ;Print prompt
mov eax, 0x4 ;syscall 4 - write()
mov ebx, 0x1 ;file desc 1 - stdout
mov ecx, fmsg ;print message
mov edx, fmsg.len ;length of message
int 80h ;syscall interupt
filein:
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, fname ;dst buffer
mov edx, fname.flen ;length of buffer
int 80h ;syscall interupt
mov [fret], eax ;save return value to file return variable
cmp eax, edx ;read max bytes or more?
jb fileopen ;jmp is bytes read < max
mov bl, [ecx+eax-1] ;grab last byte @ last index before '\0'
cmp bl, 10 ;does it = '\n' ?
je clean1
inc DWORD [fret] ;len++
clean1: ;loop to clear excess input, if any
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, tmp ;temp buffer
mov edx, 0x1 ;read only 1 byte
int 80h ;;syscall interupt
test eax, eax ;EOF?
jz fileopen ;Yes, jump to pback
mov al, [tmp] ;put character into lower 8 bits of EAX
cmp al, 10 ;is it = to lf ?
jne clean1 ;no, jump to begining of loop
fileopen:
mov eax, 0x05
mov ebx, fname ;filename
or ecx, O_CREAT ;if it doesn't exist create the file
or ecx, O_TRUNC ;truncate
mov edx, O_WRONLY ;write only
int 80h ;syscall interupt
mov [fd], eax ;save file descripor
prompt2:
mov eax, 0x4 ;syscall 4 - write()
mov ebx, 0x1 ;file desc 1 - stdout
mov ecx, umsg ;print message
mov edx, umsg.len ;length of message
int 80h
userin:
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, buff ;dst buffer
mov edx, buff.blen ;length of buffer
int 80h ;syscall interupt
mov [bret], eax ;save return value to buff return variable
cmp eax, edx ;read max bytes or more?
jb writetofile ;jmp is bytes read < max
mov bl, [ecx+eax-1] ;grab last byte @ last index before '\0'
cmp bl, 10 ;does it = '\n' ?
je clean2
inc DWORD [bret] ;len++
clean2: ;loop to clear excess input, if any
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, tmp ;temp buffer
mov edx, 0x1 ;read only 1 byte
int 80h ;syscall
test eax, eax ;EOF?
jz writetofile ;Yes, jump to pback
mov al, [tmp] ;put character into lower 8 bits of EAX
cmp al, 10 ;is it = to lf ?
jne clean2 ;no, jump to begining of loop
writetofile:
mov eax, 0x4 ;syscall 4 - write()
mov ebx, [fd] ;file desc 1 - stdout
mov ecx, buff ;print message
mov edx, [bret] ;length of message
int 80h ;syscall interupt
closefile:
mov eax, 0x6 ;syscall 6 - close()
mov ebx, [fd] ;file desc
int 80h ;syscall interupt
exit: ;return 0
mov eax, 1 ;syscall 1 - exit()
mov ebx, 0 ;return val
int 80h ;syscall interupt
文件“test.txt?”显示并显示为可执行文件,即使我只设置文件的读/写。即使我试图打开它,也没有任何东西。有什么想法吗?另外正如我所提到的,我是新人并自学,所以如果你有任何关于改进其他方面的好方法,请告诉我! :)
答案 0 :(得分:1)
我们在以下三行代码中有多个错误(或一个大错误):
or ecx, O_CREAT ;if it doesn't exist create the file
or ecx, O_TRUNC ;truncate
mov edx, O_WRONLY ;write only
问题:
寄存器ecx
和edx
在这些行之后有什么值?
您使用or
寄存器执行了两次ecx
操作,但显然此时尚未初始化!
这意味着您可以确定代表O_CREAT
和O_TRUNC
的位(无论这些值是什么意思 - 见下文)都已设置,但您不知道其他位具有哪些值。< / p>
O_WRONLY
位应设置为ecx
,而不是edx
。 edx
应该包含所需的文件模式。
不幸的是,有两种不同类型的汇编程序 - 我不知道NASM的类型是什么:
or ecx, [O_CREAT]
or ecx, address_of(O_CREAT)
在第一种情况下,指令mov edx, O_WRONLY
将从O_WRONLY
字节开始读取四个字节到edx
寄存器,因此edx
将具有值0x400201
(O_CREAT *为0x10000 + O_RDWR *为0x100 + O_WRONLY)。
在第二种情况下,edx
将包含O_WRONLY
的地址,而不是值。
在任何情况下,该值都是错误的。
答案 1 :(得分:0)
将模式常量存储在内存中效率非常低,即使你做得对(你不是这样)。
您可以通过运行strace ./writefile
跟踪系统调用的实际内容。
使用O_RDONLY: db 0
存储一个静态数据字节(由于某种原因,在读/写部分而不是.rodata
)。相反,您应该使用equ
:
O_RDONLY equ 0
O_WRONLY equ 1
O_RDWR equ 2
O_CREAT equ 100o ;create file if file doesnt exists
O_TRUNC equ 1000o ;truncate file
O_APPEND equ 2000o ;append to file
然后你可以写
mov ecx, O_CREAT | O_TRUNC | O_WRONLY
mov edx, 0777o ; mode is the permission bits if open() creates the file
请参阅the open(2)
man page了解其args的工作原理。
汇编程序将在汇编时为您执行OR,而不是让CPU从内存中执行2次加载。
你实际写的是什么:
or ecx, O_CREAT ;if it doesn't exist create the file
or ecx, O_TRUNC ;truncate
汇总到两条or ecx, imm32
条指令,其中两个dw
位置的地址或ecx
的原始值。如果你写了
movzx ecx, word [O_CREAT]
or cx, [O_TRUNC]
or cx, [O_WRONLY]
你的代码会有效,但那真的很傻。 (并且在仅写入低16位后,某些CPU会在某些内容读取满ecx
时导致部分寄存器停止。)
如果你写了or ecx, [O_TRUNC]
,它会做32位加载,所以你有效地做ecx |= (2000o << 16) | 1000o
。即,存储在O_APPEND
的字将被“或”到ECX的高16位,在那里它可能具有不同的含义。
同样,mov edx, O_WRONLY
汇编到mov edx, imm32
,其中地址是直接的。这就是为什么你最终为文件模式(包括粘滞位设置)的奇怪垃圾。
使用调试程序。