这不好。 您可能已经注意到,函数_start的编译器发出了函数序言push %rbp mov %rsp,%rbp :
400292: 55 push %rbp
400293: 48 89 e5 mov %rsp,%rbp
如果要执行此操作,请考虑至少使用-fomit-frame-pointer编译。 通过函数序言推动RBP ,当您弹出RCX时,您没有将命令行参数的数量放入RCX中 ,而是将RBP的值(现在位于堆栈的顶部)放入RCX中 。 当然,这会级联到其他使用错误值的堆栈操作。
您可以像这样直接编码_start函数,而不是忽略堆栈框架作为我的第一个建议:
asm ( ".global _start;" /* Make start symbol globally visible */
"_start:;"
"pop %rcx;" /* Contains argc */
"cmp $2, %rcx;" /* If argc = 2 (argv[0 & argv[1] exist) */
"jne exit;" /* If it's not 2, exit */
"add $8, %rsp;" /* Move stack pointer to argv[1] */
"pop %rdi;" /* Pop off stack */
"mov $85, %rax;" /* #define __NR_creat 85 */
"mov $0x2E8, %rsi;" /* move 744 to rsi */
"syscall;"
"exit:;"
"mov $60, %rax;" /* sys_exit */
"mov $2, %rdi;"
"syscall"
);
由于声明C ++函数的常规过程已被跳过,因此我们不必担心编译器会添加序言和结语代码。
您用于sys_creat的文件模式位不正确。 你有:
"mov $0x2E8, %rsi;" /* move 744 to rsi */
0x2E8 = 744十进制 我相信您的意图是将744八进制数放入%RSI中 。 八进制744是0x1e4。 为了使其更具可读性,您可以在GAS中使用八进制值,方法是在值前面加上0。这就是您要查找的内容:
"mov $0744, %rsi;" /* File mode octal 744 (rwxr--r--) */
而不是:
"pop %rsi;" /* Pop off stack */
"mov %rsi, %rdi;" /* Move argv[1] to rdi */
您可能直接跳入%rdi :
"pop %rdi;" /* Pop off stack */
您还可以将参数保留在堆栈上,并以这种方式直接访问它们:
asm ( ".global _start;" /* Make start symbol globally visible */
"_start:;"
"cmp $2, (%rsp);" /* If argc = 2 (argv[0 & argv[1] exist) */
"jne exit;" /* If it's not 2, exit */
"mov 16(%rsp), %rdi;" /* Get pointer to argv[1] */
"mov $85, %eax;" /* #define __NR_creat 85 */
"mov $0744, %esi;" /* File mode octal 744 (rwxr--r--) */
"syscall;"
"exit:;"
"mov $60, %eax;" /* sys_exit */
"mov $1, %edi;"
"syscall"
);
在最后一个代码片段中,在某些情况下,我还更改为使用32位寄存器。 您可以利用以下事实:在x86-64代码中,将值自动添加到32位寄存器中将使该值自动扩展为64位寄存器的高32位。 这样可以在指令编码上节省几个字节。
通过主要的w / 64位代码访问命令行参数
如果使用C / C ++运行时进行编译,则运行时将提供标签_start来执行程序启动,并修改OS传递的命令行参数以适合64位System V ABI 。 参数传递在3.2.3节中讨论。 特别是要以64位代码main的前两个参数是通过RDI和RSI传递的。 RDI将包含值argc , RSI将包含一个指向char *指针数组的指针。 由于这些参数不是通过堆栈传递的,因此我们不需要考虑任何函数序言和结语代码。
int main(int argc, char *argv[])
{
asm ( "cmp $2, %rdi;" /* If argc = 2 (argv[0 & argv[1] exist) */
"jne exit;" /* If it's not 2, exit */
/* _RSI_ (second arg to main) is a pointer
to an array of character pointers */
"mov 8(%rsi), %rdi;"/* Get pointer to second char * pointer in argv[] */
"mov $85, %eax;" /* #define __NR_creat 85 */
"mov $0744, %esi;" /* File mode octal 744 (rwxr--r--) */
"syscall;"
"exit:;"
"mov $60, %eax;" /* sys_exit */
"mov $1, %edi;"
"syscall"
);
}
您应该可以使用以下命令进行编译:
g++ -o testargs testargs.c -g
特别说明 :如果您打算最终将内联汇编与C / C ++代码一起使用,则必须了解GCC扩展汇编模板 ,约束,破坏等内容。这不在此问题的范围内。 如果您使用内联汇编,则与创建单独的汇编代码对象并从C / C ++调用它们相比,学习汇编程序要困难得多。 不正确地使用GCC的扩展内联组件非常容易。 代码乍一看似乎可以运行,但是随着程序变得越来越复杂,细微的错误可能会蔓延。