1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | /* |
3 | * This file is included twice from vdso2c.c. It generates code for 32-bit |
4 | * and 64-bit vDSOs. We need both for 64-bit builds, since 32-bit vDSOs |
5 | * are built for 32-bit userspace. |
6 | */ |
7 | |
8 | static void BITSFUNC(copy)(FILE *outfile, const unsigned char *data, size_t len) |
9 | { |
10 | size_t i; |
11 | |
12 | for (i = 0; i < len; i++) { |
13 | if (i % 10 == 0) |
14 | fprintf(stream: outfile, format: "\n\t" ); |
15 | fprintf(stream: outfile, format: "0x%02X, " , (int)(data)[i]); |
16 | } |
17 | } |
18 | |
19 | |
20 | /* |
21 | * Extract a section from the input data into a standalone blob. Used to |
22 | * capture kernel-only data that needs to persist indefinitely, e.g. the |
23 | * exception fixup tables, but only in the kernel, i.e. the section can |
24 | * be stripped from the final vDSO image. |
25 | */ |
26 | static void BITSFUNC(extract)(const unsigned char *data, size_t data_len, |
27 | FILE *outfile, ELF(Shdr) *sec, const char *name) |
28 | { |
29 | unsigned long offset; |
30 | size_t len; |
31 | |
32 | offset = (unsigned long)GET_LE(&sec->sh_offset); |
33 | len = (size_t)GET_LE(&sec->sh_size); |
34 | |
35 | if (offset + len > data_len) |
36 | fail(format: "section to extract overruns input data" ); |
37 | |
38 | fprintf(stream: outfile, format: "static const unsigned char %s[%zu] = {" , name, len); |
39 | BITSFUNC(copy)(outfile, data: data + offset, len); |
40 | fprintf(stream: outfile, format: "\n};\n\n" ); |
41 | } |
42 | |
43 | static void BITSFUNC(go)(void *raw_addr, size_t raw_len, |
44 | void *stripped_addr, size_t stripped_len, |
45 | FILE *outfile, const char *image_name) |
46 | { |
47 | int found_load = 0; |
48 | unsigned long load_size = -1; /* Work around bogus warning */ |
49 | unsigned long mapping_size; |
50 | ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr; |
51 | unsigned long i, syms_nr; |
52 | ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr, |
53 | *alt_sec = NULL, *extable_sec = NULL; |
54 | ELF(Dyn) *dyn = 0, *dyn_end = 0; |
55 | const char *secstrings; |
56 | INT_BITS syms[NSYMS] = {}; |
57 | |
58 | ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_LE(&hdr->e_phoff)); |
59 | |
60 | if (GET_LE(&hdr->e_type) != ET_DYN) |
61 | fail(format: "input is not a shared object\n" ); |
62 | |
63 | /* Walk the segment table. */ |
64 | for (i = 0; i < GET_LE(&hdr->e_phnum); i++) { |
65 | if (GET_LE(&pt[i].p_type) == PT_LOAD) { |
66 | if (found_load) |
67 | fail(format: "multiple PT_LOAD segs\n" ); |
68 | |
69 | if (GET_LE(&pt[i].p_offset) != 0 || |
70 | GET_LE(&pt[i].p_vaddr) != 0) |
71 | fail(format: "PT_LOAD in wrong place\n" ); |
72 | |
73 | if (GET_LE(&pt[i].p_memsz) != GET_LE(&pt[i].p_filesz)) |
74 | fail(format: "cannot handle memsz != filesz\n" ); |
75 | |
76 | load_size = GET_LE(&pt[i].p_memsz); |
77 | found_load = 1; |
78 | } else if (GET_LE(&pt[i].p_type) == PT_DYNAMIC) { |
79 | dyn = raw_addr + GET_LE(&pt[i].p_offset); |
80 | dyn_end = raw_addr + GET_LE(&pt[i].p_offset) + |
81 | GET_LE(&pt[i].p_memsz); |
82 | } |
83 | } |
84 | if (!found_load) |
85 | fail(format: "no PT_LOAD seg\n" ); |
86 | |
87 | if (stripped_len < load_size) |
88 | fail(format: "stripped input is too short\n" ); |
89 | |
90 | if (!dyn) |
91 | fail(format: "input has no PT_DYNAMIC section -- your toolchain is buggy\n" ); |
92 | |
93 | /* Walk the dynamic table */ |
94 | for (i = 0; dyn + i < dyn_end && |
95 | GET_LE(&dyn[i].d_tag) != DT_NULL; i++) { |
96 | typeof(dyn[i].d_tag) tag = GET_LE(&dyn[i].d_tag); |
97 | if (tag == DT_REL || tag == DT_RELSZ || tag == DT_RELA || |
98 | tag == DT_RELENT || tag == DT_TEXTREL) |
99 | fail(format: "vdso image contains dynamic relocations\n" ); |
100 | } |
101 | |
102 | /* Walk the section table */ |
103 | secstrings_hdr = raw_addr + GET_LE(&hdr->e_shoff) + |
104 | GET_LE(&hdr->e_shentsize)*GET_LE(&hdr->e_shstrndx); |
105 | secstrings = raw_addr + GET_LE(&secstrings_hdr->sh_offset); |
106 | for (i = 0; i < GET_LE(&hdr->e_shnum); i++) { |
107 | ELF(Shdr) *sh = raw_addr + GET_LE(&hdr->e_shoff) + |
108 | GET_LE(&hdr->e_shentsize) * i; |
109 | if (GET_LE(&sh->sh_type) == SHT_SYMTAB) |
110 | symtab_hdr = sh; |
111 | |
112 | if (!strcmp(s1: secstrings + GET_LE(&sh->sh_name), |
113 | s2: ".altinstructions" )) |
114 | alt_sec = sh; |
115 | if (!strcmp(s1: secstrings + GET_LE(&sh->sh_name), s2: "__ex_table" )) |
116 | extable_sec = sh; |
117 | } |
118 | |
119 | if (!symtab_hdr) |
120 | fail(format: "no symbol table\n" ); |
121 | |
122 | strtab_hdr = raw_addr + GET_LE(&hdr->e_shoff) + |
123 | GET_LE(&hdr->e_shentsize) * GET_LE(&symtab_hdr->sh_link); |
124 | |
125 | syms_nr = GET_LE(&symtab_hdr->sh_size) / GET_LE(&symtab_hdr->sh_entsize); |
126 | /* Walk the symbol table */ |
127 | for (i = 0; i < syms_nr; i++) { |
128 | unsigned int k; |
129 | ELF(Sym) *sym = raw_addr + GET_LE(&symtab_hdr->sh_offset) + |
130 | GET_LE(&symtab_hdr->sh_entsize) * i; |
131 | const char *sym_name = raw_addr + |
132 | GET_LE(&strtab_hdr->sh_offset) + |
133 | GET_LE(&sym->st_name); |
134 | |
135 | for (k = 0; k < NSYMS; k++) { |
136 | if (!strcmp(s1: sym_name, s2: required_syms[k].name)) { |
137 | if (syms[k]) { |
138 | fail(format: "duplicate symbol %s\n" , |
139 | required_syms[k].name); |
140 | } |
141 | |
142 | /* |
143 | * Careful: we use negative addresses, but |
144 | * st_value is unsigned, so we rely |
145 | * on syms[k] being a signed type of the |
146 | * correct width. |
147 | */ |
148 | syms[k] = GET_LE(&sym->st_value); |
149 | } |
150 | } |
151 | } |
152 | |
153 | /* Validate mapping addresses. */ |
154 | for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) { |
155 | INT_BITS symval = syms[special_pages[i]]; |
156 | |
157 | if (!symval) |
158 | continue; /* The mapping isn't used; ignore it. */ |
159 | |
160 | if (symval % 4096) |
161 | fail(format: "%s must be a multiple of 4096\n" , |
162 | required_syms[i].name); |
163 | if (symval + 4096 < syms[sym_vvar_start]) |
164 | fail(format: "%s underruns vvar_start\n" , |
165 | required_syms[i].name); |
166 | if (symval + 4096 > 0) |
167 | fail(format: "%s is on the wrong side of the vdso text\n" , |
168 | required_syms[i].name); |
169 | } |
170 | if (syms[sym_vvar_start] % 4096) |
171 | fail(format: "vvar_begin must be a multiple of 4096\n" ); |
172 | |
173 | if (!image_name) { |
174 | fwrite(ptr: stripped_addr, size: stripped_len, n: 1, s: outfile); |
175 | return; |
176 | } |
177 | |
178 | mapping_size = (stripped_len + 4095) / 4096 * 4096; |
179 | |
180 | fprintf(stream: outfile, format: "/* AUTOMATICALLY GENERATED -- DO NOT EDIT */\n\n" ); |
181 | fprintf(stream: outfile, format: "#include <linux/linkage.h>\n" ); |
182 | fprintf(stream: outfile, format: "#include <linux/init.h>\n" ); |
183 | fprintf(stream: outfile, format: "#include <asm/page_types.h>\n" ); |
184 | fprintf(stream: outfile, format: "#include <asm/vdso.h>\n" ); |
185 | fprintf(stream: outfile, format: "\n" ); |
186 | fprintf(stream: outfile, |
187 | format: "static unsigned char raw_data[%lu] __ro_after_init __aligned(PAGE_SIZE) = {" , |
188 | mapping_size); |
189 | for (i = 0; i < stripped_len; i++) { |
190 | if (i % 10 == 0) |
191 | fprintf(stream: outfile, format: "\n\t" ); |
192 | fprintf(stream: outfile, format: "0x%02X, " , |
193 | (int)((unsigned char *)stripped_addr)[i]); |
194 | } |
195 | fprintf(stream: outfile, format: "\n};\n\n" ); |
196 | if (extable_sec) |
197 | BITSFUNC(extract)(data: raw_addr, data_len: raw_len, outfile, |
198 | sec: extable_sec, name: "extable" ); |
199 | |
200 | fprintf(stream: outfile, format: "const struct vdso_image %s = {\n" , image_name); |
201 | fprintf(stream: outfile, format: "\t.data = raw_data,\n" ); |
202 | fprintf(stream: outfile, format: "\t.size = %lu,\n" , mapping_size); |
203 | if (alt_sec) { |
204 | fprintf(stream: outfile, format: "\t.alt = %lu,\n" , |
205 | (unsigned long)GET_LE(&alt_sec->sh_offset)); |
206 | fprintf(stream: outfile, format: "\t.alt_len = %lu,\n" , |
207 | (unsigned long)GET_LE(&alt_sec->sh_size)); |
208 | } |
209 | if (extable_sec) { |
210 | fprintf(stream: outfile, format: "\t.extable_base = %lu,\n" , |
211 | (unsigned long)GET_LE(&extable_sec->sh_offset)); |
212 | fprintf(stream: outfile, format: "\t.extable_len = %lu,\n" , |
213 | (unsigned long)GET_LE(&extable_sec->sh_size)); |
214 | fprintf(stream: outfile, format: "\t.extable = extable,\n" ); |
215 | } |
216 | |
217 | for (i = 0; i < NSYMS; i++) { |
218 | if (required_syms[i].export && syms[i]) |
219 | fprintf(stream: outfile, format: "\t.sym_%s = %" PRIi64 ",\n" , |
220 | required_syms[i].name, (int64_t)syms[i]); |
221 | } |
222 | fprintf(stream: outfile, format: "};\n\n" ); |
223 | fprintf(stream: outfile, format: "static __init int init_%s(void) {\n" , image_name); |
224 | fprintf(stream: outfile, format: "\treturn init_vdso_image(&%s);\n" , image_name); |
225 | fprintf(stream: outfile, format: "};\n" ); |
226 | fprintf(stream: outfile, format: "subsys_initcall(init_%s);\n" , image_name); |
227 | |
228 | } |
229 | |