An object that carries a compiler-emitted exception cleanup table cannot be linked today. The table's fields are byte offsets into a code section, materialised by a 32-bit relocation against that section's symbol with the offset itself as the implicit addend, and the linker rejects both halves of that: the relocation type is not in the list it accepts, and a relocation against an STT_SECTION symbol from a non-executable section is an outright error. Both spellings of that relocation have to be taken. LLVM emits R_BPF_64_NODYLD32 for a .long against a section symbol; GNU as emits R_BPF_64_ABS32, which is what bpf_reloc_type_lookup() maps BFD_RELOC_32 to. They describe the same value, and the selftests are built with both compilers. Keying on the relocation type rather than the section name means any non-executable section can reach the new arm, where an unconditional refusal used to stand. That refusal was covering two things the arm now has to do itself: a target may be SHT_NOBITS, which extend_sec() leaves with no raw_data, and r_offset is alignment-checked only where the section holds instructions. The arm rejects both, and bounds the offset against the section size before writing through it. Signed-off-by: Yonghong Song --- tools/lib/bpf/linker.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/linker.c b/tools/lib/bpf/linker.c index 53f64a1a1f25..26a5e53df6f0 100644 --- a/tools/lib/bpf/linker.c +++ b/tools/lib/bpf/linker.c @@ -1036,7 +1036,8 @@ static int linker_sanity_check_elf_relos(struct src_obj *obj, struct src_sec *se size_t sym_type = ELF64_R_TYPE(relo->r_info); if (sym_type != R_BPF_64_64 && sym_type != R_BPF_64_32 && - sym_type != R_BPF_64_ABS64 && sym_type != R_BPF_64_ABS32) { + sym_type != R_BPF_64_ABS64 && sym_type != R_BPF_64_ABS32 && + sym_type != R_BPF_64_NODYLD32) { pr_warn("ELF relo #%d in section #%zu has unexpected type %zu in %s\n", i, sec->sec_idx, sym_type, obj->filename); return -EINVAL; @@ -2258,6 +2259,7 @@ static int linker_append_elf_relos(struct bpf_linker *linker, struct src_obj *ob if (ELF64_ST_TYPE(src_sym->st_info) == STT_SECTION) { struct src_sec *sec = &obj->secs[src_sym->st_shndx]; struct bpf_insn *insn; + __u32 *val; if (src_linked_sec->shdr->sh_flags & SHF_EXECINSTR) { /* calls to the very first static function inside @@ -2292,6 +2294,38 @@ static int linker_append_elf_relos(struct bpf_linker *linker, struct src_obj *ob if (linker->swapped_endian) off = bswap_64(off); memcpy(ptr, &off, sizeof(off)); + } else if (sym_type == R_BPF_64_NODYLD32 || + sym_type == R_BPF_64_ABS32) { + /* + * A byte offset into a code section, + * stored in place. LLVM spells this + * relocation NODYLD32 and GNU as + * spells it ABS32; being bytes, the + * section's new start goes in as it + * is, not scaled the way a call's + * instruction index is above. + * + * r_offset is checked only for an + * executable section, and SHT_NOBITS + * has no raw_data, so bound it here -- + * subtracting, so it cannot wrap. + */ + if (!dst_linked_sec->raw_data || + dst_linked_sec->sec_sz < (int)sizeof(*val) || + dst_rel->r_offset % sizeof(*val) || + dst_rel->r_offset > + (size_t)dst_linked_sec->sec_sz - sizeof(*val)) { + pr_warn("ELF relo #%d in section #%zu points outside the data of section '%s' in %s\n", + j, src_sec->sec_idx, + dst_linked_sec->sec_name, + obj->filename); + return -EINVAL; + } + val = dst_linked_sec->raw_data + dst_rel->r_offset; + if (linker->swapped_endian) + *val = bswap_32(bswap_32(*val) + sec->dst_off); + else + *val += sec->dst_off; } else { pr_warn("relocation against STT_SECTION in non-exec section is not supported!\n"); return -EINVAL; -- 2.53.0-Meta