In 64bit sizeof(int) != sizeof(char *) and an unexpected side effect

While trying to figure out how the PATH environment variable can be set under Mac OS X, I wrote a little C program to test what getenv returns.

This is how I usually write them, not really taking care to include headers, as I leave it to gcc to do the right thing:
int main()
{
char * t = getenv("PATH");
printf(">%s<\n", t);
}


In this case however, the binary crashed with a segmentation fault in the printf call.
Compiling on 32 bit makes the problem go away, so what is the problem?

Well, if we look at the assembler code, gcc has added an extra call (cltq) to convert the 4 byte integer result of getenv to 8 bytes (the size of char * on 64 bit):

0x0000000100000ee4 <main+8>: lea    0x67(%rip),%rdi        # 0x100000f52
0x0000000100000eeb <main+15>: mov $0x0,%eax
0x0000000100000ef0 <main+20>: callq 0x100000f18 <dyld_stub_getenv>
0x0000000100000ef5 <main+25>: cltq
0x0000000100000ef7 <main+27>: mov %rax,-0x8(%rbp)
0x0000000100000efb <main+31>: mov -0x8(%rbp),%rsi
0x0000000100000eff <main+35>: lea 0x51(%rip),%rdi # 0x100000f57
0x0000000100000f06 <main+42>: mov $0x0,%eax
0x0000000100000f0b <main+47>: callq 0x100000f1e <dyld_stub_printf>


This conversion clobbers the rax register, clearing the top 4 bytes - this makes the t pointer to point outside the segment, causing the segmentation fault at printf time.

Under 32bit it works as the pointer size is the same as the integer's size.

Vaguely related, I like clang's output better:

clang output


cristi:tmp diciu$ clang test.c
test.c:3:13: warning: implicit declaration of function 'getenv' is invalid in C99 [-Wimplicit-function-declaration]
char * t = getenv("PATH");
^
test.c:3:9: warning: incompatible integer to pointer conversion initializing 'char *' with an expression of type 'int'
char * t = getenv("PATH");
^ ~~~~~~~~~~~~~~
test.c:4:2: warning: implicitly declaring C library function 'printf' with type 'int (char const *, ...)'
printf(">%s<\n", t);
^
test.c:4:2: note: please include the header or explicitly provide a declaration for 'printf'
3 warnings generated.


gcc output


cristi:tmp diciu$ gcc test.c
test.c: In function ‘main’:
test.c:3: warning: initialization makes pointer from integer without a cast
test.c:4: warning: incompatible implicit declaration of built-in function ‘printf’