如何在装配过程中使用缓冲区? [英] How do I use a buffer in an assembly procedure?

查看:47
本文介绍了如何在装配过程中使用缓冲区?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以,我理解缓冲区的一般抽象概念:它是内存中的一种分配,用于在数据被处理之前保存数据.我正在尝试完成一个作业问题,该问题要求我将 ASCII 字符串写入过程中的缓冲区.所以,我知道我应该在调用它时将数组的地址传递给过程,例如......

So, I understand the general abstract concept of a buffer: it's an allocation in memory that holds data before it gets processed. I'm trying to complete a homework problem which requires me to write an ASCII string into a buffer within a procedure. So, I understand that I'm supposed to pass an address of an array to the procedure when calling it, for example...

main PROC
mov  EAX, packed           ; pass a packed decimal to EAX
mov  ESI, OFFSET ascArray  ; pass the offset of an empty array to ESI
call PackedToAsc           ; call the function

因此该函数应该返回一个指向带有 ASCII 十进制字符串的缓冲区的指针".我可能在这里很愚蠢,但我不太清楚在这种情况下缓冲区究竟是什么.

So the function is supposed to return 'a pointer to a buffer with the ASCII decimal string'. I'm probably being stupid here, but I'm not quite clear on what exactly a buffer is in this instance.

  • 是数组吗?
  • 我需要在 .data 部分声明它吗?
  • 如何在过程中声明指向 ASCII 字符串的指针?
  • 在这种情况下缓冲区是什么意思?

更实际的是,我需要访问程序完成后数据放入的缓冲区,但我不知道该怎么做.

More practically, I need to access the buffer that the data is put into when the procedure finishes, and I'm not sure how to do that.

编辑-- 我使用的是 x86,我使用的是 MASM.

EDIT   --   I'm in x86, and I'm using MASM.

推荐答案

是的,缓冲区只是一个数组,在汇编中是一个字节序列.

Yes, a buffer is just an array, which in assembly is a sequence of bytes.

您有 3 个主要选项来分配它,就像在 C 中一样:

You have 3 main options for allocating it, exactly like in C:

  • 静态存储:像 C static char buf[100];

  section .bss                   ; this might not be proper MASM syntax
   my_buffer:  db   100 dup(?)   ; but this is definitely MASM

在标签名称和 db 之间放置一个 : 使它只是一个普通标签,就像 NASM,而不是 MASM变量";带有隐含的操作数大小.(如果 MASM 允许您在 .data/.bss 部分中执行此操作.它可能不会.)

Putting a : between the label name and the db makes it just a plain label, like NASM, not a MASM "variable" with an implied operand-size. (If MASM lets you do that in a .data / .bss section. It might not.)

100 dup 表示将下一件事重复 100 次.? 表示未初始化的存储.它实际上会在运行在像 Windows 这样的操作系统下的程序中归零,因为它不能让程序看到内核数据或同一台机器上的其他进程遗留的陈旧数据.所以 100 dup(0) 也可以工作,也许可以更好地描述你想要什么,特别是如果你的代码在没有先写入的情况下读取了这些字节中的任何一个.

100 dup means to repeat the next thing 100 times. ? means uninitialized storage. It's actually going to be zeroed in a program that runs under an OS like Windows, because it can't let programs see stale data left over from kernel data or other processes on the same machine. So 100 dup(0) would also work and maybe be a better description of what you want, especially if your code ever reads any of these bytes without writing first.

动态存储:调用 malloc,或直接调用操作系统函数,如 mmapVirtualAlloc.你可以从分配它的函数返回一个指向它的指针.

dynamic storage: call malloc, or invoke an OS function directly like mmap or VirtualAlloc. You can return a pointer to it from the function that allocated it.

自动存储(在堆栈上):就像一个 C 局部变量.当分配函数返回时自动解除分配.非常便宜且简单,除非您知道它们需要多兆字节,否则将其用于暂存缓冲区.

automatic storage (on the stack): like a C local variable. Deallocated automatically when the allocating function returns. Very cheap and easy, use this for scratch buffers unless you know they need to be multiple megabytes.

处理缓冲区的最简单方法是接受一个指向已分配缓冲区的指针,并让您的调用者选择要传递的缓冲区.

The easiest way to deal with buffers is to accept a pointer to an already-allocated buffer, and give your caller the choice of what buffer to pass.

例如,一个大写 ASCII 字母的函数可以只接受一个 src 和 dst 指针.如果你希望它就地操作,你可以只为输入和输出传递相同的指针,如果它是为了支持而编写的.它不需要关心内存管理,它只是在两个缓冲区之间运行.

For example, a function that uppercases ASCII letters could just take a src and dst pointer. If you want it to operate in-place, you can just pass the same pointer for input and output, if it's written to support that. It doesn't have to care about memory management, it just operates between two buffers.

像 C strdup 这样的函数会创建一个字符串的新副本,这只有在动态存储时才有意义.将字符串复制到静态缓冲区并返回它不会很好地工作,因为该静态缓冲区只有一个实例.下一次调用它会覆盖旧的内容.

A function like C strdup makes a new copy of a string, and that only makes sense with dynamic storage. Copying the string into a static buffer and returning that wouldn't work well, because there's only one instance of that static buffer. The next call to it would overwrite the old contents.

在堆栈上分配缓冲区:

堆栈上的可变大小缓冲区没有问题;你只需要一种方法来清理堆栈.使用 EBP/RBP 制作堆栈框架是一种简单的方法.考虑这个示例函数,它根据需要分配一个缓冲区,并使用它来保存字符串反转函数的输出,以便将其传递给 print 函数.你可以看到的在这种情况下做什么的编译器.

A variable-size buffer on the stack is no problem; you just need a way to clean up the stack afterwards. Making a stack-frame with EBP / RBP is an easy way to do that. Consider this example function that allocates a buffer as large as needed, and uses it to hold the output from a string-reverse function so it can pass it to a print function. You can see what compilers do in this case.

void string_reverse(char *d, const char*s, int len);
void print(const char*s, int len);  // modify this to an fwrite or whatever.

void print_reversed(const char *s, int len) {
    char buf[len];
    string_reverse(buf, s, len);
    print(buf, len);
}

如果 string_reverse 不需要 16 字节堆栈对齐并且不会破坏其堆栈参数,那么您可以手动执行此操作.(ABI/调用约定不能保证其中任何一个,因此我们利用我们调用的函数的特殊知识来简化print_reversed.)

This is what you might do by hand, if string_reverse doesn't need 16-byte stack alignment and it doesn't clobber its stack arg. (The ABI / calling convention doesn't guarantee either of those things, so we're taking advantage of special knowledge of the function we're calling to simplify print_reversed.)

; MSVC __fastcall convention
; args: ecx, edx    (const char *string,  size_t length)
print_reversed PROC
    push   ebp
    mov    ebp, esp         ; make a stack frame

    sub    esp, edx         ; reserve space for a buffer
    and    esp, -16         ; and realign the stack
    ; allocate buf[length] on the stack, address = esp
      ; mov eax, esp       ; if you want to copy it somewhere

        ;sub    esp, 12          ; ensure 16-byte stack alignment before CALL

    push   edx              ; 3rd arg and later args go on the stack
    mov    edx, ecx         ; 2nd arg = string
    lea    ecx, [esp+4]     ; 1st arg = output buffer = what we allocated.  (PUSH offset ESP by 4, LEA corrects for that)

    call    string_reverse   ; (dst=buf (ECX),  src=string (EDX), length=length (stack))
      ; clean up the stack after the call and set up args for print
    pop    edx              ; assuming string_reverse doesn't modify its stack arg
    mov    ecx, esp         ; esp is once again pointing to our buffer
    call   print            ; print(ECX=buf, EDX=length)

      ; lea     esp, [ebp-8]  ; if you needed to push stuff after EBP, restore this way
      ; pop  ebx  / pop esi / pop ebp
    
    leave                   ; mov esp, ebp / pop ebp to clean up the stack frame
    ret
ENDP

这是大多数 C 编译器实现 alloca 或 C99 可变长度数组的方式.

This is how most C compilers implement alloca or C99 variable-length arrays.

这篇关于如何在装配过程中使用缓冲区?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆