In order to facilitate the program, the product needs to print out the current call stack when the program crashes or encounters a problem. Because it is a Linux-based arm embedded system, there is not enough space to store the Coredump file.
The implementation method first uses the __builtin_frame_address () function to get the address of the current frame of the stack (FADDR), XFADDR (the data stored in the first cell of the stack frame) that is the return address of the current function, and the address of the instruction in the calling function. X (FADDR-1) is the address of the stack frame that invokes the function, which is the address of the stack frame in which the function was called. It is concluded that all stack frames of the same thread form a linked list. By traversing this list, you can print out all the call stacks.
The value of the address and local variables is stored in the stack frame. To facilitate debugging, you also need to translate the address into the corresponding function name and the corresponding source file. This takes advantage of the symbol table. The global function in the program and the function of exporting (exported) in the dynamic library will load the corresponding symbol table (. dynsym) into memory, and the library function dladdr () can be used to obtain the corresponding function name and its library file conveniently. But Dladdr () can only get the global function name of exported, and if the function is hidden (hidden) or static, Dladdr is powerless. In order to show these static functions, we have gone through a long process of quest. However, there is still no perfect solution. So forget about it here.
1. First is very blind, do not know why some can show, and some cannot. So a burst of crazy search, degrees Niang so that the strength of the breast, did not give any useful results. There is no way, only to chew the code again, even want to read the source of DLADDR. Well, a little over, stop for a cup of coffee, looking out of the window endless sea, heart suddenly open, calm many. Since some can be displayed, some cannot be displayed, indicating that there is no direction error. So why can't some of them? It was obvious that dladdr cheated us, and he didn't give us what we wanted. Well, it looks like a crap. However, only to determine this point, only to be emboldened to find dladdr trouble. The man, who had seen N times dladdr, found no reason. Niang is useless. Google, fortunately there are foreign agents. Or Google strong, a shot to find a very similar problem, and there are experts specifically pointed out that DLADDR can only get exported function. dladdr doesnt return the function name
Https://stackoverflow.com/questions/11731229/dladdr-doesnt-return-the-function-name
2. Finally found the cause, how to treat it? Or is it a terminal illness that can be saved without medicine? Most people with cancer will be dying, such as surgery, chemotherapy, radiotherapy, exercise, tai Chi and so on. We are no exception, find a lot of folk remedies, have tried, are invalid. For example,-wl,--export-dynamic,-rdynamic,-fvisibility,--version_script and so on. Alas, these smattering astray is really not shallow ah. More funny is that some have vowed to "verify". The so-called illness into medicine, their own side of trial and error, while remedial skills. After understanding the format of the elf file and the connection loading process of the dynamic library, it is basically clear what is going on. Static or hidden function, the connection stage sets it to the local property and is not loaded into memory. Run with dladdr to check, nature can not find. If you remove static, its properties will change to global, it will be loaded, and dladdr will naturally be able to find it. Then find a static function on the stack, remove the static, compile a try, sure enough.
3. This seems to be a step forward, but in fact, still remain in the cause analysis stage, but with a bit deeper. The boss does not care what these deep-seated reasons are, only cares about whether the problem is solved at all. Of course, this is a theoretical basis, so-called "results-oriented" well. OK, since there is no memory, is there any in the file? The answer is yes, and I'm not sure. When compiling with the-G option, which adds debug information, the resulting elf file contains all the symbolic information. Of course, the file is very large at this time. In order to thin the elf file, the actual release version, the general will use strip to delete the extra information. The advantage of this is that you can keep debugging information easy to debug, but also reduce the size of the elf file and reduce space consumption. The so-called "fish and Bear paw can be combined" it. The information you need is here (in the corresponding. so file), how do you get it out?
4. This involves DWAF Information format, binary file operations related libraries, such as: Libbfd,libelf and Libdwarf, and so on. As we have no knowledge of these libraries, it is a long process to study and learn from scratch. The problem has long been delay, and since the boss saw the dawn, he was eager to see the dawn soon. Furthermore, does software development not avoid "reinvent the wheel"? Find a way to take advantage of existing tools or library functions. During the early exploration process, the corresponding symbols in the program were resolved successfully by calculating the offset and using the Addr2line tool. A possible solution emerges naturally. First find the map address of the library file in the process (Lib_start) and the return address (faddr_n) stored in the stack (Faddr_n-lib_start) is the address of the directive in the dynamic library file, You can then use Addr2line to parse out the name of the function and the file and line number where it resides. Fadd_n already has, the key is to get lib_start.
5. We read the Dladdr man again and found that there is a dli_fbase member in the return struct (dl_info), which should be the load address of the dynamic library. The completion notes for Dl_info are as follows:
typedef struct {const char *dli_fname; /* Pathname of shared object that contains address */void *dli_fbase; /* Base address at which shared object is loaded */const char *dli_sname; /* Name of symbol whose definition overlaps addr */void *dli_saddr; /* Exact address of symbol named in dli_sname */} Dl_info; The
then began a new round of attempts following this idea. The result was a big surprise for us. All calls return the same dli_fbase! We were plunged into despair and contemplation. Obviously, this document does not correspond to our environment. After careful study of the results returned, it is found that it is actually the base address of the same library (libc.so). Libc.so is also the first library of our stack. Nor can it be completely useless to use it at least to solve the first symbol __clone (). However, we have not completely solved our problem. The
son often tests me a question: "Which city in the world has the most traffic?" The answer is Rome, because all roads pass through Rome. Since dladdr this road does not go through, we change the most close to the road--dladdr1 () try? Because DLADDR1 can return more information, the most interesting thing is the struct link_map *, because it can traverse all the dynamic libraries loaded in the process.
6. The first time you hit a lot of libraries, including many libraries that are not on the stack. Strangely enough, there is a library that does not actually have. This is very close to success, a little thought, and found the key to the problem, Link_map is a doubly linked list, we get the pointer not first moved to the table header, but directly to regain the position of the linked list (that is, libc.so position) began to print to the end of the table, Some libraries may still be before libc.so. As a result, the table header is first found, then the traversal is started, and all the libraries are printed out. There was a small stumbling block before reaching the final dawn. According to the previous understanding, LINK_MAPX should be the base address we are looking for. But experiments have shown that this is wrong. Fortunately, we left a more mind, played the Link_map in the l_addr, with this value a try, solved all the symbols. That is, the l_addr in Link_map is the base address of the. so file in memory. Link_map's comments are also listed here, because L_addr's comments are also not understood.
struct Link_map { elfw (Addr) l_addr; / * Difference between the address in the ELF file with the address in memory */ char *l_name; /* Absolute pathname where object was found * /ELFW (Dyn) *l_ld; /* Dynamic section of the Shared object */ struct link_map *l_next, *l_prev; /* Chain of Loaded objects *// * Plus additional fields private to the implementation * };
This is the end of the entire printing backtrace long journey. It is not perfect, however, because the stack cannot be played directly in the release version, and the trace is parsed again with the corresponding file with no strip. If the published version is many, there is no guarantee that the corresponding file will be found.
One question is, is there a compile option that exported the static function to global? In this way, dladdr can get all the symbols directly.
Other than that:
while traversing all the dynamic libraries, we also tried Dl_iterate_phdr (), according to man, it should also be able to print out all the libraries. The test, however, is that it only hits the program itself, which is the equivalent of nothing. Documentation attached to DL_ITERATE_PHDR ():
int DL_ITERATE_PHDR ( int (*callback) (struct dl_phdr_info *info, size_t size, void *data), void *data);
Description
The dl_iterate_phdr() function allows an application to inquire on run time to find out which shared objects it Has loaded.
The dl_iterate_phdr() function walks through the list of an application ' s GKFX objects and calls the function C1>callback once for each object, until either all shared objects has been processed or callbackreturns a n Onzero value.
callback receives three arguments: info, which is a pointer to a structure containing infor Mation about the shared object; size, which is the size of the structure pointed to by info; and data, which is a copy of whatever value were passed by the calling program as the second argument (also named data) In the dl_iterate_phdr().
The Info argument is a structure of the following type:
-
struct dl_phdr_info {ELFW (Addr) dlpi_addr; /* Base Address of Object */const char *dlpi_name; /* (null-terminated) Name of Object */const ELFW (PHDR) *dlpi_phdr; /* Pointer to array of ELF program headers for thi S Object */ELFW (half) dlpi_phnum;
/* # of items in dlpi_phdr */};
-
(the
ELFW () macro definition turns its argument to the name of an ELF data type suitable For the hardware architecture. For example, on a 32-bit platform, ELFW (ADDR) yields the data type name ELF32_ADDR. Further information on these types can is found in the
<elf.h> and
<link.h>< /em> header files.)
The dlpi_addr field indicates the base address of the shared object (i.e., the difference between the virtual mem Ory address of the shared object and the offset of this object in the file from which it is loaded). The dlpi_name field is a null-terminated string giving the pathname from which the shared object was loaded.
To understand the meaning of the dlpi_phdr and dlpi_phnum fields, we need to be aware this an ELF shared Object consists of a number of segments, each of which have a corresponding program header describing the segment. The dlpi_phdr field is a pointer to an array of the program headers for this shared object. The dlpi_phnum field indicates the size of this array.
These program headers is structures of the following form:
-
typedef struct { Elf32_word p_type; /* Segment type */ elf32_off p_offset; /* Segment file Offset */ elf32_addr p_vaddr; /* Segment Virtual Address */ elf32_addr p_paddr; /* Segment Physical Address */ elf32_word P_filesz; /* Segment size in file */ Elf32_word p_memsz; /* Segment size in memory */ Elf32_word p_flags; /* Segment Flags */ Elf32_word p_align; /* Segment alignment */} ELF32_PHDR;
-
Note that we can calculate the location of a particular program header,
x, in virtual memory using the FORMU La
Addr = = Info->dlpi_addr + info->dlpi_phdr[x].p_vaddr;
Return Value
The dl_iterate_phdr() function returns whatever value is returned by the last call to callback.
Versions
DL_ITERATE_PHDR () have been supported in glibc since version 2.2.4.
The call stack for the current process is printed in the program (BACKTRACE)