| 1 |
|
| 2 |
|
| 3 |
|
| 4 | #ifndef _ASM_RISCV_MODULE_H
|
| 5 | #define _ASM_RISCV_MODULE_H
|
| 6 |
|
| 7 | #include <asm-generic/module.h>
|
| 8 | #include <linux/elf.h>
|
| 9 |
|
| 10 | struct module;
|
| 11 | unsigned long module_emit_got_entry(struct module *mod, unsigned long val);
|
| 12 | unsigned long module_emit_plt_entry(struct module *mod, unsigned long val);
|
| 13 |
|
| 14 | #ifdef CONFIG_MODULE_SECTIONS
|
| 15 | struct mod_section {
|
| 16 | Elf_Shdr *shdr;
|
| 17 | int num_entries;
|
| 18 | int max_entries;
|
| 19 | };
|
| 20 |
|
| 21 | struct mod_arch_specific {
|
| 22 | struct mod_section got;
|
| 23 | struct mod_section plt;
|
| 24 | struct mod_section got_plt;
|
| 25 | };
|
| 26 |
|
| 27 | struct got_entry {
|
| 28 | unsigned long symbol_addr;
|
| 29 | };
|
| 30 |
|
| 31 | static inline struct got_entry emit_got_entry(unsigned long val)
|
| 32 | {
|
| 33 | return (struct got_entry) {val};
|
| 34 | }
|
| 35 |
|
| 36 | static inline struct got_entry *get_got_entry(unsigned long val,
|
| 37 | const struct mod_section *sec)
|
| 38 | {
|
| 39 | struct got_entry *got = (struct got_entry *)(sec->shdr->sh_addr);
|
| 40 | int i;
|
| 41 | for (i = 0; i < sec->num_entries; i++) {
|
| 42 | if (got[i].symbol_addr == val)
|
| 43 | return &got[i];
|
| 44 | }
|
| 45 | return NULL;
|
| 46 | }
|
| 47 |
|
| 48 | struct plt_entry {
|
| 49 | |
| 50 | * Trampoline code to real target address. The return address
|
| 51 | * should be the original (pc+4) before entring plt entry.
|
| 52 |
|
| 53 | u32 insn_auipc;
|
| 54 | u32 insn_ld;
|
| 55 | u32 insn_jr;
|
| 56 | };
|
| 57 |
|
| 58 | #define OPC_AUIPC 0x0017
|
| 59 | #define OPC_LD 0x3003
|
| 60 | #define OPC_JALR 0x0067
|
| 61 | #define REG_T0 0x5
|
| 62 | #define REG_T1 0x6
|
| 63 |
|
| 64 | static inline struct plt_entry emit_plt_entry(unsigned long val,
|
| 65 | unsigned long plt,
|
| 66 | unsigned long got_plt)
|
| 67 | {
|
| 68 | |
| 69 | * U-Type encoding:
|
| 70 | * +------------+----------+----------+
|
| 71 | * | imm[31:12] | rd[11:7] | opc[6:0] |
|
| 72 | * +------------+----------+----------+
|
| 73 | *
|
| 74 | * I-Type encoding:
|
| 75 | * +------------+------------+--------+----------+----------+
|
| 76 | * | imm[31:20] | rs1[19:15] | funct3 | rd[11:7] | opc[6:0] |
|
| 77 | * +------------+------------+--------+----------+----------+
|
| 78 | *
|
| 79 |
|
| 80 | unsigned long offset = got_plt - plt;
|
| 81 | u32 hi20 = (offset + 0x800) & 0xfffff000;
|
| 82 | u32 lo12 = (offset - hi20);
|
| 83 | return (struct plt_entry) {
|
| 84 | OPC_AUIPC | (REG_T0 << 7) | hi20,
|
| 85 | OPC_LD | (lo12 << 20) | (REG_T0 << 15) | (REG_T1 << 7),
|
| 86 | OPC_JALR | (REG_T1 << 15)
|
| 87 | };
|
| 88 | }
|
| 89 |
|
| 90 | static inline int get_got_plt_idx(unsigned long val, const struct mod_section *sec)
|
| 91 | {
|
| 92 | struct got_entry *got_plt = (struct got_entry *)sec->shdr->sh_addr;
|
| 93 | int i;
|
| 94 | for (i = 0; i < sec->num_entries; i++) {
|
| 95 | if (got_plt[i].symbol_addr == val)
|
| 96 | return i;
|
| 97 | }
|
| 98 | return -1;
|
| 99 | }
|
| 100 |
|
| 101 | static inline struct plt_entry *get_plt_entry(unsigned long val,
|
| 102 | const struct mod_section *sec_plt,
|
| 103 | const struct mod_section *sec_got_plt)
|
| 104 | {
|
| 105 | struct plt_entry *plt = (struct plt_entry *)sec_plt->shdr->sh_addr;
|
| 106 | int got_plt_idx = get_got_plt_idx(val, sec_got_plt);
|
| 107 | if (got_plt_idx >= 0)
|
| 108 | return plt + got_plt_idx;
|
| 109 | else
|
| 110 | return NULL;
|
| 111 | }
|
| 112 |
|
| 113 | #endif
|
| 114 |
|
| 115 | static inline const Elf_Shdr *find_section(const Elf_Ehdr *hdr,
|
| 116 | const Elf_Shdr *sechdrs,
|
| 117 | const char *name)
|
| 118 | {
|
| 119 | const Elf_Shdr *s, *se;
|
| 120 | const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
|
| 121 |
|
| 122 | for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
|
| 123 | if (strcmp(name, secstrs + s->sh_name) == 0)
|
| 124 | return s;
|
| 125 | }
|
| 126 |
|
| 127 | return NULL;
|
| 128 | }
|
| 129 |
|
| 130 | #endif
|