Map a literal address to a section - c

Given a literal address, how can I determine which section that address falls in? Here is an example. From a disassembly of a program, made with 'objdump', I obtain a literal address 0x8048520:
80483ea: c7 45 f4 20 85 04 08 movl $0x8048520,-0xc(%ebp)
...
On my platform (Linux 2.6.39, Gentoo) I can obtain a listing of sections simply by running 'less' on the ELF file, and see that this address falls within the .rodata section:
[15] .rodata PROGBITS 08048518 000518 000016 00 A 0 0 4
...
However, I'm looking for a convenient way to do the same thing that does not require visually scanning through and comparing addresses. Suggestions?

Using your example address of 0x8048520:
objdump -s --start-address=0x8048520 --stop-address=0x8048521 elf_file | grep section | awk '{ print $4 }' | cut -d':' -f 1
In your example, the output of this command would be:
.rodata

Related

Why does a simple C program consumes a lot of disk space? [duplicate]

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

Warning: Loadable section "my_section" outside of ELF segments

I have built a axf (elf) file using Arm Compiler v6.9 for Cortex-R4. However when I load this to the target using Arm MCU Eclipse J-link GDB plugins it fails to load the initialisation data for my segments. If I load the axf using Segger Ozone and J-Link it loads the init data correctly.
If I run the arm-none-eabi-gdb.exe on the axf file I get "Warning: Loadable section "my_section" outside of ELF segments" for all my initialised segments.
Looking at the image the initialisation data should be loaded after the image to the addresses specified by the table in Region$$Table$$Base.
We don't have this problem if we link with gcc as the initialised data is done differently.
Any ideas?
I've faced the same issue today and observed the same problem that you described:
"Looking at the image the initialisation data should be loaded after the image to the addresses specified by the table in Region$$Table$$Base."
It seems that although very similar, the ELF file generated by armlink is a bit different than the ELF generated by GCC.
Anyway, I've found a workaround for that.
Checking my main.elf, I noticed that armlinker stored the initialization data into the ER_RW section:
arm-none-eabi-readelf.exe" -S main.elf
There are 16 section headers, starting at offset 0x122b0:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] ER_RO PROGBITS 20000000 000034 001358 00 AX 0 0 4
[ 2] ER_RW PROGBITS 20002000 00138c 0000cc 00 WA 0 0 4
[ 3] ER_ZI NOBITS 200020cc 001458 0004e8 00 WA 0 0 4
[ 4] .debug_abbrev PROGBITS 00000000 001458 0005c4 00 0 0 1
[ 5] .debug_frame PROGBITS 00000000 001a1c 000dc4 00 0 0 1
...
I noticed that the issue happens because GDB loaded ER_RW at addr=0x20002000 but, in fact, I needed it to be loaded just after the of ER_RO section (i.e. at addr=0x20001358)
The workaround for that is:
1- Use fromelf to dump all sections into a binary file main.bin. Fromelf will append ER_RW just after ER_RO, as it is supposed to be:
fromelf.exe --bin -o main.bin main.elf
2- Use objcopy to replace the contents of the ER_RO section with the data from main.bin.
Please notice that we can remove the ER_RW section now since it was already merged with ER_RO into main.bin:
arm-none-eabi-objcopy.exe main.elf --update-section ER_RO=main.bin --remove-section=ER_RW main.gdb.elf
The new main.gdb.elf file can now be loaded by arm-none-eabi-gdb.exe
This is how it looks:
arm-none-eabi-readelf.exe" -S main.gdb2.elf
There are 15 section headers, starting at offset 0x11c0c:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] ER_RO PROGBITS 20000000 000054 001424 00 AX 0 0 4
[ 2] ER_ZI NOBITS 200020cc 000000 0004e8 00 WA 0 0 4
[ 3] .debug_abbrev PROGBITS 00000000 001478 0005c4 00 0 0 1
...
Happy debugging with GDB!! ;-)

Reuse symbols in disassembling/reassembling a C++ program

it's me again. I am working on a tool can that disassemble/reassemble stripped binaries and now I am sucked in a (external) symbol reuse issue.
The test is on 32-bit Linux x86 platform.
Suppose I am working on a C++ program, in the GCC compiler produced assembly code, there exists some instructions like this:
call _ZNSt8ios_baseC2Ev
movl _ZTTSt14basic_ifstreamIcSt11char_traitsIcEE+4, %ebx
movb $0, 312(%esp)
movl _ZTTSt14basic_ifstreamIcSt11char_traitsIcEE+8, %ecx
....
Please pay special attention to symbol _ZTTSt14basic_ifstreamIcSt11char_traitsIcEE.
After the compilation, suppose I get an unstripped binary, and i checked this symbol like this:
readelf -s a.out | grep "_ZTTSt14basic"
69: 080a7390 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ifstreamIcS#GLIBCXX_3.4 (3)
72: 080a7220 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ofstreamIcS#GLIBCXX_3.4 (3)
705: 080a7220 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ofstreamIcS
1033: 080a7390 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ifstreamIcS
See, this is my first question, why the name of symbol _ZTTSt14basic_ifstreamIcSt11char_traitsIcEE modified to _ZTTSt14basic_ifstreamIcS and _ZTTSt14basic_ifstreamIcS#GLIBCXX_3.4 (3) ?
What is _ZTTSt14basic_ifstreamIcS#GLIBCXX_3.4 (3) though?
Then I stripped the binary like this:
strip a.out
readelf -s a.out | grep "_ZTTSt14basic"
69: 080a7390 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ifstreamIcS#GLIBCXX_3.4 (3)
72: 080a7220 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ofstreamIcS#GLIBCXX_3.4 (3)
Then after I disassemble the binary, and the corresponding disassembled assembly instructions are :
8063ee7: e8 84 54 fe ff call 8049370 <_ZNSt8ios_baseC2Ev#plt>
8063eec: 8b 1d 94 73 0a 08 mov 0x80a7394,%ebx
8063ef2: c6 84 24 38 01 00 00 movb $0x0,0x138(%esp)
8063ef9: 00
8063efa: 8b 0d 98 73 0a 08 mov 0x80a7398,%ecx
At this point we can figure out that 0x80a7394 equals to _ZTTSt14basic_ifstreamIcSt11char_traitsIcEE+4.
In order to reuse these instructions, I modified the code:
call _ZNSt8ios_baseC2Ev
mov _ZTTSt14basic_ifstreamIcS+4,%ebx
movb $0x0,0x138(%esp)
mov _ZTTSt14basic_ifstreamIcS+8,%ecx
And did some update like these (please see this question for reference):
echo ""_ZTTSt14basic_ifstreamIcS#GLIBCXX_3.4 (3)" = 0x080a7390;" > symbolfile
g++ -Wl,--just-symbols=symbolfile final.s
readelf -s a.out | grep "_ZTTSt14basic"
3001: 080a7390 0 NOTYPE LOCAL DEFAULT 27 _ZTTSt14basic_ifstreamIcS
8412: 080a7390 0 NOTYPE GLOBAL DEFAULT ABS _ZTTSt14basic_ifstreamIcS
I debugged the newly produced binary, and to my surprise, in the newly produced binary, symbol _ZTTSt14basic_ifstreamIcS does not get any value after the function call of _ZNSt8ios_baseC2Ev, while in the original binary, after the function call, _ZTTSt14basic_ifstreamIcS do get some memory address referring to library section. Which means:
call _ZNSt8ios_baseC2Ev
mov _ZTTSt14basic_ifstreamIcS+4,%ebx <--- %ebx gets zero!
movb $0x0,0x138(%esp)
mov _ZTTSt14basic_ifstreamIcS+8,%ecx <--- %ecx gets zero!
I must state that in these lines of the original binary, registers %ebx and %ecx both gets some addresses referring to the libc section.
This is my second question, why does symbol _ZTTSt14basic_ifstreamIcS didn't get any value after function call _ZNSt8ios_baseC2Ev? I also tried with symbol name _ZTTSt14basic_ifstreamIcSt11char_traitsIcEE. But that does not work also.
Am I clear enough? Could anyone save my ass? thank you!

objdump won't show my ELF sections

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 :)

What address does the text section start?

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.

Resources