547 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			547 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| .. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
 | |
| 
 | |
| ====================
 | |
| BPF LLVM Relocations
 | |
| ====================
 | |
| 
 | |
| This document describes LLVM BPF backend relocation types.
 | |
| 
 | |
| Relocation Record
 | |
| =================
 | |
| 
 | |
| LLVM BPF backend records each relocation with the following 16-byte
 | |
| ELF structure::
 | |
| 
 | |
|   typedef struct
 | |
|   {
 | |
|     Elf64_Addr    r_offset;  // Offset from the beginning of section.
 | |
|     Elf64_Xword   r_info;    // Relocation type and symbol index.
 | |
|   } Elf64_Rel;
 | |
| 
 | |
| For example, for the following code::
 | |
| 
 | |
|   int g1 __attribute__((section("sec")));
 | |
|   int g2 __attribute__((section("sec")));
 | |
|   static volatile int l1 __attribute__((section("sec")));
 | |
|   static volatile int l2 __attribute__((section("sec")));
 | |
|   int test() {
 | |
|     return g1 + g2 + l1 + l2;
 | |
|   }
 | |
| 
 | |
| Compiled with ``clang --target=bpf -O2 -c test.c``, the following is
 | |
| the code with ``llvm-objdump -dr test.o``::
 | |
| 
 | |
|        0:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
 | |
|                 0000000000000000:  R_BPF_64_64  g1
 | |
|        2:       61 11 00 00 00 00 00 00 r1 = *(u32 *)(r1 + 0)
 | |
|        3:       18 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r2 = 0 ll
 | |
|                 0000000000000018:  R_BPF_64_64  g2
 | |
|        5:       61 20 00 00 00 00 00 00 r0 = *(u32 *)(r2 + 0)
 | |
|        6:       0f 10 00 00 00 00 00 00 r0 += r1
 | |
|        7:       18 01 00 00 08 00 00 00 00 00 00 00 00 00 00 00 r1 = 8 ll
 | |
|                 0000000000000038:  R_BPF_64_64  sec
 | |
|        9:       61 11 00 00 00 00 00 00 r1 = *(u32 *)(r1 + 0)
 | |
|       10:       0f 10 00 00 00 00 00 00 r0 += r1
 | |
|       11:       18 01 00 00 0c 00 00 00 00 00 00 00 00 00 00 00 r1 = 12 ll
 | |
|                 0000000000000058:  R_BPF_64_64  sec
 | |
|       13:       61 11 00 00 00 00 00 00 r1 = *(u32 *)(r1 + 0)
 | |
|       14:       0f 10 00 00 00 00 00 00 r0 += r1
 | |
|       15:       95 00 00 00 00 00 00 00 exit
 | |
| 
 | |
| There are four relocations in the above for four ``LD_imm64`` instructions.
 | |
| The following ``llvm-readelf -r test.o`` shows the binary values of the four
 | |
| relocations::
 | |
| 
 | |
|   Relocation section '.rel.text' at offset 0x190 contains 4 entries:
 | |
|       Offset             Info             Type               Symbol's Value  Symbol's Name
 | |
|   0000000000000000  0000000600000001 R_BPF_64_64            0000000000000000 g1
 | |
|   0000000000000018  0000000700000001 R_BPF_64_64            0000000000000004 g2
 | |
|   0000000000000038  0000000400000001 R_BPF_64_64            0000000000000000 sec
 | |
|   0000000000000058  0000000400000001 R_BPF_64_64            0000000000000000 sec
 | |
| 
 | |
| Each relocation is represented by ``Offset`` (8 bytes) and ``Info`` (8 bytes).
 | |
| For example, the first relocation corresponds to the first instruction
 | |
| (Offset 0x0) and the corresponding ``Info`` indicates the relocation type
 | |
| of ``R_BPF_64_64`` (type 1) and the entry in the symbol table (entry 6).
 | |
| The following is the symbol table with ``llvm-readelf -s test.o``::
 | |
| 
 | |
|   Symbol table '.symtab' contains 8 entries:
 | |
|      Num:    Value          Size Type    Bind   Vis       Ndx Name
 | |
|        0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT   UND
 | |
|        1: 0000000000000000     0 FILE    LOCAL  DEFAULT   ABS test.c
 | |
|        2: 0000000000000008     4 OBJECT  LOCAL  DEFAULT     4 l1
 | |
|        3: 000000000000000c     4 OBJECT  LOCAL  DEFAULT     4 l2
 | |
|        4: 0000000000000000     0 SECTION LOCAL  DEFAULT     4 sec
 | |
|        5: 0000000000000000   128 FUNC    GLOBAL DEFAULT     2 test
 | |
|        6: 0000000000000000     4 OBJECT  GLOBAL DEFAULT     4 g1
 | |
|        7: 0000000000000004     4 OBJECT  GLOBAL DEFAULT     4 g2
 | |
| 
 | |
| The 6th entry is global variable ``g1`` with value 0.
 | |
| 
 | |
| Similarly, the second relocation is at ``.text`` offset ``0x18``, instruction 3,
 | |
| has a type of ``R_BPF_64_64`` and refers to entry 7 in the symbol table.
 | |
| The second relocation resolves to global variable ``g2`` which has a symbol
 | |
| value 4. The symbol value represents the offset from the start of ``.data``
 | |
| section where the initial value of the global variable ``g2`` is stored.
 | |
| 
 | |
| The third and fourth relocations refer to static variables ``l1``
 | |
| and ``l2``. From the ``.rel.text`` section above, it is not clear
 | |
| to which symbols they really refer as they both refer to
 | |
| symbol table entry 4, symbol ``sec``, which has ``STT_SECTION`` type
 | |
| and represents a section. So for a static variable or function,
 | |
| the section offset is written to the original insn
 | |
| buffer, which is called ``A`` (addend). Looking at
 | |
| above insn ``7`` and ``11``, they have section offset ``8`` and ``12``.
 | |
| From symbol table, we can find that they correspond to entries ``2``
 | |
| and ``3`` for ``l1`` and ``l2``.
 | |
| 
 | |
| In general, the ``A`` is 0 for global variables and functions,
 | |
| and is the section offset or some computation result based on
 | |
| section offset for static variables/functions. The non-section-offset
 | |
| case refers to function calls. See below for more details.
 | |
| 
 | |
| Different Relocation Types
 | |
| ==========================
 | |
| 
 | |
| Six relocation types are supported. The following is an overview and
 | |
| ``S`` represents the value of the symbol in the symbol table::
 | |
| 
 | |
|   Enum  ELF Reloc Type     Description      BitSize  Offset        Calculation
 | |
|   0     R_BPF_NONE         None
 | |
|   1     R_BPF_64_64        ld_imm64 insn    32       r_offset + 4  S + A
 | |
|   2     R_BPF_64_ABS64     normal data      64       r_offset      S + A
 | |
|   3     R_BPF_64_ABS32     normal data      32       r_offset      S + A
 | |
|   4     R_BPF_64_NODYLD32  .BTF[.ext] data  32       r_offset      S + A
 | |
|   10    R_BPF_64_32        call insn        32       r_offset + 4  (S + A) / 8 - 1
 | |
| 
 | |
| For example, ``R_BPF_64_64`` relocation type is used for ``ld_imm64`` instruction.
 | |
| The actual to-be-relocated data (0 or section offset)
 | |
| is stored at ``r_offset + 4`` and the read/write
 | |
| data bitsize is 32 (4 bytes). The relocation can be resolved with
 | |
| the symbol value plus implicit addend. Note that the ``BitSize`` is 32 which
 | |
| means the section offset must be less than or equal to ``UINT32_MAX`` and this
 | |
| is enforced by LLVM BPF backend.
 | |
| 
 | |
| In another case, ``R_BPF_64_ABS64`` relocation type is used for normal 64-bit data.
 | |
| The actual to-be-relocated data is stored at ``r_offset`` and the read/write data
 | |
| bitsize is 64 (8 bytes). The relocation can be resolved with
 | |
| the symbol value plus implicit addend.
 | |
| 
 | |
| Both ``R_BPF_64_ABS32`` and ``R_BPF_64_NODYLD32`` types are for 32-bit data.
 | |
| But ``R_BPF_64_NODYLD32`` specifically refers to relocations in ``.BTF`` and
 | |
| ``.BTF.ext`` sections. For cases like bcc where llvm ``ExecutionEngine RuntimeDyld``
 | |
| is involved, ``R_BPF_64_NODYLD32`` types of relocations should not be resolved
 | |
| to actual function/variable address. Otherwise, ``.BTF`` and ``.BTF.ext``
 | |
| become unusable by bcc and kernel.
 | |
| 
 | |
| Type ``R_BPF_64_32`` is used for call instruction. The call target section
 | |
| offset is stored at ``r_offset + 4`` (32bit) and calculated as
 | |
| ``(S + A) / 8 - 1``.
 | |
| 
 | |
| Examples
 | |
| ========
 | |
| 
 | |
| Types ``R_BPF_64_64`` and ``R_BPF_64_32`` are used to resolve ``ld_imm64``
 | |
| and ``call`` instructions. For example::
 | |
| 
 | |
|   __attribute__((noinline)) __attribute__((section("sec1")))
 | |
|   int gfunc(int a, int b) {
 | |
|     return a * b;
 | |
|   }
 | |
|   static __attribute__((noinline)) __attribute__((section("sec1")))
 | |
|   int lfunc(int a, int b) {
 | |
|     return a + b;
 | |
|   }
 | |
|   int global __attribute__((section("sec2")));
 | |
|   int test(int a, int b) {
 | |
|     return gfunc(a, b) +  lfunc(a, b) + global;
 | |
|   }
 | |
| 
 | |
| Compiled with ``clang --target=bpf -O2 -c test.c``, we will have
 | |
| following code with `llvm-objdump -dr test.o``::
 | |
| 
 | |
|   Disassembly of section .text:
 | |
| 
 | |
|   0000000000000000 <test>:
 | |
|          0:       bf 26 00 00 00 00 00 00 r6 = r2
 | |
|          1:       bf 17 00 00 00 00 00 00 r7 = r1
 | |
|          2:       85 10 00 00 ff ff ff ff call -1
 | |
|                   0000000000000010:  R_BPF_64_32  gfunc
 | |
|          3:       bf 08 00 00 00 00 00 00 r8 = r0
 | |
|          4:       bf 71 00 00 00 00 00 00 r1 = r7
 | |
|          5:       bf 62 00 00 00 00 00 00 r2 = r6
 | |
|          6:       85 10 00 00 02 00 00 00 call 2
 | |
|                   0000000000000030:  R_BPF_64_32  sec1
 | |
|          7:       0f 80 00 00 00 00 00 00 r0 += r8
 | |
|          8:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
 | |
|                   0000000000000040:  R_BPF_64_64  global
 | |
|         10:       61 11 00 00 00 00 00 00 r1 = *(u32 *)(r1 + 0)
 | |
|         11:       0f 10 00 00 00 00 00 00 r0 += r1
 | |
|         12:       95 00 00 00 00 00 00 00 exit
 | |
| 
 | |
|   Disassembly of section sec1:
 | |
| 
 | |
|   0000000000000000 <gfunc>:
 | |
|          0:       bf 20 00 00 00 00 00 00 r0 = r2
 | |
|          1:       2f 10 00 00 00 00 00 00 r0 *= r1
 | |
|          2:       95 00 00 00 00 00 00 00 exit
 | |
| 
 | |
|   0000000000000018 <lfunc>:
 | |
|          3:       bf 20 00 00 00 00 00 00 r0 = r2
 | |
|          4:       0f 10 00 00 00 00 00 00 r0 += r1
 | |
|          5:       95 00 00 00 00 00 00 00 exit
 | |
| 
 | |
| The first relocation corresponds to ``gfunc(a, b)`` where ``gfunc`` has a value of 0,
 | |
| so the ``call`` instruction offset is ``(0 + 0)/8 - 1 = -1``.
 | |
| The second relocation corresponds to ``lfunc(a, b)`` where ``lfunc`` has a section
 | |
| offset ``0x18``, so the ``call`` instruction offset is ``(0 + 0x18)/8 - 1 = 2``.
 | |
| The third relocation corresponds to ld_imm64 of ``global``, which has a section
 | |
| offset ``0``.
 | |
| 
 | |
| The following is an example to show how R_BPF_64_ABS64 could be generated::
 | |
| 
 | |
|   int global() { return 0; }
 | |
|   struct t { void *g; } gbl = { global };
 | |
| 
 | |
| Compiled with ``clang --target=bpf -O2 -g -c test.c``, we will see a
 | |
| relocation below in ``.data`` section with command
 | |
| ``llvm-readelf -r test.o``::
 | |
| 
 | |
|   Relocation section '.rel.data' at offset 0x458 contains 1 entries:
 | |
|       Offset             Info             Type               Symbol's Value  Symbol's Name
 | |
|   0000000000000000  0000000700000002 R_BPF_64_ABS64         0000000000000000 global
 | |
| 
 | |
| The relocation says the first 8-byte of ``.data`` section should be
 | |
| filled with address of ``global`` variable.
 | |
| 
 | |
| With ``llvm-readelf`` output, we can see that dwarf sections have a bunch of
 | |
| ``R_BPF_64_ABS32`` and ``R_BPF_64_ABS64`` relocations::
 | |
| 
 | |
|   Relocation section '.rel.debug_info' at offset 0x468 contains 13 entries:
 | |
|       Offset             Info             Type               Symbol's Value  Symbol's Name
 | |
|   0000000000000006  0000000300000003 R_BPF_64_ABS32         0000000000000000 .debug_abbrev
 | |
|   000000000000000c  0000000400000003 R_BPF_64_ABS32         0000000000000000 .debug_str
 | |
|   0000000000000012  0000000400000003 R_BPF_64_ABS32         0000000000000000 .debug_str
 | |
|   0000000000000016  0000000600000003 R_BPF_64_ABS32         0000000000000000 .debug_line
 | |
|   000000000000001a  0000000400000003 R_BPF_64_ABS32         0000000000000000 .debug_str
 | |
|   000000000000001e  0000000200000002 R_BPF_64_ABS64         0000000000000000 .text
 | |
|   000000000000002b  0000000400000003 R_BPF_64_ABS32         0000000000000000 .debug_str
 | |
|   0000000000000037  0000000800000002 R_BPF_64_ABS64         0000000000000000 gbl
 | |
|   0000000000000040  0000000400000003 R_BPF_64_ABS32         0000000000000000 .debug_str
 | |
|   ......
 | |
| 
 | |
| The .BTF/.BTF.ext sections has R_BPF_64_NODYLD32 relocations::
 | |
| 
 | |
|   Relocation section '.rel.BTF' at offset 0x538 contains 1 entries:
 | |
|       Offset             Info             Type               Symbol's Value  Symbol's Name
 | |
|   0000000000000084  0000000800000004 R_BPF_64_NODYLD32      0000000000000000 gbl
 | |
| 
 | |
|   Relocation section '.rel.BTF.ext' at offset 0x548 contains 2 entries:
 | |
|       Offset             Info             Type               Symbol's Value  Symbol's Name
 | |
|   000000000000002c  0000000200000004 R_BPF_64_NODYLD32      0000000000000000 .text
 | |
|   0000000000000040  0000000200000004 R_BPF_64_NODYLD32      0000000000000000 .text
 | |
| 
 | |
| .. _btf-co-re-relocations:
 | |
| 
 | |
| =================
 | |
| CO-RE Relocations
 | |
| =================
 | |
| 
 | |
| From object file point of view CO-RE mechanism is implemented as a set
 | |
| of CO-RE specific relocation records. These relocation records are not
 | |
| related to ELF relocations and are encoded in .BTF.ext section.
 | |
| See :ref:`Documentation/bpf/btf.rst <BTF_Ext_Section>` for more
 | |
| information on .BTF.ext structure.
 | |
| 
 | |
| CO-RE relocations are applied to BPF instructions to update immediate
 | |
| or offset fields of the instruction at load time with information
 | |
| relevant for target kernel.
 | |
| 
 | |
| Field to patch is selected basing on the instruction class:
 | |
| 
 | |
| * For BPF_ALU, BPF_ALU64, BPF_LD `immediate` field is patched;
 | |
| * For BPF_LDX, BPF_STX, BPF_ST `offset` field is patched;
 | |
| * BPF_JMP, BPF_JMP32 instructions **should not** be patched.
 | |
| 
 | |
| Relocation kinds
 | |
| ================
 | |
| 
 | |
| There are several kinds of CO-RE relocations that could be split in
 | |
| three groups:
 | |
| 
 | |
| * Field-based - patch instruction with field related information, e.g.
 | |
|   change offset field of the BPF_LDX instruction to reflect offset
 | |
|   of a specific structure field in the target kernel.
 | |
| 
 | |
| * Type-based - patch instruction with type related information, e.g.
 | |
|   change immediate field of the BPF_ALU move instruction to 0 or 1 to
 | |
|   reflect if specific type is present in the target kernel.
 | |
| 
 | |
| * Enum-based - patch instruction with enum related information, e.g.
 | |
|   change immediate field of the BPF_LD_IMM64 instruction to reflect
 | |
|   value of a specific enum literal in the target kernel.
 | |
| 
 | |
| The complete list of relocation kinds is represented by the following enum:
 | |
| 
 | |
| .. code-block:: c
 | |
| 
 | |
|  enum bpf_core_relo_kind {
 | |
| 	BPF_CORE_FIELD_BYTE_OFFSET = 0,  /* field byte offset */
 | |
| 	BPF_CORE_FIELD_BYTE_SIZE   = 1,  /* field size in bytes */
 | |
| 	BPF_CORE_FIELD_EXISTS      = 2,  /* field existence in target kernel */
 | |
| 	BPF_CORE_FIELD_SIGNED      = 3,  /* field signedness (0 - unsigned, 1 - signed) */
 | |
| 	BPF_CORE_FIELD_LSHIFT_U64  = 4,  /* bitfield-specific left bitshift */
 | |
| 	BPF_CORE_FIELD_RSHIFT_U64  = 5,  /* bitfield-specific right bitshift */
 | |
| 	BPF_CORE_TYPE_ID_LOCAL     = 6,  /* type ID in local BPF object */
 | |
| 	BPF_CORE_TYPE_ID_TARGET    = 7,  /* type ID in target kernel */
 | |
| 	BPF_CORE_TYPE_EXISTS       = 8,  /* type existence in target kernel */
 | |
| 	BPF_CORE_TYPE_SIZE         = 9,  /* type size in bytes */
 | |
| 	BPF_CORE_ENUMVAL_EXISTS    = 10, /* enum value existence in target kernel */
 | |
| 	BPF_CORE_ENUMVAL_VALUE     = 11, /* enum value integer value */
 | |
| 	BPF_CORE_TYPE_MATCHES      = 12, /* type match in target kernel */
 | |
|  };
 | |
| 
 | |
| Notes:
 | |
| 
 | |
| * ``BPF_CORE_FIELD_LSHIFT_U64`` and ``BPF_CORE_FIELD_RSHIFT_U64`` are
 | |
|   supposed to be used to read bitfield values using the following
 | |
|   algorithm:
 | |
| 
 | |
|   .. code-block:: c
 | |
| 
 | |
|      // To read bitfield ``f`` from ``struct s``
 | |
|      is_signed = relo(s->f, BPF_CORE_FIELD_SIGNED)
 | |
|      off = relo(s->f, BPF_CORE_FIELD_BYTE_OFFSET)
 | |
|      sz  = relo(s->f, BPF_CORE_FIELD_BYTE_SIZE)
 | |
|      l   = relo(s->f, BPF_CORE_FIELD_LSHIFT_U64)
 | |
|      r   = relo(s->f, BPF_CORE_FIELD_RSHIFT_U64)
 | |
|      // define ``v`` as signed or unsigned integer of size ``sz``
 | |
|      v = *({s|u}<sz> *)((void *)s + off)
 | |
|      v <<= l
 | |
|      v >>= r
 | |
| 
 | |
| * The ``BPF_CORE_TYPE_MATCHES`` queries matching relation, defined as
 | |
|   follows:
 | |
| 
 | |
|   * for integers: types match if size and signedness match;
 | |
|   * for arrays & pointers: target types are recursively matched;
 | |
|   * for structs & unions:
 | |
| 
 | |
|     * local members need to exist in target with the same name;
 | |
| 
 | |
|     * for each member we recursively check match unless it is already behind a
 | |
|       pointer, in which case we only check matching names and compatible kind;
 | |
| 
 | |
|   * for enums:
 | |
| 
 | |
|     * local variants have to have a match in target by symbolic name (but not
 | |
|       numeric value);
 | |
| 
 | |
|     * size has to match (but enum may match enum64 and vice versa);
 | |
| 
 | |
|   * for function pointers:
 | |
| 
 | |
|     * number and position of arguments in local type has to match target;
 | |
|     * for each argument and the return value we recursively check match.
 | |
| 
 | |
| CO-RE Relocation Record
 | |
| =======================
 | |
| 
 | |
| Relocation record is encoded as the following structure:
 | |
| 
 | |
| .. code-block:: c
 | |
| 
 | |
|  struct bpf_core_relo {
 | |
| 	__u32 insn_off;
 | |
| 	__u32 type_id;
 | |
| 	__u32 access_str_off;
 | |
| 	enum bpf_core_relo_kind kind;
 | |
|  };
 | |
| 
 | |
| * ``insn_off`` - instruction offset (in bytes) within a code section
 | |
|   associated with this relocation;
 | |
| 
 | |
| * ``type_id`` - BTF type ID of the "root" (containing) entity of a
 | |
|   relocatable type or field;
 | |
| 
 | |
| * ``access_str_off`` - offset into corresponding .BTF string section.
 | |
|   String interpretation depends on specific relocation kind:
 | |
| 
 | |
|   * for field-based relocations, string encodes an accessed field using
 | |
|     a sequence of field and array indices, separated by colon (:). It's
 | |
|     conceptually very close to LLVM's `getelementptr <GEP_>`_ instruction's
 | |
|     arguments for identifying offset to a field. For example, consider the
 | |
|     following C code:
 | |
| 
 | |
|     .. code-block:: c
 | |
| 
 | |
|        struct sample {
 | |
|            int a;
 | |
|            int b;
 | |
|            struct { int c[10]; };
 | |
|        } __attribute__((preserve_access_index));
 | |
|        struct sample *s;
 | |
| 
 | |
|     * Access to ``s[0].a`` would be encoded as ``0:0``:
 | |
| 
 | |
|       * ``0``: first element of ``s`` (as if ``s`` is an array);
 | |
|       * ``0``: index of field ``a`` in ``struct sample``.
 | |
| 
 | |
|     * Access to ``s->a`` would be encoded as ``0:0`` as well.
 | |
|     * Access to ``s->b`` would be encoded as ``0:1``:
 | |
| 
 | |
|       * ``0``: first element of ``s``;
 | |
|       * ``1``: index of field ``b`` in ``struct sample``.
 | |
| 
 | |
|     * Access to ``s[1].c[5]`` would be encoded as ``1:2:0:5``:
 | |
| 
 | |
|       * ``1``: second element of ``s``;
 | |
|       * ``2``: index of anonymous structure field in ``struct sample``;
 | |
|       * ``0``: index of field ``c`` in anonymous structure;
 | |
|       * ``5``: access to array element #5.
 | |
| 
 | |
|   * for type-based relocations, string is expected to be just "0";
 | |
| 
 | |
|   * for enum value-based relocations, string contains an index of enum
 | |
|      value within its enum type;
 | |
| 
 | |
| * ``kind`` - one of ``enum bpf_core_relo_kind``.
 | |
| 
 | |
| .. _GEP: https://llvm.org/docs/LangRef.html#getelementptr-instruction
 | |
| 
 | |
| .. _btf_co_re_relocation_examples:
 | |
| 
 | |
| CO-RE Relocation Examples
 | |
| =========================
 | |
| 
 | |
| For the following C code:
 | |
| 
 | |
| .. code-block:: c
 | |
| 
 | |
|  struct foo {
 | |
|    int a;
 | |
|    int b;
 | |
|    unsigned c:15;
 | |
|  } __attribute__((preserve_access_index));
 | |
| 
 | |
|  enum bar { U, V };
 | |
| 
 | |
| With the following BTF definitions:
 | |
| 
 | |
| .. code-block::
 | |
| 
 | |
|  ...
 | |
|  [2] STRUCT 'foo' size=8 vlen=2
 | |
|         'a' type_id=3 bits_offset=0
 | |
|         'b' type_id=3 bits_offset=32
 | |
|         'c' type_id=4 bits_offset=64 bitfield_size=15
 | |
|  [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
 | |
|  [4] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)
 | |
|  ...
 | |
|  [16] ENUM 'bar' encoding=UNSIGNED size=4 vlen=2
 | |
|         'U' val=0
 | |
|         'V' val=1
 | |
| 
 | |
| Field offset relocations are generated automatically when
 | |
| ``__attribute__((preserve_access_index))`` is used, for example:
 | |
| 
 | |
| .. code-block:: c
 | |
| 
 | |
|   void alpha(struct foo *s, volatile unsigned long *g) {
 | |
|     *g = s->a;
 | |
|     s->a = 1;
 | |
|   }
 | |
| 
 | |
|   00 <alpha>:
 | |
|     0:  r3 = *(s32 *)(r1 + 0x0)
 | |
|            00:  CO-RE <byte_off> [2] struct foo::a (0:0)
 | |
|     1:  *(u64 *)(r2 + 0x0) = r3
 | |
|     2:  *(u32 *)(r1 + 0x0) = 0x1
 | |
|            10:  CO-RE <byte_off> [2] struct foo::a (0:0)
 | |
|     3:  exit
 | |
| 
 | |
| 
 | |
| All relocation kinds could be requested via built-in functions.
 | |
| E.g. field-based relocations:
 | |
| 
 | |
| .. code-block:: c
 | |
| 
 | |
|   void bravo(struct foo *s, volatile unsigned long *g) {
 | |
|     *g = __builtin_preserve_field_info(s->b, 0 /* field byte offset */);
 | |
|     *g = __builtin_preserve_field_info(s->b, 1 /* field byte size */);
 | |
|     *g = __builtin_preserve_field_info(s->b, 2 /* field existence */);
 | |
|     *g = __builtin_preserve_field_info(s->b, 3 /* field signedness */);
 | |
|     *g = __builtin_preserve_field_info(s->c, 4 /* bitfield left shift */);
 | |
|     *g = __builtin_preserve_field_info(s->c, 5 /* bitfield right shift */);
 | |
|   }
 | |
| 
 | |
|   20 <bravo>:
 | |
|      4:     r1 = 0x4
 | |
|             20:  CO-RE <byte_off> [2] struct foo::b (0:1)
 | |
|      5:     *(u64 *)(r2 + 0x0) = r1
 | |
|      6:     r1 = 0x4
 | |
|             30:  CO-RE <byte_sz> [2] struct foo::b (0:1)
 | |
|      7:     *(u64 *)(r2 + 0x0) = r1
 | |
|      8:     r1 = 0x1
 | |
|             40:  CO-RE <field_exists> [2] struct foo::b (0:1)
 | |
|      9:     *(u64 *)(r2 + 0x0) = r1
 | |
|     10:     r1 = 0x1
 | |
|             50:  CO-RE <signed> [2] struct foo::b (0:1)
 | |
|     11:     *(u64 *)(r2 + 0x0) = r1
 | |
|     12:     r1 = 0x31
 | |
|             60:  CO-RE <lshift_u64> [2] struct foo::c (0:2)
 | |
|     13:     *(u64 *)(r2 + 0x0) = r1
 | |
|     14:     r1 = 0x31
 | |
|             70:  CO-RE <rshift_u64> [2] struct foo::c (0:2)
 | |
|     15:     *(u64 *)(r2 + 0x0) = r1
 | |
|     16:     exit
 | |
| 
 | |
| 
 | |
| Type-based relocations:
 | |
| 
 | |
| .. code-block:: c
 | |
| 
 | |
|   void charlie(struct foo *s, volatile unsigned long *g) {
 | |
|     *g = __builtin_preserve_type_info(*s, 0 /* type existence */);
 | |
|     *g = __builtin_preserve_type_info(*s, 1 /* type size */);
 | |
|     *g = __builtin_preserve_type_info(*s, 2 /* type matches */);
 | |
|     *g = __builtin_btf_type_id(*s, 0 /* type id in this object file */);
 | |
|     *g = __builtin_btf_type_id(*s, 1 /* type id in target kernel */);
 | |
|   }
 | |
| 
 | |
|   88 <charlie>:
 | |
|     17:     r1 = 0x1
 | |
|             88:  CO-RE <type_exists> [2] struct foo
 | |
|     18:     *(u64 *)(r2 + 0x0) = r1
 | |
|     19:     r1 = 0xc
 | |
|             98:  CO-RE <type_size> [2] struct foo
 | |
|     20:     *(u64 *)(r2 + 0x0) = r1
 | |
|     21:     r1 = 0x1
 | |
|             a8:  CO-RE <type_matches> [2] struct foo
 | |
|     22:     *(u64 *)(r2 + 0x0) = r1
 | |
|     23:     r1 = 0x2 ll
 | |
|             b8:  CO-RE <local_type_id> [2] struct foo
 | |
|     25:     *(u64 *)(r2 + 0x0) = r1
 | |
|     26:     r1 = 0x2 ll
 | |
|             d0:  CO-RE <target_type_id> [2] struct foo
 | |
|     28:     *(u64 *)(r2 + 0x0) = r1
 | |
|     29:     exit
 | |
| 
 | |
| Enum-based relocations:
 | |
| 
 | |
| .. code-block:: c
 | |
| 
 | |
|   void delta(struct foo *s, volatile unsigned long *g) {
 | |
|     *g = __builtin_preserve_enum_value(*(enum bar *)U, 0 /* enum literal existence */);
 | |
|     *g = __builtin_preserve_enum_value(*(enum bar *)V, 1 /* enum literal value */);
 | |
|   }
 | |
| 
 | |
|   f0 <delta>:
 | |
|     30:     r1 = 0x1 ll
 | |
|             f0:  CO-RE <enumval_exists> [16] enum bar::U = 0
 | |
|     32:     *(u64 *)(r2 + 0x0) = r1
 | |
|     33:     r1 = 0x1 ll
 | |
|             108:  CO-RE <enumval_value> [16] enum bar::V = 1
 | |
|     35:     *(u64 *)(r2 + 0x0) = r1
 | |
|     36:     exit
 |