| Understanding The Linux Kernel Initcall Mechanism: Creating Dynamic Function-Pointer Call Tables | |||
|---|---|---|---|
| <<< Previous | Chapter 4. Simple Examples | Next >>> | Blog |
For this example we're going to build ./main composed of main.c and add.c:
/*
* Copyright (C) 2006 Trevor Woerner
*/
#include <stdio.h>
int add (int, int);
int global_val;
int gval_init = 0;
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;
}
|
/*
* Copyright (C) 2006 Trevor Woerner
*/
int
add (int i, int j)
{
return i+j;
}
|
Compiling is a simple process:
[trevor]$ gcc -c add.c
[trevor]$ gcc -c main.c
[trevor]$ gcc -o main main.o add.o
|
Here is a dump of the info from add.o:
[trevor]$ objdump -t add.o
add.o: file format elf32-i386
SYMBOL TABLE:
00000000 l df *ABS* 00000000 add.c
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .comment 00000000 .comment
00000000 g F .text 0000000b add
|
Here is a similar dump of main.o:
[trevor]$ objdump -t main.o
main.o: file format elf32-i386
SYMBOL TABLE:
00000000 l df *ABS* 00000000 main.c
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .rodata 00000000 .rodata
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .comment 00000000 .comment
00000000 g O .bss 00000004 gval_init
00000000 g F .text 0000007d main
00000004 O *COM* 00000004 global_val
00000000 *UND* 00000000 printf
00000000 *UND* 00000000 add
|
And here is the objdump -t of the final executable:
[trevor]$ objdump -t main
main: file format elf32-i386
SYMBOL TABLE:
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 l d .init 00000000 .init
08048298 l d .plt 00000000 .plt
080482d8 l d .text 00000000 .text
08048488 l d .fini 00000000 .fini
080484a4 l d .rodata 00000000 .rodata
080484ec l d .eh_frame 00000000 .eh_frame
080494f0 l d .ctors 00000000 .ctors
080494f8 l d .dtors 00000000 .dtors
08049500 l d .jcr 00000000 .jcr
08049504 l d .dynamic 00000000 .dynamic
080495cc l d .got 00000000 .got
080495d0 l d .got.plt 00000000 .got.plt
080495e8 l d .data 00000000 .data
080495f4 l d .bss 00000000 .bss
00000000 l d .comment 00000000 .comment
00000000 l d *ABS* 00000000 .shstrtab
00000000 l d *ABS* 00000000 .symtab
00000000 l d *ABS* 00000000 .strtab
080482fc l F .text 00000000 call_gmon_start
00000000 l df *ABS* 00000000 crtstuff.c
080494f0 l O .ctors 00000000 __CTOR_LIST__
080494f8 l O .dtors 00000000 __DTOR_LIST__
08049500 l O .jcr 00000000 __JCR_LIST__
080495f4 l O .bss 00000001 completed.4583
080495f0 l O .data 00000000 p.4582
08048320 l F .text 00000000 __do_global_dtors_aux
08048354 l F .text 00000000 frame_dummy
00000000 l df *ABS* 00000000 crtstuff.c
080494f4 l O .ctors 00000000 __CTOR_END__
080494fc l O .dtors 00000000 __DTOR_END__
080484ec l O .eh_frame 00000000 __FRAME_END__
08049500 l O .jcr 00000000 __JCR_END__
08048460 l F .text 00000000 __do_global_ctors_aux
00000000 l df *ABS* 00000000 main.c
00000000 l df *ABS* 00000000 add.c
08049504 g O .dynamic 00000000 _DYNAMIC
080495fc g O .bss 00000004 global_val
080484a4 g O .rodata 00000004 _fp_hw
080494f0 g *ABS* 00000000 .hidden __fini_array_end
080495ec g O .data 00000000 .hidden __dso_handle
08048458 g F .text 00000005 __libc_csu_fini
08048280 g F .init 00000000 _init
080483fc g F .text 0000000b add
080495f8 g O .bss 00000004 gval_init
080482d8 g F .text 00000000 _start
080494f0 g *ABS* 00000000 .hidden __fini_array_start
08048408 g F .text 0000004f __libc_csu_init
080495f4 g *ABS* 00000000 __bss_start
0804837c g F .text 0000007d main
00000000 F *UND* 00000187 __libc_start_main@@GLIBC_2.0
080494f0 g *ABS* 00000000 .hidden __init_array_end
080495e8 w .data 00000000 data_start
00000000 F *UND* 00000039 printf@@GLIBC_2.0
08048488 g F .fini 00000000 _fini
080494f0 g *ABS* 00000000 .hidden __preinit_array_end
080495f4 g *ABS* 00000000 _edata
080495d0 g O .got.plt 00000000 .hidden _GLOBAL_OFFSET_TABLE_
08049600 g *ABS* 00000000 _end
080494f0 g *ABS* 00000000 .hidden __init_array_start
080484a8 g O .rodata 00000004 _IO_stdin_used
080495e8 g .data 00000000 __data_start
00000000 w *UND* 00000000 _Jv_RegisterClasses
080494f0 g *ABS* 00000000 .hidden __preinit_array_start
00000000 w *UND* 00000000 __gmon_start__
|
The interesting thing for me is how such a small amount of code generates such a large number of segments! Notice how both of the *.o files contain .text, .data, and .bss segments. When they are combined into the executable main, it still contains these segments (i.e. it makes no distinction about where the specific parts come from, they all get combined into one larger segment of the same name).
If we want to know the linker script that was used (to find out how ld lays out all the sections), all we have to do is pass the --verbose flag to ld via gcc (like this: gcc -Wl,--verbose ...) and we will get the linker script spat out on stderr. Here is the linker script that I get for this code:
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf32-i386", "elf32-i386",
"elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SEARCH_DIR("/usr/i386-redhat-linux/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
/* Do we need any of these for elf?
__DYNAMIC = 0; */
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = 0x08048000); . = 0x08048000 + SIZEOF_HEADERS;
.interp : { *(.interp) }
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.dyn :
{
*(.rel.init)
*(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
*(.rel.fini)
*(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
*(.rel.data.rel.ro*)
*(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
*(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
*(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
*(.rel.ctors)
*(.rel.dtors)
*(.rel.got)
*(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
}
.rela.dyn :
{
*(.rela.init)
*(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
*(.rela.fini)
*(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
*(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
*(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
*(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
*(.rela.ctors)
*(.rela.dtors)
*(.rela.got)
*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
}
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
.init :
{
KEEP (*(.init))
} =0x90909090
.plt : { *(.plt) }
.text :
{
*(.text .stub .text.* .gnu.linkonce.t.*)
KEEP (*(.text.*personality*))
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
} =0x90909090
.fini :
{
KEEP (*(.fini))
} =0x90909090
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.eh_frame_hdr : { *(.eh_frame_hdr) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RO { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = ALIGN (0x1000) - ((0x1000 - .) & (0x1000 - 1)); . = DATA_SEGMENT_ALIGN (0x1000, 0x1000);
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
.gcc_except_table : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
/* Ensure the __preinit_array_start label is properly aligned. We
could instead move the label definition inside the section, but
the linker would then create the section even if it turns out to
be empty, which isn't pretty. */
. = ALIGN(32 / 8);
PROVIDE (__preinit_array_start = .);
.preinit_array : { KEEP (*(.preinit_array)) }
PROVIDE (__preinit_array_end = .);
PROVIDE (__init_array_start = .);
.init_array : { KEEP (*(.init_array)) }
PROVIDE (__init_array_end = .);
PROVIDE (__fini_array_start = .);
.fini_array : { KEEP (*(.fini_array)) }
PROVIDE (__fini_array_end = .);
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin*.o(.ctors))
/* We don't want to include the .ctor section from
from the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin*.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) }
.dynamic : { *(.dynamic) }
.got : { *(.got) }
. = DATA_SEGMENT_RELRO_END (12, .);
.got.plt : { *(.got.plt) }
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
KEEP (*(.gnu.linkonce.d.*personality*))
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .;
PROVIDE (edata = .);
__bss_start = .;
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections. */
. = ALIGN(32 / 8);
}
. = ALIGN(32 / 8);
_end = .;
PROVIDE (end = .);
. = DATA_SEGMENT_END (.);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/DISCARD/ : { *(.note.GNU-stack) }
}
|
This might look like it's difficult to read, but it's not. Text within /* and */, as is the same with C code, indicates comments which are ignored. A period by itself ., as is the same in assembly notation, indicates the current value of the output location counter.
At the top of the file is a bunch of housekeeping stuff. Then it gives the SECTIONS command which indicates the start of the script that defines how the sections of the output ELF file are going to be laid out. As an example, let's look at the following lines of code which lay out the .text part of the image (i.e. the part where the executable code is placed):
.text :
{
*(.text .stub .text.* .gnu.linkonce.t.*)
KEEP (*(.text.*personality*))
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
} =0x90909090
|
Now I am going to lay out a .text section in the output file, at this point in the output.
This section is going to be composed of all the .text, .stub, .text.*, and .gnu.linkonce.t.* sections I encounter (in that order) from all input files I am given (the * before the parenthesized list indicates which input files to consider).
This is followed by all .gnu.warning sections I encounter from all input files.
The =0x90909090 written at the end of the section's description tells me the fill pattern to use if there is any space between sections (mostly due to alignment constraints).
Just playing around and experimenting some more, here is the objdump -t of the executable again, with most of the cruft removed, and sorted by address:
[trevor]$ objdump -t main
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 0000007d main
080483fc g F .text 0000000b add
08048408 g F .text 0000004f __libc_csu_init
08048458 g F .text 00000005 __libc_csu_fini
08048460 l F .text 00000000 __do_global_ctors_aux
|
[trevor]$ gcc -o main add.o main.o
|
[trevor]$ objdump -t main
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 0000000b add
08048388 g F .text 0000007d main
08048408 g F .text 0000004f __libc_csu_init
08048458 g F .text 00000005 __libc_csu_fini
08048460 l F .text 00000000 __do_global_ctors_aux
|
| <<< Previous | Home | Next >>> |
| Simple Examples | Up | Putting Objects into their own ELF Sections |