Skip to content

Commit 3774705

Browse files
olsajiriAlexei Starovoitov
authored andcommitted
libbpf: Add elf symbol iterator
Adding elf symbol iterator object (and some functions) that follow open-coded iterator pattern and some functions to ease up iterating elf object symbols. The idea is to iterate single symbol section with: struct elf_sym_iter iter; struct elf_sym *sym; if (elf_sym_iter_new(&iter, elf, binary_path, SHT_DYNSYM)) goto error; while ((sym = elf_sym_iter_next(&iter))) { ... } I considered opening the elf inside the iterator and iterate all symbol sections, but then it gets more complicated wrt user checks for when the next section is processed. Plus side is the we don't need 'exit' function, because caller/user is in charge of that. The returned iterated symbol object from elf_sym_iter_next function is placed inside the struct elf_sym_iter, so no extra allocation or argument is needed. Suggested-by: Andrii Nakryiko <andrii@kernel.org> Acked-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Jiri Olsa <jolsa@kernel.org> Link: https://lore.kernel.org/r/20230809083440.3209381-11-jolsa@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent f90eb70 commit 3774705

1 file changed

Lines changed: 115 additions & 64 deletions

File tree

tools/lib/bpf/elf.c

Lines changed: 115 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,104 @@ static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn)
6060
return NULL;
6161
}
6262

63+
struct elf_sym {
64+
const char *name;
65+
GElf_Sym sym;
66+
GElf_Shdr sh;
67+
};
68+
69+
struct elf_sym_iter {
70+
Elf *elf;
71+
Elf_Data *syms;
72+
size_t nr_syms;
73+
size_t strtabidx;
74+
size_t next_sym_idx;
75+
struct elf_sym sym;
76+
int st_type;
77+
};
78+
79+
static int elf_sym_iter_new(struct elf_sym_iter *iter,
80+
Elf *elf, const char *binary_path,
81+
int sh_type, int st_type)
82+
{
83+
Elf_Scn *scn = NULL;
84+
GElf_Ehdr ehdr;
85+
GElf_Shdr sh;
86+
87+
memset(iter, 0, sizeof(*iter));
88+
89+
if (!gelf_getehdr(elf, &ehdr)) {
90+
pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1));
91+
return -EINVAL;
92+
}
93+
94+
scn = elf_find_next_scn_by_type(elf, sh_type, NULL);
95+
if (!scn) {
96+
pr_debug("elf: failed to find symbol table ELF sections in '%s'\n",
97+
binary_path);
98+
return -ENOENT;
99+
}
100+
101+
if (!gelf_getshdr(scn, &sh))
102+
return -EINVAL;
103+
104+
iter->strtabidx = sh.sh_link;
105+
iter->syms = elf_getdata(scn, 0);
106+
if (!iter->syms) {
107+
pr_warn("elf: failed to get symbols for symtab section in '%s': %s\n",
108+
binary_path, elf_errmsg(-1));
109+
return -EINVAL;
110+
}
111+
iter->nr_syms = iter->syms->d_size / sh.sh_entsize;
112+
iter->elf = elf;
113+
iter->st_type = st_type;
114+
return 0;
115+
}
116+
117+
static struct elf_sym *elf_sym_iter_next(struct elf_sym_iter *iter)
118+
{
119+
struct elf_sym *ret = &iter->sym;
120+
GElf_Sym *sym = &ret->sym;
121+
const char *name = NULL;
122+
Elf_Scn *sym_scn;
123+
size_t idx;
124+
125+
for (idx = iter->next_sym_idx; idx < iter->nr_syms; idx++) {
126+
if (!gelf_getsym(iter->syms, idx, sym))
127+
continue;
128+
if (GELF_ST_TYPE(sym->st_info) != iter->st_type)
129+
continue;
130+
name = elf_strptr(iter->elf, iter->strtabidx, sym->st_name);
131+
if (!name)
132+
continue;
133+
sym_scn = elf_getscn(iter->elf, sym->st_shndx);
134+
if (!sym_scn)
135+
continue;
136+
if (!gelf_getshdr(sym_scn, &ret->sh))
137+
continue;
138+
139+
iter->next_sym_idx = idx + 1;
140+
ret->name = name;
141+
return ret;
142+
}
143+
144+
return NULL;
145+
}
146+
147+
148+
/* Transform symbol's virtual address (absolute for binaries and relative
149+
* for shared libs) into file offset, which is what kernel is expecting
150+
* for uprobe/uretprobe attachment.
151+
* See Documentation/trace/uprobetracer.rst for more details. This is done
152+
* by looking up symbol's containing section's header and using iter's virtual
153+
* address (sh_addr) and corresponding file offset (sh_offset) to transform
154+
* sym.st_value (virtual address) into desired final file offset.
155+
*/
156+
static unsigned long elf_sym_offset(struct elf_sym *sym)
157+
{
158+
return sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset;
159+
}
160+
63161
/* Find offset of function name in the provided ELF object. "binary_path" is
64162
* the path to the ELF binary represented by "elf", and only used for error
65163
* reporting matters. "name" matches symbol name or name@@LIB for library
@@ -91,94 +189,47 @@ long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
91189
* reported as a warning/error.
92190
*/
93191
for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
94-
size_t nr_syms, strtabidx, idx;
95-
Elf_Data *symbols = NULL;
96-
Elf_Scn *scn = NULL;
192+
struct elf_sym_iter iter;
193+
struct elf_sym *sym;
97194
int last_bind = -1;
98-
const char *sname;
99-
GElf_Shdr sh;
195+
int cur_bind;
100196

101-
scn = elf_find_next_scn_by_type(elf, sh_types[i], NULL);
102-
if (!scn) {
103-
pr_debug("elf: failed to find symbol table ELF sections in '%s'\n",
104-
binary_path);
197+
ret = elf_sym_iter_new(&iter, elf, binary_path, sh_types[i], STT_FUNC);
198+
if (ret == -ENOENT)
105199
continue;
106-
}
107-
if (!gelf_getshdr(scn, &sh))
108-
continue;
109-
strtabidx = sh.sh_link;
110-
symbols = elf_getdata(scn, 0);
111-
if (!symbols) {
112-
pr_warn("elf: failed to get symbols for symtab section in '%s': %s\n",
113-
binary_path, elf_errmsg(-1));
114-
ret = -LIBBPF_ERRNO__FORMAT;
200+
if (ret)
115201
goto out;
116-
}
117-
nr_syms = symbols->d_size / sh.sh_entsize;
118-
119-
for (idx = 0; idx < nr_syms; idx++) {
120-
int curr_bind;
121-
GElf_Sym sym;
122-
Elf_Scn *sym_scn;
123-
GElf_Shdr sym_sh;
124-
125-
if (!gelf_getsym(symbols, idx, &sym))
126-
continue;
127-
128-
if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
129-
continue;
130-
131-
sname = elf_strptr(elf, strtabidx, sym.st_name);
132-
if (!sname)
133-
continue;
134-
135-
curr_bind = GELF_ST_BIND(sym.st_info);
136202

203+
while ((sym = elf_sym_iter_next(&iter))) {
137204
/* User can specify func, func@@LIB or func@@LIB_VERSION. */
138-
if (strncmp(sname, name, name_len) != 0)
205+
if (strncmp(sym->name, name, name_len) != 0)
139206
continue;
140207
/* ...but we don't want a search for "foo" to match 'foo2" also, so any
141208
* additional characters in sname should be of the form "@@LIB".
142209
*/
143-
if (!is_name_qualified && sname[name_len] != '\0' && sname[name_len] != '@')
210+
if (!is_name_qualified && sym->name[name_len] != '\0' && sym->name[name_len] != '@')
144211
continue;
145212

146-
if (ret >= 0) {
213+
cur_bind = GELF_ST_BIND(sym->sym.st_info);
214+
215+
if (ret > 0) {
147216
/* handle multiple matches */
148-
if (last_bind != STB_WEAK && curr_bind != STB_WEAK) {
217+
if (last_bind != STB_WEAK && cur_bind != STB_WEAK) {
149218
/* Only accept one non-weak bind. */
150219
pr_warn("elf: ambiguous match for '%s', '%s' in '%s'\n",
151-
sname, name, binary_path);
220+
sym->name, name, binary_path);
152221
ret = -LIBBPF_ERRNO__FORMAT;
153222
goto out;
154-
} else if (curr_bind == STB_WEAK) {
223+
} else if (cur_bind == STB_WEAK) {
155224
/* already have a non-weak bind, and
156225
* this is a weak bind, so ignore.
157226
*/
158227
continue;
159228
}
160229
}
161230

162-
/* Transform symbol's virtual address (absolute for
163-
* binaries and relative for shared libs) into file
164-
* offset, which is what kernel is expecting for
165-
* uprobe/uretprobe attachment.
166-
* See Documentation/trace/uprobetracer.rst for more
167-
* details.
168-
* This is done by looking up symbol's containing
169-
* section's header and using it's virtual address
170-
* (sh_addr) and corresponding file offset (sh_offset)
171-
* to transform sym.st_value (virtual address) into
172-
* desired final file offset.
173-
*/
174-
sym_scn = elf_getscn(elf, sym.st_shndx);
175-
if (!sym_scn)
176-
continue;
177-
if (!gelf_getshdr(sym_scn, &sym_sh))
178-
continue;
179-
180-
ret = sym.st_value - sym_sh.sh_addr + sym_sh.sh_offset;
181-
last_bind = curr_bind;
231+
ret = elf_sym_offset(sym);
232+
last_bind = cur_bind;
182233
}
183234
if (ret > 0)
184235
break;

0 commit comments

Comments
 (0)