0
votes

Like Initialized global variables data are placed in .data section and unitialized global variables are in .bss section, in which section of object file does compiler put initialized auto variables (local variables) data ?

3
For most modern architectures and compilers, automatic variables are put on the stack as part of the stack-frame when a function is called. In other words, they are not stored in object or executable files at all. - Some programmer dude
Define a constant like int findme = 0x11221144 and see where it shows up by doing a binary scan of the resulting .o file. - tadman
Just to add - the code to initialize these variables is a run-time code. So you will see assignments/memsets for these. - Eugene Sh.
@tadman: Finding where one value goes in one circumstance will not answer the question. An object initialized with a small value might be implemented as an immediate in an instruction that is pushed/stored to the stack when the routine starts. Or it might be implemented as an immediate that is used when the object is used, in place of having an actual place for the object on the stack. Or it might be folded other expressions. A larger value might be stored in the process’ read-only section. But that would be a copy of the initialization value, not an instance of the object. - Eric Postpischil

3 Answers

3
votes

An object of automatic storage duration, such as an int x defined inside a function, cannot be stored in an object file, in general. This is because a function may be called recursively (directly or indirectly) any number of times, and a different instance of the object must exist for each such call, and therefore a single location in the object file (allowing that parts of the object file might be mapped to memory in one sense or another during program execution) cannot serve as memory for the object.

It is possible in particular circumstances for one copy of data in an object module to serve for an object of automatic storage duration, as when the compiler can determine the function is not called recursively and therefore only one instance of the object can exist at a time.

However, to implement an object that is initialized, the compiler does have to provide for setting the object to its initial value. The actual object used during program execution might be on the stack or in a register, but the compiler has to set its initial value. For some initial values, such as zero or small constants, the compiler may create the initial value “on the fly” during program execution, possibly using immediate operands in instructions. (In this case, the initial value is effectively stored in the code section of the object module.) For constants that are not easily constructed on the fly, the compiler may store the value as data in the object module, typically in a read-only (constant) data section. Of course, since this data is for the compiler’s internal use (it is something the compiler is saving to use for the initial value; it is not the object itself), it will not be labeled with the name of the object. The compiler will identify it with an internal name or a numeric offset from some location.

Also, objects of automatic storage duration may be initialized with non-constant values. These of course are not stored in the object module at all; they are computed during program execution.

All of the above is subject to modification by optimization and the “as if” rule of the C standard—an automatic object might not exist at all in program memory during execution if the compiler can get the same required observable behaviors by other means, such as folding the use of the object into other computations.

What this means is there is no single location where either an object of automatic storage duration or the initial value for it is always stored.

2
votes

Automatic variables don't (and can't) have a location assigned in the object file, because they exist in one instance per live instance of the block scope they're declared in, not a single instance like static storage objects. If initialized, they must be initialized for each instance of the function, and the compiler is free to emit whatever code it likes to set their initial values. When the values are constant expressions, this could be done as a memcpy from a section in the program image (thus existant in the object/executable file), but it could and usually does just involve immediate operands inline with the executable code.

0
votes

It depends. Usually, a local declaration like int x = 1; will be assigned to a register, and compile to an instruction to load the constant 1 into that register. Less often, a memory location will be allocated on the stack, and the value will be stored there.

Any variable might be optimized out completely, if the compiler (thinks it) can prove that refactoring the program that way will not change its observable behavior. For example, if you write static const int ok = 0; then when you write x = ok; any sane compiler will just set x to the constant 0.

If you take the address of a local variable and dereference it, the compiler must put the variable in some memory location. A constant known at compile time might be stored in the read-only memory of the text segment. A static local variable would normally be stored with other static variables in the data segment. Otherwise, it would go on the stack.

In a more complicated case, such as

double matrix[4][3] = { { 1,  0,  0, -1 },
                        { 0,  1, -1,  0 },
                        { 9,  0,  0,  1 } };

You will typically see the initial values stored in a static array and then copied to a memory location on the stack, or to vector registers if the compiler can vectorize your algorithm. In other words, it’s as if you’d declared the initial values as a static const local array and then copied them to your working copy.

You might find it instructive to test a few variations of a program like this on GodBolt.

double f (const double x)
{
  double matrix[2][2] = { {1, 0},
                          {0, 1} };

  double* const begin = &matrix[0][0];
  const double* const end =
    begin + sizeof(matrix)/sizeof(matrix[0][0]);

  for ( double* p = begin; p < end; ++p ) {
    *p *= x;
  }

  return (matrix[0][0] * matrix[1][1]) -
         (matrix[0][1] * matrix[1][0]);
}

Compilers vary a lot in what code they generate. GCC optimizes away the loop and all variables except for the initial values of the array, which it stores in static memory at address .LC0 and copies into registers. Clang generates a few vector instructions that, other than registers, allocate no storage at all. In theory, a good static analyzer could optimize this function to, return x*x;.