I don't understand why I have two variables before argc.
You wrote a main, not a _start. The stack space above your return address is "not yours"; there's no standard for how much stack space the CRT startup code uses before calling main, or what it leaves on the stack between the argc/argv/env and the call to main.
In main(int argc, char **argv, char **envp), you'll find argc in EDI, a pointer to argv[] is in RSI, and a pointer to envp[] in RDX.
But we can look and see what's there to reverse-engineer main's caller:
The numbers starting with 0000 are byte offsets relative to RSP. Whatever generated your image is dumping and analyzing 8-byte stack "slots" as integers, and as pointers if they point to valid memory.
All this stuff on the stack got there by the _start code that calls main putting it there, or the kernel putting it there before entering user-space.
[rsp + 0] has main's return address, so it points to code. Presumably _start called your main with code like call main / mov edi, eax / call exit to pass your return-value to exit() if main returns (which yours doesn't). So it makes sense that your return address is pointing at a mov edi, eax.
0 is probably a frame-pointer sentinel, for the benefit of code that's compiled with -fno-omit-frame-pointer being able to back-trace a chain of saved-RBP values. Pushing a 0 in _start terminates that linked list, if the caller then does mov rbp, rsp so a push rbp in its callee will push a pointer to that terminator. The x86-64 System V ABI doc suggests doing this.
The rest of the entries look exactly like the entry-to-user-space state of the stack at _start
1 = argc means you ran the program with no args, so the shell passed 1 implicit first arg (the program name, argv[0]).
- then a NULL-terminated
argv[] (not a pointer to argv, the actual array is right there on the stack). The first element is a pointer to the string holding the path to your executable, because your caller chose to pass that to execve() as per usual
- then a NULL-terminated
envp[] array. Again not char **envp but the actual array by value. Each entry is a char* to an entry in the environment.
Again, the x86-64 System V ABI documents this stack layout. MacOS follows the x86-64 System V ABI. https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI
(I'm surprised about stack alignment though. On Linux RSP is 16-byte aligned on entry to user-space; it's not a function and isn't called so there's no return value on the stack. So argc is 16-byte aligned. But here, your code seems to show that rsp in main has the same alignment as argc. That would mean main's caller had the stack 8 bytes away from 16-byte alignment before the call. Maybe that's what OS X always does?)