| .. 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 |