Meditation, The Art of Exploitation

Thinking? At last I have discovered it--thought; this alone is inseparable from me. I am, I exist--that is certain. But for how long? For as long as I am thinking. For it could be, that were I totally to cease from thinking, I should totally cease to exist....I am, then, in the strict sense only a thing that thinks.

Wednesday, May 23, 2007

C++ When is a function static variable initialized?

The following code is a rewrite of a singleton implementation presented on clc++ recently:
typedef unsigned int size_t;
class singleton{
public:
static singleton& create(){
static singleton * s = new singleton;
return *s;
}
static int shift(int x){
static int shift_by = 10;
x = shift_by + x;
shift_by = x;
return x;
}
private:
static void * operator new(size_t size){ }
singleton(){}
~singleton(){}
};

int main(){
singleton& s = singleton::create();
int x = 3;
singleton::shift(x);
}

The question was when singleton::create::s was initialized? I immediately thought this code was wrong because in a previous blog entry I have investigated how static variables are initialized inside a function. But at that time, I was investigating plain old data types (POD).

It turns out, as suggested, C++ has a more complicated scheme to initialize function static variables depending on the variable type, as in ยง6.7/4. Compiler is free to generate code to initialize the variable on the first possible entry to the function to initialize it. For POD and some types, compiler can, at compile time, initialize the variable on the .bss or .data segment. Let's see what's going on under the hood. Again we'll use the assembly output from g++ to investigate g++ behavior. Check out the symbol table portion of the assembly output of this c++ source file (g++ -s -c file.cpp):

.LFE11:
.size main, .-main
.weak _ZGVZN9singleton6createEvE1s
.section .bss._ZGVZN9singleton6createEvE1s,"awG",@nobits,_ZGVZN9singleton6createEvE1s,comdat
.align 8
.type _ZGVZN9singleton6createEvE1s, @object
.size _ZGVZN9singleton6createEvE1s, 8
_ZGVZN9singleton6createEvE1s:
.zero 8
.weak _ZZN9singleton6createEvE1s
.section .bss._ZZN9singleton6createEvE1s,"awG",@nobits,_ZZN9singleton6createEvE1s,comdat
.align 4
.type _ZZN9singleton6createEvE1s, @object
.size _ZZN9singleton6createEvE1s, 4
_ZZN9singleton6createEvE1s:
.zero 4
.weak _ZZN9singleton5shiftEiE8shift_by
.section .data._ZZN9singleton5shiftEiE8shift_by,"awG",@progbits,_ZZN9singleton5shiftEiE8shift_by,comdat
.align 4
.type _ZZN9singleton5shiftEiE8shift_by, @object
.size _ZZN9singleton5shiftEiE8shift_by, 4
_ZZN9singleton5shiftEiE8shift_by:
.long 10
.ident "GCC: (GNU) 4.1.1 20060525 (Red Hat 4.1.1-1)"
.section .note.GNU-stack,"",@progbits


The code for singleton::create:

_ZN9singleton6createEv:
.LFB2:
pushl %ebp
.LCFI6:
movl %esp, %ebp
.LCFI7:
pushl %ebx
.LCFI8:
subl $4, %esp
.LCFI9:
movl $_ZGVZN9singleton6createEvE1s, %eax
movzbl (%eax), %eax
testb %al, %al
jne .L8 <------------initialize on demand
movl $_ZGVZN9singleton6createEvE1s, (%esp)
call __cxa_guard_acquire
testl %eax, %eax
setne %al
testb %al, %al
je .L8
movl $1, (%esp)
call _ZN9singletonnwEj
movl %eax, %ebx
movl %ebx, (%esp)
call _ZN9singletonC1Ev
movl %ebx, _ZZN9singleton6createEvE1s
movl $_ZGVZN9singleton6createEvE1s, (%esp)
call __cxa_guard_release
.L8:
movl _ZZN9singleton6createEvE1s, %eax
addl $4, %esp
popl %ebx
popl %ebp
ret

assembly for singleton::shift:

_ZN9singleton5shiftEi:
.LFB3:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
movl _ZZN9singleton5shiftEiE8shift_by, %eax <--- already initialized, memory copy
addl %eax, 8(%ebp)
movl 8(%ebp), %eax
movl %eax, _ZZN9singleton5shiftEiE8shift_by
movl 8(%ebp), %eax
popl %ebp
ret