This is the 24th day of my participation in the August Update Challenge. Background: Reread “Self-cultivation of a Programmer — Linking, loading, and Libraries”, chapter 3 of which focuses on object files. It also describes how to use some binary files as a section of the object file (see this book for details). Files like pictures and music are also binary files. This article starts with some research on this, along with a review of the Binutils tool and GDB usage.
In addition, I also applied this knowledge to my ARM development board, that is, to add the display of the image in the original, of course, the image has been placed in the executable, no additional image files are required. As it is inevitable to paste the results of program compilation and program running, make some conventions here: the file name of the picture is logo.jpg, the name of the directory file to convert it is LOGO_386. o, and the test file is logo_test.c.
1, conversion,
Convert an image file to an object file using the objCopy tool as shown in the following example:
$ objcopy -I binary -O elf32-i386 -B i386 logo.jpg logo_386.o
Copy the code
This is for 386 platform, after all, this platform is more convenient (this is nonsense, please ignore). The -i option specifies the format of the input file, in this case binary; -o specifies the format of the output file, which should be the ELF file type. -b is the architecture of the specified target file. Here is the I386 platform. For I386, Linux is a general term. Objdump to see what logo_386.o has:
$ objdump -ht logo_386.o
logo_386.o: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .data 000011e8 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
00000000 l d .data 00000000 .data
00000000 g .data 00000000 _binary_logo_jpg_start
000011e8 g .data 00000000 _binary_logo_jpg_end
000011e8 g *ABS* 00000000 _binary_logo_jpg_size
Copy the code
The -ht option represents the header of the input section and the symbol of the object file. As you can see, the last three lines output three symbols, which indicate the start address, end address and size of the image file in memory. We can declare and use these symbols directly in the program (from Programmer self-Cultivation). However, after the author’s test, found that these about some incorrect, this is later, not to mention. The three symbols are changeable and are named _binary_*_start/end/size, where * is the file and suffix of the image. If the image is foo.bmp, then the three symbols are _binary_foo_bmp_start/end/size. If you look at LoGO_386. o in hexadecimal you will see that it has a bit more information than logo.jpg which is referred to as header and tail information (header and tail are described here and not elegant). The header is an ELF header structure, but the tail is unknown to the author.
2. Test procedures
Here is the test procedure:
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * resources embedded in the picture the program file objcopy -i elf32 binary - O - i386 - B i386 logo.jpg logo_386.o gcc -g logo_test.c logo_386.o $ ./a.out hello, elf head: 52 0x8049674 0x804a85c 4584 ******************************************************************/ #include <stdio.h> #include <elf.h> extern _binary_logo_jpg_start; extern _binary_logo_jpg_end; extern _binary_logo_jpg_size; int main(void) { printf("hello, elf head: %d\n", sizeof(Elf32_Ehdr)); printf("%p %p %p\n", &_binary_logo_jpg_start, &_binary_logo_jpg_end, &_binary_logo_jpg_size); return 0; }Copy the code
Note: the _binary_LOgo_jpG_start type is not given in the program, because I can’t figure out what they are. If you compile with -wall, you get the following warning:
Logo_test. c:14: warning: type defaults to 'int' in declaration of '_binary_LOgo_JPG_start' logo_test.c:15: warning: Type defaults to 'int' in declaration of '_binary_LOgo_JPG_end' logo_test.c:16: warning: Type defaults to 'int' in declaration of '_binary_logo_jpG_size'Copy the code
This means that if the code does not specify a type explicitly, the compiler defaults to int. In the code, the word “symbol” refers to many things, pointer is a symbol, array name is a symbol, function name is a symbol, variable name is a symbol, it seems that everything is a symbol (U-boot will use “symbol” incinctly, can refer to the U-boot boot part of the C code and assembly code, This part of the C code uses “symbols” in assembly). The running results of the program are as follows:
$ ./a.out
hello, elf head: 52
0x8049674 0x804a85c 4584
Copy the code
The program specifically outputs the structure size of the ELF file header information, which results in 52 bytes, and the resulting object file has the image content at 0x34 offset preceded by 0x34 bytes, which is exactly 52 in decimal. This structure is not expanded here. Because I have been reading JPG files almost every day for a while (mainly about JPEG, MJPEG, avi), I know that JPG files start with “FF D8” and end with “FF D9”, and the target file’s “FF D8” is at 0x34. Linux hexdump to view the beginning of the content (which may not be aligned due to web pages) :
$ hexdump -C logo_386.o | head -n 4 00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............ | 00000010 01 00 00 03 01 00 00 00 00 00 00 00 00 00 00 00 |... | 00000020 40 12 00 00 00 00 00 00 00 00 00 00 00 00 | @ 28 34... 4... (.| 00000030 05 00 02 00 ff d8 ff e1 00 e6 45 78 69 66 00 00 |.......... Exif.. |Copy the code
3. Debug the program
Let’s use GDB to look at the three symbols separately: _binary_logo_jpG_start
(gdb) p/x _binary_logo_jpg_start
$1 = 0xe1ffd8ff
(gdb) p/x &_binary_logo_jpg_start
$5 = 0x8049674
Copy the code
As you can see, the value of &_binary_logo_jpg_start is 0x8049674, which indicates that the image content is located at 0x8049674, which, according to my knowledge of ELF files, should be in the.text section, meaning that the image is already part of the executable. The _binary_LOgo_jpg_start value is 0xe1FFD8FF, we know that 386 is the little endian mode, look at it byte by byte, it is ffd8 FF E1, this is not the picture content what is it? Unbelievingly, let’s look at the eight bytes of that address again:
(gdb) x/8b &_binary_logo_jpg_start
0x8049674 <_binary_logo_jpg_start>: 0xff 0xd8 0xff 0xe1 0x00 0xe6 0x45 0x78
Copy the code
The value of _binary_LOgo_jpG_start is the first four bytes of the image content. Look again at _binary_logo_jpG_end:
(gdb) p _binary_logo_jpg_end
$1 = 0
(gdb) p &_binary_logo_jpg_end
$2 = (int *) 0x804a898
(gdb) x/x 0x804a898
0x804a898 <completed.5731>: 0x00000000
(gdb) x/8b 0x804a898
0x804a898 <completed.5731>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Copy the code
It has a value of 0 and the address is 0x804a898. Let’s look at the address a little ahead of it, for example 0x804a890:
(gdb) x/12b 0x804a890
0x804a890 <_binary_logo_jpg_start+4576>: 0x8a 0x00 0xa2 0x8a 0x28 0x0f 0xff 0xd9
0x804a898 <completed.5731>: 0x00 0x00 0x00 0x00
Copy the code
We see several key values: 4576, which is close to the size of the image file, and 0xFF 0xD9, which marks the end of the image, and 0xD9, located 4584 bytes away from _binary_LOgo_jpG_start. Look again at _binary_logo_jpg_size:
(gdb) p _binary_logo_jpg_size
Cannot access memory at address 0x11e8
(gdb) p &_binary_logo_jpg_size
$8 = (int *) 0x11e8
Copy the code
0x11e8 is 4584 in decimal, which is the size of the image file. From the result printed above, the three symbols appear to be variables of type int, because their addresses are printed as follows:
(gdb) p &_binary_logo_jpg_start
$3 = (int *) 0x80496b0
(gdb) p &_binary_logo_jpg_end
$4 = (int *) 0x804a898
(gdb) p &_binary_logo_jpg_size
$5 = (int *) 0x11e8
Copy the code
If a variable is of type int, it should be possible to print its value, but the following statement:
printf("%d %d %d\n", _binary_logo_jpg_start, _binary_logo_jpg_end, _binary_logo_jpg_size);
Copy the code
The last symbol, _binary_logo_jpg_size, causes a segment error, which is a way of showing that they are not all ints. Is this a personal error (i.e., the author didn’t explicitly specify their type) or some reason unknown to the author (how do I know what variable a symbol is from the object file?) Or other reasons that haven’t been studied yet. That is why I said, I am not telling the story correctly. In use, you can think of &_binary_logo_jpg_start as the starting address of the image, and &_binary_logo_jpg_size as the size of the image. This is how it is applied in this article.
Note: 1. About I386, once upon a time, one of my classmates asked me, but I couldn’t answer. There are some online forums and some people ask what is the meaning of the i386, i586, i686 obtained by typing uname -a in Linux. There should be a Linux foundation book about this. 2. Binutils are useful, but they don’t seem to work. I learned them for a long time and now forget them. Like data structures and algorithms, they seem to work, they don’t seem to work. That’s just to say: when used, it works. Isn’t there a saying, books to time square hate less. At ordinary times more accumulation of knowledge, or useful.