1*5c742725SJiri Olsa // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2*5c742725SJiri Olsa 3*5c742725SJiri Olsa #include <libelf.h> 4*5c742725SJiri Olsa #include <gelf.h> 5*5c742725SJiri Olsa #include <fcntl.h> 6*5c742725SJiri Olsa #include <linux/kernel.h> 7*5c742725SJiri Olsa 8*5c742725SJiri Olsa #include "libbpf_internal.h" 9*5c742725SJiri Olsa #include "str_error.h" 10*5c742725SJiri Olsa 11*5c742725SJiri Olsa #define STRERR_BUFSIZE 128 12*5c742725SJiri Olsa 13*5c742725SJiri Olsa /* Return next ELF section of sh_type after scn, or first of that type if scn is NULL. */ 14*5c742725SJiri Olsa static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn) 15*5c742725SJiri Olsa { 16*5c742725SJiri Olsa while ((scn = elf_nextscn(elf, scn)) != NULL) { 17*5c742725SJiri Olsa GElf_Shdr sh; 18*5c742725SJiri Olsa 19*5c742725SJiri Olsa if (!gelf_getshdr(scn, &sh)) 20*5c742725SJiri Olsa continue; 21*5c742725SJiri Olsa if (sh.sh_type == sh_type) 22*5c742725SJiri Olsa return scn; 23*5c742725SJiri Olsa } 24*5c742725SJiri Olsa return NULL; 25*5c742725SJiri Olsa } 26*5c742725SJiri Olsa 27*5c742725SJiri Olsa /* Find offset of function name in the provided ELF object. "binary_path" is 28*5c742725SJiri Olsa * the path to the ELF binary represented by "elf", and only used for error 29*5c742725SJiri Olsa * reporting matters. "name" matches symbol name or name@@LIB for library 30*5c742725SJiri Olsa * functions. 31*5c742725SJiri Olsa */ 32*5c742725SJiri Olsa long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name) 33*5c742725SJiri Olsa { 34*5c742725SJiri Olsa int i, sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB }; 35*5c742725SJiri Olsa bool is_shared_lib, is_name_qualified; 36*5c742725SJiri Olsa long ret = -ENOENT; 37*5c742725SJiri Olsa size_t name_len; 38*5c742725SJiri Olsa GElf_Ehdr ehdr; 39*5c742725SJiri Olsa 40*5c742725SJiri Olsa if (!gelf_getehdr(elf, &ehdr)) { 41*5c742725SJiri Olsa pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1)); 42*5c742725SJiri Olsa ret = -LIBBPF_ERRNO__FORMAT; 43*5c742725SJiri Olsa goto out; 44*5c742725SJiri Olsa } 45*5c742725SJiri Olsa /* for shared lib case, we do not need to calculate relative offset */ 46*5c742725SJiri Olsa is_shared_lib = ehdr.e_type == ET_DYN; 47*5c742725SJiri Olsa 48*5c742725SJiri Olsa name_len = strlen(name); 49*5c742725SJiri Olsa /* Does name specify "@@LIB"? */ 50*5c742725SJiri Olsa is_name_qualified = strstr(name, "@@") != NULL; 51*5c742725SJiri Olsa 52*5c742725SJiri Olsa /* Search SHT_DYNSYM, SHT_SYMTAB for symbol. This search order is used because if 53*5c742725SJiri Olsa * a binary is stripped, it may only have SHT_DYNSYM, and a fully-statically 54*5c742725SJiri Olsa * linked binary may not have SHT_DYMSYM, so absence of a section should not be 55*5c742725SJiri Olsa * reported as a warning/error. 56*5c742725SJiri Olsa */ 57*5c742725SJiri Olsa for (i = 0; i < ARRAY_SIZE(sh_types); i++) { 58*5c742725SJiri Olsa size_t nr_syms, strtabidx, idx; 59*5c742725SJiri Olsa Elf_Data *symbols = NULL; 60*5c742725SJiri Olsa Elf_Scn *scn = NULL; 61*5c742725SJiri Olsa int last_bind = -1; 62*5c742725SJiri Olsa const char *sname; 63*5c742725SJiri Olsa GElf_Shdr sh; 64*5c742725SJiri Olsa 65*5c742725SJiri Olsa scn = elf_find_next_scn_by_type(elf, sh_types[i], NULL); 66*5c742725SJiri Olsa if (!scn) { 67*5c742725SJiri Olsa pr_debug("elf: failed to find symbol table ELF sections in '%s'\n", 68*5c742725SJiri Olsa binary_path); 69*5c742725SJiri Olsa continue; 70*5c742725SJiri Olsa } 71*5c742725SJiri Olsa if (!gelf_getshdr(scn, &sh)) 72*5c742725SJiri Olsa continue; 73*5c742725SJiri Olsa strtabidx = sh.sh_link; 74*5c742725SJiri Olsa symbols = elf_getdata(scn, 0); 75*5c742725SJiri Olsa if (!symbols) { 76*5c742725SJiri Olsa pr_warn("elf: failed to get symbols for symtab section in '%s': %s\n", 77*5c742725SJiri Olsa binary_path, elf_errmsg(-1)); 78*5c742725SJiri Olsa ret = -LIBBPF_ERRNO__FORMAT; 79*5c742725SJiri Olsa goto out; 80*5c742725SJiri Olsa } 81*5c742725SJiri Olsa nr_syms = symbols->d_size / sh.sh_entsize; 82*5c742725SJiri Olsa 83*5c742725SJiri Olsa for (idx = 0; idx < nr_syms; idx++) { 84*5c742725SJiri Olsa int curr_bind; 85*5c742725SJiri Olsa GElf_Sym sym; 86*5c742725SJiri Olsa Elf_Scn *sym_scn; 87*5c742725SJiri Olsa GElf_Shdr sym_sh; 88*5c742725SJiri Olsa 89*5c742725SJiri Olsa if (!gelf_getsym(symbols, idx, &sym)) 90*5c742725SJiri Olsa continue; 91*5c742725SJiri Olsa 92*5c742725SJiri Olsa if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) 93*5c742725SJiri Olsa continue; 94*5c742725SJiri Olsa 95*5c742725SJiri Olsa sname = elf_strptr(elf, strtabidx, sym.st_name); 96*5c742725SJiri Olsa if (!sname) 97*5c742725SJiri Olsa continue; 98*5c742725SJiri Olsa 99*5c742725SJiri Olsa curr_bind = GELF_ST_BIND(sym.st_info); 100*5c742725SJiri Olsa 101*5c742725SJiri Olsa /* User can specify func, func@@LIB or func@@LIB_VERSION. */ 102*5c742725SJiri Olsa if (strncmp(sname, name, name_len) != 0) 103*5c742725SJiri Olsa continue; 104*5c742725SJiri Olsa /* ...but we don't want a search for "foo" to match 'foo2" also, so any 105*5c742725SJiri Olsa * additional characters in sname should be of the form "@@LIB". 106*5c742725SJiri Olsa */ 107*5c742725SJiri Olsa if (!is_name_qualified && sname[name_len] != '\0' && sname[name_len] != '@') 108*5c742725SJiri Olsa continue; 109*5c742725SJiri Olsa 110*5c742725SJiri Olsa if (ret >= 0) { 111*5c742725SJiri Olsa /* handle multiple matches */ 112*5c742725SJiri Olsa if (last_bind != STB_WEAK && curr_bind != STB_WEAK) { 113*5c742725SJiri Olsa /* Only accept one non-weak bind. */ 114*5c742725SJiri Olsa pr_warn("elf: ambiguous match for '%s', '%s' in '%s'\n", 115*5c742725SJiri Olsa sname, name, binary_path); 116*5c742725SJiri Olsa ret = -LIBBPF_ERRNO__FORMAT; 117*5c742725SJiri Olsa goto out; 118*5c742725SJiri Olsa } else if (curr_bind == STB_WEAK) { 119*5c742725SJiri Olsa /* already have a non-weak bind, and 120*5c742725SJiri Olsa * this is a weak bind, so ignore. 121*5c742725SJiri Olsa */ 122*5c742725SJiri Olsa continue; 123*5c742725SJiri Olsa } 124*5c742725SJiri Olsa } 125*5c742725SJiri Olsa 126*5c742725SJiri Olsa /* Transform symbol's virtual address (absolute for 127*5c742725SJiri Olsa * binaries and relative for shared libs) into file 128*5c742725SJiri Olsa * offset, which is what kernel is expecting for 129*5c742725SJiri Olsa * uprobe/uretprobe attachment. 130*5c742725SJiri Olsa * See Documentation/trace/uprobetracer.rst for more 131*5c742725SJiri Olsa * details. 132*5c742725SJiri Olsa * This is done by looking up symbol's containing 133*5c742725SJiri Olsa * section's header and using it's virtual address 134*5c742725SJiri Olsa * (sh_addr) and corresponding file offset (sh_offset) 135*5c742725SJiri Olsa * to transform sym.st_value (virtual address) into 136*5c742725SJiri Olsa * desired final file offset. 137*5c742725SJiri Olsa */ 138*5c742725SJiri Olsa sym_scn = elf_getscn(elf, sym.st_shndx); 139*5c742725SJiri Olsa if (!sym_scn) 140*5c742725SJiri Olsa continue; 141*5c742725SJiri Olsa if (!gelf_getshdr(sym_scn, &sym_sh)) 142*5c742725SJiri Olsa continue; 143*5c742725SJiri Olsa 144*5c742725SJiri Olsa ret = sym.st_value - sym_sh.sh_addr + sym_sh.sh_offset; 145*5c742725SJiri Olsa last_bind = curr_bind; 146*5c742725SJiri Olsa } 147*5c742725SJiri Olsa if (ret > 0) 148*5c742725SJiri Olsa break; 149*5c742725SJiri Olsa } 150*5c742725SJiri Olsa 151*5c742725SJiri Olsa if (ret > 0) { 152*5c742725SJiri Olsa pr_debug("elf: symbol address match for '%s' in '%s': 0x%lx\n", name, binary_path, 153*5c742725SJiri Olsa ret); 154*5c742725SJiri Olsa } else { 155*5c742725SJiri Olsa if (ret == 0) { 156*5c742725SJiri Olsa pr_warn("elf: '%s' is 0 in symtab for '%s': %s\n", name, binary_path, 157*5c742725SJiri Olsa is_shared_lib ? "should not be 0 in a shared library" : 158*5c742725SJiri Olsa "try using shared library path instead"); 159*5c742725SJiri Olsa ret = -ENOENT; 160*5c742725SJiri Olsa } else { 161*5c742725SJiri Olsa pr_warn("elf: failed to find symbol '%s' in '%s'\n", name, binary_path); 162*5c742725SJiri Olsa } 163*5c742725SJiri Olsa } 164*5c742725SJiri Olsa out: 165*5c742725SJiri Olsa return ret; 166*5c742725SJiri Olsa } 167*5c742725SJiri Olsa 168*5c742725SJiri Olsa /* Find offset of function name in ELF object specified by path. "name" matches 169*5c742725SJiri Olsa * symbol name or name@@LIB for library functions. 170*5c742725SJiri Olsa */ 171*5c742725SJiri Olsa long elf_find_func_offset_from_file(const char *binary_path, const char *name) 172*5c742725SJiri Olsa { 173*5c742725SJiri Olsa char errmsg[STRERR_BUFSIZE]; 174*5c742725SJiri Olsa long ret = -ENOENT; 175*5c742725SJiri Olsa Elf *elf; 176*5c742725SJiri Olsa int fd; 177*5c742725SJiri Olsa 178*5c742725SJiri Olsa fd = open(binary_path, O_RDONLY | O_CLOEXEC); 179*5c742725SJiri Olsa if (fd < 0) { 180*5c742725SJiri Olsa ret = -errno; 181*5c742725SJiri Olsa pr_warn("failed to open %s: %s\n", binary_path, 182*5c742725SJiri Olsa libbpf_strerror_r(ret, errmsg, sizeof(errmsg))); 183*5c742725SJiri Olsa return ret; 184*5c742725SJiri Olsa } 185*5c742725SJiri Olsa elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 186*5c742725SJiri Olsa if (!elf) { 187*5c742725SJiri Olsa pr_warn("elf: could not read elf from %s: %s\n", binary_path, elf_errmsg(-1)); 188*5c742725SJiri Olsa close(fd); 189*5c742725SJiri Olsa return -LIBBPF_ERRNO__FORMAT; 190*5c742725SJiri Olsa } 191*5c742725SJiri Olsa 192*5c742725SJiri Olsa ret = elf_find_func_offset(elf, binary_path, name); 193*5c742725SJiri Olsa elf_end(elf); 194*5c742725SJiri Olsa close(fd); 195*5c742725SJiri Olsa return ret; 196*5c742725SJiri Olsa } 197*5c742725SJiri Olsa 198