简体   繁体   English

如何在 Assembly 中读取 C arguments?

[英]How do I read C arguments in Assembly?

I'm currently building a (very simple) OS, using NASM.我目前正在使用 NASM 构建一个(非常简单的)操作系统。 I'm trying to write an assembly function that will read a byte from an address given as a function parameter, then return the data, like so:我正在尝试编写一个程序集 function,它将从作为 function 参数给出的地址读取一个字节,然后返回数据,如下所示:

// kernel.c
extern int readbyte();

int main(void) {
  int x = readbyte(0xdeadbeef); //returns data stored at 0xdeadbeef
  return 0;
}
; kernel_entry.asm
[bits 32]
[extern main]
[extern readbyte]

call main

readbyte:
  ; somehow read function parameter here and store it to ecx
  mov eax, [ecx] ; read byte from given address
  ret ; return the data to the C function

Returning data via the eax register is working fine, but I can't figure out where the parameter data given by the C program ( 0xdeadbeef ) is or how I can access it.通过eax寄存器返回数据工作正常,但我无法弄清楚 C 程序 ( 0xdeadbeef ) 给出的参数数据在哪里或如何访问它。 When I try to pop from the stack, the whole program just crashes.当我尝试从堆栈中弹出时,整个程序就崩溃了。 I've tried reading from various registers, but those don't ever match up with the parameter given in the C script.我试过从各种寄存器中读取,但这些寄存器与 C 脚本中给出的参数不匹配。

Could somebody point me in the right direction?有人能指出我正确的方向吗?

Edit:编辑:

Following Michael Petch's suggestion, I wrote an assembly function that returns the first parameter:按照 Michael Petch 的建议,我编写了一个返回第一个参数的程序集 function:

readbyte:
  mov ecx, [esp + 4]
  mov eax, ecx
  ret

The issue is that when I test it in Qemu, the display rapidly flashes between the boot sequence screen and displaying the correct data.问题是当我在 Qemu 中测试它时,显示在启动顺序屏幕和显示正确数据之间快速闪烁。 Does anybody know why it's crashing, yet also showing the proper data?有谁知道它为什么崩溃,但也显示正确的数据?

Edit 2:编辑 2:

So I discovered that the above code will run without entering a reboot loop if I pass a hexadecimal value less than 4 digits long.所以我发现如果我传递一个长度小于 4 位的十六进制值,上面的代码将在不进入重启循环的情况下运行。 So 0xABC will work properly, but 0xABCD will crash the system.所以0xABC会正常工作,但0xABCD会使系统崩溃。

In modern operating systems, that depends on the ABI (the set of rules that specify how the interface with a separately compiled module is established) This assumes that some CPU registers are reserved for passing parameters (eg four of them) while the rest are pushed on the stack.在现代操作系统中,这取决于 ABI(指定如何与单独编译的模块建立接口的规则集)这假设某些 CPU 寄存器保留用于传递参数(例如其中四个),而 rest 被推送在堆栈上。 Let's assume that the ABI establishes that the first four registers are used to pass function arguments, and that you are called with eg 6 arguments, the way a function call is made is to first push the last two arguments to the stack (in reverse order to how they are written in the code, so first the last argument, then the fifth, and then the four registers are filled with the rest of arguments, then a call is made to the routine address, which means that the return address is also pushed on the stack. Inside your routine, there's normally a function preamble code that consists in storing in a register the value of the stack pointer, by first pushing the value of this register to the stack in a form like the following:让我们假设 ABI 确定前四个寄存器用于传递 function arguments,并且使用例如 6 arguments 调用您,调用 function 的方式是首先将最后两个 arguments 压入堆栈(以相反的顺序)到代码中是怎么写的,所以先是最后一个参数,然后是第五个,然后四个寄存器填充arguments的rest,然后调用例程地址,也就是说返回地址也是推入堆栈。在您的例程中,通常有一个 function 前导码代码,用于将堆栈指针的值存储在寄存器中,方法是首先将该寄存器的值以如下形式推入堆栈:

    push ebp
    mov  ebp, esp

This will allow you to mangle with the stack pointer (pushing local storage or doing calculations) without having to consider the SP position to access parameters or the like, when accessing the function parameters.这将允许您在访问 function 参数时,无需考虑 SP position 来访问参数等,即可处理堆栈指针(推送本地存储或进行计算)。

Also, local storage is reserved, by subtracting (at this point) a proper value from the stack pointer.此外,通过从堆栈指针中减去(此时)适当的值,保留本地存储。 This leads to the following stack:这导致以下堆栈:

       |  more stack... |
       +~~~~~~~~~~~~~~~~+
       |  sixth param   |
       +----------------+
       |  fifth param   |
       +----------------+ (previous parameters are in cpu registers)
       |  return address|
       +----------------+
       |  old value BP  | <--- BP points here now.
       +----------------+
       |  local vars    |
       .                .
       |                | <--- SP points here at function entry, after preamble
       +----------------+
       | local stack    |

So, you should use (in this case, for this function):所以,你应该使用(在这种情况下,对于这个函数):

address地址 to access...访问...
[EBP + 12] sixth parameter第六个参数
[EBP + 8] fifth parameter第五个参数
[EBP + 4] return address退货地址
[EBP + 0] old linkage BP老联动BP
[EBP - 4] first local var第一个本地变量

But, as I say, you should conform to the ABI your compiler is using.但是,正如我所说,您应该符合您的编译器正在使用的 ABI。 This should be found with your compiler documentation (or at least, a reference to the document describing it)这应该在您的编译器文档中找到(或者至少是对描述它的文档的引用)

Consider also that the offsets depend on the variable size and it's alignment .还要考虑偏移量取决于变量大小,它是 alignment This is important.这个很重要。

At the end, by the way, there's also some epilogue code, to undo the changes of the prologue.最后,顺便说一句,还有一些结语代码,用于撤消序言的更改。 If you have been conservative with the stack, it should point to the same value as the EBP register, so NO mov sp, ebp will be needed, but如果您对堆栈比较保守,它应该指向与 EBP 寄存器相同的值,因此不需要mov mov sp, ebp ,但是

pop ebp

Will, to restore the old link register.请问,恢复旧的链接寄存器。 This leaves the return address on top of the stack, and we can return.这会将返回地址留在堆栈顶部,我们可以返回。 The calling procedure is responsible of adjusting the stack from this point on (popping the pushed stack registers used for the call)调用过程负责从这一点开始调整堆栈(弹出用于调用的压栈寄存器)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM