diff options
author | Zong Li <zong@andestech.com> | 2018-03-15 04:50:42 -0400 |
---|---|---|
committer | Palmer Dabbelt <palmer@sifive.com> | 2018-04-02 23:00:54 -0400 |
commit | b8bde0ef12bd43f013d879973a1900930bfb95ee (patch) | |
tree | 953afd76c9ed57a34ed2ee3c042c4e061bc9d5dc | |
parent | ab1ef68e54019937cf859f2c86c9ead6f3e62f19 (diff) |
RISC-V: Add section of GOT.PLT for kernel module
Separate the function symbol address from .plt to .got.plt section.
The original plt entry has trampoline code with symbol address,
there is a 32-bit padding bwtween jar instruction and symbol address.
Extract the symbol address to .got.plt to reduce the module size.
Signed-off-by: Zong Li <zong@andestech.com>
Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
-rw-r--r-- | arch/riscv/include/asm/module.h | 40 | ||||
-rw-r--r-- | arch/riscv/kernel/module-sections.c | 21 | ||||
-rw-r--r-- | arch/riscv/kernel/module.lds | 1 |
3 files changed, 45 insertions, 17 deletions
diff --git a/arch/riscv/include/asm/module.h b/arch/riscv/include/asm/module.h index e61d73f82d4d..349df33808c4 100644 --- a/arch/riscv/include/asm/module.h +++ b/arch/riscv/include/asm/module.h | |||
@@ -21,6 +21,7 @@ struct mod_section { | |||
21 | struct mod_arch_specific { | 21 | struct mod_arch_specific { |
22 | struct mod_section got; | 22 | struct mod_section got; |
23 | struct mod_section plt; | 23 | struct mod_section plt; |
24 | struct mod_section got_plt; | ||
24 | }; | 25 | }; |
25 | 26 | ||
26 | struct got_entry { | 27 | struct got_entry { |
@@ -48,13 +49,10 @@ struct plt_entry { | |||
48 | /* | 49 | /* |
49 | * Trampoline code to real target address. The return address | 50 | * Trampoline code to real target address. The return address |
50 | * should be the original (pc+4) before entring plt entry. | 51 | * should be the original (pc+4) before entring plt entry. |
51 | * For 8 byte alignment of symbol_addr, | ||
52 | * don't pack structure to remove the padding. | ||
53 | */ | 52 | */ |
54 | u32 insn_auipc; /* auipc t0, 0x0 */ | 53 | u32 insn_auipc; /* auipc t0, 0x0 */ |
55 | u32 insn_ld; /* ld t1, 0x10(t0) */ | 54 | u32 insn_ld; /* ld t1, 0x10(t0) */ |
56 | u32 insn_jr; /* jr t1 */ | 55 | u32 insn_jr; /* jr t1 */ |
57 | u64 symbol_addr; /* the real jump target address */ | ||
58 | }; | 56 | }; |
59 | 57 | ||
60 | #define OPC_AUIPC 0x0017 | 58 | #define OPC_AUIPC 0x0017 |
@@ -62,9 +60,8 @@ struct plt_entry { | |||
62 | #define OPC_JALR 0x0067 | 60 | #define OPC_JALR 0x0067 |
63 | #define REG_T0 0x5 | 61 | #define REG_T0 0x5 |
64 | #define REG_T1 0x6 | 62 | #define REG_T1 0x6 |
65 | #define IMM_OFFSET 0x10 | ||
66 | 63 | ||
67 | static inline struct plt_entry emit_plt_entry(u64 val) | 64 | static inline struct plt_entry emit_plt_entry(u64 val, u64 plt, u64 got_plt) |
68 | { | 65 | { |
69 | /* | 66 | /* |
70 | * U-Type encoding: | 67 | * U-Type encoding: |
@@ -78,24 +75,37 @@ static inline struct plt_entry emit_plt_entry(u64 val) | |||
78 | * +------------+------------+--------+----------+----------+ | 75 | * +------------+------------+--------+----------+----------+ |
79 | * | 76 | * |
80 | */ | 77 | */ |
78 | u64 offset = got_plt - plt; | ||
79 | u32 hi20 = (offset + 0x800) & 0xfffff000; | ||
80 | u32 lo12 = (offset - hi20); | ||
81 | return (struct plt_entry) { | 81 | return (struct plt_entry) { |
82 | OPC_AUIPC | (REG_T0 << 7), | 82 | OPC_AUIPC | (REG_T0 << 7) | hi20, |
83 | OPC_LD | (IMM_OFFSET << 20) | (REG_T0 << 15) | (REG_T1 << 7), | 83 | OPC_LD | (lo12 << 20) | (REG_T0 << 15) | (REG_T1 << 7), |
84 | OPC_JALR | (REG_T1 << 15), | 84 | OPC_JALR | (REG_T1 << 15) |
85 | val | ||
86 | }; | 85 | }; |
87 | } | 86 | } |
88 | 87 | ||
89 | static inline struct plt_entry *get_plt_entry(u64 val, | 88 | static inline int get_got_plt_idx(u64 val, const struct mod_section *sec) |
90 | const struct mod_section *sec) | ||
91 | { | 89 | { |
92 | struct plt_entry *plt = (struct plt_entry *)sec->shdr->sh_addr; | 90 | struct got_entry *got_plt = (struct got_entry *)sec->shdr->sh_addr; |
93 | int i; | 91 | int i; |
94 | for (i = 0; i < sec->num_entries; i++) { | 92 | for (i = 0; i < sec->num_entries; i++) { |
95 | if (plt[i].symbol_addr == val) | 93 | if (got_plt[i].symbol_addr == val) |
96 | return &plt[i]; | 94 | return i; |
97 | } | 95 | } |
98 | return NULL; | 96 | return -1; |
97 | } | ||
98 | |||
99 | static inline struct plt_entry *get_plt_entry(u64 val, | ||
100 | const struct mod_section *sec_plt, | ||
101 | const struct mod_section *sec_got_plt) | ||
102 | { | ||
103 | struct plt_entry *plt = (struct plt_entry *)sec_plt->shdr->sh_addr; | ||
104 | int got_plt_idx = get_got_plt_idx(val, sec_got_plt); | ||
105 | if (got_plt_idx >= 0) | ||
106 | return plt + got_plt_idx; | ||
107 | else | ||
108 | return NULL; | ||
99 | } | 109 | } |
100 | 110 | ||
101 | #endif /* CONFIG_MODULE_SECTIONS */ | 111 | #endif /* CONFIG_MODULE_SECTIONS */ |
diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c index 94ba1551eac3..bbbd26e19bfd 100644 --- a/arch/riscv/kernel/module-sections.c +++ b/arch/riscv/kernel/module-sections.c | |||
@@ -30,18 +30,23 @@ u64 module_emit_got_entry(struct module *mod, u64 val) | |||
30 | 30 | ||
31 | u64 module_emit_plt_entry(struct module *mod, u64 val) | 31 | u64 module_emit_plt_entry(struct module *mod, u64 val) |
32 | { | 32 | { |
33 | struct mod_section *got_plt_sec = &mod->arch.got_plt; | ||
34 | struct got_entry *got_plt; | ||
33 | struct mod_section *plt_sec = &mod->arch.plt; | 35 | struct mod_section *plt_sec = &mod->arch.plt; |
34 | struct plt_entry *plt = get_plt_entry(val, plt_sec); | 36 | struct plt_entry *plt = get_plt_entry(val, plt_sec, got_plt_sec); |
35 | int i = plt_sec->num_entries; | 37 | int i = plt_sec->num_entries; |
36 | 38 | ||
37 | if (plt) | 39 | if (plt) |
38 | return (u64)plt; | 40 | return (u64)plt; |
39 | 41 | ||
40 | /* There is no duplicate entry, create a new one */ | 42 | /* There is no duplicate entry, create a new one */ |
43 | got_plt = (struct got_entry *)got_plt_sec->shdr->sh_addr; | ||
44 | got_plt[i] = emit_got_entry(val); | ||
41 | plt = (struct plt_entry *)plt_sec->shdr->sh_addr; | 45 | plt = (struct plt_entry *)plt_sec->shdr->sh_addr; |
42 | plt[i] = emit_plt_entry(val); | 46 | plt[i] = emit_plt_entry(val, (u64)&plt[i], (u64)&got_plt[i]); |
43 | 47 | ||
44 | plt_sec->num_entries++; | 48 | plt_sec->num_entries++; |
49 | got_plt_sec->num_entries++; | ||
45 | BUG_ON(plt_sec->num_entries > plt_sec->max_entries); | 50 | BUG_ON(plt_sec->num_entries > plt_sec->max_entries); |
46 | 51 | ||
47 | return (u64)&plt[i]; | 52 | return (u64)&plt[i]; |
@@ -94,6 +99,8 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, | |||
94 | mod->arch.plt.shdr = sechdrs + i; | 99 | mod->arch.plt.shdr = sechdrs + i; |
95 | else if (!strcmp(secstrings + sechdrs[i].sh_name, ".got")) | 100 | else if (!strcmp(secstrings + sechdrs[i].sh_name, ".got")) |
96 | mod->arch.got.shdr = sechdrs + i; | 101 | mod->arch.got.shdr = sechdrs + i; |
102 | else if (!strcmp(secstrings + sechdrs[i].sh_name, ".got.plt")) | ||
103 | mod->arch.got_plt.shdr = sechdrs + i; | ||
97 | } | 104 | } |
98 | 105 | ||
99 | if (!mod->arch.plt.shdr) { | 106 | if (!mod->arch.plt.shdr) { |
@@ -104,6 +111,10 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, | |||
104 | pr_err("%s: module GOT section(s) missing\n", mod->name); | 111 | pr_err("%s: module GOT section(s) missing\n", mod->name); |
105 | return -ENOEXEC; | 112 | return -ENOEXEC; |
106 | } | 113 | } |
114 | if (!mod->arch.got_plt.shdr) { | ||
115 | pr_err("%s: module GOT.PLT section(s) missing\n", mod->name); | ||
116 | return -ENOEXEC; | ||
117 | } | ||
107 | 118 | ||
108 | /* Calculate the maxinum number of entries */ | 119 | /* Calculate the maxinum number of entries */ |
109 | for (i = 0; i < ehdr->e_shnum; i++) { | 120 | for (i = 0; i < ehdr->e_shnum; i++) { |
@@ -135,5 +146,11 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, | |||
135 | mod->arch.got.num_entries = 0; | 146 | mod->arch.got.num_entries = 0; |
136 | mod->arch.got.max_entries = num_gots; | 147 | mod->arch.got.max_entries = num_gots; |
137 | 148 | ||
149 | mod->arch.got_plt.shdr->sh_type = SHT_NOBITS; | ||
150 | mod->arch.got_plt.shdr->sh_flags = SHF_ALLOC; | ||
151 | mod->arch.got_plt.shdr->sh_addralign = L1_CACHE_BYTES; | ||
152 | mod->arch.got_plt.shdr->sh_size = (num_plts + 1) * sizeof(struct got_entry); | ||
153 | mod->arch.got_plt.num_entries = 0; | ||
154 | mod->arch.got_plt.max_entries = num_plts; | ||
138 | return 0; | 155 | return 0; |
139 | } | 156 | } |
diff --git a/arch/riscv/kernel/module.lds b/arch/riscv/kernel/module.lds index 7ef580e62883..295ecfb341a2 100644 --- a/arch/riscv/kernel/module.lds +++ b/arch/riscv/kernel/module.lds | |||
@@ -4,4 +4,5 @@ | |||
4 | SECTIONS { | 4 | SECTIONS { |
5 | .plt (NOLOAD) : { BYTE(0) } | 5 | .plt (NOLOAD) : { BYTE(0) } |
6 | .got (NOLOAD) : { BYTE(0) } | 6 | .got (NOLOAD) : { BYTE(0) } |
7 | .got.plt (NOLOAD) : { BYTE(0) } | ||
7 | } | 8 | } |