int bpf_core_apply_relo_insn()

in compile/remote-compile/lbc/libbpf/src/relo_core.c [1145:1295]


int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn,
			     int insn_idx,
			     const struct bpf_core_relo *relo,
			     int relo_idx,
			     const struct btf *local_btf,
			     struct bpf_core_cand_list *cands)
{
	struct bpf_core_spec local_spec, cand_spec, targ_spec = {};
	struct bpf_core_relo_res cand_res, targ_res;
	const struct btf_type *local_type;
	const char *local_name;
	__u32 local_id;
	const char *spec_str;
	int i, j, err;

	local_id = relo->type_id;
	local_type = btf__type_by_id(local_btf, local_id);
	if (!local_type)
		return -EINVAL;

	local_name = btf__name_by_offset(local_btf, local_type->name_off);
	if (!local_name)
		return -EINVAL;

	spec_str = btf__name_by_offset(local_btf, relo->access_str_off);
	if (str_is_empty(spec_str))
		return -EINVAL;

	err = bpf_core_parse_spec(local_btf, local_id, spec_str, relo->kind, &local_spec);
	if (err) {
		pr_warn("prog '%s': relo #%d: parsing [%d] %s %s + %s failed: %d\n",
			prog_name, relo_idx, local_id, btf_kind_str(local_type),
			str_is_empty(local_name) ? "<anon>" : local_name,
			spec_str, err);
		return -EINVAL;
	}

	pr_debug("prog '%s': relo #%d: kind <%s> (%d), spec is ", prog_name,
		 relo_idx, core_relo_kind_str(relo->kind), relo->kind);
	bpf_core_dump_spec(LIBBPF_DEBUG, &local_spec);
	libbpf_print(LIBBPF_DEBUG, "\n");

	/* TYPE_ID_LOCAL relo is special and doesn't need candidate search */
	if (relo->kind == BPF_TYPE_ID_LOCAL) {
		targ_res.validate = true;
		targ_res.poison = false;
		targ_res.orig_val = local_spec.root_type_id;
		targ_res.new_val = local_spec.root_type_id;
		goto patch_insn;
	}

	/* libbpf doesn't support candidate search for anonymous types */
	if (str_is_empty(spec_str)) {
		pr_warn("prog '%s': relo #%d: <%s> (%d) relocation doesn't support anonymous types\n",
			prog_name, relo_idx, core_relo_kind_str(relo->kind), relo->kind);
		return -EOPNOTSUPP;
	}


	for (i = 0, j = 0; i < cands->len; i++) {
		err = bpf_core_spec_match(&local_spec, cands->cands[i].btf,
					  cands->cands[i].id, &cand_spec);
		if (err < 0) {
			pr_warn("prog '%s': relo #%d: error matching candidate #%d ",
				prog_name, relo_idx, i);
			bpf_core_dump_spec(LIBBPF_WARN, &cand_spec);
			libbpf_print(LIBBPF_WARN, ": %d\n", err);
			return err;
		}

		pr_debug("prog '%s': relo #%d: %s candidate #%d ", prog_name,
			 relo_idx, err == 0 ? "non-matching" : "matching", i);
		bpf_core_dump_spec(LIBBPF_DEBUG, &cand_spec);
		libbpf_print(LIBBPF_DEBUG, "\n");

		if (err == 0)
			continue;

		err = bpf_core_calc_relo(prog_name, relo, relo_idx, &local_spec, &cand_spec, &cand_res);
		if (err)
			return err;

		if (j == 0) {
			targ_res = cand_res;
			targ_spec = cand_spec;
		} else if (cand_spec.bit_offset != targ_spec.bit_offset) {
			/* if there are many field relo candidates, they
			 * should all resolve to the same bit offset
			 */
			pr_warn("prog '%s': relo #%d: field offset ambiguity: %u != %u\n",
				prog_name, relo_idx, cand_spec.bit_offset,
				targ_spec.bit_offset);
			return -EINVAL;
		} else if (cand_res.poison != targ_res.poison || cand_res.new_val != targ_res.new_val) {
			/* all candidates should result in the same relocation
			 * decision and value, otherwise it's dangerous to
			 * proceed due to ambiguity
			 */
			pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %u != %s %u\n",
				prog_name, relo_idx,
				cand_res.poison ? "failure" : "success", cand_res.new_val,
				targ_res.poison ? "failure" : "success", targ_res.new_val);
			return -EINVAL;
		}

		cands->cands[j++] = cands->cands[i];
	}

	/*
	 * For BPF_FIELD_EXISTS relo or when used BPF program has field
	 * existence checks or kernel version/config checks, it's expected
	 * that we might not find any candidates. In this case, if field
	 * wasn't found in any candidate, the list of candidates shouldn't
	 * change at all, we'll just handle relocating appropriately,
	 * depending on relo's kind.
	 */
	if (j > 0)
		cands->len = j;

	/*
	 * If no candidates were found, it might be both a programmer error,
	 * as well as expected case, depending whether instruction w/
	 * relocation is guarded in some way that makes it unreachable (dead
	 * code) if relocation can't be resolved. This is handled in
	 * bpf_core_patch_insn() uniformly by replacing that instruction with
	 * BPF helper call insn (using invalid helper ID). If that instruction
	 * is indeed unreachable, then it will be ignored and eliminated by
	 * verifier. If it was an error, then verifier will complain and point
	 * to a specific instruction number in its log.
	 */
	if (j == 0) {
		pr_debug("prog '%s': relo #%d: no matching targets found\n",
			 prog_name, relo_idx);

		/* calculate single target relo result explicitly */
		err = bpf_core_calc_relo(prog_name, relo, relo_idx, &local_spec, NULL, &targ_res);
		if (err)
			return err;
	}

patch_insn:
	/* bpf_core_patch_insn() should know how to handle missing targ_spec */
	err = bpf_core_patch_insn(prog_name, insn, insn_idx, relo, relo_idx, &targ_res);
	if (err) {
		pr_warn("prog '%s': relo #%d: failed to patch insn #%u: %d\n",
			prog_name, relo_idx, relo->insn_off / 8, err);
		return -EINVAL;
	}

	return 0;
}