2

I am trying to write my own _start function using inline assembly. But when I try to read argc and argv from stack (%rsp and %rsp + 8) I get wrong values. I don't know what I am doing wrong.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <syscall.h>

int main(int argc, char *argv[]) {
    printf("%d\n", argc);
    printf("%s\n", argv[0]);
    printf("got here\n");
    return 0;
}

void _start() {
    __asm__(
    "xor %rbp, %rbp;"
    "movl (%rsp), %edi;"
    "lea 8(%rsp), %rsi;"
    "xor %rax, %rax;"
    "call main"
...

Terminal:

$ gcc test.c -nostartfiles
$ ./a.out one two three
0
Segmentation fault (core dumped)
$

Any idea where my fault could be ? I am using a Ubuntu 20.04 VM

4
  • Have you tried debugging it and getting the PC at which seg fault happens? Commented Dec 6, 2020 at 15:34
  • Seg fault happens because the adress in rsi (argv) is a random adress instead of what should be a pointer to ["one","two","three"], but in the System V abi it says that this pointer should be at $rsp + 8 Commented Dec 6, 2020 at 15:41
  • If you are using -nostartfiles, then most likely the code which sets up processing of command line arguments is not called at all. Commented Dec 6, 2020 at 15:50
  • 2
    @scrutari: You're thinking of Windows. On Unix systems (and GNU/Linux), command args aren't a flat string, they're already separated (as passed to execve by the shell or whatever) with argc and argv[] on the stack on entry to user-space. This looks correct for a minimal _start: if the OP didn't put it inside a non-naked C function that will use the stack before the asm statement. How Get arguments value using inline assembly in C without Glibc?. Never use GNU C Basic asm statements in a non-naked function. Commented Dec 6, 2020 at 15:53

1 Answer 1

5

This looks correct for a minimal _start: but you put it inside a non-naked C function. Compiler-generated code will run, e.g. push %rbp / mov %rsp, %rbp, before execution enters before the asm statement. To see this, look at gcc -S output, or single-step in a debugger such as GDB.

Put your asm statement at global scope (like in How Get arguments value using inline assembly in C without Glibc?) or use __attribute__((naked)) on your _start(). Note that _start isn't really a function

As a rule, never use GNU C Basic asm statements in a non-naked function. Although you might get this to work with -O3 because that would imply -fomit-frame-pointer so the stack would still be pointing at argc and argv when your code ran.

A dynamically linked executable on GNU/Linux will run libc startup code from dynamic linker hooks, so you actually can use printf from _start without manually calling those init functions. Unlike if this was statically linked.

However, your main tries to return to your _start, but you don't show _start calling exit. You should call exit instead of making an _exit system call directly, to make sure stdio buffers get flushed even if output is redirected to a file (making stdout full buffered). Falling off the end of _start would be bad, crashing or getting into an infinite loop depending on what execution falls in to.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.