如何在没有 Glibc 的情况下在 C 中使用内联汇编获取参数值? [英] How Get arguments value using inline assembly in C without Glibc?
问题描述
如何在没有 Glibc 的情况下在 C 中使用内联汇编获取参数值?
我需要此代码用于 Linux
架构 x86_64
和 i386
.如果您了解 MAC OS X
或 Windows
,请提交并指导.
void exit(int code){//这个函数不重要!//...}无效_开始(){//如何使用内联汇编获取参数值//在没有Glibc的C中?//参数//参数退出(0);}
<小时>
新更新
https://gist.github.com/apsun/deccca33244471c1849d29cc6bba>7
和
#define ReadRdi(To) asm("movq %%rdi,%0" : "=r"(To));#define ReadRsi(To) asm("movq %%rsi,%0" : "=r"(To));长 argcL;长argvL;读取Rdi(argcL);ReadRsi(argvL);int argc = (int) argcL;//char **argv = (char **) argvL;退出(argc);
但它仍然返回0.所以这段代码是错误的!请帮忙.
如注释中所述,argc
和 argv
是在栈上提供的,所以不能使用一个常规的 C 函数来获取它们,即使使用内联汇编,因为编译器会接触堆栈指针来分配局部变量,设置堆栈框架 &公司;因此,_start
必须用汇编编写,就像在 glibc 中完成的一样 (x86; x86_64).可以编写一个小的存根来根据常规调用约定获取内容并将其转发到您的真实"C 入口点.
这是一个读取 argc
和 argv
的程序的最小示例(适用于 x86 和 x86_64),打印 argv
中的所有值在标准输出上(由换行符分隔)并使用 argc
作为状态码退出;它可以使用通常的 gcc -nostdlib
(和 -static
来确保不涉及 ld.so
这里有任何伤害).
#ifdef __x86_64__汇编(".global _start
""_开始:
"" xorl %ebp,%ebp
"//标记最外层栈帧" movq 0(%rsp),%rdi
"//获取 argc" lea 8(%rsp),%rsi
"//参数被推到下面,所以 argv = %rbp + 8" call bare_main
"//调用我们的bare_main" movq %rax,%rdi
"//获取主返回码并将其用作...的第一个参数" movl $60,%eax
"//... 退出系统调用" 系统调用
"" int3
");//以防万一汇编("bare_write:
"//编写系统调用包装器;调用约定非常好" movq $1,%rax
"//1 = 在 x86_64 上写入系统调用" 系统调用
"" ret
");#万一#ifdef __i386__汇编(".global _start
""_开始:
"" xorl %ebp,%ebp
"//标记最外层栈帧" movl 0(%esp),%edi
"//argc 在栈顶" lea 4(%esp),%esi
"//同上,但使用 4 字节指针" sub $8,%esp
"//开始是16字节对齐的,我们要压入2*4字节;浪费"8个字节" pushl %esi
"//在推送我们的参数后保持对齐" pushl %edi
"" call bare_main
"//调用我们的bare_main" add $8,%esp
"//调用后修复栈(这里其实没用)" movl %eax,%ebx
"//获取主返回码并将其用作...的第一个参数" movl $1,%eax
"//... 退出系统调用" int $0x80
"" int3
");//以防万一汇编("bare_write:
"//编写系统调用包装器;将用户模式调用约定转换为系统调用约定" pushl %ebx
"//ebx 是被调用者保留的" movl 8(%esp),%ebx
"//将堆栈中的内容移动到正确的寄存器" movl 12(%esp),%ecx
"" movl 16(%esp),%edx
"" mov $4,%eax
"//4 = 在 i386 上写入系统调用" int $0x80
"" popl %ebx
"//恢复 ebx" ret
");//注意:%eax 中的返回值已经确定#万一int裸写(int fd,const void *buf,无符号计数);无符号 my_strlen(const char *ch) {const char *ptr;for(ptr = ch; *ptr;++ptr);返回 ptr-ch;}int裸主(int argc,char *argv []){for(int i = 0; i < argc; ++i) {int len = my_strlen(argv[i]);裸写(1,argv[i],len);裸写(1,
",1);}返回 argc;}
请注意,这里忽略了几个细微之处 - 特别是 atexit
位.所有关于机器特定启动状态的文档都从上面链接的两个 glibc 文件中的注释中提取出来.
How Get arguments value using inline assembly in C without Glibc?
i require this code for Linux
archecture x86_64
and i386
.
if you know about MAC OS X
or Windows
, also submit and please guide.
void exit(int code)
{
//This function not important!
//...
}
void _start()
{
//How Get arguments value using inline assembly
//in C without Glibc?
//argc
//argv
exit(0);
}
New Update
https://gist.github.com/apsun/deccca33244471c1849d29cc6bb5c78e
and
#define ReadRdi(To) asm("movq %%rdi,%0" : "=r"(To));
#define ReadRsi(To) asm("movq %%rsi,%0" : "=r"(To));
long argcL;
long argvL;
ReadRdi(argcL);
ReadRsi(argvL);
int argc = (int) argcL;
//char **argv = (char **) argvL;
exit(argc);
But it still returns 0. So this code is wrong! please help.
As specified in the comment, argc
and argv
are provided on the stack, so you cannot use a regular C function to get them, even with inline assembly, as the compiler will touch the stack pointer to allocate the local variables, setup the stack frame & co.; hence, _start
must be written in assembly, as it's done in glibc (x86; x86_64). A small stub can be written to just grab the stuff and forward it to your "real" C entrypoint according to the regular calling convention.
Here a minimal example of a program (both for x86 and x86_64) that reads argc
and argv
, prints all the values in argv
on stdout (separated by newline) and exits using argc
as status code; it can be compiled with the usual gcc -nostdlib
(and -static
to make sure ld.so
isn't involved; not that it does any harm here).
#ifdef __x86_64__
asm(
".global _start
"
"_start:
"
" xorl %ebp,%ebp
" // mark outermost stack frame
" movq 0(%rsp),%rdi
" // get argc
" lea 8(%rsp),%rsi
" // the arguments are pushed just below, so argv = %rbp + 8
" call bare_main
" // call our bare_main
" movq %rax,%rdi
" // take the main return code and use it as first argument for...
" movl $60,%eax
" // ... the exit syscall
" syscall
"
" int3
"); // just in case
asm(
"bare_write:
" // write syscall wrapper; the calling convention is pretty much ok as is
" movq $1,%rax
" // 1 = write syscall on x86_64
" syscall
"
" ret
");
#endif
#ifdef __i386__
asm(
".global _start
"
"_start:
"
" xorl %ebp,%ebp
" // mark outermost stack frame
" movl 0(%esp),%edi
" // argc is on the top of the stack
" lea 4(%esp),%esi
" // as above, but with 4-byte pointers
" sub $8,%esp
" // the start starts 16-byte aligned, we have to push 2*4 bytes; "waste" 8 bytes
" pushl %esi
" // to keep it aligned after pushing our arguments
" pushl %edi
"
" call bare_main
" // call our bare_main
" add $8,%esp
" // fix the stack after call (actually useless here)
" movl %eax,%ebx
" // take the main return code and use it as first argument for...
" movl $1,%eax
" // ... the exit syscall
" int $0x80
"
" int3
"); // just in case
asm(
"bare_write:
" // write syscall wrapper; convert the user-mode calling convention to the syscall convention
" pushl %ebx
" // ebx is callee-preserved
" movl 8(%esp),%ebx
" // just move stuff from the stack to the correct registers
" movl 12(%esp),%ecx
"
" movl 16(%esp),%edx
"
" mov $4,%eax
" // 4 = write syscall on i386
" int $0x80
"
" popl %ebx
" // restore ebx
" ret
"); // notice: the return value is already ok in %eax
#endif
int bare_write(int fd, const void *buf, unsigned count);
unsigned my_strlen(const char *ch) {
const char *ptr;
for(ptr = ch; *ptr; ++ptr);
return ptr-ch;
}
int bare_main(int argc, char *argv[]) {
for(int i = 0; i < argc; ++i) {
int len = my_strlen(argv[i]);
bare_write(1, argv[i], len);
bare_write(1, "
", 1);
}
return argc;
}
Notice that here several subtleties are ignored - in particular, the atexit
bit. All the documentation about the machine-specific startup state has been extracted from the comments in the two glibc files linked above.
这篇关于如何在没有 Glibc 的情况下在 C 中使用内联汇编获取参数值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!