Skip to content

Commit 1f8e2bc

Browse files
anakryikoAlexei Starovoitov
authored and
Alexei Starovoitov
committed
libbpf: Refactor relocation handling
Relocation handling code is convoluted and unnecessarily deeply nested. Split out per-relocation logic into separate function. Also refactor the logic to be more a sequence of per-relocation type checks and processing steps, making it simpler to follow control flow. This makes it easier to further extends it to new kinds of relocations (e.g., support for extern variables). This patch also makes relocation's section verification more robust. Previously relocations against not yet supported externs were silently ignored because of obj->efile.text_shndx was zero, when all BPF programs had custom section names and there was no .text section. Also, invalid LDIMM64 relocations against non-map sections were passed through, if they were pointing to a .text section (or 0, which is invalid section). All these bugs are fixed within this refactoring and checks are made more appropriate for each type of relocation. Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent ffc8817 commit 1f8e2bc

File tree

1 file changed

+143
-118
lines changed

1 file changed

+143
-118
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 143 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,8 @@ struct bpf_object {
276276
struct {
277277
GElf_Shdr shdr;
278278
Elf_Data *data;
279-
} *reloc;
280-
int nr_reloc;
279+
} *reloc_sects;
280+
int nr_reloc_sects;
281281
int maps_shndx;
282282
int btf_maps_shndx;
283283
int text_shndx;
@@ -575,8 +575,8 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
575575
obj->efile.rodata = NULL;
576576
obj->efile.bss = NULL;
577577

578-
zfree(&obj->efile.reloc);
579-
obj->efile.nr_reloc = 0;
578+
zfree(&obj->efile.reloc_sects);
579+
obj->efile.nr_reloc_sects = 0;
580580
zclose(obj->efile.fd);
581581
obj->efile.obj_buf = NULL;
582582
obj->efile.obj_buf_sz = 0;
@@ -1693,8 +1693,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj, bool relaxed_maps,
16931693
pr_debug("skip section(%d) %s\n", idx, name);
16941694
}
16951695
} else if (sh.sh_type == SHT_REL) {
1696-
int nr_reloc = obj->efile.nr_reloc;
1697-
void *reloc = obj->efile.reloc;
1696+
int nr_sects = obj->efile.nr_reloc_sects;
1697+
void *sects = obj->efile.reloc_sects;
16981698
int sec = sh.sh_info; /* points to other section */
16991699

17001700
/* Only do relo for section with exec instructions */
@@ -1704,18 +1704,18 @@ static int bpf_object__elf_collect(struct bpf_object *obj, bool relaxed_maps,
17041704
continue;
17051705
}
17061706

1707-
reloc = reallocarray(reloc, nr_reloc + 1,
1708-
sizeof(*obj->efile.reloc));
1709-
if (!reloc) {
1710-
pr_warn("realloc failed\n");
1707+
sects = reallocarray(sects, nr_sects + 1,
1708+
sizeof(*obj->efile.reloc_sects));
1709+
if (!sects) {
1710+
pr_warn("reloc_sects realloc failed\n");
17111711
return -ENOMEM;
17121712
}
17131713

1714-
obj->efile.reloc = reloc;
1715-
obj->efile.nr_reloc++;
1714+
obj->efile.reloc_sects = sects;
1715+
obj->efile.nr_reloc_sects++;
17161716

1717-
obj->efile.reloc[nr_reloc].shdr = sh;
1718-
obj->efile.reloc[nr_reloc].data = data;
1717+
obj->efile.reloc_sects[nr_sects].shdr = sh;
1718+
obj->efile.reloc_sects[nr_sects].data = data;
17191719
} else if (sh.sh_type == SHT_NOBITS && strcmp(name, ".bss") == 0) {
17201720
obj->efile.bss = data;
17211721
obj->efile.bss_shndx = idx;
@@ -1780,14 +1780,6 @@ static bool bpf_object__shndx_is_maps(const struct bpf_object *obj,
17801780
shndx == obj->efile.btf_maps_shndx;
17811781
}
17821782

1783-
static bool bpf_object__relo_in_known_section(const struct bpf_object *obj,
1784-
int shndx)
1785-
{
1786-
return shndx == obj->efile.text_shndx ||
1787-
bpf_object__shndx_is_maps(obj, shndx) ||
1788-
bpf_object__shndx_is_data(obj, shndx);
1789-
}
1790-
17911783
static enum libbpf_map_type
17921784
bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx)
17931785
{
@@ -1801,14 +1793,124 @@ bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx)
18011793
return LIBBPF_MAP_UNSPEC;
18021794
}
18031795

1796+
static int bpf_program__record_reloc(struct bpf_program *prog,
1797+
struct reloc_desc *reloc_desc,
1798+
__u32 insn_idx, const char *name,
1799+
const GElf_Sym *sym, const GElf_Rel *rel)
1800+
{
1801+
struct bpf_insn *insn = &prog->insns[insn_idx];
1802+
size_t map_idx, nr_maps = prog->obj->nr_maps;
1803+
struct bpf_object *obj = prog->obj;
1804+
__u32 shdr_idx = sym->st_shndx;
1805+
enum libbpf_map_type type;
1806+
struct bpf_map *map;
1807+
1808+
/* sub-program call relocation */
1809+
if (insn->code == (BPF_JMP | BPF_CALL)) {
1810+
if (insn->src_reg != BPF_PSEUDO_CALL) {
1811+
pr_warn("incorrect bpf_call opcode\n");
1812+
return -LIBBPF_ERRNO__RELOC;
1813+
}
1814+
/* text_shndx can be 0, if no default "main" program exists */
1815+
if (!shdr_idx || shdr_idx != obj->efile.text_shndx) {
1816+
pr_warn("bad call relo against section %u\n", shdr_idx);
1817+
return -LIBBPF_ERRNO__RELOC;
1818+
}
1819+
if (sym->st_value % 8) {
1820+
pr_warn("bad call relo offset: %lu\n", sym->st_value);
1821+
return -LIBBPF_ERRNO__RELOC;
1822+
}
1823+
reloc_desc->type = RELO_CALL;
1824+
reloc_desc->insn_idx = insn_idx;
1825+
reloc_desc->text_off = sym->st_value / 8;
1826+
obj->has_pseudo_calls = true;
1827+
return 0;
1828+
}
1829+
1830+
if (insn->code != (BPF_LD | BPF_IMM | BPF_DW)) {
1831+
pr_warn("bpf: relocation: invalid relo for insns[%d].code 0x%x\n",
1832+
insn_idx, insn->code);
1833+
return -LIBBPF_ERRNO__RELOC;
1834+
}
1835+
if (!shdr_idx || shdr_idx >= SHN_LORESERVE) {
1836+
pr_warn("relocation: not yet supported relo for non-static global \'%s\' variable in special section (0x%x) found in insns[%d].code 0x%x\n",
1837+
name, shdr_idx, insn_idx, insn->code);
1838+
return -LIBBPF_ERRNO__RELOC;
1839+
}
1840+
1841+
type = bpf_object__section_to_libbpf_map_type(obj, shdr_idx);
1842+
1843+
/* generic map reference relocation */
1844+
if (type == LIBBPF_MAP_UNSPEC) {
1845+
if (!bpf_object__shndx_is_maps(obj, shdr_idx)) {
1846+
pr_warn("bad map relo against section %u\n",
1847+
shdr_idx);
1848+
return -LIBBPF_ERRNO__RELOC;
1849+
}
1850+
for (map_idx = 0; map_idx < nr_maps; map_idx++) {
1851+
map = &obj->maps[map_idx];
1852+
if (map->libbpf_type != type ||
1853+
map->sec_idx != sym->st_shndx ||
1854+
map->sec_offset != sym->st_value)
1855+
continue;
1856+
pr_debug("found map %zd (%s, sec %d, off %zu) for insn %u\n",
1857+
map_idx, map->name, map->sec_idx,
1858+
map->sec_offset, insn_idx);
1859+
break;
1860+
}
1861+
if (map_idx >= nr_maps) {
1862+
pr_warn("map relo failed to find map for sec %u, off %llu\n",
1863+
shdr_idx, (__u64)sym->st_value);
1864+
return -LIBBPF_ERRNO__RELOC;
1865+
}
1866+
reloc_desc->type = RELO_LD64;
1867+
reloc_desc->insn_idx = insn_idx;
1868+
reloc_desc->map_idx = map_idx;
1869+
return 0;
1870+
}
1871+
1872+
/* global data map relocation */
1873+
if (!bpf_object__shndx_is_data(obj, shdr_idx)) {
1874+
pr_warn("bad data relo against section %u\n", shdr_idx);
1875+
return -LIBBPF_ERRNO__RELOC;
1876+
}
1877+
if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL) {
1878+
pr_warn("relocation: not yet supported relo for non-static global \'%s\' variable found in insns[%d].code 0x%x\n",
1879+
name, insn_idx, insn->code);
1880+
return -LIBBPF_ERRNO__RELOC;
1881+
}
1882+
if (!obj->caps.global_data) {
1883+
pr_warn("relocation: kernel does not support global \'%s\' variable access in insns[%d]\n",
1884+
name, insn_idx);
1885+
return -LIBBPF_ERRNO__RELOC;
1886+
}
1887+
for (map_idx = 0; map_idx < nr_maps; map_idx++) {
1888+
map = &obj->maps[map_idx];
1889+
if (map->libbpf_type != type)
1890+
continue;
1891+
pr_debug("found data map %zd (%s, sec %d, off %zu) for insn %u\n",
1892+
map_idx, map->name, map->sec_idx, map->sec_offset,
1893+
insn_idx);
1894+
break;
1895+
}
1896+
if (map_idx >= nr_maps) {
1897+
pr_warn("data relo failed to find map for sec %u\n",
1898+
shdr_idx);
1899+
return -LIBBPF_ERRNO__RELOC;
1900+
}
1901+
1902+
reloc_desc->type = RELO_DATA;
1903+
reloc_desc->insn_idx = insn_idx;
1904+
reloc_desc->map_idx = map_idx;
1905+
return 0;
1906+
}
1907+
18041908
static int
18051909
bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
18061910
Elf_Data *data, struct bpf_object *obj)
18071911
{
18081912
Elf_Data *symbols = obj->efile.symbols;
1809-
struct bpf_map *maps = obj->maps;
1810-
size_t nr_maps = obj->nr_maps;
1811-
int i, nrels;
1913+
int err, i, nrels;
18121914

18131915
pr_debug("collecting relocating info for: '%s'\n", prog->section_name);
18141916
nrels = shdr->sh_size / shdr->sh_entsize;
@@ -1821,114 +1923,37 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
18211923
prog->nr_reloc = nrels;
18221924

18231925
for (i = 0; i < nrels; i++) {
1824-
struct bpf_insn *insns = prog->insns;
1825-
enum libbpf_map_type type;
1826-
unsigned int insn_idx;
1827-
unsigned int shdr_idx;
18281926
const char *name;
1829-
size_t map_idx;
1927+
__u32 insn_idx;
18301928
GElf_Sym sym;
18311929
GElf_Rel rel;
18321930

18331931
if (!gelf_getrel(data, i, &rel)) {
18341932
pr_warn("relocation: failed to get %d reloc\n", i);
18351933
return -LIBBPF_ERRNO__FORMAT;
18361934
}
1837-
18381935
if (!gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym)) {
18391936
pr_warn("relocation: symbol %"PRIx64" not found\n",
18401937
GELF_R_SYM(rel.r_info));
18411938
return -LIBBPF_ERRNO__FORMAT;
18421939
}
1940+
if (rel.r_offset % sizeof(struct bpf_insn))
1941+
return -LIBBPF_ERRNO__FORMAT;
18431942

1943+
insn_idx = rel.r_offset / sizeof(struct bpf_insn);
18441944
name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
18451945
sym.st_name) ? : "<?>";
18461946

1847-
pr_debug("relo for %lld value %lld name %d (\'%s\')\n",
1848-
(long long) (rel.r_info >> 32),
1849-
(long long) sym.st_value, sym.st_name, name);
1850-
1851-
shdr_idx = sym.st_shndx;
1852-
insn_idx = rel.r_offset / sizeof(struct bpf_insn);
1853-
pr_debug("relocation: insn_idx=%u, shdr_idx=%u\n",
1854-
insn_idx, shdr_idx);
1855-
1856-
if (shdr_idx >= SHN_LORESERVE) {
1857-
pr_warn("relocation: not yet supported relo for non-static global \'%s\' variable in special section (0x%x) found in insns[%d].code 0x%x\n",
1858-
name, shdr_idx, insn_idx,
1859-
insns[insn_idx].code);
1860-
return -LIBBPF_ERRNO__RELOC;
1861-
}
1862-
if (!bpf_object__relo_in_known_section(obj, shdr_idx)) {
1863-
pr_warn("Program '%s' contains unrecognized relo data pointing to section %u\n",
1864-
prog->section_name, shdr_idx);
1865-
return -LIBBPF_ERRNO__RELOC;
1866-
}
1867-
1868-
if (insns[insn_idx].code == (BPF_JMP | BPF_CALL)) {
1869-
if (insns[insn_idx].src_reg != BPF_PSEUDO_CALL) {
1870-
pr_warn("incorrect bpf_call opcode\n");
1871-
return -LIBBPF_ERRNO__RELOC;
1872-
}
1873-
if (sym.st_value % 8) {
1874-
pr_warn("bad call relo offset: %lu\n", sym.st_value);
1875-
return -LIBBPF_ERRNO__RELOC;
1876-
}
1877-
prog->reloc_desc[i].type = RELO_CALL;
1878-
prog->reloc_desc[i].insn_idx = insn_idx;
1879-
prog->reloc_desc[i].text_off = sym.st_value / 8;
1880-
obj->has_pseudo_calls = true;
1881-
continue;
1882-
}
1883-
1884-
if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
1885-
pr_warn("bpf: relocation: invalid relo for insns[%d].code 0x%x\n",
1886-
insn_idx, insns[insn_idx].code);
1887-
return -LIBBPF_ERRNO__RELOC;
1888-
}
1889-
1890-
if (bpf_object__shndx_is_maps(obj, shdr_idx) ||
1891-
bpf_object__shndx_is_data(obj, shdr_idx)) {
1892-
type = bpf_object__section_to_libbpf_map_type(obj, shdr_idx);
1893-
if (type != LIBBPF_MAP_UNSPEC) {
1894-
if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL) {
1895-
pr_warn("bpf: relocation: not yet supported relo for non-static global \'%s\' variable found in insns[%d].code 0x%x\n",
1896-
name, insn_idx, insns[insn_idx].code);
1897-
return -LIBBPF_ERRNO__RELOC;
1898-
}
1899-
if (!obj->caps.global_data) {
1900-
pr_warn("bpf: relocation: kernel does not support global \'%s\' variable access in insns[%d]\n",
1901-
name, insn_idx);
1902-
return -LIBBPF_ERRNO__RELOC;
1903-
}
1904-
}
1905-
1906-
for (map_idx = 0; map_idx < nr_maps; map_idx++) {
1907-
if (maps[map_idx].libbpf_type != type)
1908-
continue;
1909-
if (type != LIBBPF_MAP_UNSPEC ||
1910-
(maps[map_idx].sec_idx == sym.st_shndx &&
1911-
maps[map_idx].sec_offset == sym.st_value)) {
1912-
pr_debug("relocation: found map %zd (%s, sec_idx %d, offset %zu) for insn %u\n",
1913-
map_idx, maps[map_idx].name,
1914-
maps[map_idx].sec_idx,
1915-
maps[map_idx].sec_offset,
1916-
insn_idx);
1917-
break;
1918-
}
1919-
}
1920-
1921-
if (map_idx >= nr_maps) {
1922-
pr_warn("bpf relocation: map_idx %d larger than %d\n",
1923-
(int)map_idx, (int)nr_maps - 1);
1924-
return -LIBBPF_ERRNO__RELOC;
1925-
}
1947+
pr_debug("relo for shdr %u, symb %llu, value %llu, type %d, bind %d, name %d (\'%s\'), insn %u\n",
1948+
(__u32)sym.st_shndx, (__u64)GELF_R_SYM(rel.r_info),
1949+
(__u64)sym.st_value, GELF_ST_TYPE(sym.st_info),
1950+
GELF_ST_BIND(sym.st_info), sym.st_name, name,
1951+
insn_idx);
19261952

1927-
prog->reloc_desc[i].type = type != LIBBPF_MAP_UNSPEC ?
1928-
RELO_DATA : RELO_LD64;
1929-
prog->reloc_desc[i].insn_idx = insn_idx;
1930-
prog->reloc_desc[i].map_idx = map_idx;
1931-
}
1953+
err = bpf_program__record_reloc(prog, &prog->reloc_desc[i],
1954+
insn_idx, name, &sym, &rel);
1955+
if (err)
1956+
return err;
19321957
}
19331958
return 0;
19341959
}
@@ -3671,9 +3696,9 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
36713696
return -LIBBPF_ERRNO__INTERNAL;
36723697
}
36733698

3674-
for (i = 0; i < obj->efile.nr_reloc; i++) {
3675-
GElf_Shdr *shdr = &obj->efile.reloc[i].shdr;
3676-
Elf_Data *data = obj->efile.reloc[i].data;
3699+
for (i = 0; i < obj->efile.nr_reloc_sects; i++) {
3700+
GElf_Shdr *shdr = &obj->efile.reloc_sects[i].shdr;
3701+
Elf_Data *data = obj->efile.reloc_sects[i].data;
36773702
int idx = shdr->sh_info;
36783703
struct bpf_program *prog;
36793704

0 commit comments

Comments
 (0)