This question already has an answer here:
SIZE command in UNIX
(1 answer)
Closed 7 years ago.
In this program i am not declaring any variable still is showing data segment (BSS) size as 8 bytes.why ?
achul#achul:~/chennai/misclaneous$ vi memory.c
#include<stdio.h>
main()
{
}
achul#achul:~/chennai/misclaneous$ cc memory.c -o mem
achul#achul:~/chennai/misclaneous$ size mem
text data bss dec hex filename
1056 252 8 1316 524 mem
here i am not using any global variable as BSS is reserved for global variable only anyone plz ?
In a normal (hosted) environment, the .c files and compiled to object files, and this object files are linked with the standard clib. And the library contains a module (generally called crt0.o) that is natively loaded by the system, optionnaly decodes the command line and calls the function main passing argc, argv and environment, and also generally sets some global variables.
This crt0 module does contain global variables.
Here are couple of outputs
Below is a compiled object and not at all linked, purely the simple program above with no global content as indicated.
size t1.o
text data bss dec hex filename
62 0 0 62 3e t1.o
size --format=SysV t1.o
t1.o :
section size addr
.text 6 0
.data 0 0
.bss 0 0
.comment 42 0
.note.GNU-stack 0 0
.eh_frame 56 0
Total 104
And this Below is a complete executable likned with startup code and other supported stuff.
size a.out
text data bss dec hex filename
1115 552 8 1675 68b a.out
size --format=SysV a.out
a.out :
section size addr
.interp 28 4194872
.note.ABI-tag 32 4194900
.note.gnu.build-id 36 4194932
.gnu.hash 28 4194968
.dynsym 72 4195000
.dynstr 56 4195072
.gnu.version 6 4195128
.gnu.version_r 32 4195136
.rela.dyn 24 4195168
.rela.plt 48 4195192
.init 26 4195240
.plt 48 4195280
.text 370 4195328
.fini 9 4195700
.rodata 4 4195712
.eh_frame_hdr 52 4195716
.eh_frame 244 4195768
.init_array 8 6295056
.fini_array 8 6295064
.jcr 8 6295072
.dynamic 464 6295080
.got 8 6295544
.got.plt 40 6295552
.data 16 6295592
.bss 8 6295608
.comment 77 0
Total 1752
Related
I'm investigating the size of an extremely small C program on Linux (ubuntu 20.04).
I'm compiling as follows:
gcc -s -nostdlib test.c -o test
the following progam:
__attribute__((naked))
void _start() {
asm("movl $1,%eax;"
"xorl %ebx,%ebx;"
"int $0x80");
}
Basically the idea is to make the Linux system call to exit rather than depending on the C runtime to do that for us. (which would be the case in void main() { }). The program moves 1 into register EAX, clears register EBX (which would otherwise contain the return value), and then executes the linux system call interrupt 0x80. This interrupt triggers the kernel to process our call.
I would expect this program to be extremely small (less than 1K), however ..
du -h test
# >> 16K
ldd test
# >> statically linked
Why is this program still 16K?
du reports the disk space used by a file whereas ls reports the actual size of a file. Typically the size reported by du is significantly larger for small files.
You can significantly reduce the size of the binary by changing compile and linking options and stripping out unnecessary sections.
$ cat test.c
void _start() {
asm("movl $1,%eax;"
"xorl %ebx,%ebx;"
"int $0x80");
}
$ gcc -s -nostdlib test.c -o test
$ ./test
$ ls -l test
-rwxrwxr-x 1 fpm fpm 8840 Dec 9 04:09 test
$ readelf -W --section-headers test
There are 7 section headers, starting at offset 0x20c8:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .note.gnu.build-id NOTE 0000000000400190 000190 000024 00 A 0 0 4
[ 2] .text PROGBITS 0000000000401000 001000 000010 00 AX 0 0 1
[ 3] .eh_frame_hdr PROGBITS 0000000000402000 002000 000014 00 A 0 0 4
[ 4] .eh_frame PROGBITS 0000000000402018 002018 000038 00 A 0 0 8
[ 5] .comment PROGBITS 0000000000000000 002050 00002e 01 MS 0 0 1
[ 6] .shstrtab STRTAB 0000000000000000 00207e 000045 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
$
$ gcc -s -nostdlib -Wl,--nmagic test.c -o test
$ ls -l test
-rwxrwxr-x 1 fpm fpm 984 Dec 9 16:55 test
$ strip -R .comment -R .note.gnu.build-id test
$ strip -R .eh_frame_hdr -R .eh_frame test
$ ls -l test
-rwxrwxr-x 1 fpm fpm 520 Dec 9 17:03 test
$
Note that clang can produce a significantly smaller binary than gcc by default in this particular instance. However, after compiling with clang and stripping unnecessary sections, the final size of the binary is 736 bytes, which is bigger than the 520 bytes possible with gcc -s -nostdlib -Wl,--nmagic test.c -o test.
$ clang -static -nostdlib -flto -fuse-ld=lld -o test test.c
$ ls -l test
-rwxrwxr-x 1 fpm fpm 1344 Dec 9 04:15 test
$
$ readelf -W --section-headers test
There are 9 section headers, starting at offset 0x300:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .note.gnu.build-id NOTE 0000000000200190 000190 000018 00 A 0 0 4
[ 2] .eh_frame_hdr PROGBITS 00000000002001a8 0001a8 000014 00 A 0 0 4
[ 3] .eh_frame PROGBITS 00000000002001c0 0001c0 00003c 00 A 0 0 8
[ 4] .text PROGBITS 0000000000201200 000200 00000f 00 AX 0 0 16
[ 5] .comment PROGBITS 0000000000000000 00020f 000040 01 MS 0 0 1
[ 6] .symtab SYMTAB 0000000000000000 000250 000048 18 8 2 8
[ 7] .shstrtab STRTAB 0000000000000000 000298 000055 00 0 0 1
[ 8] .strtab STRTAB 0000000000000000 0002ed 000012 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
$
$ strip -R .eh_frame_hdr -R .eh_frame test
$ strip -R .comment -R .note.gnu.build-id test
strip: test: warning: empty loadable segment detected at vaddr=0x200000, is this intentional?
$ ls -l test
-rwxrwxr-x 1 fpm fpm 736 Dec 9 04:19 test
$ readelf -W --section-headers test
There are 3 section headers, starting at offset 0x220:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 0000000000201200 000200 00000f 00 AX 0 0 16
[ 2] .shstrtab STRTAB 0000000000000000 00020f 000011 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
$
.text is your code, .shstrtab is the Section Header String table. Each ElfHeader structure contains an e_shstrndx member which is an index into the .shstrtab table. If you use this index, you can find the name of that section.
du, by default, reports the space used on disk by the file--this means that the smallest value will be one disk block. If you want to know the actual size of the file, use ls -l.
The modified program to get an exit code equal to 3 (for the fun):
void _start() {
asm("movl $1,%eax;"
"movl $3,%ebx;"
"int $0x80");
}
The build with:
-s
Remove all symbol table and relocation information from the executable.
-nostdlib
Do not use the standard system startup files or libraries when linking.
$ gcc -s -nostdlib pg.c -o pg
$ ./pg
$ echo $?
3
$ ldd ./pg
statically linked
The size of the resulting executable file is 13 KB:
$ ls -l ./pg
-rwxrwxr-x 1 xxx xxx 13296 dec. 9 11:42 ./pg
The code disassembly shows that the text section is actually 23 bytes long and there is no data:
$ objdump -S ./pg
./pg: file format elf64-x86-64
Disassembly of section .text:
0000000000001000 <.text>:
1000: f3 0f 1e fa endbr64
1004: 55 push %rbp
1005: 48 89 e5 mov %rsp,%rbp
1008: b8 01 00 00 00 mov $0x1,%eax
100d: bb 03 00 00 00 mov $0x3,%ebx
1012: cd 80 int $0x80
1014: 90 nop
1015: 5d pop %rbp
1016: c3 retq
But the size utility shows that the data section is 224 (size of the .dynamic section) and the size reported for the text is 248 bytes. This is the total size of the other sections minus the .comment:
$ size pg
text data bss dec hex filename
248 224 0 472 1d8 pg
$ size pg --format=SysV
pg :
section size addr
.interp 28 792
.note.gnu.property 32 824
.note.gnu.build-id 36 856
.gnu.hash 28 896
.dynsym 24 928
.dynstr 1 952
.text 23 4096
.eh_frame_hdr 20 8192
.eh_frame 56 8216
.dynamic 224 16160
.comment 42 0
Total 514
If we rebuild the program adding:
-static
On systems that support dynamic linking, this overrides -pie and prevents linking with the shared libraries. On other systems, this option has no effect.
The size of the file decreases (8816 bytes instead of 13 KB):
$ gcc -s -static -nostdlib pg.c -o pg
$ ls -l ./pg
-rwxrwxr-x 1 xxxx xxxx 8816 dec. 9 13:03 ./pg
The "-static" option made disappear several dynamic linking related sections:
$ size pg
text data bss dec hex filename
147 0 0 147 93 pg
$ size pg --format=SysV
pg :
section size addr
.note.gnu.property 32 4194760
.note.gnu.build-id 36 4194792
.text 23 4198400
.eh_frame 56 4202496
.comment 42 0
Total 189
I am on a quest to understand low-level computing. I have noticed my compiled binaries are a lot bigger then I think they should be. So I tried to build the smallest possible c program without any stdlib code as follows:
void _start()
{
while(1) {};
}
gcc -nostdlib -o minimal minimal.c
When I disasseble the binary, it shows me exactly what I expect, namely this exact code in three lines of assembly.
$ objdump -d minimal
minimal: file format elf64-x86-64
Disassembly of section .text:
0000000000001000 <_start>:
1000: 55 push %rbp
1001: 48 89 e5 mov %rsp,%rbp
1004: eb fe jmp 1004 <_start+0x4>
But my actual executable is still 13856 Bytes in size. What is it, that makes this so large? What else is in that file? Does the OS need more than these 6 Bytes of machine code?
Edit #1:
The output of size is:
$ size -A minimal
minimal :
section size addr
.interp 28 680
.note.gnu.build-id 36 708
.gnu.hash 28 744
.dynsym 24 776
.dynstr 1 800
.text 6 4096
.eh_frame_hdr 20 8192
.eh_frame 52 8216
.dynamic 208 16176
.comment 18 0
Total 421
Modern compilers and linkers aren't really optimized for producing ultra-small code on full-scale platforms. Not because the job is difficult, but because there's usually no need to. It isn't necessarily that the compiler or linker adds additional code (although it might), but rather that it won't try hard to pack your data and code into the smallest possible space.
In your case, I note that you're using dynamic linking, even though nothing is actually linked. Using "-static" will shave off about 8kB. "-s" (strip) will get rid of a bit more.
I don't know if it's even possible with gcc to make a truly minimal ELF executable. In your case, that ought to be about 400 bytes, nearly all of which will be the various ELF headers, section table, etc.
I don't know if I'm allowed to link my own website (I'm sure somebody will put me right if not), but I have an article on producing a tiny ELF executable by building it from scratch in binary:
http://kevinboone.me/elfdemo.html
There are many different executable file formats. .com, .exe, .elf, .coff, a.out, etc. They ideally contain the machine code and other sections (.text (code), .data, .bss, .rodata and possibly others, names depend on toolchain) plus they contain debugging information. Notice how your disassembly showed the label _start? that is a string among others and other info to be able to connect that string to the address for debugging. The output of objdump also showed that you are using an elf file, you can easily look up the file format and can trivially write your own program to parse through the file, or try to use readelf and other tools to see what is in there (high level not raw).
On an operating system where in general (not always, but think pc) the programs are being loaded into ram and then run, so you want to have first and foremost a file format that is supported by the operating system, there is no reason for them to support more than one, but they might. It is os/system design dependent, but the os may be designed to not only load the code, but also load/initialize the data (.data, .bss). When booting say an mcu you need to embed the data into the binary blob and the application itself copies the data to ram from the flash, but in an os that isn't necessarily required, but in order to do it you need a file format that can distinguish the sections, target locations, and sizes. Which means extra bytes in the file to define this and a file format.
A binary includes the bootstrap code before it can enter the C generated code, depending on the system, depending on the C library (multiple/many C libraries can be used on a computer and bootstrap is specific to the C library in general not the target, nor operating system, not a compiler thing), so some percentage of the file is the bootstrap code, too when your main program is very tiny the a lot of the file size is overhead.
You can for example use strip to make the file smaller by getting rid of some symbols and other non-essential items like that the file size should get smaller but the objdump disassembly will then not have labels and for the case of x86, a variable length instruction set which is difficult at best to disassemble gets much harder, so the output with or without labels may not reflect the actual instructions, but without the labels the gnu disassembler doesn't reset itself at the labels and can make the output worse.
If you use clang 10.0 and lld 10.0 and strip out unnecessary sections you can get the size of a 64-bit statically linked executable to under 800 bytes.
$ cat minimal.c
void _start(void)
{
int i = 0;
while (i < 11) {
i++;
}
asm( "int $0x80" :: "a"(1), "b"(i) );
}
$ clang -static -nostdlib -flto -fuse-ld=lld -o minimal minimal.c
$ ls -l minimal
-rwxrwxr-x 1 fpm fpm 1376 Sep 4 17:38 minimal
$ readelf --string-dump .comment minimal
String dump of section '.comment':
[ 0] Linker: LLD 10.0.0
[ 13] clang version 10.0.0 (Fedora 10.0.0-2.fc32)
$ readelf -W --section-headers minimal
There are 9 section headers, starting at offset 0x320:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .note.gnu.build-id NOTE 0000000000200190 000190 000018 00 A 0 0 4
[ 2] .eh_frame_hdr PROGBITS 00000000002001a8 0001a8 000014 00 A 0 0 4
[ 3] .eh_frame PROGBITS 00000000002001c0 0001c0 00003c 00 A 0 0 8
[ 4] .text PROGBITS 0000000000201200 000200 00002a 00 AX 0 0 16
[ 5] .comment PROGBITS 0000000000000000 00022a 000040 01 MS 0 0 1
[ 6] .symtab SYMTAB 0000000000000000 000270 000048 18 8 2 8
[ 7] .shstrtab STRTAB 0000000000000000 0002b8 000055 00 0 0 1
[ 8] .strtab STRTAB 0000000000000000 00030d 000012 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
$ strip -R .eh_frame_hdr -R .eh_frame minimal
$ strip -R .comment -R .note.gnu.build-id minimal
strip: minimal: warning: empty loadable segment detected at vaddr=0x200000, is this intentional?
$ readelf -W --section-headers minimal
There are 3 section headers, starting at offset 0x240:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 0000000000201200 000200 00002a 00 AX 0 0 16
[ 2] .shstrtab STRTAB 0000000000000000 00022a 000011 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
$ ll minimal
-rwxrwxr-x 1 fpm fpm 768 Sep 4 17:45 minimal
I'm working on a project which involves parsing an arm elf file and extracting the sections from it.
There are obviously plenty of sections in an elf file which do not get loaded into flash, but I'm wondering how exactly objcopy knows which sections to include in a binary to be flashed directly into flash?
Take for example the following readelf of an arm elf file:
Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .isr_vector PROGBITS 08020000 010000 0001f8 00 WA 0 0 4
[ 2] .firmware_header_ PROGBITS 080201f8 0101f8 000004 00 WA 0 0 4
[ 3] .text PROGBITS 08020200 010200 01e11c 00 AX 0 0 64
[ 4] .ARM.extab PROGBITS 0803e31c 033a68 000000 00 W 0 0 1
[ 5] .exidx ARM_EXIDX 0803e31c 02e31c 000008 00 AL 3 0 4
[ 6] .ARM.attributes ARM_ATTRIBUTES 0803e324 033a68 000030 00 0 0 1
[ 7] .init_array INIT_ARRAY 0803e324 02e324 000008 04 WA 0 0 4
[ 8] .fini_array FINI_ARRAY 0803e32c 02e32c 000004 04 WA 0 0 4
[ 9] .firmware_header PROGBITS 0803e330 02e330 000008 00 WA 0 0 4
[10] .data PROGBITS 20000000 030000 0009c8 00 WA 0 0 8
[11] .RxDecripSection PROGBITS 200009c8 0309c8 000080 00 WA 0 0 4
[12] .RxarraySection PROGBITS 20000a48 030a48 0017d0 00 WA 0 0 4
[13] .TxDescripSection PROGBITS 20002218 032218 000080 00 WA 0 0 4
[14] .TxarraySection PROGBITS 20002298 032298 0017d0 00 WA 0 0 4
[15] .bss NOBITS 20003a68 033a68 045bc0 00 WA 0 0 8
[16] .heap PROGBITS 20049628 033a98 000000 00 W 0 0 1
[17] .reserved_for_sta PROGBITS 20049628 033a98 000000 00 W 0 0 1
[18] .battery_backed_s NOBITS 40024000 034000 00000c 00 WA 0 0 4
[19] .comment PROGBITS 00000000 033a98 000075 01 MS 0 0 1
[20] .debug_frame PROGBITS 00000000 033b10 001404 00 0 0 4
[21] .stab PROGBITS 00000000 034f14 000084 0c 22 0 4
[22] .stabstr STRTAB 00000000 034f98 000117 00 0 0 1
[23] .symtab SYMTAB 00000000 0350b0 009010 10 24 1646 4
[24] .strtab STRTAB 00000000 03e0c0 003dc8 00 0 0 1
[25] .shstrtab STRTAB 00000000 041e88 000132 00 0 0 1
Now, obviously quite a few of these sections (like .TxarraySection) are not loaded into flash. However, that section type is PROGBITS and it has a writable and allocated flag. This is no different than isr_vector, which is loaded but has the same type and flags. What am I missing here? Should I be looking in the program header?
How sure are you that TxarraySection isn't resident in flash? My guess is that it is, but your system initialization ignores it on startup.
Here's what the objcopy man page has to say about what it's doing:
objcopy can be used to generate a raw binary file by using an output target of ‘binary’ (e.g., use -O binary). When objcopy generates a raw binary file, it will essentially produce a memory dump of the contents of the input object file. All symbols and relocation information will be discarded. The memory dump will start at the load address of the lowest section copied into the output file.
I have my own Cortex-M7 elf file that I used to test this. I can convert to binary using objcopy probably the same way you are:
objcopy -O binary in.axf out.bin
In my case the size of out.bin is 376480. I can then inspect the input ELF file using readelf probably the same way you are:
readelf -S in.axf
I get a list of sections similar to the ones you've shown. If I then go through and add up the sizes of everything that is of type PROGBITS which also includes an Alloc flag, it adds up to exactly 376480. As described, objcopy has simply gone through the list of allocated sections in order and copied them to the output.
In general objcopy is pretty "dumb" in the sense that it doesn't try to make any sophisticated decisions about what to include or exclude. There are flags you can use to explicitly include or exclude certain regions, but in general if you're working on a bare-metal platform, it's up to you to decide exactly how your flash image should be laid out.
Without knowing all of the details of your particular system it's very hard to know exactly how to answer your question, but the most important detail to highlight is that objcopy doesn't really know anything, it will blindly copy just about anything, which means in most cases if you're just using -O binary with no other guidance, you're probably ending up with junk in your flash image that you don't actually need or want.
The sections copied to binary are those with a nonzero size, containing the SHF_ALLOC flag, with a type not equal to NOBITS.
The address tells you that it's in RAM, so unless your flash programmer can also handle SRAM, you can eliminate them that way. Ditto that's how you can handle debug symbols too, their addresses are zero.
I have a tool emitting an ELF, which as far as I can tell is compliant to the spec. Readelf output looks fine, but objdump refuses to disassemble anything.
I have simplified the input to a single global var, and "int main(void) { return 0;}" to aid debugging - the tiny section sizes are correct.
In particular, objdump seems unable to find the sections table:
$ arm-none-linux-gnueabi-readelf -S davidm.elf
There are 4 section headers, starting at offset 0x74:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text NULL ff000000 000034 00001c 00 AX 0 0 4
[ 2] .data NULL ff00001c 000050 000004 00 WA 0 0 4
[ 3] .shstrtab NULL 00000000 000114 000017 00 0 0 0
$ arm-none-linux-gnueabi-objdump -h davidm.elf
davidm.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
I also have another ELF, built from the exact same objects, only produced with regular toolchain use:
$ objdump -h kernel.elf
kernel.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000001c ff000000 ff000000 00008000 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000004 ff00001c ff00001c 0000801c 2**2
CONTENTS, ALLOC, LOAD, DATA
Even after I stripped .comment and .ARM.attributes sections (incase objdump requires them) from the 'known good' kernel.elf, it still happily lists the sections there, but not in my tool's davidm.elf.
I have confirmed the contents of the sections are identical between the two with readelf -x.
The only thing I can image is that the ELF file layout is different and breaks some expectations of BFD, which could explain why readelf (and my tool) processes it just fine but objdump has troubles.
Full readelf:
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
Entry point address: 0xff000000
Start of program headers: 84 (bytes into file)
Start of section headers: 116 (bytes into file)
Flags: 0x5000002, has entry point, Version5 EABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 1
Size of section headers: 40 (bytes)
Number of section headers: 4
Section header string table index: 3
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text NULL ff000000 000034 00001c 00 AX 0 0 4
[ 2] .data NULL ff00001c 000050 000004 00 WA 0 0 4
[ 3] .shstrtab NULL 00000000 000114 000017 00 0 0 0
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000034 0xff000000 0xff000000 0x00020 0x00020 RWE 0x8000
Section to Segment mapping:
Segment Sections...
00 .text .data
There is no dynamic section in this file.
There are no relocations in this file.
There are no unwind sections in this file.
No version information found in this file.
Could the aggressive packing of the on-disk layout be causing troubles? Am I in violation of some bytestream alignment restrictions BFD expects, documented or otherwise?
Lastly - this file is not intended to be mmap'd into an address space, a loader will memcpy segment data into the desired location, so there is no requirement to play mmap-friendly file-alignment tricks. Keeping the ELF small is more important.
Cheers,
DavidM
EDIT: I was asked to upload the file, and/or provide 'objdump -x'. So I've done both:
davidm.elf
$ objdump -x davidm.elf
davidm.elf: file format elf32-littlearm
davidm.elf
architecture: arm, flags 0x00000002:
EXEC_P
start address 0xff000000
Program Header:
LOAD off 0x00000034 vaddr 0xff000000 paddr 0xff000000 align 2**15
filesz 0x00000020 memsz 0x00000020 flags rwx
private flags = 5000002: [Version5 EABI] [has entry point]
Sections:
Idx Name Size VMA LMA File off Algn
SYMBOL TABLE:
no symbols
OK - finally figured it out.
After building and annotating/debugging libbfd (function elf_object_p()) in the context of a little test app, I found why it was not matching on any of BFD supported targets.
I had bad sh_type flags for the section headers: NULL. Emitting STRTAB or PROGBITS (and eventually NOBITS when I get that far) as appropriate and objdump happily walks my image.
Not really surprising, in retrospect - I'm more annoyed I didn't catch this in comparing readelf outputs than anything else :(
Thanks for the help all :)
I read in a book (can't recollect the name) that in a 32 bit system text section always starts at 0x0848000. But when I do readelf -S example_executable it does not reflect the same information. Why is that? Do other sections (bss,data,rodata etc) also start at fixed addresses? How can I find the alignment boundary for these sections?
There is a good explanation here of how Linux virtual memory works when allocating storage for a particular program.
ua alberta - linux memory allocation
The designers of the compiler/linker tool chain need to allocate an arbitary address for particular blocks of memory. In order to make it easier for other components of the tool chain like debuggers and profilers they always allocatate the same block to the same addresses. The actual addresses chosen are completely arbitrary.
When the program is loaded the virtual address will be mapped to some random piece of free memory (this is mostly done in hardware). This mapping is done on a per process basis to several programs can address virtual address x'0848000' but be pointed at different "real" memory addresses.
It all depends on the implementation on the particular machine.For a linux machine the behaviour will be different than that from windows machine.
Note however that the virtual memory addresses need to start at some fixed address in order to make life easier for debuggers.However the real addresses will be different depending on the pages available in RAM.
If you look the output of readelf -S more carefully you will notice you will notice subtracting the offset from the address indeed gives you 0x0848000.
As i had mentioned earlier this magic number 0x0848000 will depend on the type of executable format.
here is the output i get on my ubuntu 32 bit machine:
readelf -S ~/a.out
There are 29 section headers, starting at offset 0x1130:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048134 000134 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048148 000148 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048168 000168 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0804818c 00018c 000020 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481ac 0001ac 000050 10 A 6 1 4
[ 6] .dynstr STRTAB 080481fc 0001fc 00004c 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048248 000248 00000a 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 08048254 000254 000020 00 A 6 1 4
[ 9] .rel.dyn REL 08048274 000274 000008 08 A 5 0 4
[10] .rel.plt REL 0804827c 00027c 000018 08 A 5 12 4
[11] .init PROGBITS 08048294 000294 000030 00 AX 0 0 4
[12] .plt PROGBITS 080482c4 0002c4 000040 04 AX 0 0 4
[13] .text PROGBITS 08048310 000310 00018c 00 AX 0 0 16
[14] .fini PROGBITS 0804849c 00049c 00001c 00 AX 0 0 4
There is no consistent address between operating systems and architectures for the text section or any other section. Additionally position independent code and address space layout randomizations make these values even inconsistent on the same machine for some systems.