本文介绍进程打印堆栈的原理。

1.1 涉及结构体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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 this object */
    ElfW(Half)        dlpi_phnum; /* # of items in dlpi_phdr */
    
    /* The following fields were added in glibc 2.4, after the first version of this structure was available.  Check the size
    argument passed to the dl_iterate_phdr callback to determine whether or not each later member is available.  */
    
    unsigned long long dlpi_adds;  /* Incremented when a new object may have been added */
    unsigned long long dlpi_subs; /* Incremented when an object may have been removed */
    size_t dlpi_tls_modid; /* If there is a PT_TLS segment, its module ID as used in TLS relocations, else zero */
    void  *dlpi_tls_data; /* The address of the calling thread's instance of this module's PT_TLS segment, if it has
                    one and it has been allocated in the calling thread, otherwise a null pointer */
};

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;

typedef struct  {
    Elf64_Word    st_name;     /* Symbol name */
    unsigned char st_info;     /* Symbol type and binding */
    unsigned char st_other;    /* Symbol visibility */
    Elf64_Section st_shndx;    /* Section index */
    Elf64_Addr    st_value;    /* Symbol value */
    Elf64_Xword   st_size;     /* Symbol size */
} Elf64_Sym;

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;

typedef struct {
    size_t dls_size;           /* Size in bytes of the whole buffer */
    unsigned int dls_cnt;      /* Number of elements in 'dls_serpath' */
    Dl_serpath dls_serpath[1]; /* Actually longer, 'dls_cnt' elements */
} Dl_serinfo;

typedef struct {
    char *dls_name;            /* Name of library search path directory */
    unsigned int dls_flags;    /* Indicates where this directory came from */
} Dl_serpath;

struct link_map {
    ElfW(Addr) l_addr;  /* Difference between the address in the ELF file and 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 */
};

1.2 涉及接口

1
2
3
4
5
6
7
8
#include <dlfcn.h>
int dl_iterate_phdr(int (*callback)(struct dl_phdr_info *info, size_t size, void *data), void *data);
void *dlopen(const char *filename, int flags);
void *dlsym(void *restrict handle, const char *restrict symbol);
int dlclose(void *handle);
int dladdr(const void *addr, Dl_info *info);
int dlinfo(void *restrict handle, int request, void *restrict info);
char *dlerror(void);