4.3. Putting Objects into their own ELF Sections

I'm going to start with the code that we saw before in the section on code layout and modify it a bit so that different parts will now be in their own ELF sections:

/*
 * Copyright (C) 2006  Trevor Woerner
 */
#include <stdio.h>
int add (int, int) __attribute__ ((section ("my_code_section")));
int global_val     __attribute__ ((section ("my_data_section")));
int gval_init      __attribute__ ((section ("my_data_section"))) = 29;
int add (int i, int j)
{
	return i+j;
}
int
main (void)
{
	int local_val = 25;
	global_val = 17;
	printf ("local_val: %d    global_val: %d    gval_init: %d\n",
			local_val, global_val, gval_init);
	printf ("%d + %d = %d\n", local_val, global_val,
			add (local_val, global_val));
	return 0;
}
       

Now when we do an objdump -t on the result we get the following (I'll spare you the details of the entire output, line up the columns properly, and also sort it for you):

00000000       F *UND*  00000039              printf@@GLIBC_2.0
00000000       F *UND*  00000187              __libc_start_main@@GLIBC_2.0
00000000  w      *UND*  00000000              _Jv_RegisterClasses
00000000  w      *UND*  00000000              __gmon_start__
00000000 l    d  *ABS*  00000000              .shstrtab
00000000 l    d  *ABS*  00000000              .strtab
00000000 l    d  *ABS*  00000000              .symtab
00000000 l    d  .comment       00000000              .comment
00000000 l    df *ABS*  00000000              crtstuff.c
00000000 l    df *ABS*  00000000              crtstuff.c
00000000 l    df *ABS*  00000000              new.c
08048114 l    d  .interp        00000000              .interp
08048128 l    d  .note.ABI-tag  00000000              .note.ABI-tag
08048148 l    d  .hash  00000000              .hash
08048174 l    d  .dynsym        00000000              .dynsym
080481d4 l    d  .dynstr        00000000              .dynstr
08048234 l    d  .gnu.version   00000000              .gnu.version
08048240 l    d  .gnu.version_r 00000000              .gnu.version_r
08048260 l    d  .rel.dyn       00000000              .rel.dyn
08048268 l    d  .rel.plt       00000000              .rel.plt
08048280 g     F .init  00000000              _init
08048280 l    d  .init  00000000              .init
08048298 l    d  .plt   00000000              .plt
080482d8 g     F .text  00000000              _start
080482d8 l    d  .text  00000000              .text
080482fc l     F .text  00000000              call_gmon_start
08048320 l     F .text  00000000              __do_global_dtors_aux
08048354 l     F .text  00000000              frame_dummy
0804837c g     F .text  0000007a              main
080483f8 g     F .text  0000004f              __libc_csu_init
08048448 g     F .text  00000005              __libc_csu_fini
08048450 l     F .text  00000000              __do_global_ctors_aux
08048478 g       *ABS*  00000000              __start_my_code_section
08048478 g     F my_code_section        0000000b              add
08048478 l    d  my_code_section        00000000              my_code_section
08048483 g       *ABS*  00000000              __stop_my_code_section
08048484 g     F .fini  00000000              _fini
08048484 l    d  .fini  00000000              .fini
080484a0 g     O .rodata        00000004              _fp_hw
080484a0 l    d  .rodata        00000000              .rodata
080484a4 g     O .rodata        00000004              _IO_stdin_used
080484e8 l     O .eh_frame      00000000              __FRAME_END__
080484e8 l    d  .eh_frame      00000000              .eh_frame
080494ec g       *ABS*  00000000              .hidden __fini_array_end
080494ec g       *ABS*  00000000              .hidden __fini_array_start
080494ec g       *ABS*  00000000              .hidden __init_array_end
080494ec g       *ABS*  00000000              .hidden __init_array_start
080494ec g       *ABS*  00000000              .hidden __preinit_array_end
080494ec g       *ABS*  00000000              .hidden __preinit_array_start
080494ec l     O .ctors 00000000              __CTOR_LIST__
080494ec l    d  .ctors 00000000              .ctors
080494f0 l     O .ctors 00000000              __CTOR_END__
080494f4 l     O .dtors 00000000              __DTOR_LIST__
080494f4 l    d  .dtors 00000000              .dtors
080494f8 l     O .dtors 00000000              __DTOR_END__
080494fc l     O .jcr   00000000              __JCR_END__
080494fc l     O .jcr   00000000              __JCR_LIST__
080494fc l    d  .jcr   00000000              .jcr
08049500 g     O .dynamic       00000000              _DYNAMIC
08049500 l    d  .dynamic       00000000              .dynamic
080495c8 l    d  .got   00000000              .got
080495cc g     O .got.plt       00000000              .hidden _GLOBAL_OFFSET_TABLE_
080495cc l    d  .got.plt       00000000              .got.plt
080495e4  w      .data  00000000              data_start
080495e4 g       .data  00000000              __data_start
080495e4 l    d  .data  00000000              .data
080495e8 g     O .data  00000000              .hidden __dso_handle
080495ec l     O .data  00000000              p.4582
080495f0 g       *ABS*  00000000              __start_my_data_section
080495f0 g     O my_data_section        00000004              gval_init
080495f0 l    d  my_data_section        00000000              my_data_section
080495f4 g     O my_data_section        00000004              global_val
080495f8 g       *ABS*  00000000              __bss_start
080495f8 g       *ABS*  00000000              __stop_my_data_section
080495f8 g       *ABS*  00000000              _edata
080495f8 l     O .bss   00000001              completed.4583
080495f8 l    d  .bss   00000000              .bss
080495fc g       *ABS*  00000000              _end
       

Running the executable gives:

[trevor]$ ./sections 
local_val: 25    global_val: 17    gval_init: 29
25 + 17 = 42
       

The first thing to note is that the executable works! (yea!) The second thing you should notice are the existance of new section names (my_code_section and my_data_section) in the executable image. You will also notice that in these sections are found the objects that we placed in them.

...
08048478 g       *ABS*  00000000              __start_my_code_section
08048478 g     F my_code_section        0000000b              add
08048478 l    d  my_code_section        00000000              my_code_section
08048483 g       *ABS*  00000000              __stop_my_code_section
...
080495f0 g       *ABS*  00000000              __start_my_data_section
080495f0 g     O my_data_section        00000004              gval_init
080495f0 l    d  my_data_section        00000000              my_data_section
080495f4 g     O my_data_section        00000004              global_val
080495f8 g       *ABS*  00000000              __bss_start
080495f8 g       *ABS*  00000000              __stop_my_data_section
       
Something else that is very worthy of note is the fact that ld has been kind enough to add a couple of global absolute symbols which delimit our newly-defined sections without us needing to ask it to: __start_my_code_section/__stop_my_code_section and __start_my_data_section/__stop_my_data_section. Notice how __start_my_code_section has the same address as our add() function and that __start_my_data_section has the same address as our gval_init.

You may have just asked yourself: "In the generated my_data_section above, why did the gval_init object come first?". Having a look at the generated assembly (gcc -S) helps us to investigate this question:

.globl gval_init
        .section        my_data_section,"aw",@progbits
        .align 4
        .type   gval_init, @object
        .size   gval_init, 4
gval_init:
        .long   29
        .section        my_code_section,"ax",@progbits
.globl add
        .type   add, @function
add:
        pushl   %ebp
        movl    %esp, %ebp
        movl    12(%ebp), %eax
        addl    8(%ebp), %eax
        leave
        ret
        .size   add, .-add
        .section        .rodata
        .align 4
.LC0:
        .string "local_val: %d    global_val: %d    gval_init: %d\n"
.LC1:
        .string "%d + %d = %d\n"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        andl    $-16, %esp
        movl    $0, %eax
        addl    $15, %eax
        addl    $15, %eax
        shrl    $4, %eax
        sall    $4, %eax
        subl    %eax, %esp
        movl    $25, -4(%ebp)
        movl    $17, global_val
        movl    gval_init, %eax
        movl    global_val, %edx
        pushl   %eax
        pushl   %edx
        pushl   -4(%ebp)
        pushl   $.LC0
        call    printf
        addl    $16, %esp
        movl    global_val, %eax
        pushl   %eax
        pushl   -4(%ebp)
        call    add
        addl    $8, %esp
        movl    global_val, %edx
        pushl   %eax
        pushl   %edx
        pushl   -4(%ebp)
        pushl   $.LC1
        call    printf
        addl    $16, %esp
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
.globl global_val
        .section        my_data_section
        .align 4
        .type   global_val, @object
        .size   global_val, 4
global_val:
        .zero   4
       
Notice how the two variables ended up in different sections (but which both have the same name). Why did this happen? I'm not 100% sure why, but the gcc sub-program that converts the C code into assembly did originally setup two different sections for the two variables, but because the two sections have the same name, they ended up together. I don't know why the initialized global data ended up at the top and the non-intialized one ended up at the bottom. Perhaps this is something to explore some other day.

Basically, the answer to the above question "why gval_init ended up first" is that gcc separated them that way. If we make them both the same type of global variable we'll see that gcc will only create one segment for both of them, and that they'll appear in our segment in the order in which they're found in the source code:

code:
int global_val     __attribute__ ((section ("my_data_section")));
int gval_init      __attribute__ ((section ("my_data_section")));
assembly: (at the bottom of file)
.globl global_val
        .section        my_data_section,"aw",@progbits
        .align 4
        .type   global_val, @object
        .size   global_val, 4
global_val:
        .zero   4
.globl gval_init
        .align 4
        .type   gval_init, @object
        .size   gval_init, 4
gval_init:
        .zero   4
objdump -t | sort:
080495f0 g       *ABS*  00000000              __start_my_data_section
080495f0 g     O my_data_section        00000004              global_val
080495f0 l    d  my_data_section        00000000              my_data_section
080495f4 g     O my_data_section        00000004              gval_init
080495f8 g       *ABS*  00000000              __bss_start
080495f8 g       *ABS*  00000000              __stop_my_data_section
       

code:
int gval_init      __attribute__ ((section ("my_data_section")));
int global_val     __attribute__ ((section ("my_data_section")));
assembly: (at the bottom of file)
.globl gval_init
        .section        my_data_section,"aw",@progbits
        .align 4
        .type   gval_init, @object
        .size   gval_init, 4
gval_init:
        .zero   4
.globl global_val
        .align 4
        .type   global_val, @object
        .size   global_val, 4
global_val:
        .zero   4
objdump -t | sort:
080495f0 g       *ABS*  00000000              __start_my_data_section
080495f0 g     O my_data_section        00000004              gval_init
080495f0 l    d  my_data_section        00000000              my_data_section
080495f4 g     O my_data_section        00000004              global_val
080495f8 g       *ABS*  00000000              __bss_start
080495f8 g       *ABS*  00000000              __stop_my_data_section