1 | /* |
2 | * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. |
3 | */ |
4 | |
5 | /* |
6 | * This file is included up to twice from vdso2c.c. It generates code for |
7 | * 32-bit and 64-bit vDSOs. We will eventually need both for 64-bit builds, |
8 | * since 32-bit vDSOs will then be built for 32-bit userspace. |
9 | */ |
10 | |
11 | static void BITSFUNC(go)(void *raw_addr, size_t raw_len, |
12 | void *stripped_addr, size_t stripped_len, |
13 | FILE *outfile, const char *name) |
14 | { |
15 | int found_load = 0; |
16 | unsigned long load_size = -1; /* Work around bogus warning */ |
17 | unsigned long mapping_size; |
18 | int i; |
19 | unsigned long j; |
20 | ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr; |
21 | ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr; |
22 | ELF(Dyn) *dyn = 0, *dyn_end = 0; |
23 | INT_BITS syms[NSYMS] = {}; |
24 | |
25 | ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_BE(&hdr->e_phoff)); |
26 | |
27 | /* Walk the segment table. */ |
28 | for (i = 0; i < GET_BE(&hdr->e_phnum); i++) { |
29 | if (GET_BE(&pt[i].p_type) == PT_LOAD) { |
30 | if (found_load) |
31 | fail(format: "multiple PT_LOAD segs\n" ); |
32 | |
33 | if (GET_BE(&pt[i].p_offset) != 0 || |
34 | GET_BE(&pt[i].p_vaddr) != 0) |
35 | fail(format: "PT_LOAD in wrong place\n" ); |
36 | |
37 | if (GET_BE(&pt[i].p_memsz) != GET_BE(&pt[i].p_filesz)) |
38 | fail(format: "cannot handle memsz != filesz\n" ); |
39 | |
40 | load_size = GET_BE(&pt[i].p_memsz); |
41 | found_load = 1; |
42 | } else if (GET_BE(&pt[i].p_type) == PT_DYNAMIC) { |
43 | dyn = raw_addr + GET_BE(&pt[i].p_offset); |
44 | dyn_end = raw_addr + GET_BE(&pt[i].p_offset) + |
45 | GET_BE(&pt[i].p_memsz); |
46 | } |
47 | } |
48 | if (!found_load) |
49 | fail(format: "no PT_LOAD seg\n" ); |
50 | |
51 | if (stripped_len < load_size) |
52 | fail(format: "stripped input is too short\n" ); |
53 | |
54 | /* Walk the dynamic table */ |
55 | for (i = 0; dyn + i < dyn_end && |
56 | GET_BE(&dyn[i].d_tag) != DT_NULL; i++) { |
57 | typeof(dyn[i].d_tag) tag = GET_BE(&dyn[i].d_tag); |
58 | typeof(dyn[i].d_un.d_val) val = GET_BE(&dyn[i].d_un.d_val); |
59 | |
60 | if ((tag == DT_RELSZ || tag == DT_RELASZ) && (val != 0)) |
61 | fail(format: "vdso image contains dynamic relocations\n" ); |
62 | } |
63 | |
64 | /* Walk the section table */ |
65 | for (i = 0; i < GET_BE(&hdr->e_shnum); i++) { |
66 | ELF(Shdr) *sh = raw_addr + GET_BE(&hdr->e_shoff) + |
67 | GET_BE(&hdr->e_shentsize) * i; |
68 | if (GET_BE(&sh->sh_type) == SHT_SYMTAB) |
69 | symtab_hdr = sh; |
70 | } |
71 | |
72 | if (!symtab_hdr) |
73 | fail(format: "no symbol table\n" ); |
74 | |
75 | strtab_hdr = raw_addr + GET_BE(&hdr->e_shoff) + |
76 | GET_BE(&hdr->e_shentsize) * GET_BE(&symtab_hdr->sh_link); |
77 | |
78 | /* Walk the symbol table */ |
79 | for (i = 0; |
80 | i < GET_BE(&symtab_hdr->sh_size) / GET_BE(&symtab_hdr->sh_entsize); |
81 | i++) { |
82 | int k; |
83 | |
84 | ELF(Sym) *sym = raw_addr + GET_BE(&symtab_hdr->sh_offset) + |
85 | GET_BE(&symtab_hdr->sh_entsize) * i; |
86 | const char *name = raw_addr + GET_BE(&strtab_hdr->sh_offset) + |
87 | GET_BE(&sym->st_name); |
88 | |
89 | for (k = 0; k < NSYMS; k++) { |
90 | if (!strcmp(s1: name, s2: required_syms[k].name)) { |
91 | if (syms[k]) { |
92 | fail(format: "duplicate symbol %s\n" , |
93 | required_syms[k].name); |
94 | } |
95 | |
96 | /* |
97 | * Careful: we use negative addresses, but |
98 | * st_value is unsigned, so we rely |
99 | * on syms[k] being a signed type of the |
100 | * correct width. |
101 | */ |
102 | syms[k] = GET_BE(&sym->st_value); |
103 | } |
104 | } |
105 | } |
106 | |
107 | /* Validate mapping addresses. */ |
108 | if (syms[sym_vvar_start] % 8192) |
109 | fail(format: "vvar_begin must be a multiple of 8192\n" ); |
110 | |
111 | if (!name) { |
112 | fwrite(ptr: stripped_addr, size: stripped_len, n: 1, s: outfile); |
113 | return; |
114 | } |
115 | |
116 | mapping_size = (stripped_len + 8191) / 8192 * 8192; |
117 | |
118 | fprintf(stream: outfile, format: "/* AUTOMATICALLY GENERATED -- DO NOT EDIT */\n\n" ); |
119 | fprintf(stream: outfile, format: "#include <linux/cache.h>\n" ); |
120 | fprintf(stream: outfile, format: "#include <asm/vdso.h>\n" ); |
121 | fprintf(stream: outfile, format: "\n" ); |
122 | fprintf(stream: outfile, |
123 | format: "static unsigned char raw_data[%lu] __ro_after_init __aligned(8192)= {" , |
124 | mapping_size); |
125 | for (j = 0; j < stripped_len; j++) { |
126 | if (j % 10 == 0) |
127 | fprintf(stream: outfile, format: "\n\t" ); |
128 | fprintf(stream: outfile, format: "0x%02X, " , |
129 | (int)((unsigned char *)stripped_addr)[j]); |
130 | } |
131 | fprintf(stream: outfile, format: "\n};\n\n" ); |
132 | |
133 | fprintf(stream: outfile, format: "const struct vdso_image %s_builtin = {\n" , name); |
134 | fprintf(stream: outfile, format: "\t.data = raw_data,\n" ); |
135 | fprintf(stream: outfile, format: "\t.size = %lu,\n" , mapping_size); |
136 | for (i = 0; i < NSYMS; i++) { |
137 | if (required_syms[i].export && syms[i]) |
138 | fprintf(stream: outfile, format: "\t.sym_%s = %" PRIi64 ",\n" , |
139 | required_syms[i].name, (int64_t)syms[i]); |
140 | } |
141 | fprintf(stream: outfile, format: "};\n" ); |
142 | } |
143 | |