binutils toolset

If you use gcc as the compiler, then binutils is an essential toolset. Some of the tools in the toolset are used by gcc in the background to create program files (object files, library files or executable programs) for us, and others are helpful for development and debugging.
In many embedded development environments, depending on the target platform, the name of the compiler is often not gcc, but a name like arm-rtems-gcc. For compilers of this naming form, the binutils working set is usually also named arm-rtems-xxx.
In the binutils tool set, the following tools are what we need to master when doing embedded software development.

1. addr2line, the instruction address translator is
used to obtain the function corresponding to the program instruction address, as well as the source file name and line number where the function is located. Suppose now there is a function:
--main.c--
#include <stdio.h>

void foo()
{
	printf("The address of foo() is %p.\n", foo); //print the address of the foo function
}

intmain()
{
	foo();
	return 0;
}
When using addr2line, the executable program must contain debugging information (for debugging information, you can refer to objdump), which requires adding the -g option when compiling.
$ gcc -g main.c -o test
$ ./test
$$ The address of foo() is 0x804841d.
Now you can directly view the content of this address (where the function, file name and line number where the function is located) through the addr2line tool:
$ addr2line 0x404841d -f -e test
$$ foo
$$ /home/binutils/main.c:5
[-f means to view the function name, -e means to set the input file (default a.out)]
The address here is printed by the program Obtained, in real work, it is often obtained in some way when the program crashes. In this case, you can use the addr2line tool to view the crash point and then correct the program.
Through the nm tool (refer to nm), you can get the symbol information of the program:
$ nm -n test
$$ 0804841d T foo
$$ 08048439 T main
[omit other content]
In fact, the addresses 0x0804841d~0x08048439 belong to the assembly content of foo, and addr2line can locate this function through any address in between.
Addr2line seems to be a little different for C++ programs. Suppose there is a main.cpp as follows:
--main.cpp--
#include <iostream>

using namespace std;

void foo()
{
	cout << "The address of foo() is 0x" << hex << int(foo) <<endl;
}

intmain()
{
	foo();
	return 0;
}
$ g++ -g main.cpp -o cpptest
$ ./cpptest
$$ The address of foo() is 0x804872d
$ addr2line 0x804872d -f -e cpptest
The print is:
$$ _Z3foov
$$ /home/binutils/ main.cpp:6
C++ In order to meet the needs of overloading, the C++ processor will rename each function according to a certain encoding method. This process is the name splitting process. And _Z3foov is exactly the form after the name split. The original name can be obtained by adding the --demangle option:
$ addr2line 0x804872d --demangle=gnu-v3 -f -e cpptest 
$$ foo()

$$ /home/binutils/main.cpp:6


2. ar, static library generator
There are two types of libraries in Linux, one is a static library with a suffix of .a; the other is a dynamic library with a suffix of .so; the difference between the two is probably like the internal Linked functions are like ordinary functions. When an executable program is linked with a static library, the functions and data in the used library will be copied to the final executable program, while the program in the dynamic library will not be copied. The entire system The only one in it. There is no doubt that dynamic libraries save more memory, but in embedded systems, in most cases, the entire software is an executable program and does not support dynamic loading, that is, static is the main method.
ar in binutils is used to manage static libraries. A static library is actually an archive file produced by packaging *.o files.
Now suppose there are two files foo.c and bar.c,
--foo.c--
#include <stdio.h>

void foo()
{
	printf("This is foo().\n");
}

--bar.c--

#include <stdio.h>

void bar()
{
	printf("This is bar().\n");
}

First compile each of them into object files:

$ gcc -c foo.c
$ gcc -c bar.c
This will generate foo.o and bar.o. Merge them into libmy.a:
$ ar crs libmy.a foo.o bar.o
where, c -- create an archive file; r -- add files to the created library file; s -- generate library Index to improve link efficiency. You can view the contents of the library through the following commands:
$ ar t libmy.a
$$ foo.o
$$ bar.o
Parameter d can delete an object file in the library, for example: ar d libmy.a foo.o
Parameter x can decompress This library file, like: ar x libmy.a 
now assumes main.c changes as follows:
--main.c--
extern void foo();
extern void bar();

intmain()
{
	foo();
	bar();
	return 0;
}
Compile and link libmy.a:
$ gcc main.c libmy.a -o test
$ ./test
$$ This is foo().
$$ This is bar().

3. nm, symbol display In
general , nm uses for listing symbols in program files. Get a feel for nm first:
$ nm -n test
$$ 0804841d T main
$$ 08048434 T foo
$$ 08048448 T bar
[omit everything else...]
Each line listed by nm consists of three parts, the first column is The starting address of the function or variable, the second column is which segment the corresponding symbol is located in, and the last column is the name of the symbol. Regarding the second column, T here represents the code segment, and others are similar:
A--the value corresponding to the symbol is absolute and will not change in the subsequent linking process
B/b--the symbol is located in the uninitialized data segment (.bss segment)
C-- public symbols that are not initialized
D/d-- symbols are located in the initialized data segment (.data segment)
N-- symbols are used for debugging
P-- symbols are located in a stack traceback segment
R/r--symbols are located in the read-only data segment (.rdata segment)
T/t--symbol is located in the code segment (.text segment)
U--symbol is not defined

In order to better illustrate the above symbols, it is now assumed that there is main.c as follows:

--main.c--

#include <time.h>

int global1; //1. Uninitialized global variables are allocated in the .bss segment
								// C when not linked, B after linking
int global2 = 3; //2. The initialized global variable is allocated in the .data section,
const int GLOBAL = 5; //3, read-only data is stored in the .rdata segment

static int static_global1; //4. Uninitialized static variables are allocated in the .bss segment
static int static_global2 = 3; //5. The initialized static variable is allocated in the .data segment

void foo() //6, the function is placed in the .text segment, T--non-static
{
	static int internal1;		//同4
	static int internal2 = 3;	//同5
}

static void bar() //8, the function is placed in the .text segment, t--static
{
}

int main() //same as 6
{
	int local1; //9. Local variables exist on the stack, and nm cannot view them
	int local2 = 3;

	foo();
	return 0;
}

Compile it into an object file:

$ gcc -c -g main.c
Use nm to view object files:
$ nm -n main.o
$$ 00000000 T foo
$$ 00000000 D global2
$$ 00000000 R global3
$$ 00000000 b static_global1
$$ 00000004 C global1
$$ 00000004 b internal1.1555
$$ 00000004 d static_global2
$$ 00000005 t bar
$$ 00000008 d internal2.1556
$$ 0000000a T main
Since the program is not linked, the addresses listed at this time are relative offset addresses. Also, the _time symbol is not defined in the file because its implementation is in the C standard library. After checking the link again, the address will become a specific address.

Fourth, objdump, information viewer
In the development of embedded software, sometimes you need to know the segment information in the generated program file to analyze the problem, or you need to view the assembly code corresponding to the C language. At this time, objdump comes in handy.
Use the following commands to view segment information:
$ objdump -h test
-d option can display the assembly code of the program file:
$ objdump -d test
-d When viewing the assembly code, combine with -S to display the C/C++ source program corresponding to the assembly code ( Do not use the optimization option when compiling the program at this time):
$ objdump -S -d test
-f option can display the header information of the program file:
$ objdump -f test
-s -j can be used together to view the specific content of a segment :
$ objdump -s -j .data test

5. objcopy, segment editor
objdump can filter and edit segments.
You can specify the segment that needs to be edited through the -j option (this is also the most important function in embedded systems. In some embedded systems, such as when making a bootloader, objcopy is needed to extract the code segment, and then copy the It is programmed to the operating address of the system):
$ objcopy -j .text test onlytext
Clip multiple segments:
$ objcopy -j .text -j .data -j .bss test onlytext Opposite
to -j option, -R option Certain segments can be deleted:
$ objcopy -R .text test notext
--strip-debug option can delete debugging information in the program:
$ objcopy --strip-debug notext

Sixth, ranlib, library index generator Generate file index information
in the archive file, as mentioned in the second to the ar s parameter. For example:
$ ranlib libmy.a

7. size, segment size observer
You can use it to view the size of each segment of the program file:
$ size test (-A can view detailed information)

8. strings, the string peeker
strings can view the program file Displayable characters in , such as printed string information, function names, etc.

Nine, strip, program file thinning device
Remove the debugging information in the program file in order to reduce the size of the program file. It functions the same as objcopy --strip-debug.









Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324606495&siteId=291194637