尝试运行此代码以便我可以创建 bmp 文件 - 我写标题,然后我想将内容写入文件 - 一切都单独工作但不能一起工作。 如果重要的话,我正在使用 hexedit 检查文件。
如果我运行带有标题编写部分的代码,它就可以工作。 如果我运行带有内容编写部分的代码,它就可以工作。 当我同时运行它们时却没有。
有什么想法吗?
代码如下:
section .text
global _start
_start:
;#######################################################################
;### main ##############################################################
;#######################################################################
; open file
mov eax,8 ;system call number - open/create file
mov ebx,msg ;file name
mov ecx,111111111b ;file mode
int 0x80 ;call kernel
; save file descriptor to r8d
mov r8d, eax
; write headline to file
mov eax, 4 ;write 54 bytes to file
mov ebx, r8d ;load file desc
mov ecx, bmpheadline ;load adress of memory to write
mov edx, 54 ;load number of bytes
int 0x80 ;call kernel
; write content to file
mov eax, 4 ;number of syscall - write
mov ebx, r8d ;load file desc
;add ebx, 54 ;add 54 bytes to location of file location
mov ecx, empty_space ;load adress of buffer
mov edx, 40054 ;load number of bytes
int 0x80 ;call kernel
; close file
mov eax, 6 ;load syscall number - close
mov ebx, r8d ;load file desc
int 0x80 ;call kernel
; exit program
mov eax,1 ;syscall number - exit
int 0x80 ;call kernel
section .data
msg db 'filename.bmp',0x00 ;name of out file, 0x00 = end of string
bmpheadline db 0x42,0x4D,0xB6,0xDA,0x01,0x00,0x00,0x00,0x00,0x00,0x7A,0x00,0x00,0x00,0x6C,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0xC9,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x3C,0xDA,0x01,0x00,0x13,0x0B,0x00,0x00,0x13,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x47,0x52,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
section .bss
empty_space: resb 40054
最佳答案
您的代码有 2 个重大问题。 R8D (R8) 未在 int 0x80
中保留。其次,您原始问题中的 add ebx, 54
是不正确的。您不需要更改文件描述符。
64 位代码首选 SYSCALL
int 0x80
是 Linux 内核中的 IA32 兼容性功能。大多数 64 位 Linux 内核通常会启用此功能,但也可以将其关闭。您不能将 64 位指针与 int 0x80
一起使用。这可以防止使用基于堆栈的地址作为 int 0x80
的参数。出于这些原因,最好对 64 位程序使用 SYSCALL 而不是 int 0x80
。
有关在 Linux 中使用 SYSCALL 的更多信息,请参阅 Ryan Chapman's Blog .请注意,与 SYSCALL 一起使用的系统调用号与 int 0x80
不同。用于传递参数的寄存器不同,SYSCALL 中唯一不保留的寄存器是 RCX、R11 和 RAX(RAX 是返回值)。当前的 64-bit Linux System V ABI 中详细描述了系统调用约定。 .特别是:
- User-level applications use as integer registers for passing the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9. The kernel interface uses %rdi, %rsi, %rdx, %r10, %r8 and %r9.
- A system-call is done via the syscall instruction. The kernel destroys registers %rcx and %r11.
- The number of the syscall has to be passed in register %rax.
- System-calls are limited to six arguments, no argument is passed directly on the stack.
- Returning from the syscall, register %rax contains the result of the system-call. A value in the range between -4095 and -1 indicates an error, it is -errno.
- Only values of class INTEGER or class MEMORY are passed to the kernel
如果您希望您的 64 位代码使用 INT 0x80
INT 0x80 在 64 位代码中有一些怪癖。它遵守保留 RBX、RCX、RDX、RSI、 的 32 位调用约定RDI 和RBP。对于其他 64 位寄存器,适用 64 位 C 调用约定。来自 ABI:
A.2.1 Calling Conventions
... applications that like to call system calls should use the functions from the C library. The interface between the C library and the Linux kernel is the same as for the user-level applications
参见上面链接的 64 位 Linux ABI 中的图 3.4:寄存器用法。 R12、R13、R14 和 R15 也将被保留。
这意味着 RAX、R8、R9、R10 和 R11 不会被保留。将您的代码从使用 R8D 更改为保存的寄存器之一。 R12D 例如。
为什么你的代码会失败?
由于 R8D 未在 int 0x80
中保留,因此它可能被 SYS_WRITE 系统调用覆盖。第一个写入有效,第二个无效,因为 R8D 可能被第一个 SYS_WRITE 破坏,并且 R8D 可能成为无效的文件描述符.使用将被保留的寄存器之一应该可以解决这个问题。如果您用完了寄存器,您始终可以在堆栈上分配空间用于临时存储。
关于linux - NASM:两个后续文件写入不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40915909/