1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #include <stdio.h> |
4 | #include <stdarg.h> |
5 | #include <stdlib.h> |
6 | #include <stdint.h> |
7 | #include <inttypes.h> |
8 | #include <string.h> |
9 | #include <errno.h> |
10 | #include <unistd.h> |
11 | #include <elf.h> |
12 | #include <byteswap.h> |
13 | #define USE_BSD |
14 | #include <endian.h> |
15 | |
16 | #define ELF_BITS 64 |
17 | |
18 | #define ELF_MACHINE EM_S390 |
19 | #define ELF_MACHINE_NAME "IBM S/390" |
20 | #define SHT_REL_TYPE SHT_RELA |
21 | #define Elf_Rel Elf64_Rela |
22 | |
23 | #define ELF_CLASS ELFCLASS64 |
24 | #define ELF_ENDIAN ELFDATA2MSB |
25 | #define ELF_R_SYM(val) ELF64_R_SYM(val) |
26 | #define ELF_R_TYPE(val) ELF64_R_TYPE(val) |
27 | #define ELF_ST_TYPE(o) ELF64_ST_TYPE(o) |
28 | #define ELF_ST_BIND(o) ELF64_ST_BIND(o) |
29 | #define ELF_ST_VISIBILITY(o) ELF64_ST_VISIBILITY(o) |
30 | |
31 | #define ElfW(type) _ElfW(ELF_BITS, type) |
32 | #define _ElfW(bits, type) __ElfW(bits, type) |
33 | #define __ElfW(bits, type) Elf##bits##_##type |
34 | |
35 | #define Elf_Addr ElfW(Addr) |
36 | #define Elf_Ehdr ElfW(Ehdr) |
37 | #define Elf_Phdr ElfW(Phdr) |
38 | #define Elf_Shdr ElfW(Shdr) |
39 | #define Elf_Sym ElfW(Sym) |
40 | |
41 | static Elf_Ehdr ehdr; |
42 | static unsigned long shnum; |
43 | static unsigned int shstrndx; |
44 | |
45 | struct relocs { |
46 | uint32_t *offset; |
47 | unsigned long count; |
48 | unsigned long size; |
49 | }; |
50 | |
51 | static struct relocs relocs64; |
52 | #define FMT PRIu64 |
53 | |
54 | struct section { |
55 | Elf_Shdr shdr; |
56 | struct section *link; |
57 | Elf_Rel *reltab; |
58 | }; |
59 | |
60 | static struct section *secs; |
61 | |
62 | #if BYTE_ORDER == LITTLE_ENDIAN |
63 | #define le16_to_cpu(val) (val) |
64 | #define le32_to_cpu(val) (val) |
65 | #define le64_to_cpu(val) (val) |
66 | #define be16_to_cpu(val) bswap_16(val) |
67 | #define be32_to_cpu(val) bswap_32(val) |
68 | #define be64_to_cpu(val) bswap_64(val) |
69 | #endif |
70 | |
71 | #if BYTE_ORDER == BIG_ENDIAN |
72 | #define le16_to_cpu(val) bswap_16(val) |
73 | #define le32_to_cpu(val) bswap_32(val) |
74 | #define le64_to_cpu(val) bswap_64(val) |
75 | #define be16_to_cpu(val) (val) |
76 | #define be32_to_cpu(val) (val) |
77 | #define be64_to_cpu(val) (val) |
78 | #endif |
79 | |
80 | static uint16_t elf16_to_cpu(uint16_t val) |
81 | { |
82 | if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) |
83 | return le16_to_cpu(val); |
84 | else |
85 | return be16_to_cpu(val); |
86 | } |
87 | |
88 | static uint32_t elf32_to_cpu(uint32_t val) |
89 | { |
90 | if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) |
91 | return le32_to_cpu(val); |
92 | else |
93 | return be32_to_cpu(val); |
94 | } |
95 | |
96 | #define elf_half_to_cpu(x) elf16_to_cpu(x) |
97 | #define elf_word_to_cpu(x) elf32_to_cpu(x) |
98 | |
99 | static uint64_t elf64_to_cpu(uint64_t val) |
100 | { |
101 | return be64_to_cpu(val); |
102 | } |
103 | |
104 | #define elf_addr_to_cpu(x) elf64_to_cpu(x) |
105 | #define elf_off_to_cpu(x) elf64_to_cpu(x) |
106 | #define elf_xword_to_cpu(x) elf64_to_cpu(x) |
107 | |
108 | static void die(char *fmt, ...) |
109 | { |
110 | va_list ap; |
111 | |
112 | va_start(ap, fmt); |
113 | vfprintf(stderr, format: fmt, arg: ap); |
114 | va_end(ap); |
115 | exit(status: 1); |
116 | } |
117 | |
118 | static void read_ehdr(FILE *fp) |
119 | { |
120 | if (fread(ptr: &ehdr, size: sizeof(ehdr), n: 1, stream: fp) != 1) |
121 | die(fmt: "Cannot read ELF header: %s\n" , strerror(errno)); |
122 | if (memcmp(s1: ehdr.e_ident, ELFMAG, SELFMAG) != 0) |
123 | die(fmt: "No ELF magic\n" ); |
124 | if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) |
125 | die(fmt: "Not a %d bit executable\n" , ELF_BITS); |
126 | if (ehdr.e_ident[EI_DATA] != ELF_ENDIAN) |
127 | die(fmt: "ELF endian mismatch\n" ); |
128 | if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) |
129 | die(fmt: "Unknown ELF version\n" ); |
130 | |
131 | /* Convert the fields to native endian */ |
132 | ehdr.e_type = elf_half_to_cpu(ehdr.e_type); |
133 | ehdr.e_machine = elf_half_to_cpu(ehdr.e_machine); |
134 | ehdr.e_version = elf_word_to_cpu(ehdr.e_version); |
135 | ehdr.e_entry = elf_addr_to_cpu(ehdr.e_entry); |
136 | ehdr.e_phoff = elf_off_to_cpu(ehdr.e_phoff); |
137 | ehdr.e_shoff = elf_off_to_cpu(ehdr.e_shoff); |
138 | ehdr.e_flags = elf_word_to_cpu(ehdr.e_flags); |
139 | ehdr.e_ehsize = elf_half_to_cpu(ehdr.e_ehsize); |
140 | ehdr.e_phentsize = elf_half_to_cpu(ehdr.e_phentsize); |
141 | ehdr.e_phnum = elf_half_to_cpu(ehdr.e_phnum); |
142 | ehdr.e_shentsize = elf_half_to_cpu(ehdr.e_shentsize); |
143 | ehdr.e_shnum = elf_half_to_cpu(ehdr.e_shnum); |
144 | ehdr.e_shstrndx = elf_half_to_cpu(ehdr.e_shstrndx); |
145 | |
146 | shnum = ehdr.e_shnum; |
147 | shstrndx = ehdr.e_shstrndx; |
148 | |
149 | if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) |
150 | die(fmt: "Unsupported ELF header type\n" ); |
151 | if (ehdr.e_machine != ELF_MACHINE) |
152 | die(fmt: "Not for %s\n" , ELF_MACHINE_NAME); |
153 | if (ehdr.e_version != EV_CURRENT) |
154 | die(fmt: "Unknown ELF version\n" ); |
155 | if (ehdr.e_ehsize != sizeof(Elf_Ehdr)) |
156 | die(fmt: "Bad Elf header size\n" ); |
157 | if (ehdr.e_phentsize != sizeof(Elf_Phdr)) |
158 | die(fmt: "Bad program header entry\n" ); |
159 | if (ehdr.e_shentsize != sizeof(Elf_Shdr)) |
160 | die(fmt: "Bad section header entry\n" ); |
161 | |
162 | if (shnum == SHN_UNDEF || shstrndx == SHN_XINDEX) { |
163 | Elf_Shdr shdr; |
164 | |
165 | if (fseek(stream: fp, off: ehdr.e_shoff, SEEK_SET) < 0) |
166 | die(fmt: "Seek to %" FMT " failed: %s\n" , ehdr.e_shoff, strerror(errno)); |
167 | |
168 | if (fread(ptr: &shdr, size: sizeof(shdr), n: 1, stream: fp) != 1) |
169 | die(fmt: "Cannot read initial ELF section header: %s\n" , strerror(errno)); |
170 | |
171 | if (shnum == SHN_UNDEF) |
172 | shnum = elf_xword_to_cpu(shdr.sh_size); |
173 | |
174 | if (shstrndx == SHN_XINDEX) |
175 | shstrndx = elf_word_to_cpu(shdr.sh_link); |
176 | } |
177 | |
178 | if (shstrndx >= shnum) |
179 | die(fmt: "String table index out of bounds\n" ); |
180 | } |
181 | |
182 | static void read_shdrs(FILE *fp) |
183 | { |
184 | Elf_Shdr shdr; |
185 | int i; |
186 | |
187 | secs = calloc(nmemb: shnum, size: sizeof(struct section)); |
188 | if (!secs) |
189 | die(fmt: "Unable to allocate %ld section headers\n" , shnum); |
190 | |
191 | if (fseek(stream: fp, off: ehdr.e_shoff, SEEK_SET) < 0) |
192 | die(fmt: "Seek to %" FMT " failed: %s\n" , ehdr.e_shoff, strerror(errno)); |
193 | |
194 | for (i = 0; i < shnum; i++) { |
195 | struct section *sec = &secs[i]; |
196 | |
197 | if (fread(ptr: &shdr, size: sizeof(shdr), n: 1, stream: fp) != 1) { |
198 | die(fmt: "Cannot read ELF section headers %d/%ld: %s\n" , |
199 | i, shnum, strerror(errno)); |
200 | } |
201 | |
202 | sec->shdr.sh_name = elf_word_to_cpu(shdr.sh_name); |
203 | sec->shdr.sh_type = elf_word_to_cpu(shdr.sh_type); |
204 | sec->shdr.sh_flags = elf_xword_to_cpu(shdr.sh_flags); |
205 | sec->shdr.sh_addr = elf_addr_to_cpu(shdr.sh_addr); |
206 | sec->shdr.sh_offset = elf_off_to_cpu(shdr.sh_offset); |
207 | sec->shdr.sh_size = elf_xword_to_cpu(shdr.sh_size); |
208 | sec->shdr.sh_link = elf_word_to_cpu(shdr.sh_link); |
209 | sec->shdr.sh_info = elf_word_to_cpu(shdr.sh_info); |
210 | sec->shdr.sh_addralign = elf_xword_to_cpu(shdr.sh_addralign); |
211 | sec->shdr.sh_entsize = elf_xword_to_cpu(shdr.sh_entsize); |
212 | |
213 | if (sec->shdr.sh_link < shnum) |
214 | sec->link = &secs[sec->shdr.sh_link]; |
215 | } |
216 | |
217 | } |
218 | |
219 | static void read_relocs(FILE *fp) |
220 | { |
221 | int i, j; |
222 | |
223 | for (i = 0; i < shnum; i++) { |
224 | struct section *sec = &secs[i]; |
225 | |
226 | if (sec->shdr.sh_type != SHT_REL_TYPE) |
227 | continue; |
228 | |
229 | sec->reltab = malloc(size: sec->shdr.sh_size); |
230 | if (!sec->reltab) |
231 | die(fmt: "malloc of %" FMT " bytes for relocs failed\n" , sec->shdr.sh_size); |
232 | |
233 | if (fseek(stream: fp, off: sec->shdr.sh_offset, SEEK_SET) < 0) |
234 | die(fmt: "Seek to %" FMT " failed: %s\n" , sec->shdr.sh_offset, strerror(errno)); |
235 | |
236 | if (fread(ptr: sec->reltab, size: 1, n: sec->shdr.sh_size, stream: fp) != sec->shdr.sh_size) |
237 | die(fmt: "Cannot read symbol table: %s\n" , strerror(errno)); |
238 | |
239 | for (j = 0; j < sec->shdr.sh_size / sizeof(Elf_Rel); j++) { |
240 | Elf_Rel *rel = &sec->reltab[j]; |
241 | |
242 | rel->r_offset = elf_addr_to_cpu(rel->r_offset); |
243 | rel->r_info = elf_xword_to_cpu(rel->r_info); |
244 | #if (SHT_REL_TYPE == SHT_RELA) |
245 | rel->r_addend = elf_xword_to_cpu(rel->r_addend); |
246 | #endif |
247 | } |
248 | } |
249 | } |
250 | |
251 | static void add_reloc(struct relocs *r, uint32_t offset) |
252 | { |
253 | if (r->count == r->size) { |
254 | unsigned long newsize = r->size + 50000; |
255 | void *mem = realloc(ptr: r->offset, size: newsize * sizeof(r->offset[0])); |
256 | |
257 | if (!mem) |
258 | die(fmt: "realloc of %ld entries for relocs failed\n" , newsize); |
259 | |
260 | r->offset = mem; |
261 | r->size = newsize; |
262 | } |
263 | r->offset[r->count++] = offset; |
264 | } |
265 | |
266 | static int do_reloc(struct section *sec, Elf_Rel *rel) |
267 | { |
268 | unsigned int r_type = ELF64_R_TYPE(rel->r_info); |
269 | ElfW(Addr) offset = rel->r_offset; |
270 | |
271 | switch (r_type) { |
272 | case R_390_NONE: |
273 | case R_390_PC32: |
274 | case R_390_PC64: |
275 | case R_390_PC16DBL: |
276 | case R_390_PC32DBL: |
277 | case R_390_PLT32DBL: |
278 | case R_390_GOTENT: |
279 | case R_390_GOTPCDBL: |
280 | case R_390_GOTOFF64: |
281 | break; |
282 | case R_390_64: |
283 | add_reloc(r: &relocs64, offset); |
284 | break; |
285 | default: |
286 | die(fmt: "Unsupported relocation type: %d\n" , r_type); |
287 | break; |
288 | } |
289 | |
290 | return 0; |
291 | } |
292 | |
293 | static void walk_relocs(void) |
294 | { |
295 | int i; |
296 | |
297 | /* Walk through the relocations */ |
298 | for (i = 0; i < shnum; i++) { |
299 | struct section *sec_applies; |
300 | int j; |
301 | struct section *sec = &secs[i]; |
302 | |
303 | if (sec->shdr.sh_type != SHT_REL_TYPE) |
304 | continue; |
305 | |
306 | sec_applies = &secs[sec->shdr.sh_info]; |
307 | if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) |
308 | continue; |
309 | |
310 | for (j = 0; j < sec->shdr.sh_size / sizeof(Elf_Rel); j++) { |
311 | Elf_Rel *rel = &sec->reltab[j]; |
312 | |
313 | do_reloc(sec, rel); |
314 | } |
315 | } |
316 | } |
317 | |
318 | static int cmp_relocs(const void *va, const void *vb) |
319 | { |
320 | const uint32_t *a, *b; |
321 | |
322 | a = va; b = vb; |
323 | return (*a == *b) ? 0 : (*a > *b) ? 1 : -1; |
324 | } |
325 | |
326 | static void sort_relocs(struct relocs *r) |
327 | { |
328 | qsort(base: r->offset, nmemb: r->count, size: sizeof(r->offset[0]), compar: cmp_relocs); |
329 | } |
330 | |
331 | static int print_reloc(uint32_t v) |
332 | { |
333 | return fprintf(stdout, format: "\t.long 0x%08" PRIx32"\n" , v) > 0 ? 0 : -1; |
334 | } |
335 | |
336 | static void emit_relocs(void) |
337 | { |
338 | int i; |
339 | |
340 | walk_relocs(); |
341 | sort_relocs(r: &relocs64); |
342 | |
343 | printf(format: ".section \".vmlinux.relocs_64\",\"a\"\n" ); |
344 | for (i = 0; i < relocs64.count; i++) |
345 | print_reloc(v: relocs64.offset[i]); |
346 | } |
347 | |
348 | static void process(FILE *fp) |
349 | { |
350 | read_ehdr(fp); |
351 | read_shdrs(fp); |
352 | read_relocs(fp); |
353 | emit_relocs(); |
354 | } |
355 | |
356 | static void usage(void) |
357 | { |
358 | die(fmt: "relocs vmlinux\n" ); |
359 | } |
360 | |
361 | int main(int argc, char **argv) |
362 | { |
363 | unsigned char e_ident[EI_NIDENT]; |
364 | const char *fname; |
365 | FILE *fp; |
366 | |
367 | fname = NULL; |
368 | |
369 | if (argc != 2) |
370 | usage(); |
371 | |
372 | fname = argv[1]; |
373 | |
374 | fp = fopen(filename: fname, modes: "r" ); |
375 | if (!fp) |
376 | die(fmt: "Cannot open %s: %s\n" , fname, strerror(errno)); |
377 | |
378 | if (fread(ptr: &e_ident, size: 1, EI_NIDENT, stream: fp) != EI_NIDENT) |
379 | die(fmt: "Cannot read %s: %s" , fname, strerror(errno)); |
380 | |
381 | rewind(stream: fp); |
382 | |
383 | process(fp); |
384 | |
385 | fclose(stream: fp); |
386 | return 0; |
387 | } |
388 | |