Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 将命令行参数正确放置到堆栈上_Assembly_Command Line_X86_Stack - Fatal编程技术网

Assembly 将命令行参数正确放置到堆栈上

Assembly 将命令行参数正确放置到堆栈上,assembly,command-line,x86,stack,Assembly,Command Line,X86,Stack,如果我的术语不正确,请原谅 我正在尝试实现一个基于x86GNUC的系统,它能够将命令行参数传递给程序。不要与在程序中访问它们混淆,实际上是在将执行传递给用户程序之前设置堆栈 __asm__ __volatile__ ("pushl %%ds\n" /* save data and extra segment registers */ "pushl %%es\n" "movl %%esp, %%ebx\n" "movl %%ebx, oldsp\

如果我的术语不正确,请原谅

我正在尝试实现一个基于x86GNUC的系统,它能够将命令行参数传递给程序。不要与在程序中访问它们混淆,实际上是在将执行传递给用户程序之前设置堆栈

__asm__ __volatile__ ("pushl %%ds\n" /* save data and extra segment registers */
        "pushl %%es\n" 
        "movl %%esp, %%ebx\n" 
        "movl %%ebx, oldsp\n" 
        "movl %%ss, %%ebx\n" 
        "movl %%ebx, oldss\n" 
        "movl %0, %%ds\n"   /* set data segment to new user base */

        "movl %0, %%ss\n" 
        "movl $0xfff0, %%ebx\n" /* start of the new user stack pointer */
        "movl %%ebx, %%esp\n"
        "movl %2, %%eax\n"  /* place i into eax - push it onto the stack*/
        "pushl %%eax\n"
        "pushl %%eax\n"
        "lcallw  *%%fs:(%1)\n" 
        "movl %%fs:oldss, %%ebx\n" 
        "movl %%ebx, %%ss\n" 
        "movl %%fs:oldsp, %%ebx\n" 
        "movl %%ebx, %%esp\n" 
        "popl  %%es\n"  /* restore old segment registers */
        "popl  %%ds\n"
        :
        :"a" (userbase), "d" (&useg), "r" (i)
        :"%ebx", "eax", "memory");  /* prevents gcc from optimizing useg away*/
从我收集的数据中,argc和argv被推到堆栈中,但我在构建过程中遗漏了一些东西。下面是我如何执行另一个程序

__asm__ __volatile__ ("pushl %%ds\n" /* save data and extra segment registers */
        "pushl %%es\n" 
        "movl %%esp, %%ebx\n" 
        "movl %%ebx, oldsp\n" 
        "movl %%ss, %%ebx\n" 
        "movl %%ebx, oldss\n" 
        "movl %0, %%ds\n"   /* set data segment to new user base */

        "movl %0, %%ss\n" 
        "movl $0xfff0, %%ebx\n" /* start of the new user stack pointer */
        "movl %%ebx, %%esp\n"
        "movl %2, %%eax\n"  /* place i into eax - push it onto the stack*/
        "pushl %%eax\n"
        "pushl %%eax\n"
        "lcallw  *%%fs:(%1)\n" 
        "movl %%fs:oldss, %%ebx\n" 
        "movl %%ebx, %%ss\n" 
        "movl %%fs:oldsp, %%ebx\n" 
        "movl %%ebx, %%esp\n" 
        "popl  %%es\n"  /* restore old segment registers */
        "popl  %%ds\n"
        :
        :"a" (userbase), "d" (&useg), "r" (i)
        :"%ebx", "eax", "memory");  /* prevents gcc from optimizing useg away*/
我的印象是,在堆栈指针更新后,我可以将值推送到堆栈上。很明显,我没有得到我推到堆栈上的值,所以我甚至不确定我是否以正确的方式进行

下面是一个简单的测试程序,它试图将argc打印读取到屏幕上

.file    "prog3.c"
#APP
.code16gcc

call main

lretw

.section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string    "\r\nstring: %u"
#NO_APP
.section    .text.startup,"ax",@progbits
.globl    main
.type    main, @function
main:
.LFB0:
.cfi_startproc
pushl    %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl    %esp, %ebp
.cfi_def_cfa_register 5
andl    $-16, %esp
subl    $16, %esp
movl    8(%ebp), %eax
movl    %eax, 4(%esp)
movl    $.LC0, (%esp)
call    printf
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size    main, .-main
.ident    "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section    .note.GNU-stack,"",@progbits
我已经了解了堆栈是如何为函数调用做好准备的,认为它可能是一个类似的过程,但我仍然处于脱节状态。
有什么想法吗?

让我们看看程序试图读取其参数时的堆栈,以及所有内容是如何到达的:

mov    %ebx, %esp # 0xfff0
push   %eax       # 0xffec i
push   %eax       # 0xffe8 i
lcallw *%fs:useg  # 0xffe4 return address 1
call   main       # 0xffe0 return address 2
push   %ebp       # 0xffdc old ebp
mov    %esp, %ebp # ebp = 0xffdc
GCC希望函数参数正好位于堆栈上的返回地址之上。这意味着第一个参数应该位于
ebp+8
。但是,您可以在这里看到,该位置的值是
lcallw
指令的返回地址,因此主函数将其视为第一个参数。为了获得放置在那里的参数,您需要让调用
main
(我们称之为
start
)的用户代码在两个返回地址之间复制它,或者根本不更改该代码中堆栈的大小

由于堆栈上参数的长度当前是恒定的,而且很小,因此
start
简单地复制数据并不困难。您只需重新推送这两个值,因此
start
将如下所示:

push   8(%esp) # Copy the high value
push   8(%esp) # Copy the low value
call   main
add    $8,%esp
lretw
对于较长或非常量的参数,这会比较慢,因为您必须确定要复制多少,并复制所有参数

如果
start
在调用
main
之前未更改堆栈的大小,则可以不复制而执行此操作。由于它需要在堆栈上放置自己的返回地址,这意味着它需要将另一个返回地址移动到其他地方。最好的方法是让您的系统假设正常保存的其中一个寄存器不存在,以便
start
可以将返回地址存储在那里。看起来您没有对运行程序的代码中的任何内容使用
ebp
esi
edi
,因此您可以使用其中任何一个,只需将其添加到内联程序集中已销毁的寄存器列表中,然后更改
start
以在其中存储第一个返回地址即可

pop    %esi  # Pop first return address
call   main
push   %esi  # Restore first return address
lretw

进步:我想我可能已经找到了一个粗糙的工作。我从主声明中完全删除了argc和argv。原因有二:1。我将暂时填充我自己的变量,第二,在我的宠物操作系统中可能缺少gcc在编译这些测试程序时所假定的其他东西。现在,我从一些内联程序集中手动获取堆栈中的值,这并不难。但我仍然希望它能正常工作。添加$4,%esp#堆栈上的参数;移动(%esp),%eax#移动到下一个