5 ms·
good point, the gcc optimizer is smart enough to omit the preamble. it's still hacked though. inline assembly is one thing, messing with compiler-owned register
by rian 12y ago
good point, the gcc optimizer is smart enough to omit the preamble. it's still hacked though. inline assembly is one thing, messing with compiler-owned registers is another especially without a clobber list. btw why do you prefix your x84_64 start code with "mov %rsp,%rbp"? "xor %ebp, %ebp" is more idiomatic and efficient.
according to the abi (http://www.x86-64.org/documentation/abi.pdf http://www.x86-64.org/documentation/abi.pdf), the stack frame is set up like this:
argc = [RSP+0]
argv = RSP+8
envp = RSP+8+8*argc+8
the same for x86 but replace 8 with 4. your code mirrors this but retrieves argv by taking the address of the second function argument (because you pass [RSP+8] to __init(), which is actually argv[0]). C provides no guarantees on the stability of addresses of passed argument values between caller and callee, so this makes your code subject to non-standard behavior. i can see this working for x86 since values as passed through the stack but not for x86_64 where values are passed through registers.
if you're seeing bugs between gcc -O3 and -Os it's likely due to that, or it's due to improper use of inline assembly/clobbering registers.
- oso2k 12y agoPoint taken. I'll fix up the clobbers and noreturns. And you're right about me having issues with calculating envp. There's multiple examples of envp=argv+argc+1. But when I examined the stack frames, that lead to miss calculations and with -Os envp was getting clobbered. Try running t/test.exe, what I have now works.
- rian 12y agoit only works because you're skipping over environment variables: char **argv = &stack; char **envp = __environ = argv + ( 1 * sizeof( void* ) ); let's say argv = 0x8, then according to this code, on x86_64, envp = 0x48 (0x8 + sizeof(void * ) * sizeof(char *)). here's a sample stack where argc = 1: stack: [0x0] = 1 (argc) [0x8] = "program path" [0x10] = 0 [0x18] = "FOO1=FOO1" [0x20] = "FOO2=FOO2" [0x28] = "FOO3=FOO3" [0x30] = "FOO4=FOO4" [0x38] = "FOO5=FOO5" [0x40] = "FOO6=FOO6" [0x48] = "FOO7=FOO7" ... since you set envp to 0x48 it now points to FOO7=FOO7, you've inadvertently skipped FOO1-FOO6. if argc = 2, then envp would point to FOO6 and you skipped FOO1-FOO5. try this with your code, pass 8 arguments to your test. the environment will point to the last element in argv and then terminate, completely missing the actual environment. again, that's only the behavior on x86, on x86_64, passing any argument will cause a segfault.
- oso2k 12y agoI think I've fixed it [0]. Ran into the issue where gcc was kindly re-aligning the stack to 16-bytes on i386 for no good reason (stack was already aligned?!?!). But that's fixed. Just hope gcc on i386 doesn't stop doing `sub ESP, 0x1C` upon entry to _start. [0] https://github.com/lpsantil/rt0/blob/master/lib/00_start.c https://github.com/lpsantil/rt0/blob/master/lib/00_start.c