| #!/usr/bin/env python3 |
| # SPDX-License-Identifier: LGPL-2.1+ |
| |
| import argparse |
| import logging |
| import pathlib |
| import re |
| import subprocess |
| import sys |
| |
| def clang_arch_flag(arch): |
| return '-D__{}__'.format(arch) |
| |
| |
| def target_triplet(): |
| gcc_exec = 'gcc' |
| |
| try: |
| return subprocess.check_output([gcc_exec, '-dumpmachine'], |
| universal_newlines=True).strip() |
| except subprocess.CalledProcessError as e: |
| logging.error('Failed to get target triplet: {}'.format(e)) |
| except FileNotFoundError: |
| logging.error('gcc not installed') |
| return None |
| |
| |
| def clang_compile(clang_exec, clang_flags, src_c, out_file, target_arch, |
| target_triplet): |
| clang_args = [clang_exec, *clang_flags, target_arch, '-I.'] |
| |
| if target_triplet: |
| clang_args += [ |
| '-isystem', |
| '/usr/include/{}'.format(target_triplet)] |
| |
| clang_args += [ |
| '-idirafter', |
| '/usr/local/include', |
| '-idirafter', |
| '/usr/include'] |
| |
| clang_args += [src_c, '-o', out_file] |
| |
| logging.debug('{}'.format(' '.join(clang_args))) |
| subprocess.check_call(clang_args) |
| |
| |
| def llvm_strip(llvm_strip_exec, in_file): |
| llvm_strip_args = [llvm_strip_exec, '-g', in_file] |
| |
| logging.debug('Stripping useless DWARF info:') |
| logging.debug('{}'.format(' '.join(llvm_strip_args))) |
| |
| subprocess.check_call(llvm_strip_args) |
| |
| |
| def gen_bpf_skeleton(bpftool_exec, in_file, out_fd): |
| bpftool_args = [bpftool_exec, 'g', 's', in_file] |
| |
| logging.debug('Generating BPF skeleton:') |
| logging.debug('{}'.format(' '.join(bpftool_args))) |
| skel = subprocess.check_output(bpftool_args, universal_newlines=True) |
| # libbpf is used via dlopen(), so rename symbols as defined |
| # in src/shared/bpf-dlopen.h |
| skel = re.sub(r'(bpf_object__\w+_skeleton)', r'sym_\1', skel) |
| out_fd.write(skel) |
| |
| |
| def bpf_build(args): |
| clang_flags = [ |
| '-Wno-compare-distinct-pointer-types', |
| '-O2', |
| '-target', |
| 'bpf', |
| '-g', |
| '-c', |
| ] |
| |
| clang_out_path = pathlib.Path(args.bpf_src_c).with_suffix('.o') |
| with clang_out_path.open(mode='w') as clang_out, \ |
| open(args.bpf_skel_h, mode='w') as bpf_skel_h: |
| clang_compile(clang_exec=args.clang_exec, |
| clang_flags=clang_flags, |
| src_c=args.bpf_src_c, |
| out_file=clang_out.name, |
| target_arch=clang_arch_flag(args.arch), |
| target_triplet=target_triplet()) |
| |
| llvm_strip(llvm_strip_exec=args.llvm_strip_exec, in_file=clang_out.name) |
| |
| gen_bpf_skeleton(bpftool_exec=args.bpftool_exec, |
| in_file=clang_out.name, |
| out_fd=bpf_skel_h) |
| |
| if __name__ == '__main__': |
| parser = argparse.ArgumentParser() |
| |
| parser.add_argument( |
| 'bpf_src_c', |
| help='Path to *.c source of BPF program in systemd source tree \ |
| relative to the work directory') |
| |
| parser.add_argument( |
| 'bpf_skel_h', |
| help='Path to C header file') |
| |
| parser.add_argument( |
| '--clang_exec', |
| help='Path to clang exec') |
| |
| parser.add_argument( |
| '--llvm_strip_exec', |
| help='Path to llvm-strip exec') |
| |
| parser.add_argument( |
| '--bpftool_exec', |
| help='Path to bpftool exec') |
| |
| parser.add_argument( |
| '--arch', |
| help='Target CPU architecture', |
| default='x86_64') |
| |
| args = parser.parse_args(); |
| |
| logging.basicConfig(stream=sys.stderr, level=logging.WARNING) |
| bpf_build(args) |