Skip to content

Commit 5c74272

Browse files
olsajiriAlexei Starovoitov
authored andcommitted
libbpf: Move elf_find_func_offset* functions to elf object
Adding new elf object that will contain elf related functions. There's no functional change. Suggested-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Jiri Olsa <jolsa@kernel.org> Link: https://lore.kernel.org/r/20230809083440.3209381-9-jolsa@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 8097e46 commit 5c74272

4 files changed

Lines changed: 202 additions & 186 deletions

File tree

tools/lib/bpf/Build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o \
22
netlink.o bpf_prog_linfo.o libbpf_probes.o hashmap.o \
33
btf_dump.o ringbuf.o strset.o linker.o gen_loader.o relo_core.o \
4-
usdt.o zip.o
4+
usdt.o zip.o elf.o

tools/lib/bpf/elf.c

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

tools/lib/bpf/libbpf.c

Lines changed: 0 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -11004,191 +11004,6 @@ static int perf_event_uprobe_open_legacy(const char *probe_name, bool retprobe,
1100411004
return err;
1100511005
}
1100611006

11007-
/* Return next ELF section of sh_type after scn, or first of that type if scn is NULL. */
11008-
static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn)
11009-
{
11010-
while ((scn = elf_nextscn(elf, scn)) != NULL) {
11011-
GElf_Shdr sh;
11012-
11013-
if (!gelf_getshdr(scn, &sh))
11014-
continue;
11015-
if (sh.sh_type == sh_type)
11016-
return scn;
11017-
}
11018-
return NULL;
11019-
}
11020-
11021-
/* Find offset of function name in the provided ELF object. "binary_path" is
11022-
* the path to the ELF binary represented by "elf", and only used for error
11023-
* reporting matters. "name" matches symbol name or name@@LIB for library
11024-
* functions.
11025-
*/
11026-
static long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
11027-
{
11028-
int i, sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
11029-
bool is_shared_lib, is_name_qualified;
11030-
long ret = -ENOENT;
11031-
size_t name_len;
11032-
GElf_Ehdr ehdr;
11033-
11034-
if (!gelf_getehdr(elf, &ehdr)) {
11035-
pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1));
11036-
ret = -LIBBPF_ERRNO__FORMAT;
11037-
goto out;
11038-
}
11039-
/* for shared lib case, we do not need to calculate relative offset */
11040-
is_shared_lib = ehdr.e_type == ET_DYN;
11041-
11042-
name_len = strlen(name);
11043-
/* Does name specify "@@LIB"? */
11044-
is_name_qualified = strstr(name, "@@") != NULL;
11045-
11046-
/* Search SHT_DYNSYM, SHT_SYMTAB for symbol. This search order is used because if
11047-
* a binary is stripped, it may only have SHT_DYNSYM, and a fully-statically
11048-
* linked binary may not have SHT_DYMSYM, so absence of a section should not be
11049-
* reported as a warning/error.
11050-
*/
11051-
for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
11052-
size_t nr_syms, strtabidx, idx;
11053-
Elf_Data *symbols = NULL;
11054-
Elf_Scn *scn = NULL;
11055-
int last_bind = -1;
11056-
const char *sname;
11057-
GElf_Shdr sh;
11058-
11059-
scn = elf_find_next_scn_by_type(elf, sh_types[i], NULL);
11060-
if (!scn) {
11061-
pr_debug("elf: failed to find symbol table ELF sections in '%s'\n",
11062-
binary_path);
11063-
continue;
11064-
}
11065-
if (!gelf_getshdr(scn, &sh))
11066-
continue;
11067-
strtabidx = sh.sh_link;
11068-
symbols = elf_getdata(scn, 0);
11069-
if (!symbols) {
11070-
pr_warn("elf: failed to get symbols for symtab section in '%s': %s\n",
11071-
binary_path, elf_errmsg(-1));
11072-
ret = -LIBBPF_ERRNO__FORMAT;
11073-
goto out;
11074-
}
11075-
nr_syms = symbols->d_size / sh.sh_entsize;
11076-
11077-
for (idx = 0; idx < nr_syms; idx++) {
11078-
int curr_bind;
11079-
GElf_Sym sym;
11080-
Elf_Scn *sym_scn;
11081-
GElf_Shdr sym_sh;
11082-
11083-
if (!gelf_getsym(symbols, idx, &sym))
11084-
continue;
11085-
11086-
if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
11087-
continue;
11088-
11089-
sname = elf_strptr(elf, strtabidx, sym.st_name);
11090-
if (!sname)
11091-
continue;
11092-
11093-
curr_bind = GELF_ST_BIND(sym.st_info);
11094-
11095-
/* User can specify func, func@@LIB or func@@LIB_VERSION. */
11096-
if (strncmp(sname, name, name_len) != 0)
11097-
continue;
11098-
/* ...but we don't want a search for "foo" to match 'foo2" also, so any
11099-
* additional characters in sname should be of the form "@@LIB".
11100-
*/
11101-
if (!is_name_qualified && sname[name_len] != '\0' && sname[name_len] != '@')
11102-
continue;
11103-
11104-
if (ret >= 0) {
11105-
/* handle multiple matches */
11106-
if (last_bind != STB_WEAK && curr_bind != STB_WEAK) {
11107-
/* Only accept one non-weak bind. */
11108-
pr_warn("elf: ambiguous match for '%s', '%s' in '%s'\n",
11109-
sname, name, binary_path);
11110-
ret = -LIBBPF_ERRNO__FORMAT;
11111-
goto out;
11112-
} else if (curr_bind == STB_WEAK) {
11113-
/* already have a non-weak bind, and
11114-
* this is a weak bind, so ignore.
11115-
*/
11116-
continue;
11117-
}
11118-
}
11119-
11120-
/* Transform symbol's virtual address (absolute for
11121-
* binaries and relative for shared libs) into file
11122-
* offset, which is what kernel is expecting for
11123-
* uprobe/uretprobe attachment.
11124-
* See Documentation/trace/uprobetracer.rst for more
11125-
* details.
11126-
* This is done by looking up symbol's containing
11127-
* section's header and using it's virtual address
11128-
* (sh_addr) and corresponding file offset (sh_offset)
11129-
* to transform sym.st_value (virtual address) into
11130-
* desired final file offset.
11131-
*/
11132-
sym_scn = elf_getscn(elf, sym.st_shndx);
11133-
if (!sym_scn)
11134-
continue;
11135-
if (!gelf_getshdr(sym_scn, &sym_sh))
11136-
continue;
11137-
11138-
ret = sym.st_value - sym_sh.sh_addr + sym_sh.sh_offset;
11139-
last_bind = curr_bind;
11140-
}
11141-
if (ret > 0)
11142-
break;
11143-
}
11144-
11145-
if (ret > 0) {
11146-
pr_debug("elf: symbol address match for '%s' in '%s': 0x%lx\n", name, binary_path,
11147-
ret);
11148-
} else {
11149-
if (ret == 0) {
11150-
pr_warn("elf: '%s' is 0 in symtab for '%s': %s\n", name, binary_path,
11151-
is_shared_lib ? "should not be 0 in a shared library" :
11152-
"try using shared library path instead");
11153-
ret = -ENOENT;
11154-
} else {
11155-
pr_warn("elf: failed to find symbol '%s' in '%s'\n", name, binary_path);
11156-
}
11157-
}
11158-
out:
11159-
return ret;
11160-
}
11161-
11162-
/* Find offset of function name in ELF object specified by path. "name" matches
11163-
* symbol name or name@@LIB for library functions.
11164-
*/
11165-
static long elf_find_func_offset_from_file(const char *binary_path, const char *name)
11166-
{
11167-
char errmsg[STRERR_BUFSIZE];
11168-
long ret = -ENOENT;
11169-
Elf *elf;
11170-
int fd;
11171-
11172-
fd = open(binary_path, O_RDONLY | O_CLOEXEC);
11173-
if (fd < 0) {
11174-
ret = -errno;
11175-
pr_warn("failed to open %s: %s\n", binary_path,
11176-
libbpf_strerror_r(ret, errmsg, sizeof(errmsg)));
11177-
return ret;
11178-
}
11179-
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
11180-
if (!elf) {
11181-
pr_warn("elf: could not read elf from %s: %s\n", binary_path, elf_errmsg(-1));
11182-
close(fd);
11183-
return -LIBBPF_ERRNO__FORMAT;
11184-
}
11185-
11186-
ret = elf_find_func_offset(elf, binary_path, name);
11187-
elf_end(elf);
11188-
close(fd);
11189-
return ret;
11190-
}
11191-
1119211007
/* Find offset of function name in archive specified by path. Currently
1119311008
* supported are .zip files that do not compress their contents, as used on
1119411009
* Android in the form of APKs, for example. "file_name" is the name of the ELF

0 commit comments

Comments
 (0)