Sys-Ebpf

 view release on metacpan or  search on metacpan

lib/Sys/Ebpf/Loader.pm  view on Meta::CPAN

    };

    return $self->load_bpf_program($bpf_attrs);
}

sub load_bpf_program {
    my ( $self, $bpf_attrs ) = @_;

    my $defaults = {
        prog_type    => 0,
        insn_cnt     => 0,
        insns        => "",
        license      => "",
        log_level    => 1,
        log_size     => 1024 * 10,
        log_buf      => "\0" x ( 1024 * 10 ),
        kern_version => 0,
        prog_flags   => 0,
    };
    my $attrs = { %$defaults, %$bpf_attrs, };

    # bpf_attr構造体のパック
    my $attr = pack(
        "L L Q Q L L Q L L",
        $attrs->{prog_type},
        $attrs->{insn_cnt},
        unpack( "Q", pack( "P", $attrs->{insns} ) ),
        unpack( "Q", pack( "P", $attrs->{license} ) ),
        $attrs->{log_level},
        $attrs->{log_size},
        unpack( "Q", pack( "P", $attrs->{log_buf} ) ),
        $attrs->{kern_version},
        $attrs->{prog_flags}
    );

    # syscallの実行
    my $fd = syscall( Sys::Ebpf::Syscall::SYS_bpf(),
        BPF_PROG_LOAD, $attr, length($attr) );

    if ( $fd < 0 ) {
        my $errno = $!;
        warn "Errno: $errno\n";
        if ( $errno == EACCES || $errno == EPERM ) {
            warn "Permission denied. Are you running as root?\n";
        }
        warn "Log buffer content:\n", $attrs->{log_buf}, "\n";
        die "BPF program load failed: $!\n";
    }
    return $fd;
}

# リロケーションを適用
# args:
#   prob_section: プログラムセクション
#   reloc_sections: リロケーションセクション
#   elf: ELFデータ
#   map_hash_ref: マップデータのリファレンス
# r_offsetを使って、修正すべき命令(インストラクション)をkprobe/sys_execveセクション内から特定します。
# r_infoからシンボルインデックスを取得し、そのシンボルのアドレスをシンボルテーブル(.symtab)から取得します。
# 修正すべきインストラクションに、シンボルのアドレスを適用して、正しいマップへの参照に書き換えます。
sub apply_map_relocations {
    my ( $self, $prob_section, $reloc_sections, $elf, $map_hash_ref ) = @_;
    my $symbols_section = $elf->{symbols};
    for my $reloc_section (@$reloc_sections) {
        my $r_info = $reloc_section->{r_info};
        my $r_offset
            = $reloc_section->{r_offset} + $prob_section->{sh_offset};

        my $sym_index  = $r_info >> 32;           # シンボルインデックスを取得
        my $reloc_type = $r_info & 0xFFFFFFFF;    # リロケーションタイプを取得

        # シンボルテーブルからrelocation対象になり得るシンボル名を取得
        my $symbol = $symbols_section->[$sym_index] // undef;
        if ( !$symbol ) {
            print "Symbol Table not found for index: $sym_index\n";
            next;
        }
        my $sym_name = $symbol->{st_name};
        if ( $symbol->{st_shndx} == 0 ) {
            print "Symbol Name Table not found for index: $sym_index\n";
            next;
        }

        # `$map_data` の中のタプルを確認して、期待してるマップ名が存在するかチェック(fdを取得)
        my $map_fd = undef;
        for my $map_name ( keys %$map_hash_ref ) {
            my $map = $map_hash_ref->{$map_name};
            if ( $sym_name eq $map_name ) {
                $map_fd = $map->{map_fd};
                last;    # マップが見つかったらループを抜ける
            }
        }
        if ( defined $map_fd ) {

            # 指定されたオフセット位置にある `lddw` 命令(16バイト)を取得
            my $bpf_insn
                = substr( $self->{reader}->{raw_elf_data}, $r_offset, 16 )
                ;    # 16バイトを取得
            my $bpf_insn_len = length($bpf_insn);
            my ( $high, $low )
                = Sys::Ebpf::Asm::deserialize_128bit_instruction($bpf_insn);

            # 即値 (64ビット) にマップFDを設定
            $high->set_imm($map_fd);
            $low->set_imm( $map_fd >> 32 );

            # src_reg に PSEUDO_MAP_FD (1) を設定
            $high->set_src_reg(1);

            # 修正後の命令をパックして、元の場所に書き戻す
            my $new_bpf_insn
                = Sys::Ebpf::Asm::serialize_128bit_instruction( $high, $low );
            substr( $self->{reader}->{raw_elf_data},
                $r_offset, 16, $new_bpf_insn );

            # 書き換えた後の命令を出力
            my $after_bpf_insn
                = substr( $self->{reader}->{raw_elf_data}, $r_offset, 16 );
        }
    }
}

# BPF プログラムとマップをロード
# args:
#   section_name: BPF プログラムのセクション名
# return:
#   map_collection: マップ名とFDの組になったhashのリファレンス
#   prog_fd: プログラムFD
sub load_bpf {
    my ( $self, $section_name ) = @_;
    my $bpfelf = $self->{bpfelf};
    my $maps   = $self->extract_bpf_map_attributes('maps');

    my %map_collection;

    # map_attr_refの各キー(マップ名)に対して処理を実行
    for my $map (@$maps) {
        my $map_name     = $map->{map_name};
        my $map_instance = Sys::Ebpf::Map->create($map);
        my $map_fd       = $map_instance->{map_fd};
        if ( $map_fd < 0 ) {
            die "Failed to load BPF map: $map_name (FD: $map_fd})\n";
        }
        $map_collection{$map_name} = $map_instance;
    }

    # リロケーションを適用
    my $reloc_section = $bpfelf->{relocations}{ ".rel" . $section_name };
    my $prob_section
        = find_symbol_table_from_name( $bpfelf->{sections}, $section_name );
    if ( defined $reloc_section ) {
        $self->apply_map_relocations( $prob_section, $reloc_section, $bpfelf,
            \%map_collection );
    }

    # todo: bpfprobが複数あるケースにも対応する
    # BPF プログラムをロード
    my $prog_fd = $self->load_bpf_program_from_elf($section_name);

    return ( \%map_collection, $prog_fd );
}

1;



( run in 0.517 second using v1.01-cache-2.11-cpan-71847e10f99 )