Section 13.5. Binary Utilities

13.5. Binary Utilities

Binary utilities, or binutils, are a critical component of any toolchain. Indeed, to build a compiler, you must first have successfully built binutils. In this section, we briefly introduce the more useful tools that the embedded developer needs to know about. As with most of the other tools in this chapter, these are cross-utilities and must be built to execute on your development host while operating on binary files targeted to your chosen architecture. Alternatively, you could compile or obtain versions of these to run on your target, but we assume a cross-development environment for these examples.

13.5.1. readelf

The readelf utility examines the composition of your target ELF binary file. This is particularly useful for building images targeted for ROM or Flash memory where explicit control of the image layout is required. It is also a great tool for learning how your toolchain builds images and for understanding the ELF file format.

For example, to display the symbol table in an ELF image, use this command:

$ readelf -s <elf-image>

To discover and display all the sections in your ELF image, use this command:

$ readelf -e <elf-image>

Use the -S flag to list the section headers in your ELF image. You might be surprised to learn that even a simple seven-line "hello world" program contains 38 separate sections. Some of them will be familiar to you, such as the .text and .data sections. Listing 13-15 contains a partial listing of sections from our "hello world" example. For simplicity, we have listed only those sections that are likely to be familiar or relevant to the embedded developer.

Listing 13-15. readelf Section Headers

$ ppc_82xx-readelf -S  hello-ex There are 38 section headers, starting at offset 0x32f4: Section Headers: [ Nr] Name        Type        Addr     Off    Size   ES Flg Lk Inf Al ...  [11] .text       PROGBITS    100002f0 0002f0 000568 00  AX  0   0  4 ...  [13] .rodata     PROGBITS    10000878 000878 000068 00   A  0   0  4 ...  [15] .data       PROGBITS    100108e0 0008e0 00000c 00  WA  0   0  4 ...  [22] .sdata      PROGBITS    100109e0 0009e0 00001c 00  WA  0   0  4  [23] .sbss       NOBITS      100109fc 0009fc 000000 00  WA  0   0  1 ...  [25] .bss        NOBITS      10010a74 0009fc 00001c 00  WA  0   0  4 ...

The .text section contains the executable program code. The .rodata section contains constant data in your program. The .data section generally contains initialized global data used by the C library prologue code and can contain large initialized data items from your application. The .sdata section is used for smaller initialized global data items and exists only on some architectures. Some processor architectures can make use of optimized data access when the attributes of the memory area are known. The .sdata and .sbss sections enable these optimizations. The .bss and .sbss sections contain uninitialized data in your program. These sections occupy no space in the program imagetheir memory space is allocated and initialized to zero on program startup by C library prologue code.

We can dump any of these sections and display the contents. Given this line in your C program declared outside of any function, we can examine how it is placed in the .rodata section:

char *hello_rodata = "This is a read-only data string\n";

Issue the readelf command specifying the section number we want to dump from Listing 13-15:

$ ppc_82xx-readelf -x 13 hello-ex Hex dump of section '.rodata':   0x10000878 100189e0 10000488 1000050c 1000058c ................   0x10000888 00020001 54686973 20697320 61207265 ....This is a read-   0x10000898 61642d6f 6e6c7920 64617461 20737472 only data string   0x100008a8 696e670a 00000000 54686973 20697320 .....This is   0x100008b8 73746174 69632064 6174610a 00000000 static data.....   0x100008c8 48656c6c 6f20456d 62656464 65640a00 Hello Embedded..   0x100008d8 25730a00 25780a00                   %s..%x..

We see that the initialized global variable that we declared is represented in the .rodata section, together with all the constant strings defined in the program.

13.5.2. Examining Debug Info Using readelf

One of the more useful features of readelf is to display the debug information contained in an ELF file. When the -g compiler flag is issued during a compilation, the compiler generates debug information in a series of sections within the resulting ELF file. We can use readelf to display these ELF section headers within the ELF file:

$ ppc-linux-readelf -S ex_sync | grep debug   [28] .debug_aranges    PROGBITS   00000000 000c38 0000b8 00   0   0  8   [29] .debug_pubnames   PROGBITS   00000000 000cf0 00007a 00   0   0  1   [30] .debug_info       PROGBITS   00000000 000d6a 00079b 00   0   0  1   [31] .debug_abbrev     PROGBITS   00000000 001505 000207 00   0   0  1   [32] .debug_line       PROGBITS   00000000 00170c 000354 00   0   0  1   [33] .debug_frame      PROGBITS   00000000 001a60 000080 00   0   0  4   [34] .debug_str        PROGBITS   00000000 001ae0 00014d 00   0   0  1

Using readelf with the --debug-dump option, we can display the contents of any one of these .debug_* sections. You will see how this information can be useful in Chapter 14, "Kernel Debugging Techniques," when we discuss the challenge of debugging optimized kernel code.

Debug information can be very large. Displaying all the debug information in the Linux kernel ELF file vmlinux produces more than six million lines of output. However daunting it might appear, having at least a familiarity with debug information will make you a better embedded engineer.

Listing 13-16 is a partial listing of the contents of the .debug_info section from a small example application. For space considerations, we have shown only a few records.

Listing 13-16. Partial Debug Info Dump

$ ppc-linux-readelf -debug-dump=info ex_sync 1 The section .debug_info contains: 2 3   Compilation Unit @ 0: 4    Length:        109 5    Version:       2 6    Abbrev Offset: 0 7    Pointer Size:  4 8  <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit) 9      DW_AT_stmt_list   : 0 10      DW_AT_low_pc      : 0x10000368 11      DW_AT_high_pc     : 0x1000038c 12      DW_AT_name        : ../sysdeps/powerpc/powerpc32/elf/start.S 13      DW_AT_comp_dir    : /var/tmp/BUILD/glibc-2.3.3/csu 14      DW_AT_producer    : GNU AS 2.15.94 15      DW_AT_language    : 32769  (MIPS assembler) ... 394  <1><5a1>: Abbrev Number: 14 (DW_TAG_subprogram) 395      DW_AT_sibling     : <5fa> 396      DW_AT_external    : 1 397      DW_AT_name        : main 398      DW_AT_decl_file   : 1 399      DW_AT_decl_line   : 9 400      DW_AT_prototyped  : 1 401      DW_AT_type        : <248> 402      DW_AT_low_pc      : 0x100004b8 403      DW_AT_high_pc     : 0x10000570 404      DW_AT_frame_base  : 1 byte block: 6f       (DW_OP_reg31) ... 423  <2><5e9>: Abbrev Number: 16 (DW_TAG_variable) 424      DW_AT_name        : mybuf 425      DW_AT_decl_file   : 1 426      DW_AT_decl_line   : 11 427      DW_AT_type        : <600> 428      DW_AT_location    : 2 byte block: 91 20    (DW_OP_fbreg: 32) ...

The first record identified by the Dwarf2[8] tag DW_TAG_compile_unit identifies the first compilation unit of this PowerPC executable. It is a file called start.S, which provides startup prologue for a C program. The next record identified by DW_TAG_subprogram identifies the start of the user program, the familiar function main(). This Dwarf2 debug record contains a reference to the file and line number where main() is found. The final record in Listing 13-16 identifies a local variable in the main() routine called mybuf. Again, the line number and file are provided by this record. You can deduce from this information that main() is at line 9, and mybuf is at line 11 of the source file. Other debug records in the ELF file correlate the filename via the Dwarf2 DW_AT_decl_file attribute.

[8] A reference for the Dwarf2 Debug Information Specification is provided at the end of this chapter.

You can discover all the details of the Dwarf2 debug information format via the reference given in Section 13.7.1 at the end of this chapter.

13.5.3. objdump

The objdump utility has considerable overlap with the readelf tool. However, one of the more useful features of objdump is its capability to display disassembled object code. Listing 13-17 provides an example of disassembly of the .text section of the simple "hello world" PowerPC version. We include only the main() routine, to save space. The entire dump, including C library prologue and epilogue, would consume many pages.

Listing 13-17. Disassembly Using objdump

$ ppc_82xx-objdump -S -m powerpc:common -j .text hello ... 10000488 <main>: 10000488:       94 21 ff e0     stwu    r1,-32(r1) 1000048c:       7c 08 02 a6     mflr    r0 10000490:       93 e1 00 1c     stw     r31,28(r1) 10000494:       90 01 00 24     stw     r0,36(r1) 10000498:       7c 3f 0b 78     mr      r31,r1 1000049c:       90 7f 00 08     stw     r3,8(r31) 100004a0:       90 9f 00 0c     stw     r4,12(r31) 100004a4:       3d 20 10 00     lis     r9,4096 100004a8:       38 69 08 54     addi    r3,r9,2132 100004ac:       4c c6 31 82     crclr   4*cr1+eq 100004b0:       48 01 05 11     bl      100109c0 <__bss_start+0x60> 100004b4:       38 00 00 00     li      r0,0 100004b8:       7c 03 03 78     mr      r3,r0 100004bc:       81 61 00 00     lwz     r11,0(r1) 100004c0:       80 0b 00 04     lwz     r0,4(r11) 100004c4:       7c 08 03 a6     mtlr    r0 100004c8:       83 eb ff fc     lwz     r31,-4(r11) 100004cc:       7d 61 5b 78     mr      r1,r11 100004d0:       4e 80 00 20     blr ...

Much of the code from the simple main() routine is stack frame creation and destruction. The actual call to printf() is represented by the branch link (bl) instruction near the center of the listing at address 0x100004b0. This is a PowerPC function call. Because this program was compiled as a dynamically linked object, we will not have an address for the printf() function until runtime, when it is linked with the shared library printf() routine. Had we compiled this as a statically linked object, we would see the symbol and corresponding address for the call to printf().

13.5.4. objcopy

objcopy formats and, optionally, converts the format of a binary object file. This utility is quite useful for generating code for ROM or Flash resident images. The U-Boot bootloader introduced in Chapter 7 makes use of objcopy to produce binary and s-record[9] output formats from the final ELF file. This example usage illustrates the capabilities of objcopy and its use to build Flash images.

[9] S-record files are an ASCII representation of a binary file, used by many device programmers and software binary utilities.

$ ppc_82xx-objcopy --gap-fill=0xff -O binary u-boot u-boot.bin

This objcopy invocation shows how an image might be prepared for Flash memory. The input fileu-boot, in this exampleis the complete ELF U-Boot image, including symbols and relocation information. The objcopy utility takes only the relevant sections containing program code and data and places the image in the output file, specified here as u-boot.bin.

Flash memory contains all ones in its erased state. Therefore, filling gaps in a binary image with all ones improves programming efficiency and prolongs the life of the Flash memory, which today has limited write cycles. This is done with the --gap-fill parameter to objcopy.

This is but one simple example usage of objcopy. This utility can be used to generate s-records and convert from one format to another. See the man page for complete details.

Embedded Linux Primer(c) A Practical Real-World Approach
Embedded Linux Primer: A Practical Real-World Approach
ISBN: 0131679848
EAN: 2147483647
Year: 2007
Pages: 167

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: