我正在玩Linux上的ARM汇编作为一个学习练习。 我正在使用“裸”程序集,即没有libcrt或libgcc。 任何人都可以告诉我有关堆栈指针和其他寄存器在第一条指令被调用之前在程序开始时会处于什么状态的信息? 显然pc / r15指向_start,其余的似乎被初始化为0,有两个例外; sp / r13指向远离我的程序的地址,r1指向稍高的地址。
所以对于一些可靠的问题:
任何指针将不胜感激。
下面是我用我的编译器启动的一个Linux / ARM程序:
/** The initial entry point. */ asm( " .text\n" " .globl _start\n" " .align 2\n" "_start:\n" " sub lr, lr, lr\n" // Clear the link register. " ldr r0, [sp]\n" // Get argc... " add r1, sp, #4\n" // ... and argv ... " add r2, r1, r0, LSL #2\n" // ... and compute environ. " bl _estart\n" // Let's go! " b .\n" // Never gets here. " .size _start, .-_start\n" );
正如你所看到的,我只是从堆栈中获得了argc,argv和environ的东西。
稍加说明:堆栈指针指向进程内存中的有效区域。 r0,r1,r2和r3是被调用函数的前三个参数。 我分别用argc,argv和environ填充它们。
既然这是Linux,你可以看看它是如何实现的内核。
这些寄存器似乎是通过在start_thread
结尾调用start_thread
来设置的(如果您使用的是现代Linux系统,则几乎总是使用ELF格式)。 对于ARM,寄存器似乎设置如下:
r0 = first word in the stack r1 = second word in the stack r2 = third word in the stack sp = address of the stack pc = binary entry point cpsr = endianess, thumb mode, and address limit set as needed
显然你有一个有效的堆栈。 我认为r0
– r2
的价值是垃圾,你应该从堆栈中读取所有的东西(你会明白为什么我会这么想)。 现在,我们来看看堆栈中的内容。 您将从堆栈中读取的内容由create_elf_tables
填充。
这里需要注意的一点是,这个函数是独立于架构的,所以在每个基于ELF的Linux架构上,相同的东西(大部分)将被放在堆栈上。 以下是堆栈中的顺序,您可以阅读它:
argc
main()
argc
)。 main()
中argv
的内容; argv
指向这些指针中的第一个)。 main()
罕见的envp
第三个参数的内容; envp
指向这些指针中的第一个)。 AT_NULL
) AT_NULL
。 这个辅助矢量有一些有趣和有用的信息,你可以看到(如果你使用的是glibc),通过运行LD_SHOW_AUXV
环境变量设置为1
(例如LD_SHOW_AUXV=1 /bin/true
)的任何动态链接的程序。 这也是根据架构不同而有所不同的地方。 由于每个体系结构的结构都是相同的,所以您可以在SYSV 386 ABI的第54页上的图形中查找实例,以更好地了解如何组合在一起(但请注意,该文档上的辅助矢量类型常量与Linux使用的不同,所以你应该看看它们的Linux头文件)。
现在你可以看到为什么r0
– r2
的内容是垃圾。 堆栈中的第一个单词是argc
,第二个单词是指向程序名( argv[0]
)的指针,第三个单词可能是零,因为你没有参数调用这个程序(它会是argv[1]
) 。 我想他们是以这种方式为旧的a.out
二进制格式设置的,正如你可以在create_aout_tables
看到的那样, argc
, argv
和envp
放在堆栈中(所以它们将按照预期的顺序在r0
– r2
中结束调用main()
)。
最后,为什么r0
为零,而不是一个( argc
应该是一个,如果你没有参数调用程序)? 我猜测系统调用机制中的一些东西用系统调用的返回值覆盖了它(自从exec成功以来它将为零)。 你可以在kernel_execve
中看到(它不使用系统调用机制,因为它是内核模式下的内核调用),故意用do_execve
的返回值覆盖r0
。
这是uClibc crt 。 似乎表明所有寄存器都是未定义的,除了r0
(它包含一个函数指针要注册到atexit()
)和sp
,它包含一个有效的堆栈地址。
所以,你在r1
看到的价值可能不是你可以依赖的东西。
有些数据放在堆栈上供您使用。
我从来没有使用ARM Linux,但我建议你或者看看libcrt的源代码,看看它们做什么,或者使用gdb来进入现有的可执行文件。 你不应该只需要通过汇编代码的源代码。
一切你需要找出应该发生在任何二进制可执行文件执行的第一个代码。
希望这可以帮助。
托尼