diff --git a/source/ucli_py/libunwind/Makefile b/source/ucli_py/libunwind/Makefile index 2c7ba94..5cd8a1e 100644 --- a/source/ucli_py/libunwind/Makefile +++ b/source/ucli_py/libunwind/Makefile @@ -1,8 +1,8 @@ TARGET_SO=libunwind.so -SOURCES=unwind.cc symbol.cc +SOURCES=unwind.cc symbol.cc accessors.cc elf.cc OBJECTS=$(SOURCES:.cc=.o) -INCLUDES=-I. +INCLUDES=-I/usr/include/elf CFLAGS=-g -O0 -fPIC @@ -12,7 +12,7 @@ LFLAGS=-shared $(CXX) $(CFLAGS) $(INCLUDES) -c $< -o $@ $(TARGET_SO): $(OBJECTS) - $(CXX) $(LFLAGS) $^ -o $@ + $(CXX) $(LFLAGS) $^ -o $@ -lunwind-x86_64 -lunwind -lelf clean: rm -f $(TARGET_SO) $(OBJECTS) \ No newline at end of file diff --git a/source/ucli_py/libunwind/accessors.cc b/source/ucli_py/libunwind/accessors.cc index 5c39a98..621ad95 100644 --- a/source/ucli_py/libunwind/accessors.cc +++ b/source/ucli_py/libunwind/accessors.cc @@ -2,6 +2,8 @@ #include // for GElf_Ehdr | Elf #include #include +#include +#include #include "unwind.h" @@ -28,17 +30,6 @@ int UNW_OBJ(dwarf_search_unwind_table)(unw_addr_space_t as, unw_word_t ip, #define DW_EH_PE_absptr 0x00 /* absolute value */ #define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ -unw_accessors_t accessors = { - .find_proc_info = find_proc_info, - .put_unwind_info = put_unwind_info, - .get_dyn_info_list_addr = get_dyn_info_list_addr, - .access_mem = access_mem, - .access_reg = access_reg, - .access_fpreg = access_fpreg, - .resume = resume, - .get_proc_name = get_proc_name, -}; - static vma *find_map(unw_word_t ip, struct unwind_info *ui) { return ui->sp->find_vma(ui->pid, ip); } @@ -105,6 +96,8 @@ struct eh_frame_hdr { char data[0]; } __attribute__((__packed__)); +int dso_data_fd(vma *dso) { return open(dso->name.c_str(), O_RDONLY); } + ssize_t dso_read(vma *dso, u64 offset, u8 *data, ssize_t size) { ssize_t ret = -1; int fd; @@ -234,8 +227,6 @@ static int unwind_spec_ehframe(vma *dso, u64 offset, u64 *table_data, return 0; } -int dso_data_fd(vma *dso) { return open(dso->name.c_str(), O_RDONLY); } - static int read_unwind_spec(vma *dso, u64 *table_data, u64 *segbase, u64 *fde_count) { int ret = -EINVAL, fd; @@ -480,4 +471,15 @@ static int resume(unw_addr_space_t as, unw_cursor_t *cu, void *arg) { static int get_proc_name(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, unw_word_t *offp, void *arg) { return -UNW_EINVAL; -} \ No newline at end of file +} + +unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, +}; \ No newline at end of file diff --git a/source/ucli_py/libunwind/elf.cc b/source/ucli_py/libunwind/elf.cc new file mode 100644 index 0000000..7be03f1 --- /dev/null +++ b/source/ucli_py/libunwind/elf.cc @@ -0,0 +1,336 @@ +#include "elf.h" + +#include +#include // for open +#include // for GElf_Ehdr +#include +#include +#include +#include + +#define NS_NAME_LEN 128 + +struct sym_section_ctx { + Elf_Data *syms; + Elf_Data *symstrs; + Elf_Data *rel_data; + int is_reloc; + int is_plt; + int sym_count; + int plt_rel_type; + unsigned long plt_offset; + unsigned long plt_entsize; +}; + +struct symbol_sections_ctx { + sym_section_ctx symtab; + sym_section_ctx symtab_in_dynsym; + sym_section_ctx dynsymtab; +}; + +struct section_info { + Elf_Scn *sec; + GElf_Shdr *hdr; +}; + +struct plt_ctx { + section_info dynsym; + section_info plt_rel; + section_info plt; +}; + +static int global_mnt_ns_fd = -1; +static char global_mnt_ns_name[NS_NAME_LEN]; +static char g_mnt_ns_name[128]; + +int init_global_env(void) { + struct utsname utsname; + int ret = uname(&utsname); + if (ret < 0) { + return -1; + } + + if (readlink("/proc/1/ns/mnt", g_mnt_ns_name, sizeof(g_mnt_ns_name)) < 0) { + return -1; + } + + ret = readlink("/proc/1/ns/mnt", global_mnt_ns_name, NS_NAME_LEN); + if (ret <= 0) { + return -1; + } else { + global_mnt_ns_fd = open("/proc/1/ns/mnt", 0); + if (global_mnt_ns_fd < 0) { + return -1; + } + } + return 0; +} + +static int get_symbols_in_section(sym_section_ctx *sym, Elf *elf, Elf_Scn *sec, + GElf_Shdr *shdr, int is_reloc) { + sym->syms = elf_getdata(sec, NULL); + if (!sym->syms) { + return -1; + } + + Elf_Scn *symstrs_sec = elf_getscn(elf, shdr->sh_link); + if (!sec) { + return -1; + } + + sym->symstrs = elf_getdata(symstrs_sec, NULL); + if (!sym->symstrs) { + return -1; + } + + sym->sym_count = shdr->sh_size / shdr->sh_entsize; + sym->is_plt = 0; + sym->is_reloc = is_reloc; + + return 0; +} + +static int get_plt_symbols_in_section(sym_section_ctx *sym, Elf *elf, + plt_ctx *plt) { + sym->syms = elf_getdata(plt->dynsym.sec, NULL); + if (!sym->syms) { + return -1; + } + + sym->rel_data = elf_getdata(plt->plt_rel.sec, NULL); + if (!sym->rel_data) { + return -1; + } + + Elf_Scn *symstrs_sec = elf_getscn(elf, plt->dynsym.hdr->sh_link); + if (!symstrs_sec) { + return -1; + } + + sym->symstrs = elf_getdata(symstrs_sec, NULL); + if (!sym->symstrs) { + return -1; + } + + sym->is_plt = 1; + sym->plt_entsize = plt->plt.hdr->sh_type; + sym->plt_offset = plt->plt.hdr->sh_offset; + sym->sym_count = plt->plt_rel.hdr->sh_size / plt->plt_rel.hdr->sh_entsize; + sym->plt_rel_type = plt->plt_rel.hdr->sh_type; + + return 0; +} + +static int is_function(const GElf_Sym *sym) { + return GELF_ST_TYPE(sym->st_info) == STT_FUNC && sym->st_name != 0 && + sym->st_shndx != SHN_UNDEF; +} + +static void __get_symbol_without_plt(std::set &ss, sym_section_ctx *tab, + Elf *elf) { + GElf_Sym sym; + int index = 0; + const char *sym_name; + symbol s; + s.end = 0; + s.start = 0; + + while (index < tab->sym_count) { + gelf_getsym(tab->syms, index, &sym); + index++; + if (sym.st_shndx == SHN_ABS) { + continue; + } + if (!is_function(&sym)) { + continue; + } + sym_name = (const char *)tab->symstrs->d_buf + sym.st_name; + if (tab->is_reloc) { + Elf_Scn *sec = elf_getscn(elf, sym.st_shndx); + if (!sec) { + continue; + } + GElf_Shdr shdr; + gelf_getshdr(sec, &shdr); + sym.st_value -= shdr.sh_addr - shdr.sh_offset; + } + s.start = sym.st_value & 0xffffffff; + s.end = s.start + sym.st_size; + s.ip = s.start; + s.name = sym_name; + ss.insert(s); + } +} + +static void __get_symbol(std::set &ss, symbol_sections_ctx *si, + Elf *elf) { + symbol s; + s.end = 0; + s.start = 0; + + if (!si->symtab.syms && !si->dynsymtab.syms) { + return; + } + + sym_section_ctx *tab = &si->symtab; + __get_symbol_without_plt(ss, tab, elf); + tab = &si->symtab_in_dynsym; + __get_symbol_without_plt(ss, tab, elf); +} + +static void __get_plt_symbol(std::set &ss, symbol_sections_ctx *si, + Elf *elf) { + symbol s; + GElf_Sym sym; + int symidx; + int index = 0; + const char *sym_name = NULL; + + s.end = 0; + s.start = 0; + + if (!si->dynsymtab.syms) { + return; + } + + while (index < si->dynsymtab.sym_count) { + if (si->dynsymtab.plt_rel_type == SHT_RELA) { + GElf_Rela pos_mem, *pos; + pos = gelf_getrela(si->dynsymtab.rel_data, index, &pos_mem); + symidx = GELF_R_SYM(pos->r_info); + } else if (si->dynsymtab.plt_rel_type == SHT_REL) { + GElf_Rel pos_mem, *pos; + pos = gelf_getrel(si->dynsymtab.rel_data, index, &pos_mem); + symidx = GELF_R_SYM(pos->r_info); + } else { + return; + } + index++; + si->dynsymtab.plt_offset += si->dynsymtab.plt_entsize; + gelf_getsym(si->dynsymtab.syms, symidx, &sym); + + sym_name = (const char *)si->dynsymtab.symstrs->d_buf + sym.st_name; + s.start = si->dynsymtab.plt_offset; + s.end = s.start + si->dynsymtab.plt_entsize; + s.ip = s.start; + s.name = sym_name; + ss.insert(s); + } +} + +static void get_all_symbols(std::set &ss, symbol_sections_ctx *si, + Elf *elf) { + __get_symbol(ss, si, elf); + __get_plt_symbol(ss, si, elf); +} + +bool get_symbol_from_elf(std::set &ss, const char *path) { + static int first_init = 0; + + if (!first_init) { + first_init = true; + init_global_env(); + } + + int is_reloc = 0; + elf_version(EV_CURRENT); + int fd = open(path, O_RDONLY); + + Elf *elf = elf_begin(fd, ELF_C_READ, NULL); + if (elf == NULL) { + close(fd); + return false; + } + + Elf_Kind ek = elf_kind(elf); + if (ek != ELF_K_ELF) { + elf_end(elf); + close(fd); + return false; + } + GElf_Ehdr hdr; + if (gelf_getehdr(elf, &hdr) == NULL) { + elf_end(elf); + close(fd); + return false; + } + + if (hdr.e_type == ET_EXEC) { + is_reloc = 1; + } + + if (!elf_rawdata(elf_getscn(elf, hdr.e_shstrndx), NULL)) { + elf_end(elf); + close(fd); + return false; + } + + GElf_Shdr shdr; + GElf_Shdr symtab_shdr; + GElf_Shdr dynsym_shdr; + GElf_Shdr plt_shdr; + GElf_Shdr plt_rel_shdr; + memset(&shdr, 0, sizeof(shdr)); + memset(&symtab_shdr, 0, sizeof(symtab_shdr)); + memset(&dynsym_shdr, 0, sizeof(dynsym_shdr)); + memset(&plt_shdr, 0, sizeof(plt_shdr)); + memset(&plt_rel_shdr, 0, sizeof(plt_rel_shdr)); + + Elf_Scn *sec = NULL; + Elf_Scn *dynsym_sec = NULL; + Elf_Scn *symtab_sec = NULL; + Elf_Scn *plt_sec = NULL; + Elf_Scn *plt_rel_sec = NULL; + + while ((sec = elf_nextscn(elf, sec)) != NULL) { + char *str; + gelf_getshdr(sec, &shdr); + str = elf_strptr(elf, hdr.e_shstrndx, shdr.sh_name); + + if (str && strcmp(".symtab", str) == 0) { + symtab_sec = sec; + memcpy(&symtab_shdr, &shdr, sizeof(dynsym_shdr)); + } + if (str && strcmp(".dynsym", str) == 0) { + dynsym_sec = sec; + memcpy(&dynsym_shdr, &shdr, sizeof(dynsym_shdr)); + } + if (str && strcmp(".rela.plt", str) == 0) { + plt_rel_sec = sec; + memcpy(&plt_rel_shdr, &shdr, sizeof(plt_rel_shdr)); + } + if (str && strcmp(".plt", str) == 0) { + plt_sec = sec; + memcpy(&plt_shdr, &shdr, sizeof(plt_shdr)); + } + if (str && strcmp(".gnu.prelink_undo", str) == 0) { + is_reloc = 1; + } + } + + plt_ctx plt; + plt.dynsym.hdr = &dynsym_shdr; + plt.dynsym.sec = dynsym_sec; + plt.plt.hdr = &plt_shdr; + plt.plt.sec = plt_sec; + plt.plt_rel.hdr = &plt_rel_shdr; + plt.plt_rel.sec = plt_rel_sec; + + symbol_sections_ctx si; + memset(&si, 0, sizeof(si)); + if (symtab_sec) { + get_symbols_in_section(&si.symtab, elf, symtab_sec, &symtab_shdr, is_reloc); + } + if (dynsym_sec) { + get_symbols_in_section(&si.symtab_in_dynsym, elf, dynsym_sec, &dynsym_shdr, + is_reloc); + } + if (dynsym_sec && plt_sec) { + get_plt_symbols_in_section(&si.dynsymtab, elf, &plt); + } + + get_all_symbols(ss, &si, elf); + elf_end(elf); + close(fd); + return true; +} \ No newline at end of file diff --git a/source/ucli_py/libunwind/elf.h b/source/ucli_py/libunwind/elf.h new file mode 100644 index 0000000..7b4c5e9 --- /dev/null +++ b/source/ucli_py/libunwind/elf.h @@ -0,0 +1,11 @@ +#ifndef _PERF_ELF_H__ +#define _PERF_ELF_H__ + +#include +#include + +#include "symbol.h" + +bool get_symbol_from_elf(std::set &ss, const char *path); + +#endif diff --git a/source/ucli_py/libunwind/symbol.cc b/source/ucli_py/libunwind/symbol.cc index e3e03a5..bc084ff 100644 --- a/source/ucli_py/libunwind/symbol.cc +++ b/source/ucli_py/libunwind/symbol.cc @@ -1,32 +1,214 @@ -#include "symbol.h" +#include +#include + +#include "elf.h" symbol_parser g_symbol_parser; -bool symbol_parser::find_vma(pid_t pid, vma &vm) -{ - std::map::iterator proc_vma_map; +vma *symbol_parser::find_vma(pid_t pid, size_t pc) { + std::map::iterator it; - proc_vma_map = machine_vma.find(pid); - if (proc_vma_map == machine_vma.end()) { - return false; - } + it = machine_vma.find(pid); + if (it == machine_vma.end()) { + return NULL; + } - proc_vma::const_iterator vma_iter = proc_vma_map->second.upper_bound(vm.pc); - if (vma_iter == proc_vma_map->second.end()) { - return false; - } - if (vma_iter->second.end < vm.pc) { - return false; - } + proc_vma::iterator vma_iter = it->second.upper_bound(pc); + if (vma_iter == it->second.end() || vma_iter->second.end < pc) { + return NULL; + } - if (vma_iter != proc_vma_map->second.begin()) { - --vma_iter; - } + if (vma_iter != it->second.begin()) { + --vma_iter; + } - vm.start = vma_iter->second.start; - vm.end = vma_iter->second.end; - vm.name = vma_iter->second.name; - vm.offset = vma_iter->second.offset; + return &vma_iter->second; +} + +bool symbol_parser::find_vma(pid_t pid, vma &vm) { + std::map::iterator proc_vma_map; + + proc_vma_map = machine_vma.find(pid); + if (proc_vma_map == machine_vma.end()) { + return false; + } + + proc_vma::const_iterator vma_iter = proc_vma_map->second.upper_bound(vm.pc); + if (vma_iter == proc_vma_map->second.end()) { + return false; + } + if (vma_iter->second.end < vm.pc) { + return false; + } + + if (vma_iter != proc_vma_map->second.begin()) { + --vma_iter; + } + + vm.start = vma_iter->second.start; + vm.end = vma_iter->second.end; + vm.name = vma_iter->second.name; + vm.offset = vma_iter->second.offset; + + return true; +} + +bool symbol_parser::load_pid_maps(int pid) { + std::map::iterator it; + it = machine_vma.find(pid); + if (it != machine_vma.end()) { + return true; + } + + proc_vma proc; + char fn[256]; + sprintf(fn, "/proc/%d/maps", pid); + FILE *fp = fopen(fn, "r"); + if (!fp) { + return false; + } + + char buf[4096]; + char exename[4096]; + size_t start, end, offset; + while (fgets(buf, sizeof(buf), fp) != NULL) { + start = end = offset = 0; + exename[0] = '\0'; + sscanf(buf, "%lx-%lx %*s %lx %*x:%*x %*u %s %*s\n", &start, &end, &offset, + exename); + if (exename[0] == '\0') { + strcpy(exename, "[anon]"); + } + vma vm(start, end, offset, exename); + proc.insert(std::make_pair(vm.start, std::move(vm))); + } + + fclose(fp); + + machine_vma.insert(std::make_pair(pid, std::move(proc))); + it = machine_vma.find(pid); + if (it == machine_vma.end()) { + return false; + } + + return true; +} + +bool symbol_parser::find_symbol_in_cache(int tgid, unsigned long addr, + std::string &symbol) { + std::map >::const_iterator it_pid = + symbols_cache.find(tgid); + + if (it_pid != symbols_cache.end()) { + std::map map = symbols_cache[tgid]; + std::map::const_iterator it_symbol = + map.find(addr); + + if (it_symbol != map.end()) { + symbol = map[addr]; + + return true; + } + } + + return false; +} + +bool symbol_parser::get_symbol_info(int pid, symbol &sym, elf_file &file) { + std::map::iterator proc_vma_info; + + if (java_only) { + file.type = UNKNOWN; + return true; + } + + proc_vma_info = machine_vma.find(pid); + if (proc_vma_info == machine_vma.end()) { + if (!load_pid_maps(pid)) { + return false; + } + } + + vma area(sym.ip); + if (!find_vma(pid, area)) { + return false; + } + if (area.name == "[anon]") { + file.type = JIT_TYPE; + } + + file.reset(area.name); + if (file.type != JIT_TYPE) { + sym.reset(area.map(sym.ip)); + } + + return true; +} + +bool symbol_parser::putin_symbol_cache(int tgid, unsigned long addr, + std::string &symbol) { + std::map >::const_iterator it_pid = + symbols_cache.find(tgid); + + if (it_pid == symbols_cache.end()) { + std::map map; + symbols_cache.insert(std::make_pair(tgid, map)); + } + + std::map &map = symbols_cache[tgid]; + std::map::const_iterator it_symbol = + map.find(addr); + + if (it_symbol == map.end()) { + map[addr] = symbol; + return true; + } + + return false; +} + +bool search_symbol(const std::set &ss, symbol &sym) { + std::set::const_iterator it = ss.find(sym); + + if (it != ss.end()) { + sym.end = it->end; + sym.start = it->start; + sym.name = it->name; return true; + } + + return false; +} + +bool symbol_parser::load_elf(pid_t pid, const elf_file &file) { + std::map >::iterator it; + it = file_symbols.find(file); + std::set tmp; + std::set &syms = tmp; + if (it != file_symbols.end()) { + return true; + } + if (get_symbol_from_elf(syms, file.filename.c_str())) { + file_symbols.insert(make_pair(file, std::move(syms))); + return true; + } + return false; +} + +bool symbol_parser::find_elf_symbol(symbol &sym, const elf_file &file, int pid, + int pid_ns) { + std::map >::iterator it; + it = file_symbols.find(file); + std::set ss; + if (it == file_symbols.end()) { + if (!load_elf(pid, file)) { + return false; + } + it = file_symbols.find(file); + return search_symbol(it->second, sym); + } else { + return search_symbol(it->second, sym); + } + return true; } \ No newline at end of file diff --git a/source/ucli_py/libunwind/symbol.h b/source/ucli_py/libunwind/symbol.h index b32180c..a2bd973 100644 --- a/source/ucli_py/libunwind/symbol.h +++ b/source/ucli_py/libunwind/symbol.h @@ -1,3 +1,6 @@ +#ifndef __PERF_SYMBOL_H__ +#define __PERF_SYMBOL_H__ + #include #include #include @@ -141,4 +144,6 @@ public: int user_symbol; }; -extern symbol_parser g_symbol_parser; \ No newline at end of file +extern symbol_parser g_symbol_parser; + +#endif \ No newline at end of file diff --git a/source/ucli_py/libunwind/unwind.cc b/source/ucli_py/libunwind/unwind.cc index fa2a972..feac03e 100644 --- a/source/ucli_py/libunwind/unwind.cc +++ b/source/ucli_py/libunwind/unwind.cc @@ -112,7 +112,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, symbol_parser *sp, } void diag_printf_raw_stack(int pid, int ns_pid, const char *comm, - raw_stack_detail *raw_stack, int attach) { + raw_stack_detail *raw_stack, int attach){ struct perf_sample stack_sample; entry_cb_arg_t unwind_arg; static u64 regs_buf[3]; diff --git a/source/ucli_py/libunwind/unwind.h b/source/ucli_py/libunwind/unwind.h index 7c5623a..d31b000 100644 --- a/source/ucli_py/libunwind/unwind.h +++ b/source/ucli_py/libunwind/unwind.h @@ -1,3 +1,6 @@ +#ifndef __UNWIND_H +#define __UNWIND_H + #include #include #include @@ -99,5 +102,7 @@ struct unwind_info { symbol_parser *sp; }; -void diag_printf_raw_stack(int pid, int ns_pid, const char *comm, - raw_stack_detail *raw_stack, int attach); \ No newline at end of file +extern "C" void diag_printf_raw_stack(int pid, int ns_pid, const char *comm, + raw_stack_detail *raw_stack, int attach); + +#endif /* __UNWIND_H */