xref: /aosp_15_r20/external/bcc/src/cc/bcc_syms.cc (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 /*
2  * Copyright (c) 2016 GitHub, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "bcc_syms.h"
18 
19 #include <cxxabi.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <linux/elf.h>
23 #include <signal.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <sys/sysmacros.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 
30 #include <cstdio>
31 #include <cstring>
32 
33 #include "bcc_elf.h"
34 #include "bcc_perf_map.h"
35 #include "bcc_proc.h"
36 #include "common.h"
37 #include "syms.h"
38 #include "vendor/tinyformat.hpp"
39 
ModulePath(const std::string & ns_path,int root_fd,int pid,bool enter_ns)40 ProcSyms::ModulePath::ModulePath(const std::string &ns_path, int root_fd,
41                                  int pid, bool enter_ns) {
42   if (!enter_ns) {
43     path_ = ns_path;
44     proc_root_path_ = ns_path;
45     return;
46   }
47   proc_root_path_ = tfm::format("/proc/%d/root%s", pid, ns_path);
48   // filename for openat must not contain any starting slashes, otherwise
49   // it would get treated as an absolute path
50   std::string trimmed_path;
51   size_t non_slash_pos;
52   for (non_slash_pos = 0;
53        non_slash_pos < ns_path.size() && ns_path[non_slash_pos] == '/';
54        non_slash_pos++)
55     ;
56   trimmed_path = ns_path.substr(non_slash_pos);
57   fd_ = openat(root_fd, trimmed_path.c_str(), O_RDONLY);
58   if (fd_ > 0)
59     path_ = tfm::format("/proc/self/fd/%d", fd_);
60   else
61     // openat failed, fall back to /proc/.../root path
62     path_ = proc_root_path_;
63 }
64 
getinode_(ino_t & inode)65 bool ProcStat::getinode_(ino_t &inode) {
66   struct stat s;
67   if (!stat(procfs_.c_str(), &s)) {
68     inode = s.st_ino;
69     return true;
70   } else {
71     return false;
72   }
73 }
74 
refresh_root()75 bool ProcStat::refresh_root() {
76   // try to current root and mount namespace for process
77   char current_root[PATH_MAX], current_mount_ns[PATH_MAX];
78   if (readlink(root_symlink_.c_str(), current_root, PATH_MAX) < 0 ||
79       readlink(mount_ns_symlink_.c_str(), current_mount_ns, PATH_MAX) < 0)
80     // readlink failed, process might not exist anymore; keep old fd
81     return false;
82 
83   // check if root fd is up to date
84   if (root_fd_ != -1 && current_root == root_ && current_mount_ns == mount_ns_)
85     return false;
86 
87   root_ = current_root;
88   mount_ns_ = current_mount_ns;
89 
90   // either root fd is invalid or process root and/or mount namespace changed;
91   // re-open root note: when /proc/.../root changes, the open file descriptor
92   // still refers to the old one
93   int original_root_fd = root_fd_;
94   root_fd_ = open(root_symlink_.c_str(), O_PATH);
95   if (root_fd_ == -1)
96     std::cerr << "Opening " << root_symlink_ << " failed: " << strerror(errno)
97               << std::endl;
98   if (original_root_fd > 0)
99     close(original_root_fd);
100   return original_root_fd != root_fd_;
101 }
102 
is_stale()103 bool ProcStat::is_stale() {
104   ino_t cur_inode;
105   return getinode_(cur_inode) && (cur_inode != inode_) && refresh_root();
106 }
107 
ProcStat(int pid)108 ProcStat::ProcStat(int pid)
109     : procfs_(tfm::format("/proc/%d/exe", pid)),
110       root_symlink_(tfm::format("/proc/%d/root", pid)),
111       mount_ns_symlink_(tfm::format("/proc/%d/ns/mnt", pid)) {
112   getinode_(inode_);
113   refresh_root();
114 }
115 
_add_symbol(const char * symname,const char * modname,uint64_t addr,void * p)116 void KSyms::_add_symbol(const char *symname, const char *modname, uint64_t addr, void *p) {
117   KSyms *ks = static_cast<KSyms *>(p);
118   ks->syms_.emplace_back(symname, modname, addr);
119 }
120 
refresh()121 void KSyms::refresh() {
122   if (syms_.empty()) {
123     bcc_procutils_each_ksym(_add_symbol, this);
124     std::sort(syms_.begin(), syms_.end());
125   }
126 }
127 
resolve_addr(uint64_t addr,struct bcc_symbol * sym,bool demangle)128 bool KSyms::resolve_addr(uint64_t addr, struct bcc_symbol *sym, bool demangle) {
129   refresh();
130 
131   std::vector<Symbol>::iterator it;
132 
133   if (syms_.empty())
134     goto unknown_symbol;
135 
136   it = std::upper_bound(syms_.begin(), syms_.end(), Symbol("", "", addr));
137   if (it != syms_.begin()) {
138     it--;
139     sym->name = (*it).name.c_str();
140     if (demangle)
141       sym->demangle_name = sym->name;
142     sym->module = (*it).mod.c_str();
143     sym->offset = addr - (*it).addr;
144     return true;
145   }
146 
147 unknown_symbol:
148   memset(sym, 0, sizeof(struct bcc_symbol));
149   return false;
150 }
151 
resolve_name(const char * _unused,const char * name,uint64_t * addr)152 bool KSyms::resolve_name(const char *_unused, const char *name,
153                          uint64_t *addr) {
154   refresh();
155 
156   if (syms_.size() != symnames_.size()) {
157     symnames_.clear();
158     for (Symbol &sym : syms_) {
159       symnames_[sym.name] = sym.addr;
160     }
161   }
162 
163   auto it = symnames_.find(name);
164   if (it == symnames_.end())
165     return false;
166 
167   *addr = it->second;
168   return true;
169 }
170 
ProcSyms(int pid,struct bcc_symbol_option * option)171 ProcSyms::ProcSyms(int pid, struct bcc_symbol_option *option)
172     : pid_(pid), procstat_(pid) {
173   if (option)
174     std::memcpy(&symbol_option_, option, sizeof(bcc_symbol_option));
175   else
176     symbol_option_ = {
177       .use_debug_file = 1,
178       .check_debug_file_crc = 1,
179       .lazy_symbolize = 1,
180       .use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC)
181     };
182   load_modules();
183 }
184 
load_modules()185 void ProcSyms::load_modules() {
186   bcc_procutils_each_module(pid_, _add_module, this);
187 }
188 
refresh()189 void ProcSyms::refresh() {
190   modules_.clear();
191   load_modules();
192   procstat_.reset();
193 }
194 
_add_module(mod_info * mod,int enter_ns,void * payload)195 int ProcSyms::_add_module(mod_info *mod, int enter_ns, void *payload) {
196   ProcSyms *ps = static_cast<ProcSyms *>(payload);
197   std::shared_ptr<ModulePath> modpath =
198       std::make_shared<ModulePath>(mod->name, ps->procstat_.get_root_fd(),
199                                    ps->pid_, enter_ns && ps->pid_ != -1);
200   auto it = std::find_if(
201       ps->modules_.begin(), ps->modules_.end(),
202       [=](const ProcSyms::Module &m) { return m.name_ == mod->name; });
203   if (it == ps->modules_.end()) {
204     auto module = Module(
205         mod->name, modpath, &ps->symbol_option_);
206 
207     // pid/maps doesn't account for file_offset of text within the ELF.
208     // It only gives the mmap offset. We need the real offset for symbol
209     // lookup.
210     if (module.type_ == ModuleType::SO) {
211       if (bcc_elf_get_text_scn_info(modpath->path(), &module.elf_so_addr_,
212                                     &module.elf_so_offset_) < 0) {
213         fprintf(stderr, "WARNING: Couldn't find .text section in %s\n",
214                 modpath->alt_path());
215         fprintf(stderr, "WARNING: BCC can't handle sym look ups for %s",
216                 modpath->alt_path());
217       }
218     }
219 
220     if (!bcc_is_perf_map(modpath->path()) ||
221         module.type_ != ModuleType::UNKNOWN)
222       // Always add the module even if we can't read it, so that we could
223       // report correct module name. Unless it's a perf map that we only
224       // add readable ones.
225       it = ps->modules_.insert(ps->modules_.end(), std::move(module));
226     else
227       return 0;
228   }
229   it->ranges_.emplace_back(mod->start_addr, mod->end_addr, mod->file_offset);
230   // perf-PID map is added last. We try both inside the Process's mount
231   // namespace + chroot, and in global /tmp. Make sure we only add one.
232   if (it->type_ == ModuleType::PERF_MAP)
233     return -1;
234 
235   return 0;
236 }
237 
resolve_addr(uint64_t addr,struct bcc_symbol * sym,bool demangle)238 bool ProcSyms::resolve_addr(uint64_t addr, struct bcc_symbol *sym,
239                             bool demangle) {
240   if (procstat_.is_stale())
241     refresh();
242 
243   memset(sym, 0, sizeof(struct bcc_symbol));
244 
245   const char *original_module = nullptr;
246   uint64_t offset;
247   bool only_perf_map = false;
248   for (Module &mod : modules_) {
249     if (only_perf_map && (mod.type_ != ModuleType::PERF_MAP))
250       continue;
251     if (mod.contains(addr, offset)) {
252       if (mod.find_addr(offset, sym)) {
253         if (demangle) {
254           if (sym->name && (!strncmp(sym->name, "_Z", 2) || !strncmp(sym->name, "___Z", 4)))
255             sym->demangle_name =
256                 abi::__cxa_demangle(sym->name, nullptr, nullptr, nullptr);
257           if (!sym->demangle_name)
258             sym->demangle_name = sym->name;
259         }
260         return true;
261       } else if (mod.type_ != ModuleType::PERF_MAP) {
262         // In this case, we found the address in the range of a module, but
263         // not able to find a symbol of that address in the module.
264         // Thus, we would try to find the address in perf map, and
265         // save the module's name in case we will need it later.
266         original_module = mod.name_.c_str();
267         only_perf_map = true;
268       }
269     }
270   }
271   // If we didn't find the symbol anywhere, the module name is probably
272   // set to be the perf map's name as it would be the last we tried.
273   // In this case, if we have found the address previously in a module,
274   // report the saved original module name instead.
275   if (original_module)
276     sym->module = original_module;
277   return false;
278 }
279 
resolve_name(const char * module,const char * name,uint64_t * addr)280 bool ProcSyms::resolve_name(const char *module, const char *name,
281                             uint64_t *addr) {
282   if (procstat_.is_stale())
283     refresh();
284 
285   for (Module &mod : modules_) {
286     if (mod.name_ == module)
287       return mod.find_name(name, addr);
288   }
289   return false;
290 }
291 
Module(const char * name,std::shared_ptr<ModulePath> path,struct bcc_symbol_option * option)292 ProcSyms::Module::Module(const char *name, std::shared_ptr<ModulePath> path,
293                          struct bcc_symbol_option *option)
294     : name_(name),
295       path_(path),
296       loaded_(false),
297       symbol_option_(option),
298       type_(ModuleType::UNKNOWN) {
299   int elf_type = bcc_elf_get_type(path_->path());
300   // The Module is an ELF file
301   if (elf_type >= 0) {
302     if (elf_type == ET_EXEC)
303       type_ = ModuleType::EXEC;
304     else if (elf_type == ET_DYN)
305       type_ = ModuleType::SO;
306     return;
307   }
308   // Other symbol files
309   if (bcc_is_valid_perf_map(path_->path()) == 1)
310     type_ = ModuleType::PERF_MAP;
311   else if (bcc_elf_is_vdso(path_->path()) == 1)
312     type_ = ModuleType::VDSO;
313 
314   // Will be stored later
315   elf_so_offset_ = 0;
316   elf_so_addr_ = 0;
317 }
318 
_add_symbol(const char * symname,uint64_t start,uint64_t size,void * p)319 int ProcSyms::Module::_add_symbol(const char *symname, uint64_t start,
320                                   uint64_t size, void *p) {
321   Module *m = static_cast<Module *>(p);
322   auto res = m->symnames_.emplace(symname);
323   m->syms_.emplace_back(&*(res.first), start, size);
324   return 0;
325 }
326 
_add_symbol_lazy(size_t section_idx,size_t str_table_idx,size_t str_len,uint64_t start,uint64_t size,int debugfile,void * p)327 int ProcSyms::Module::_add_symbol_lazy(size_t section_idx, size_t str_table_idx,
328                                        size_t str_len, uint64_t start,
329                                        uint64_t size, int debugfile, void *p) {
330   Module *m = static_cast<Module *>(p);
331   m->syms_.emplace_back(
332       section_idx, str_table_idx, str_len, start, size, debugfile);
333   return 0;
334 }
335 
load_sym_table()336 void ProcSyms::Module::load_sym_table() {
337   if (loaded_)
338     return;
339   loaded_ = true;
340 
341   if (type_ == ModuleType::UNKNOWN)
342     return;
343 
344   if (type_ == ModuleType::PERF_MAP)
345     bcc_perf_map_foreach_sym(path_->path(), _add_symbol, this);
346   if (type_ == ModuleType::EXEC || type_ == ModuleType::SO) {
347     if (symbol_option_->lazy_symbolize)
348       bcc_elf_foreach_sym_lazy(path_->path(), _add_symbol_lazy, symbol_option_,
349                                this);
350     else
351       bcc_elf_foreach_sym(path_->path(), _add_symbol, symbol_option_, this);
352   }
353   if (type_ == ModuleType::VDSO)
354     bcc_elf_foreach_vdso_sym(_add_symbol, this);
355 
356   std::sort(syms_.begin(), syms_.end());
357 }
358 
contains(uint64_t addr,uint64_t & offset) const359 bool ProcSyms::Module::contains(uint64_t addr, uint64_t &offset) const {
360   for (const auto &range : ranges_) {
361     if (addr >= range.start && addr < range.end) {
362       if (type_ == ModuleType::SO || type_ == ModuleType::VDSO) {
363         offset = __so_calc_mod_offset(range.start, range.file_offset,
364                                       elf_so_addr_, elf_so_offset_, addr);
365       } else {
366         offset = addr;
367       }
368 
369       return true;
370     }
371   }
372 
373   return false;
374 }
375 
find_name(const char * symname,uint64_t * addr)376 bool ProcSyms::Module::find_name(const char *symname, uint64_t *addr) {
377   struct Payload {
378     const char *symname;
379     uint64_t *out;
380     bool found;
381   };
382 
383   Payload payload;
384   payload.symname = symname;
385   payload.out = addr;
386   payload.found = false;
387 
388   auto cb = [](const char *name, uint64_t start, uint64_t size, void *p) {
389     Payload *payload = static_cast<Payload*>(p);
390 
391     if (!strcmp(payload->symname, name)) {
392       payload->found = true;
393       *(payload->out) = start;
394       return -1;  // Stop iteration
395     }
396 
397     return 0;
398   };
399 
400   if (type_ == ModuleType::PERF_MAP)
401     bcc_perf_map_foreach_sym(path_->path(), cb, &payload);
402   if (type_ == ModuleType::EXEC || type_ == ModuleType::SO) {
403     bcc_elf_foreach_sym(path_->path(), cb, symbol_option_, &payload);
404     if (path_->path() != path_->alt_path())
405       // some features (e.g. some kinds of debug info) don't work with /proc/self/fd/... path
406       bcc_elf_foreach_sym(path_->alt_path(), cb, symbol_option_, &payload);
407   }
408   if (type_ == ModuleType::VDSO)
409     bcc_elf_foreach_vdso_sym(cb, &payload);
410 
411   if (!payload.found)
412     return false;
413 
414   if (type_ == ModuleType::SO)
415     *(payload.out) += start();
416 
417   return true;
418 }
419 
find_addr(uint64_t offset,struct bcc_symbol * sym)420 bool ProcSyms::Module::find_addr(uint64_t offset, struct bcc_symbol *sym) {
421   load_sym_table();
422 
423   sym->module = name_.c_str();
424   sym->offset = offset;
425 
426   auto it = std::upper_bound(syms_.begin(), syms_.end(), Symbol(nullptr, offset, 0));
427   if (it == syms_.begin())
428     return false;
429 
430   // 'it' points to the symbol whose start address is strictly greater than
431   // the address we're looking for. Start stepping backwards as long as the
432   // current symbol is still below the desired address, and see if the end
433   // of the current symbol (start + size) is above the desired address. Once
434   // we have a matching symbol, return it. Note that simply looking at '--it'
435   // is not enough, because symbols can be nested. For example, we could be
436   // looking for offset 0x12 with the following symbols available:
437   // SYMBOL   START   SIZE    END
438   // goo      0x0     0x6     0x0 + 0x6 = 0x6
439   // foo      0x6     0x10    0x6 + 0x10 = 0x16
440   // bar      0x8     0x4     0x8 + 0x4 = 0xc
441   // baz      0x16    0x10    0x16 + 0x10 = 0x26
442   // The upper_bound lookup will return baz, and then going one symbol back
443   // brings us to bar, which does not contain offset 0x12 and is nested inside
444   // foo. Going back one more symbol brings us to foo, which contains 0x12
445   // and is a match.
446   // However, we also don't want to walk through the entire symbol list for
447   // unknown / missing symbols. So we will break if we reach a function that
448   // doesn't cover the function immediately before 'it', which means it is
449   // not possibly a nested function containing the address we're looking for.
450   --it;
451   uint64_t limit = it->start;
452   for (; offset >= it->start; --it) {
453     if (offset < it->start + it->size) {
454       // Resolve and cache the symbol name if necessary
455       if (!it->is_name_resolved) {
456         std::string sym_name(it->data.name_idx.str_len + 1, '\0');
457         if (bcc_elf_symbol_str(path_->path(), it->data.name_idx.section_idx,
458                                it->data.name_idx.str_table_idx, &sym_name[0],
459                                sym_name.size(), it->data.name_idx.debugfile))
460           break;
461 
462         it->data.name = &*(symnames_.emplace(std::move(sym_name)).first);
463         it->is_name_resolved = true;
464       }
465 
466       sym->name = it->data.name->c_str();
467       sym->offset = (offset - it->start);
468       return true;
469     }
470     if (limit > it->start + it->size)
471       break;
472     // But don't step beyond begin()!
473     if (it == syms_.begin())
474       break;
475   }
476 
477   return false;
478 }
479 
load_sym_table()480 bool BuildSyms::Module::load_sym_table()
481 {
482   if (loaded_)
483     return true;
484 
485   symbol_option_ = {
486     .use_debug_file = 1,
487     .check_debug_file_crc = 1,
488     .lazy_symbolize = 1,
489     .use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC)
490   };
491 
492   bcc_elf_foreach_sym(module_name_.c_str(), _add_symbol, &symbol_option_, this);
493   std::sort(syms_.begin(), syms_.end());
494 
495   for(std::vector<Symbol>::iterator it = syms_.begin();
496       it != syms_.end(); ++it++) {
497   }
498   loaded_ = true;
499   return true;
500 }
501 
_add_symbol(const char * symname,uint64_t start,uint64_t size,void * p)502 int BuildSyms::Module::_add_symbol(const char *symname, uint64_t start,
503                                    uint64_t size, void *p)
504 {
505   BuildSyms::Module *m = static_cast<BuildSyms::Module *> (p);
506   auto res = m->symnames_.emplace(symname);
507   m->syms_.emplace_back(&*(res.first), start, size);
508   return 0;
509 }
510 
resolve_addr(uint64_t offset,struct bcc_symbol * sym,bool demangle)511 bool BuildSyms::Module::resolve_addr(uint64_t offset, struct bcc_symbol* sym,
512                                      bool demangle)
513 {
514   std::vector<Symbol>::iterator it;
515 
516   load_sym_table();
517 
518   if (syms_.empty())
519     goto unknown_symbol;
520 
521   it = std::upper_bound(syms_.begin(), syms_.end(), Symbol(nullptr, offset, 0));
522   if (it != syms_.begin()) {
523     it--;
524     sym->name = (*it).name->c_str();
525     if (demangle)
526       sym->demangle_name = sym->name;
527     sym->offset = offset - (*it).start;
528     sym->module = module_name_.c_str();
529     return true;
530   }
531 
532 unknown_symbol:
533   memset(sym, 0, sizeof(struct bcc_symbol));
534   return false;
535 }
536 
add_module(const std::string module_name)537 bool BuildSyms::add_module(const std::string module_name)
538 {
539   struct stat s;
540   char buildid[BPF_BUILD_ID_SIZE*2+1];
541 
542   if (stat(module_name.c_str(), &s) < 0)
543      return false;
544 
545   if (bcc_elf_get_buildid(module_name.c_str(), buildid) < 0)
546       return false;
547 
548   std::string elf_buildid(buildid);
549   std::unique_ptr<BuildSyms::Module> ptr(new BuildSyms::Module(module_name.c_str()));
550   buildmap_[elf_buildid] = std::move(ptr);
551   return true;
552 }
553 
resolve_addr(std::string build_id,uint64_t offset,struct bcc_symbol * sym,bool demangle)554 bool BuildSyms::resolve_addr(std::string build_id, uint64_t offset,
555                              struct bcc_symbol *sym, bool demangle)
556 {
557   std::unordered_map<std::string,std::unique_ptr<BuildSyms::Module> >::iterator it;
558 
559   it = buildmap_.find(build_id);
560   if (it == buildmap_.end())
561     /*build-id not added to the BuildSym*/
562     return false;
563 
564   BuildSyms::Module *mod = it->second.get();
565   return mod->resolve_addr(offset, sym, demangle);
566 }
567 
568 extern "C" {
569 
bcc_symcache_new(int pid,struct bcc_symbol_option * option)570 void *bcc_symcache_new(int pid, struct bcc_symbol_option *option) {
571   if (pid < 0)
572     return static_cast<void *>(new KSyms());
573   return static_cast<void *>(new ProcSyms(pid, option));
574 }
575 
bcc_free_symcache(void * symcache,int pid)576 void bcc_free_symcache(void *symcache, int pid) {
577   if (pid < 0)
578     delete static_cast<KSyms*>(symcache);
579   else
580     delete static_cast<ProcSyms*>(symcache);
581 }
582 
bcc_symbol_free_demangle_name(struct bcc_symbol * sym)583 void bcc_symbol_free_demangle_name(struct bcc_symbol *sym) {
584   if (sym->demangle_name && (sym->demangle_name != sym->name))
585     free(const_cast<char*>(sym->demangle_name));
586 }
587 
bcc_symcache_resolve(void * resolver,uint64_t addr,struct bcc_symbol * sym)588 int bcc_symcache_resolve(void *resolver, uint64_t addr,
589                          struct bcc_symbol *sym) {
590   SymbolCache *cache = static_cast<SymbolCache *>(resolver);
591   return cache->resolve_addr(addr, sym) ? 0 : -1;
592 }
593 
bcc_symcache_resolve_no_demangle(void * resolver,uint64_t addr,struct bcc_symbol * sym)594 int bcc_symcache_resolve_no_demangle(void *resolver, uint64_t addr,
595                                      struct bcc_symbol *sym) {
596   SymbolCache *cache = static_cast<SymbolCache *>(resolver);
597   return cache->resolve_addr(addr, sym, false) ? 0 : -1;
598 }
599 
bcc_symcache_resolve_name(void * resolver,const char * module,const char * name,uint64_t * addr)600 int bcc_symcache_resolve_name(void *resolver, const char *module,
601                               const char *name, uint64_t *addr) {
602   SymbolCache *cache = static_cast<SymbolCache *>(resolver);
603   return cache->resolve_name(module, name, addr) ? 0 : -1;
604 }
605 
bcc_symcache_refresh(void * resolver)606 void bcc_symcache_refresh(void *resolver) {
607   SymbolCache *cache = static_cast<SymbolCache *>(resolver);
608   cache->refresh();
609 }
610 
bcc_buildsymcache_new(void)611 void *bcc_buildsymcache_new(void) {
612   return static_cast<void *>(new BuildSyms());
613 }
614 
bcc_free_buildsymcache(void * symcache)615 void bcc_free_buildsymcache(void *symcache) {
616   delete static_cast<BuildSyms*>(symcache);
617 }
618 
bcc_buildsymcache_add_module(void * resolver,const char * module_name)619 int  bcc_buildsymcache_add_module(void *resolver, const char *module_name)
620 {
621   BuildSyms *bsym = static_cast<BuildSyms *>(resolver);
622   return  bsym->add_module(module_name) ? 0 : -1;
623 }
624 
bcc_buildsymcache_resolve(void * resolver,struct bpf_stack_build_id * trace,struct bcc_symbol * sym)625 int bcc_buildsymcache_resolve(void *resolver,
626                               struct bpf_stack_build_id *trace,
627                               struct bcc_symbol *sym)
628 {
629   std::string build_id;
630   unsigned char *c = &trace->build_id[0];
631   int idx = 0;
632 
633   /*cannot resolve in case of fallback*/
634   if (trace->status == BPF_STACK_BUILD_ID_EMPTY ||
635       trace->status == BPF_STACK_BUILD_ID_IP)
636     return 0;
637 
638   while( idx < 20) {
639     int nib1 = (c[idx]&0xf0)>>4;
640     int nib2 = (c[idx]&0x0f);
641     build_id += "0123456789abcdef"[nib1];
642     build_id += "0123456789abcdef"[nib2];
643     idx++;
644   }
645 
646   BuildSyms *bsym = static_cast<BuildSyms *>(resolver);
647   return bsym->resolve_addr(build_id, trace->offset, sym) ? 0 : -1;
648 }
649 
650 struct mod_search {
651   const char *name;
652   uint64_t inode;
653   uint64_t dev_major;
654   uint64_t dev_minor;
655   uint64_t addr;
656   uint8_t inode_match_only;
657 
658   uint64_t start;
659   uint64_t file_offset;
660 };
661 
_bcc_syms_find_module(mod_info * info,int enter_ns,void * p)662 int _bcc_syms_find_module(mod_info *info, int enter_ns, void *p) {
663   struct mod_search *mod = (struct mod_search *)p;
664   // use inode + dev to determine match if inode set
665   if (mod->inode) {
666     if (mod->inode != info->inode)
667       return 0;
668 
669     // look at comment on USDT::set_probe_matching_kludge
670     // in api/BPF.h for explanation of why this might be
671     // necessary
672     if (mod->inode_match_only)
673       goto file_match;
674 
675     if(mod->dev_major == info->dev_major
676         && mod->dev_minor == info->dev_minor)
677       goto file_match;
678 
679     return 0;
680   }
681 
682   // fallback to name match
683   if (!strcmp(info->name, mod->name))
684     goto file_match;
685 
686   return 0;
687 
688 file_match:
689   mod->start = info->start_addr;
690   mod->file_offset = info->file_offset;
691   return -1;
692 }
693 
__so_calc_global_addr(uint64_t mod_start_addr,uint64_t mod_file_offset,uint64_t elf_sec_start_addr,uint64_t elf_sec_file_offset,uint64_t offset)694 uint64_t __so_calc_global_addr(uint64_t mod_start_addr,
695                                uint64_t mod_file_offset,
696                                uint64_t elf_sec_start_addr,
697                                uint64_t elf_sec_file_offset, uint64_t offset) {
698   return offset + (mod_start_addr - mod_file_offset) -
699          (elf_sec_start_addr - elf_sec_file_offset);
700 }
701 
__so_calc_mod_offset(uint64_t mod_start_addr,uint64_t mod_file_offset,uint64_t elf_sec_start_addr,uint64_t elf_sec_file_offset,uint64_t global_addr)702 uint64_t __so_calc_mod_offset(uint64_t mod_start_addr, uint64_t mod_file_offset,
703                               uint64_t elf_sec_start_addr,
704                               uint64_t elf_sec_file_offset,
705                               uint64_t global_addr) {
706   return global_addr - (mod_start_addr - mod_file_offset) +
707          (elf_sec_start_addr - elf_sec_file_offset);
708 }
709 
bcc_resolve_global_addr(int pid,const char * module,const uint64_t address,uint8_t inode_match_only,uint64_t * global)710 int bcc_resolve_global_addr(int pid, const char *module, const uint64_t address,
711                             uint8_t inode_match_only, uint64_t *global) {
712   struct stat s;
713   uint64_t elf_so_addr, elf_so_offset;
714   if (stat(module, &s))
715     return -1;
716 
717   struct mod_search mod = {module, s.st_ino, major(s.st_dev), minor(s.st_dev),
718                            address, inode_match_only,
719                            0x0, 0x0};
720   if (bcc_procutils_each_module(pid, _bcc_syms_find_module, &mod) < 0 ||
721       mod.start == 0x0)
722     return -1;
723 
724   if (bcc_elf_get_text_scn_info(module, &elf_so_addr, &elf_so_offset) < 0)
725     return -1;
726 
727   *global = __so_calc_global_addr(mod.start, mod.file_offset, elf_so_addr,
728                                   elf_so_offset, address);
729   return 0;
730 }
731 
_sym_cb_wrapper(const char * symname,uint64_t addr,uint64_t,void * payload)732 static int _sym_cb_wrapper(const char *symname, uint64_t addr, uint64_t,
733                            void *payload) {
734   SYM_CB cb = (SYM_CB) payload;
735   return cb(symname, addr);
736 }
737 
bcc_foreach_function_symbol(const char * module,SYM_CB cb)738 int bcc_foreach_function_symbol(const char *module, SYM_CB cb) {
739   if (module == 0 || cb == 0)
740     return -1;
741 
742   static struct bcc_symbol_option default_option = {
743     .use_debug_file = 1,
744     .check_debug_file_crc = 1,
745     .lazy_symbolize = 1,
746     .use_symbol_type = (1 << STT_FUNC) | (1 << STT_GNU_IFUNC)
747   };
748 
749   return bcc_elf_foreach_sym(
750       module, _sym_cb_wrapper, &default_option, (void *)cb);
751 }
752 
_find_sym(const char * symname,uint64_t addr,uint64_t,void * payload)753 static int _find_sym(const char *symname, uint64_t addr, uint64_t,
754                      void *payload) {
755   struct bcc_symbol *sym = (struct bcc_symbol *)payload;
756   if (!strcmp(sym->name, symname)) {
757     sym->offset = addr;
758     return -1;
759   }
760   return 0;
761 }
762 
763 struct load_addr_t {
764   uint64_t target_addr;
765   uint64_t binary_addr;
766 };
_find_load(uint64_t v_addr,uint64_t mem_sz,uint64_t file_offset,void * payload)767 int _find_load(uint64_t v_addr, uint64_t mem_sz, uint64_t file_offset,
768                        void *payload) {
769   struct load_addr_t *addr = static_cast<load_addr_t *>(payload);
770   if (addr->target_addr >= v_addr && addr->target_addr < (v_addr + mem_sz)) {
771     addr->binary_addr = addr->target_addr - v_addr + file_offset;
772     return -1;
773   }
774   return 0;
775 }
776 
bcc_resolve_symname(const char * module,const char * symname,const uint64_t addr,int pid,struct bcc_symbol_option * option,struct bcc_symbol * sym)777 int bcc_resolve_symname(const char *module, const char *symname,
778                         const uint64_t addr, int pid,
779                         struct bcc_symbol_option *option,
780                         struct bcc_symbol *sym) {
781   int module_type;
782   static struct bcc_symbol_option default_option = {
783     .use_debug_file = 1,
784     .check_debug_file_crc = 1,
785     .lazy_symbolize = 1,
786 #if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2
787     .use_symbol_type = BCC_SYM_ALL_TYPES | (1 << STT_PPC64_ELFV2_SYM_LEP),
788 #else
789     .use_symbol_type = BCC_SYM_ALL_TYPES,
790 #endif
791   };
792 
793   if (module == NULL)
794     return -1;
795 
796   memset(sym, 0, sizeof(bcc_symbol));
797 
798   if (strchr(module, '/')) {
799     sym->module = strdup(module);
800   } else {
801     sym->module = bcc_procutils_which_so(module, pid);
802   }
803   if (sym->module == NULL)
804     return -1;
805   if (pid != 0 && pid != -1 && strstr(sym->module, "/proc") != sym->module){
806     char *temp = (char*)sym->module;
807     sym->module = strdup(tfm::format("/proc/%d/root%s", pid, sym->module).c_str());
808     free(temp);
809   }
810 
811   sym->name = symname;
812   sym->offset = addr;
813   if (option == NULL)
814     option = &default_option;
815 
816   if (sym->name && sym->offset == 0x0)
817     if (bcc_elf_foreach_sym(sym->module, _find_sym, option, sym) < 0)
818       goto invalid_module;
819   if (sym->offset == 0x0)
820     goto invalid_module;
821 
822   // For executable (ET_EXEC) binaries and shared objects (ET_DYN), translate
823   // the virtual address to physical address in the binary file.
824   module_type = bcc_elf_get_type(sym->module);
825   if (module_type == ET_EXEC || module_type == ET_DYN) {
826     struct load_addr_t addr = {
827       .target_addr = sym->offset,
828       .binary_addr = 0x0,
829     };
830     if (bcc_elf_foreach_load_section(sym->module, &_find_load, &addr) < 0)
831       goto invalid_module;
832     if (!addr.binary_addr)
833       goto invalid_module;
834     sym->offset = addr.binary_addr;
835   }
836   return 0;
837 
838 invalid_module:
839   if (sym->module) {
840     ::free(const_cast<char*>(sym->module));
841     sym->module = NULL;
842   }
843   return -1;
844 }
845 }
846