Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
75 views
in Technique[技术] by (71.8m points)

How Get arguments value using inline assembly in C without 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.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...