I'm trying to compile an executable (ELF file) that does not use a dynamic loader. I built a cross compiler that compiles mips from linux to be used on a simulator I made. I asserted the flag -static-libgcc on compilation of my hello.cpp file (hello world program). Apparently this is not enough though. Because there is still a segment in my executable which contains the name/path of the dynamic loader. What flags do I use to generate an executable which contains EVERYTHING needed to be run? Do I need to rebuild my cross compiler?

Is there a good resource describing the exact result of each of the gcc flags? I'm not 100% sure whether I'm actually disabling dynamic loading or not. When I use -static I still have the name of a dynamic loader placed into my .interp section of the elf file.

Recommended Answers

All 8 Replies

> I asserted the flag -static-libgcc on compilation of my hello.cpp file (hello world program).
> Apparently this is not enough though. Because there is still a segment in my executable which contains the name/path of the dynamic loader.
> What flags do I use to generate an executable which contains EVERYTHING needed to be run?

The flag to use is -static to link everything statically or -static-libgcc to just link to libgcc statically.

For example, with this hello.c

#include <stdio.h>

int main()
{
    puts( "hello world" ) ;
    return 0 ;
}

This would link to the shared libraries:
gcc -Wall -std=c89 -pedantic -Werror hello.c -o hello && ldd hello
libc.so.7 => /lib/libc.so.7 (0x2808e000)

And this should create a standalone executable:
>gcc -Wall -std=c89 -pedantic -Werror -static hello.c -o hello && ldd hello
ldd: hello: not a dynamic ELF executable

Executable files need some code that initially sets up the process environment for an executable, initializes the C and C++ libraries, in C++, perform dynamic initialization of statics (eg. invoke constructors of global objects); all that needs to be done before entering main(). Then this code calls main, and when main returns does the deinitialization and returns to the kernel via the exit syscall. The actual code is obviously highly platform specific.

In elf executables generated by the gnu toolchain, to set the entry point of the executable, the linker looks for a symbol named _start. This symbol is usually provided by the the libgcc library. (Unless the -nostartfiles linker switch is used, in which case we have to provide our own _start procedure). When the -static switch is used, the linker statically links to the _start procedure directly from object files instead of from the shared libgcc.

In your case, what appears to have happened is that these object files (or a static version of libgcc) have not been built during the cross-compiler build, or if you have built them, the linker can't find them. Since the linker cannot find an _start to link with statically, it picks up the one from libgcc.so and links to it dynamically.

If you have built crt1.o crti.o and crtn.o as part of of your cross-compiler libgcc build, you can ask gcc to just compile the files, and then invoke the linker with the -nostartfiles switch, and provide all the required object files and libraries on the command line.
> gcc -Wall -std=c89 -pedantic -Werror -static -c hello.c -o hello.o && ld -static -nostartfiles -o hello -L`gcc -print-file-name=` hello.o /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o -lc -lgcc && ldd hello
ldd: hello: not a dynamic ELF executable

Note: The above was compiled and linked on Unix (FreeBSD), but AFAIK, it should be similar for Linux. The precise filenames and/or paths for the object files containing the startup code may be different, though. For brevity, the example was in C; for C++, you need to also add libstdc++ and a couple of more startup object files (crtbegin.o, crtend.o and so on).

> Is there a good resource describing the exact result of each of the gcc flags?

The gcc manuals do specify the flags: http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/
The part that you may want to look at is here: http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Link-Options.html#Link-Options

You might find the explanations a bit cryptic; in which case 'The Definitive Guide to GCC' http://www.amazon.com/Definitive-Guide-GCC-Second/dp/1590595858 might be helpful.

commented: Always learn something from your posts :) +3

Thanks for the resources. I'm still quite new to GCC options so having a nice guide to it all is very useful.

When I compile my program using -static I still have an ELF file with an interp section that contains the name of the native dynamic linker. The ".dyn..." segments are also there even though lld says that the binary isn't dynamic. When I use the flag listing you described:

gcc -Wall -std=c89 -pedantic -Werror -static hello.c -o hello

Then I do in fact have what seems to be a statically linked standalone executable as you said. This has improved the test depth of my program considerably.

My biggest question now is, does the loading sequence for an ELF file start (from the absolute beginning of the loading process) at the _start tag in .text? I'm hoping not because an instruction near the beginning of _start loads deadbeef into the program which denotes that some previous instructions should have placed a real value where the deadbeef was.

There is a .init segment and a reginfo segment. Perhaps these?

> does the loading sequence for an ELF file start (from the absolute beginning of the loading process) at the _start tag in .text?

The loading sequence starts with a syscall to execve with the file name and comandline as args. execve reads the file, examines the elf headers, loads program sections into the virtual adress space, initializes the runtime stack and then passes control to the entry point specified in the elf header.

The following is for Unix (FreeBSD/i386); I suspect Linux could be somewhat different from Unix. If you have access to the target MIPS machine, copy the program to that and then check it out yourself - that is always the best way.

>ktrace ./hello && kdump
hello world
 51605 ktrace   RET   ktrace 0
 51605 ktrace   CALL  execve(0xbfbfe85b,0xbfbfe6b0,0xbfbfe6b8)
 51605 ktrace   NAMI  "./hello"
 51605 hello    RET   execve 0
 51605 hello    CALL  fstat(0x1,0xbfbfe514)
 ...

The entry point is _start, so yes, the program begins executing at the _start tag in .text

> an instruction near the beginning of _start loads deadbeef into the program
> which denotes that some previous instructions should have placed a real value where the deadbeef was.

Verify that there is no dynamic segment in your executable.
A standalone executable (all of the following are only for executables without a dynamic segment in them) should look something like this:

>readelf -d hello
There is no dynamic segment in this file.

>readelf -S hello
There are 14 section headers, starting at offset 0x2b700:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .init             PROGBITS        080480ac 0000ac 000007 00  AX  0   0  4
  [ 2] .text             PROGBITS        080480c0 0000c0 024070 00  AX  0   0 16
  [ 3] .fini             PROGBITS        0806c130 024130 000007 00  AX  0   0  4
  [ 4] .rodata           PROGBITS        0806c140 024140 0039cd 00   A  0   0 32
  [ 5] .data             PROGBITS        08070000 028000 001ef4 00  WA  0   0 32
  [ 6] .ctors            PROGBITS        08071ef4 029ef4 000004 00  WA  0   0  4
  [ 7] .dtors            PROGBITS        08071ef8 029ef8 000004 00  WA  0   0  4
  [ 8] .bss              NOBITS          08071f00 029f00 008ee8 00  WA  0   0 32
  [ 9] .comment          PROGBITS        00000000 029f00 001799 00      0   0  1
  [10] .note.ABI-tag     NOTE            08048094 000094 000018 00   A  0   0  4
  [11] .shstrtab         STRTAB          00000000 02b699 000065 00      0   0  1
  [12] .symtab           SYMTAB          00000000 02b930 006fc0 10     13 4bc  4
  [13] .strtab           STRTAB          00000000 0328f0 004808 00      0   0  1

Note that while .ctors and .dtors are writeable, .text is not.

>readelf -h hello | grep Entry
  Entry point address:               0x80480f0

(gdb) disas 0x80480f0
Dump of assembler code for function _start:
0x080480f0 <_start+0>:  push   %ebp
0x080480f1 <_start+1>:  mov    %esp,%ebp
0x080480f3 <_start+3>:  push   %esi
0x080480f4 <_start+4>:  push   %ebx
0x080480f5 <_start+5>:  sub    $0x10,%esp
0x080480f8 <_start+8>:  and    $0xfffffff0,%esp
0x080480fb <_start+11>: mov    0x4(%ebp),%ebx
0x080480fe <_start+14>: mov    %edx,%ecx
0x08048100 <_start+16>: lea    0xc(%ebp,%ebx,4),%esi
0x08048104 <_start+20>: test   %ebx,%ebx
0x08048106 <_start+22>: mov    %esi,0x807adc0
0x0804810c <_start+28>: jle    0x8048144 <_start+84>
0x0804810e <_start+30>: mov    0x8(%ebp),%eax
0x08048111 <_start+33>: test   %eax,%eax
0x08048113 <_start+35>: je     0x8048144 <_start+84>
0x08048115 <_start+37>: mov    %eax,0x8070000
0x0804811a <_start+42>: movzbl (%eax),%edx
0x0804811d <_start+45>: test   %dl,%dl
0x0804811f <_start+47>: je     0x8048144 <_start+84>
0x08048121 <_start+49>: add    $0x1,%eax
0x08048124 <_start+52>: jmp    0x8048130 <_start+64>
0x08048126 <_start+54>: movzbl (%eax),%edx
0x08048129 <_start+57>: add    $0x1,%eax
0x0804812c <_start+60>: test   %dl,%dl
0x0804812e <_start+62>: je     0x8048144 <_start+84>
0x08048130 <_start+64>: cmp    $0x2f,%dl
0x08048133 <_start+67>: jne    0x8048126 <_start+54>
0x08048135 <_start+69>: mov    %eax,0x8070000
0x0804813a <_start+74>: movzbl (%eax),%edx
0x0804813d <_start+77>: add    $0x1,%eax
0x08048140 <_start+80>: test   %dl,%dl
0x08048142 <_start+82>: jne    0x8048130 <_start+64>
0x08048144 <_start+84>: mov    $0x0,%eax
0x08048149 <_start+89>: test   %eax,%eax
0x0804814b <_start+91>: je     0x8048181 <_start+145>
0x0804814d <_start+93>: mov    %ecx,(%esp)
0x08048150 <_start+96>: call   0x80519b0 <atexit>
0x08048155 <_start+101>:        movl   $0x806c130,(%esp)
0x0804815c <_start+108>:        call   0x80519b0 <atexit>
0x08048161 <_start+113>:        call   0x80480ac <_init>
0x08048166 <_start+118>:        lea    0x8(%ebp),%eax
0x08048169 <_start+121>:        mov    %esi,0x8(%esp)
0x0804816d <_start+125>:        mov    %eax,0x4(%esp)
0x08048171 <_start+129>:        mov    %ebx,(%esp)
0x08048174 <_start+132>:        call   0x80480c0 <main>
0x08048179 <_start+137>:        mov    %eax,(%esp)
0x0804817c <_start+140>:        call   0x8050a60 <exit>
0x08048181 <_start+145>:        call   0x80481b0 <_init_tls>
0x08048186 <_start+150>:        jmp    0x8048155 <_start+101>
0x08048188 <_start+152>:        nop
0x08048189 <_start+153>:        nop
0x0804818a <_start+154>:        nop
0x0804818b <_start+155>:        nop
0x0804818c <_start+156>:        nop
0x0804818d <_start+157>:        nop
0x0804818e <_start+158>:        nop
0x0804818f <_start+159>:        nop
End of assembler dump.

_start calls atexit, _init and then main, on return from main it calls exit.

For this trivial C program, _init is essentially a no op.

(gdb) disas _init
Dump of assembler code for function _init:
0x080480ac <_init+0>:   sub    $0xc,%esp
0x080480af <_init+3>:   add    $0xc,%esp
0x080480b2 <_init+6>:   ret
End of assembler dump.

And exit cleans up and makes the syscall _exit to exit from the process.

(gdb) disas exit
Dump of assembler code for function exit:
0x08050a60 <exit+0>:    push   %ebp
0x08050a61 <exit+1>:    mov    %esp,%ebp
0x08050a63 <exit+3>:    sub    $0x8,%esp
0x08050a66 <exit+6>:    movl   $0x0,(%esp)
0x08050a6d <exit+13>:   movl   $0x1,0x8079ed4
0x08050a77 <exit+23>:   call   0x80516c0 <__cxa_finalize>
0x08050a7c <exit+28>:   mov    0x807adc8,%eax
0x08050a81 <exit+33>:   test   %eax,%eax
0x08050a83 <exit+35>:   je     0x8050a87 <exit+39>
0x08050a85 <exit+37>:   call   *%eax
0x08050a87 <exit+39>:   mov    0x8(%ebp),%eax
0x08050a8a <exit+42>:   mov    %eax,(%esp)
0x08050a8d <exit+45>:   call   0x8050c1c <_exit>
0x08050a92 <exit+50>:   nop
0x08050a93 <exit+51>:   nop
0x08050a94 <exit+52>:   nop
0x08050a95 <exit+53>:   nop

The initialization and deinitialization is more elaborate for C++ programs.
For example, for a hello.cc:

#include <iostream>

int main()
{
  std::cout << "hello world\n" ;
}
>g++ -Wall -std=c++98 -pedantic -Werror -static -o hello++ hello.cc && readelf -S hello++
There are 19 section headers, starting at offset 0xb0f60:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .init             PROGBITS        080480cc 0000cc 000011 00  AX  0   0  4
  [ 2] .text             PROGBITS        080480e0 0000e0 08fadf 00  AX  0   0 16
  [ 3] .fini             PROGBITS        080d7bc0 08fbc0 00000c 00  AX  0   0  4
  [ 4] .rodata           PROGBITS        080d7be0 08fbe0 008e97 00   A  0   0 32
  [ 5] .data             PROGBITS        080e1a80 098a80 002040 00  WA  0   0 32
  [ 6] .tbss             NOBITS          080e3ac0 09aac0 000008 00 WAT  0   0  4
  [ 7] .eh_frame         PROGBITS        080e3ac0 09aac0 00f58c 00   A  0   0  4
  [ 8] .gcc_except_table PROGBITS        080f304c 0aa04c 004588 00   A  0   0  4
  [ 9] .ctors            PROGBITS        080f75d4 0ae5d4 000028 00  WA  0   0  4
  [10] .dtors            PROGBITS        080f75fc 0ae5fc 00000c 00  WA  0   0  4
  [11] .jcr              PROGBITS        080f7608 0ae608 000004 00  WA  0   0  4
  [12] .got              PROGBITS        080f760c 0ae60c 000010 04  WA  0   0  4
  [13] .bss              NOBITS          080f7620 0ae620 00f828 00  WA  0   0 32
  [14] .comment          PROGBITS        00000000 0ae620 0028ac 00      0   0  1
  [15] .note.ABI-tag     NOTE            080480b4 0000b4 000018 00   A  0   0  4
  [16] .shstrtab         STRTAB          00000000 0b0ecc 000091 00      0   0  1
  [17] .symtab           SYMTAB          00000000 0b1258 013690 10     18 6df  4
  [18] .strtab           STRTAB          00000000 0c48e8 024642 00      0   0  1

>readelf -d hello++
There is no dynamic segment in this file.

>readelf -h hello++ | grep Entry
  Entry point address:               0x80480e0

_start is identical to that of the C program:

(gdb) disas 0x80480e0
Dump of assembler code for function _start:
0x080480e0 <_start+0>:  push   %ebp
0x080480e1 <_start+1>:  mov    %esp,%ebp
...
...
0x08048140 <_start+96>: call   0x80bfd50 <atexit>
0x08048145 <_start+101>:        movl   $0x80d7bc0,(%esp)
0x0804814c <_start+108>:        call   0x80bfd50 <atexit>
0x08048151 <_start+113>:        call   0x80480cc <_init>
0x08048156 <_start+118>:        lea    0x8(%ebp),%eax
0x08048159 <_start+121>:        mov    %esi,0x8(%esp)
0x0804815d <_start+125>:        mov    %eax,0x4(%esp)
0x08048161 <_start+129>:        mov    %ebx,(%esp)
0x08048164 <_start+132>:        call   0x8048330 <main>
0x08048169 <_start+137>:        mov    %eax,(%esp)
0x0804816c <_start+140>:        call   0x80ba130 <exit>
0x08048171 <_start+145>:        call   0x80ae0d0 <_init_tls>
0x08048176 <_start+150>:        jmp    0x8048145 <_start+101>
0x08048178 <_start+152>:        nop
0x08048179 <_start+153>:        nop

_init calls frame_dummy (set up the exception frame) and then __do_global_ctors_aux (dynamic initialization of statics, .ctors contains a table of function pointers).

(gdb) disas _init
Dump of assembler code for function _init:
0x080480cc <_init+0>:   sub    $0xc,%esp
0x080480cf <_init+3>:   call   0x80481d0 <frame_dummy>
0x080480d4 <_init+8>:   call   0x80d7b50 <__do_global_ctors_aux>
0x080480d9 <_init+13>:  add    $0xc,%esp
0x080480dc <_init+16>:  ret
End of assembler dump.

(gdb) disas frame_dummy
Dump of assembler code for function frame_dummy:
0x080481d0 <frame_dummy+0>:     push   %ebp
0x080481d1 <frame_dummy+1>:     mov    $0x80acb50,%eax
0x080481d6 <frame_dummy+6>:     mov    %esp,%ebp
0x080481d8 <frame_dummy+8>:     sub    $0x8,%esp
0x080481db <frame_dummy+11>:    test   %eax,%eax
0x080481dd <frame_dummy+13>:    je     0x80481f3 <frame_dummy+35>
0x080481df <frame_dummy+15>:    movl   $0x80f7624,0x4(%esp)
0x080481e7 <frame_dummy+23>:    movl   $0x80e3ac0,(%esp)
0x080481ee <frame_dummy+30>:    call   0x80acb50 <__register_frame_info>
0x080481f3 <frame_dummy+35>:    mov    0x80f7608,%eax
0x080481f8 <frame_dummy+40>:    test   %eax,%eax
0x080481fa <frame_dummy+42>:    je     0x804820e <frame_dummy+62>
0x080481fc <frame_dummy+44>:    mov    $0x0,%eax
0x08048201 <frame_dummy+49>:    test   %eax,%eax
0x08048203 <frame_dummy+51>:    je     0x804820e <frame_dummy+62>
0x08048205 <frame_dummy+53>:    movl   $0x80f7608,(%esp)
0x0804820c <frame_dummy+60>:    call   *%eax
0x0804820e <frame_dummy+62>:    leave
0x0804820f <frame_dummy+63>:    ret
End of assembler dump.

(gdb) disas __do_global_ctors_aux
Dump of assembler code for function __do_global_ctors_aux:
0x080d7b50 <__do_global_ctors_aux+0>:   push   %ebp
0x080d7b51 <__do_global_ctors_aux+1>:   mov    %esp,%ebp
0x080d7b53 <__do_global_ctors_aux+3>:   push   %ebx
0x080d7b54 <__do_global_ctors_aux+4>:   sub    $0x4,%esp
0x080d7b57 <__do_global_ctors_aux+7>:   mov    0x80f75f4,%eax
0x080d7b5c <__do_global_ctors_aux+12>:  cmp    $0xffffffff,%eax
0x080d7b5f <__do_global_ctors_aux+15>:  je     0x80d7b73 <__do_global_ctors_aux+35>
0x080d7b61 <__do_global_ctors_aux+17>:  xor    %ebx,%ebx
0x080d7b63 <__do_global_ctors_aux+19>:  call   *%eax
0x080d7b65 <__do_global_ctors_aux+21>:  mov    0x80f75f0(%ebx),%eax
0x080d7b6b <__do_global_ctors_aux+27>:  sub    $0x4,%ebx
0x080d7b6e <__do_global_ctors_aux+30>:  cmp    $0xffffffff,%eax
0x080d7b71 <__do_global_ctors_aux+33>:  jne    0x80d7b63 <__do_global_ctors_aux+19>
0x080d7b73 <__do_global_ctors_aux+35>:  add    $0x4,%esp
0x080d7b76 <__do_global_ctors_aux+38>:  pop    %ebx
0x080d7b77 <__do_global_ctors_aux+39>:  pop    %ebp
0x080d7b78 <__do_global_ctors_aux+40>:  ret
0x080d7b79 <__do_global_ctors_aux+41>:  nop
0x080d7b7a <__do_global_ctors_aux+42>:  nop
0x080d7b7b <__do_global_ctors_aux+43>:  nop

*note that my program is a c++ "hello world" file.

... execve reads the file, examines the elf headers, loads program sections into the virtual adress space, initializes the runtime stack and then passes control to the entry point specified in the elf header.

What does initializing the run-time stack entail? Does this process define the stack pointer? Shouldn't that information already be "known" and readied within __start? For instance loading the stack pointer into register 29 (MIPS)?

Verify that there is no dynamic segment in your executable.

babbage-dasnyder 3% mips-unknown-linux-gnu-readelf -d hello
There is no dynamic segment in this file.

My ELF file has the following headers(I assume that somewhere along the way I have enabled debugging which has generated several additional segments):

babbage-dasnyder 4% mips-unknown-linux-gnu-readelf -S hello
There are 34 section headers, starting at offset 0x355dc4:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .reginfo          MIPS_REGINFO    004000d4 0000d4 000018 18   A  0   0  4
  [ 2] .init             PROGBITS        004000ec 0000ec 0000a8 00  AX  0   0  4
  [ 3] .text             PROGBITS        004001a0 0001a0 0f5cf8 00  AX  0   0 16
  [ 4] __libc_freeres_fn PROGBITS        004f5e98 0f5e98 000cf4 00  AX  0   0  4
  [ 5] .fini             PROGBITS        004f6b8c 0f6b8c 000058 00  AX  0   0  4
  [ 6] .rodata           PROGBITS        004f6bf0 0f6bf0 015c92 00   A  0   0 16
  [ 7] .data             PROGBITS        10000000 110000 0052d0 00  WA  0   0 16
  [ 8] __libc_subfreeres PROGBITS        100052d0 1152d0 000024 00  WA  0   0  4
  [ 9] __libc_atexit     PROGBITS        100052f4 1152f4 000004 00  WA  0   0  4
  [10] .eh_frame         PROGBITS        100052f8 1152f8 00d114 00  WA  0   0  4
  [11] .gcc_except_table PROGBITS        1001240c 12240c 004194 00  WA  0   0  4
  [12] .ctors            PROGBITS        100165a0 1265a0 00001c 00  WA  0   0  4
  [13] .dtors            PROGBITS        100165bc 1265bc 00000c 00  WA  0   0  4
  [14] .jcr              PROGBITS        100165c8 1265c8 000004 00  WA  0   0  4
  [15] .got              PROGBITS        100165d0 1265d0 0020f8 04 WAp  0   0 16
  [16] .sbss             NOBITS          100186c8 1286c8 0000b0 00 WAp  0   0  8
  [17] .bss              NOBITS          10018780 1286d0 00666c 00  WA  0   0 16
  [18] __libc_freeres_pt NOBITS          1001edec 1286d0 000018 00  WA  0   0  4
  [19] .comment          PROGBITS        00000000 1286d0 001aca 00      0   0  1
  [20] .debug_aranges    MIPS_DWARF      00000000 12a1a0 004238 00      0   0  8
  [21] .debug_pubnames   MIPS_DWARF      00000000 12e3d8 023599 00      0   0  1
  [22] .debug_info       MIPS_DWARF      00000000 151971 154f27 00      0   0  1
  [23] .debug_abbrev     MIPS_DWARF      00000000 2a6898 01023b 00      0   0  1
  [24] .debug_line       MIPS_DWARF      00000000 2b6ad3 03a691 00      0   0  1
  [25] .debug_frame      MIPS_DWARF      00000000 2f1164 00e470 00      0   0  4
  [26] .debug_str        MIPS_DWARF      00000000 2ff5d4 02e950 01  MS  0   0  1
  [27] .pdr              PROGBITS        00000000 32df24 016140 00      0   0  4
  [28] .note.ABI-tag     NOTE            004000b4 0000b4 000020 00   A  0   0  4
  [29] .mdebug.abi32     PROGBITS        00000000 344064 000000 00      0   0  1
  [30] .debug_ranges     MIPS_DWARF      00000000 344064 011c00 00      0   0  1
  [31] .shstrtab         STRTAB          00000000 355c64 00015e 00      0   0  1
  [32] .symtab           SYMTAB          00000000 356314 014730 10     33 640  4
  [33] .strtab           STRTAB          00000000 36aa44 024739 00      0   0  1

From what I can tell my __start section does not actually explicitly call init or main. It looks like hlt is called but hlt just loops in on itself. Do you know why my __start section looks so different (__start shown below)?

EDIT: One moment, my disassembly of __start didn't seem to work just right. I'll try to get it.
EDIT: I think I can assume _ftext is the same thing as __start as they both have the same starting virtual address.

Also, why do the virtual addresses shown in gdb jump around and increment by 2 sometimes, larger numbers other times. Shouldn't the instruction interval always be 4 bytes? (32bit)

(gdb) disas __start
Dump of assembler code for function _ftext:
0x004001a0 <_ftext+0>:	add    %eax,%esp
0x004001a2 <_ftext+2>:	add    %ah,(%ecx)
0x004001a4 <_ftext+4>:	add    $0x11,%al
0x004001a6 <_ftext+6>:	add    %al,(%ecx)
0x004001a8 <_ftext+8>:	add    %al,(%eax)
0x004001aa <_ftext+10>:	add    %al,(%eax)
0x004001ac <_ftext+12>:	cmp    $0x1c,%al
0x004001ae <_ftext+14>:	(bad)  
0x004001af <_ftext+15>:	ret    $0x9c27
0x004001b2 <_ftext+18>:	in     $0x14,%al
0x004001b4 <_ftext+20>:	add    0x21e0(%edi),%ebx
0x004001ba <_ftext+26>:	clc    
0x004001bb <_ftext+27>:	and    %ecx,0x8f0c8184(%edi)
0x004001c1 <_ftext+33>:	movsl  %ds:(%esi),%es:(%edi)
0x004001c2 <_ftext+34>:	add    %al,(%eax)
0x004001c4 <_ftext+36>:	daa    
0x004001c5 <_ftext+37>:	cmpsb  %es:(%edi),%ds:(%esi)
0x004001c6 <_ftext+38>:	add    %al,(%esp)
0x004001c9 <_ftext+41>:	add    %edi,%edi
0x004001cb <_ftext+43>:	clc    
0x004001cc <_ftext+44>:	add    0xbd2724e8(%ecx),%esp
0x004001d2 <_ftext+50>:	jmp    *%eax
0x004001d4 <_ftext+52>:	popl   0x888f888f(%edi)
0x004001da <_ftext+58>:	sahf   
0x004001db <_ftext+59>:	hlt    
0x004001dc <_ftext+60>:	add    %al,(%eax)
0x004001de <_ftext+62>:	add    %al,(%eax)
0x004001e0 <_ftext+64>:	scas   %es:(%edi),%eax
0x004001e1 <_ftext+65>:	test   $0x0,%al
0x004001e3 <_ftext+67>:	adc    %ch,0xaf1400a2(%edi)
0x004001e9 <_ftext+73>:	mov    $0x998f1800,%ebp
0x004001ee <_ftext+78>:	xchg   %eax,%ecx
0x004001ef <_ftext+79>:	movw   %es,(%eax)
0x004001f1 <_ftext+81>:	add    %al,(%eax)
0x004001f3 <_ftext+83>:	add    %al,(%ebx)
0x004001f5 <_ftext+85>:	and    %bh,%al
0x004001f7 <_ftext+87>:	or     %eax,(%eax)
0x004001f9 <_ftext+89>:	add    %al,(%eax)
0x004001fb <_ftext+91>:	add    %dl,(%eax)
End of assembler dump.

Also, this is the objdump of __start using objdump. This doesn't resemble what I assumed was __start using gdb:

Disassembly of section .text:

004001a0 <__start>:
  4001a0:	03e00021 	move	zero,ra
  4001a4:	04110001 	bal	4001ac <__start+0xc>
  4001a8:	00000000 	nop
  4001ac:	3c1c0fc2 	lui	gp,0xfc2
  4001b0:	279ce414 	addiu	gp,gp,-7148
  4001b4:	039fe021 	addu	gp,gp,ra
  4001b8:	0000f821 	move	ra,zero
  4001bc:	8f84810c 	lw	a0,-32500(gp)
  4001c0:	8fa50000 	lw	a1,0(sp)
  4001c4:	27a60004 	addiu	a2,sp,4
  4001c8:	2401fff8 	li	at,-8
  4001cc:	03a1e824 	and	sp,sp,at
  4001d0:	27bdffe0 	addiu	sp,sp,-32
  4001d4:	8f878f88 	lw	a3,-28792(gp)
  4001d8:	8f889ef4 	lw	t0,-24844(gp)
  4001dc:	00000000 	nop
  4001e0:	afa80010 	sw	t0,16(sp)
  4001e4:	afa20014 	sw	v0,20(sp)
  4001e8:	afbd0018 	sw	sp,24(sp)
  4001ec:	8f99918c 	lw	t9,-28276(gp)
  4001f0:	00000000 	nop
  4001f4:	0320f809 	jalr	t9
  4001f8:	00000000 	nop

004001fc <hlt>:
  4001fc:	1000ffff 	b	4001fc <hlt>
  400200:	00000000 	nop

Is it possible to execute a program using its object file?

I initialized the stack pointer to the beginning of the data segment and the test depth doubled (doesn't necessarily mean anything I know.) I still get deadbeef loaded into memory eventually. I've thoroughly verified that my loading instruction works perfectly so I don't quite see what the problem is. I still am having trouble with load order I suppose, unless some other values need initializing as well.

> What does initializing the run-time stack entail? Does this process define the stack pointer?
> Shouldn't that information already be "known" and readied within __start?
> For instance loading the stack pointer into register 29 (MIPS)?

On MIPS, that could be the case, with _start being passed all information via registers.

> My ELF file has the following headers(I assume that somewhere along the way I have enabled debugging which has generated several additional segments):

most of the additional sections are because of debug information, a few others like

.reginfo          MIPS_REGINFO

are MIPS specific. Presumably, this contains the information about MIPS register usage by the program.

> Do you know why my __start section looks so different (__start shown below)?
> Also, why do the virtual addresses shown in gdb jump around and increment by 2 sometimes, larger numbers other times.
> Shouldn't the instruction interval always be 4 bytes? (32bit)

The output produced by gdb you have used is garbage; it is trying to interpret the MIPS binary, thinking that it is an Intel binary. You need to use a gdb built for MIPS executables.

> Also, this is the objdump of __start using objdump. This doesn't resemble what I assumed was __start using gdb

The objdump output is the correct one; this would have been produced by the objdump built as part of binutils during the cross-compiler build.

> From what I can tell my __start section does not actually explicitly call init or main.

In MIPS elf executables, you don't get any names for function calls - the functions are called, using addresses in the GOT (Global Offset Table). In our case, the more complicated Procedure Linkage Table (PLT) + GOT does not apply, as we have no dynamic sections.

As per the MIPS elf specs, every function is called by executing jalr $t9 (where $t9 contains the address of the function, picked up from the GOT); on entry into every function, $t9 holds its virtual address. You will not see call instructions with the name of the function in the diaassembly; rather something like what you have in _start:

4001ec: 8f99918c lw t9,-28276(gp)
4001f0: 00000000 nop
4001f4: 0320f809 jalr t9
4001f8: 00000000 nop

This calls a function whose address is given by a pointer placed an offset of -28276 from the address in gp; gp is initialised by _start to point to an affset in the GOT, and holds that value throughout the program; so right at the beginning of _start, we see gp being initialized:

4001a0: 03e00021 move zero,ra
4001a4: 04110001 bal 4001ac <__start+0xc>
4001a8: 00000000 nop
4001ac: 3c1c0fc2 lui gp,0xfc2
4001b0: 279ce414 addiu gp,gp,-7148
4001b4: 039fe021 addu gp,gp,ra
4001b8: 0000f821 move ra,zero
etc.

Using gdb (built for the target MIPS platform) makes it much easier as gdb figures out the name of the function and displays it.

By now, I've reached my level of incompetence with MIPS elf; you would be better off posting additional questions on a Linux-MIPS mailing list or forum. There is information scattered all around the web, much of it dated. For example, this: http://www.cr0.org/paper/mips.elf.external.resolution.txt

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.