NASM: 两次后续文件写入无效

NASM: Two subsequent file writes not working

尝试 运行 此代码以便我可以创建 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

你在没有解释的情况下将54添加到文件描述符;我完全不知道你为什么要那样做。

我怀疑您误解了文件描述符,认为您需要将到目前为止写入的数据总量添加到描述符中。事实并非如此。从您 open/create 到您关闭文件句柄,描述符没有改变。验证您的评论是否与您的代码同步是一个非常好的主意。当您编写详细注释时,没有注释的行会立即变得可疑(例如 add 指令。)

您似乎从一开始就有一些问题。例如,您的评论说 "open file" 和 "sys_write" 但您的代码不匹配。您的代码当前所做的是尝试调用 sys_creat。你所说的文件描述符实际上是权限模式。 ebx 应该包含表示路径的字符串的地址...注释似乎表明它应该是标准输出,但显然不是。 :)

您也没有说明这是针对 64 位还是 32 位 Linux。您的代码似乎混合了两者,使用 r8d 并使用 int 0x80.

您的代码有 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 . Note that the system call numbers used with SYSCALL are different from int 0x80. The registers used to pass parameters are different, and the only registers not preserved across a SYSCALL are RCX, R11, and RAX (RAX being the return value). The system calling convention is thoroughly described in the current 64-bit Linux System V ABI。特别是:

  1. 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.
  2. A system-call is done via the syscall instruction. The kernel destroys registers %rcx and %r11.
  3. The number of the syscall has to be passed in register %rax.
  4. System-calls are limited to six arguments, no argument is passed directly on the stack.
  5. 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.
  6. Only values of class INTEGER or class MEMORY are passed to the kernel

如果您希望 64 位代码使用 INT 0x80

INT 0x80 在 64 位代码中有一些怪癖。它遵守保留RBXRCXRDX、[=41]的32位调用约定=]RSI、RDIRBP。对于其他 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

参见图 3.4:在上面链接的 64 位 Linux ABI 中注册使用R12R13R14R15 也将被保留。

这表示RAX,R8,R9,R10 R11 将不会被保留。将您的代码从使用 R8D 更改为保存的寄存器之一。 R12D 例如。


为什么你的代码会失败?

由于 R8D 未在 int 0x80 中保留,因此它可能被 SYS_WRITE 系统调用覆盖.第一个写的有效,第二个没有,因为 R8D 很可能被第一个 SYS_WRITER8D 可能成为无效的文件描述符。使用将被保留的寄存器之一应该可以解决这个问题。如果你 运行 超出了寄存器,你总是可以在堆栈上分配 space 用于临时存储。

(代表OP发布解决方案).

这是解决方案的源代码,64 位版本:

section     .text
global      _start                              ;must be declared for linker (ld)

_start:                                         ;tell linker entry point

;#######################################################################
;### This program creates empty bmp file - 64 bit version ##############
;#######################################################################
;### main ##############################################################
;#######################################################################

    ; open file
    mov     rax,85                               ;system call number - open/create file
    mov     rdi,msg                             ;file name
                                                ;flags
    mov     rsi,111111111b                      ;mode
    syscall                                     ;call kernel

    ; save file descriptor
    mov     r8, rax

    ; write headline to file
    mov     rax, 1                              ;write to file
    mov     rdi, r8                             ;load file desc
    mov     rsi, bmpheadline                    ;load adress of memory to write
    mov     rdx, 54                             ;load number of bytes
    syscall                                     ;call kernel

    ; write content to file
    mov     rax, 1                              ;write to file
    mov     rdi, r8                             ;load file desc
    mov     rsi, empty_space                    ;load adress of memory to write
    mov     rdx, 40000                          ;load number of bytes
    syscall                                     ;call kernel

    ; close file
    mov     rax, 3                              ;load syscall number - close
    mov     rdi, r8                             ;load file desc
    syscall                                     ;call kernel

    ; exit program
    mov     rax,60                               ;system call number (sys_exit)
    syscall                                     ;call kernel

section     .data

    msg     db  'filename.bmp',0x00                 ;name of out file, 0x00 = end of string
    len     equ $ - msg                             ;length of our dear 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 40000

生成文件:

all: a.out

a.out: main.o
        ld main.o

main.o: main64.asm
        nasm -f elf64 main64.asm -o main.o