diff options
Diffstat (limited to 'arch/x86/vdso/vdso2c.h')
-rw-r--r-- | arch/x86/vdso/vdso2c.h | 202 |
1 files changed, 172 insertions, 30 deletions
diff --git a/arch/x86/vdso/vdso2c.h b/arch/x86/vdso/vdso2c.h index c6eefaf389b9..11b65d4f9414 100644 --- a/arch/x86/vdso/vdso2c.h +++ b/arch/x86/vdso/vdso2c.h | |||
@@ -4,23 +4,139 @@ | |||
4 | * are built for 32-bit userspace. | 4 | * are built for 32-bit userspace. |
5 | */ | 5 | */ |
6 | 6 | ||
7 | static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) | 7 | /* |
8 | * We're writing a section table for a few reasons: | ||
9 | * | ||
10 | * The Go runtime had a couple of bugs: it would read the section | ||
11 | * table to try to figure out how many dynamic symbols there were (it | ||
12 | * shouldn't have looked at the section table at all) and, if there | ||
13 | * were no SHT_SYNDYM section table entry, it would use an | ||
14 | * uninitialized value for the number of symbols. An empty DYNSYM | ||
15 | * table would work, but I see no reason not to write a valid one (and | ||
16 | * keep full performance for old Go programs). This hack is only | ||
17 | * needed on x86_64. | ||
18 | * | ||
19 | * The bug was introduced on 2012-08-31 by: | ||
20 | * https://code.google.com/p/go/source/detail?r=56ea40aac72b | ||
21 | * and was fixed on 2014-06-13 by: | ||
22 | * https://code.google.com/p/go/source/detail?r=fc1cd5e12595 | ||
23 | * | ||
24 | * Binutils has issues debugging the vDSO: it reads the section table to | ||
25 | * find SHT_NOTE; it won't look at PT_NOTE for the in-memory vDSO, which | ||
26 | * would break build-id if we removed the section table. Binutils | ||
27 | * also requires that shstrndx != 0. See: | ||
28 | * https://sourceware.org/bugzilla/show_bug.cgi?id=17064 | ||
29 | * | ||
30 | * elfutils might not look for PT_NOTE if there is a section table at | ||
31 | * all. I don't know whether this matters for any practical purpose. | ||
32 | * | ||
33 | * For simplicity, rather than hacking up a partial section table, we | ||
34 | * just write a mostly complete one. We omit non-dynamic symbols, | ||
35 | * though, since they're rather large. | ||
36 | * | ||
37 | * Once binutils gets fixed, we might be able to drop this for all but | ||
38 | * the 64-bit vdso, since build-id only works in kernel RPMs, and | ||
39 | * systems that update to new enough kernel RPMs will likely update | ||
40 | * binutils in sync. build-id has never worked for home-built kernel | ||
41 | * RPMs without manual symlinking, and I suspect that no one ever does | ||
42 | * that. | ||
43 | */ | ||
44 | struct BITSFUNC(fake_sections) | ||
45 | { | ||
46 | ELF(Shdr) *table; | ||
47 | unsigned long table_offset; | ||
48 | int count, max_count; | ||
49 | |||
50 | int in_shstrndx; | ||
51 | unsigned long shstr_offset; | ||
52 | const char *shstrtab; | ||
53 | size_t shstrtab_len; | ||
54 | |||
55 | int out_shstrndx; | ||
56 | }; | ||
57 | |||
58 | static unsigned int BITSFUNC(find_shname)(struct BITSFUNC(fake_sections) *out, | ||
59 | const char *name) | ||
60 | { | ||
61 | const char *outname = out->shstrtab; | ||
62 | while (outname - out->shstrtab < out->shstrtab_len) { | ||
63 | if (!strcmp(name, outname)) | ||
64 | return (outname - out->shstrtab) + out->shstr_offset; | ||
65 | outname += strlen(outname) + 1; | ||
66 | } | ||
67 | |||
68 | if (*name) | ||
69 | printf("Warning: could not find output name \"%s\"\n", name); | ||
70 | return out->shstr_offset + out->shstrtab_len - 1; /* Use a null. */ | ||
71 | } | ||
72 | |||
73 | static void BITSFUNC(init_sections)(struct BITSFUNC(fake_sections) *out) | ||
74 | { | ||
75 | if (!out->in_shstrndx) | ||
76 | fail("didn't find the fake shstrndx\n"); | ||
77 | |||
78 | memset(out->table, 0, out->max_count * sizeof(ELF(Shdr))); | ||
79 | |||
80 | if (out->max_count < 1) | ||
81 | fail("we need at least two fake output sections\n"); | ||
82 | |||
83 | PUT_LE(&out->table[0].sh_type, SHT_NULL); | ||
84 | PUT_LE(&out->table[0].sh_name, BITSFUNC(find_shname)(out, "")); | ||
85 | |||
86 | out->count = 1; | ||
87 | } | ||
88 | |||
89 | static void BITSFUNC(copy_section)(struct BITSFUNC(fake_sections) *out, | ||
90 | int in_idx, const ELF(Shdr) *in, | ||
91 | const char *name) | ||
92 | { | ||
93 | uint64_t flags = GET_LE(&in->sh_flags); | ||
94 | |||
95 | bool copy = flags & SHF_ALLOC && | ||
96 | (GET_LE(&in->sh_size) || | ||
97 | (GET_LE(&in->sh_type) != SHT_RELA && | ||
98 | GET_LE(&in->sh_type) != SHT_REL)) && | ||
99 | strcmp(name, ".altinstructions") && | ||
100 | strcmp(name, ".altinstr_replacement"); | ||
101 | |||
102 | if (!copy) | ||
103 | return; | ||
104 | |||
105 | if (out->count >= out->max_count) | ||
106 | fail("too many copied sections (max = %d)\n", out->max_count); | ||
107 | |||
108 | if (in_idx == out->in_shstrndx) | ||
109 | out->out_shstrndx = out->count; | ||
110 | |||
111 | out->table[out->count] = *in; | ||
112 | PUT_LE(&out->table[out->count].sh_name, | ||
113 | BITSFUNC(find_shname)(out, name)); | ||
114 | |||
115 | /* elfutils requires that a strtab have the correct type. */ | ||
116 | if (!strcmp(name, ".fake_shstrtab")) | ||
117 | PUT_LE(&out->table[out->count].sh_type, SHT_STRTAB); | ||
118 | |||
119 | out->count++; | ||
120 | } | ||
121 | |||
122 | static void BITSFUNC(go)(void *addr, size_t len, | ||
123 | FILE *outfile, const char *name) | ||
8 | { | 124 | { |
9 | int found_load = 0; | 125 | int found_load = 0; |
10 | unsigned long load_size = -1; /* Work around bogus warning */ | 126 | unsigned long load_size = -1; /* Work around bogus warning */ |
11 | unsigned long data_size; | 127 | unsigned long data_size; |
12 | Elf_Ehdr *hdr = (Elf_Ehdr *)addr; | 128 | ELF(Ehdr) *hdr = (ELF(Ehdr) *)addr; |
13 | int i; | 129 | int i; |
14 | unsigned long j; | 130 | unsigned long j; |
15 | Elf_Shdr *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr, | 131 | ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr, |
16 | *alt_sec = NULL; | 132 | *alt_sec = NULL; |
17 | Elf_Dyn *dyn = 0, *dyn_end = 0; | 133 | ELF(Dyn) *dyn = 0, *dyn_end = 0; |
18 | const char *secstrings; | 134 | const char *secstrings; |
19 | uint64_t syms[NSYMS] = {}; | 135 | uint64_t syms[NSYMS] = {}; |
20 | 136 | ||
21 | uint64_t fake_sections_value = 0, fake_sections_size = 0; | 137 | struct BITSFUNC(fake_sections) fake_sections = {}; |
22 | 138 | ||
23 | Elf_Phdr *pt = (Elf_Phdr *)(addr + GET_LE(&hdr->e_phoff)); | 139 | ELF(Phdr) *pt = (ELF(Phdr) *)(addr + GET_LE(&hdr->e_phoff)); |
24 | 140 | ||
25 | /* Walk the segment table. */ | 141 | /* Walk the segment table. */ |
26 | for (i = 0; i < GET_LE(&hdr->e_phnum); i++) { | 142 | for (i = 0; i < GET_LE(&hdr->e_phnum); i++) { |
@@ -51,7 +167,7 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) | |||
51 | for (i = 0; dyn + i < dyn_end && | 167 | for (i = 0; dyn + i < dyn_end && |
52 | GET_LE(&dyn[i].d_tag) != DT_NULL; i++) { | 168 | GET_LE(&dyn[i].d_tag) != DT_NULL; i++) { |
53 | typeof(dyn[i].d_tag) tag = GET_LE(&dyn[i].d_tag); | 169 | typeof(dyn[i].d_tag) tag = GET_LE(&dyn[i].d_tag); |
54 | if (tag == DT_REL || tag == DT_RELSZ || | 170 | if (tag == DT_REL || tag == DT_RELSZ || tag == DT_RELA || |
55 | tag == DT_RELENT || tag == DT_TEXTREL) | 171 | tag == DT_RELENT || tag == DT_TEXTREL) |
56 | fail("vdso image contains dynamic relocations\n"); | 172 | fail("vdso image contains dynamic relocations\n"); |
57 | } | 173 | } |
@@ -61,7 +177,7 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) | |||
61 | GET_LE(&hdr->e_shentsize)*GET_LE(&hdr->e_shstrndx); | 177 | GET_LE(&hdr->e_shentsize)*GET_LE(&hdr->e_shstrndx); |
62 | secstrings = addr + GET_LE(&secstrings_hdr->sh_offset); | 178 | secstrings = addr + GET_LE(&secstrings_hdr->sh_offset); |
63 | for (i = 0; i < GET_LE(&hdr->e_shnum); i++) { | 179 | for (i = 0; i < GET_LE(&hdr->e_shnum); i++) { |
64 | Elf_Shdr *sh = addr + GET_LE(&hdr->e_shoff) + | 180 | ELF(Shdr) *sh = addr + GET_LE(&hdr->e_shoff) + |
65 | GET_LE(&hdr->e_shentsize) * i; | 181 | GET_LE(&hdr->e_shentsize) * i; |
66 | if (GET_LE(&sh->sh_type) == SHT_SYMTAB) | 182 | if (GET_LE(&sh->sh_type) == SHT_SYMTAB) |
67 | symtab_hdr = sh; | 183 | symtab_hdr = sh; |
@@ -82,29 +198,63 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) | |||
82 | i < GET_LE(&symtab_hdr->sh_size) / GET_LE(&symtab_hdr->sh_entsize); | 198 | i < GET_LE(&symtab_hdr->sh_size) / GET_LE(&symtab_hdr->sh_entsize); |
83 | i++) { | 199 | i++) { |
84 | int k; | 200 | int k; |
85 | Elf_Sym *sym = addr + GET_LE(&symtab_hdr->sh_offset) + | 201 | ELF(Sym) *sym = addr + GET_LE(&symtab_hdr->sh_offset) + |
86 | GET_LE(&symtab_hdr->sh_entsize) * i; | 202 | GET_LE(&symtab_hdr->sh_entsize) * i; |
87 | const char *name = addr + GET_LE(&strtab_hdr->sh_offset) + | 203 | const char *name = addr + GET_LE(&strtab_hdr->sh_offset) + |
88 | GET_LE(&sym->st_name); | 204 | GET_LE(&sym->st_name); |
89 | 205 | ||
90 | for (k = 0; k < NSYMS; k++) { | 206 | for (k = 0; k < NSYMS; k++) { |
91 | if (!strcmp(name, required_syms[k])) { | 207 | if (!strcmp(name, required_syms[k].name)) { |
92 | if (syms[k]) { | 208 | if (syms[k]) { |
93 | fail("duplicate symbol %s\n", | 209 | fail("duplicate symbol %s\n", |
94 | required_syms[k]); | 210 | required_syms[k].name); |
95 | } | 211 | } |
96 | syms[k] = GET_LE(&sym->st_value); | 212 | syms[k] = GET_LE(&sym->st_value); |
97 | } | 213 | } |
98 | } | 214 | } |
99 | 215 | ||
100 | if (!strcmp(name, "vdso_fake_sections")) { | 216 | if (!strcmp(name, "fake_shstrtab")) { |
101 | if (fake_sections_value) | 217 | ELF(Shdr) *sh; |
102 | fail("duplicate vdso_fake_sections\n"); | 218 | |
103 | fake_sections_value = GET_LE(&sym->st_value); | 219 | fake_sections.in_shstrndx = GET_LE(&sym->st_shndx); |
104 | fake_sections_size = GET_LE(&sym->st_size); | 220 | fake_sections.shstrtab = addr + GET_LE(&sym->st_value); |
221 | fake_sections.shstrtab_len = GET_LE(&sym->st_size); | ||
222 | sh = addr + GET_LE(&hdr->e_shoff) + | ||
223 | GET_LE(&hdr->e_shentsize) * | ||
224 | fake_sections.in_shstrndx; | ||
225 | fake_sections.shstr_offset = GET_LE(&sym->st_value) - | ||
226 | GET_LE(&sh->sh_addr); | ||
105 | } | 227 | } |
106 | } | 228 | } |
107 | 229 | ||
230 | /* Build the output section table. */ | ||
231 | if (!syms[sym_VDSO_FAKE_SECTION_TABLE_START] || | ||
232 | !syms[sym_VDSO_FAKE_SECTION_TABLE_END]) | ||
233 | fail("couldn't find fake section table\n"); | ||
234 | if ((syms[sym_VDSO_FAKE_SECTION_TABLE_END] - | ||
235 | syms[sym_VDSO_FAKE_SECTION_TABLE_START]) % sizeof(ELF(Shdr))) | ||
236 | fail("fake section table size isn't a multiple of sizeof(Shdr)\n"); | ||
237 | fake_sections.table = addr + syms[sym_VDSO_FAKE_SECTION_TABLE_START]; | ||
238 | fake_sections.table_offset = syms[sym_VDSO_FAKE_SECTION_TABLE_START]; | ||
239 | fake_sections.max_count = (syms[sym_VDSO_FAKE_SECTION_TABLE_END] - | ||
240 | syms[sym_VDSO_FAKE_SECTION_TABLE_START]) / | ||
241 | sizeof(ELF(Shdr)); | ||
242 | |||
243 | BITSFUNC(init_sections)(&fake_sections); | ||
244 | for (i = 0; i < GET_LE(&hdr->e_shnum); i++) { | ||
245 | ELF(Shdr) *sh = addr + GET_LE(&hdr->e_shoff) + | ||
246 | GET_LE(&hdr->e_shentsize) * i; | ||
247 | BITSFUNC(copy_section)(&fake_sections, i, sh, | ||
248 | secstrings + GET_LE(&sh->sh_name)); | ||
249 | } | ||
250 | if (!fake_sections.out_shstrndx) | ||
251 | fail("didn't generate shstrndx?!?\n"); | ||
252 | |||
253 | PUT_LE(&hdr->e_shoff, fake_sections.table_offset); | ||
254 | PUT_LE(&hdr->e_shentsize, sizeof(ELF(Shdr))); | ||
255 | PUT_LE(&hdr->e_shnum, fake_sections.count); | ||
256 | PUT_LE(&hdr->e_shstrndx, fake_sections.out_shstrndx); | ||
257 | |||
108 | /* Validate mapping addresses. */ | 258 | /* Validate mapping addresses. */ |
109 | for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) { | 259 | for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) { |
110 | if (!syms[i]) | 260 | if (!syms[i]) |
@@ -112,25 +262,17 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) | |||
112 | 262 | ||
113 | if (syms[i] % 4096) | 263 | if (syms[i] % 4096) |
114 | fail("%s must be a multiple of 4096\n", | 264 | fail("%s must be a multiple of 4096\n", |
115 | required_syms[i]); | 265 | required_syms[i].name); |
116 | if (syms[i] < data_size) | 266 | if (syms[i] < data_size) |
117 | fail("%s must be after the text mapping\n", | 267 | fail("%s must be after the text mapping\n", |
118 | required_syms[i]); | 268 | required_syms[i].name); |
119 | if (syms[sym_end_mapping] < syms[i] + 4096) | 269 | if (syms[sym_end_mapping] < syms[i] + 4096) |
120 | fail("%s overruns end_mapping\n", required_syms[i]); | 270 | fail("%s overruns end_mapping\n", |
271 | required_syms[i].name); | ||
121 | } | 272 | } |
122 | if (syms[sym_end_mapping] % 4096) | 273 | if (syms[sym_end_mapping] % 4096) |
123 | fail("end_mapping must be a multiple of 4096\n"); | 274 | fail("end_mapping must be a multiple of 4096\n"); |
124 | 275 | ||
125 | /* Remove sections or use fakes */ | ||
126 | if (fake_sections_size % sizeof(Elf_Shdr)) | ||
127 | fail("vdso_fake_sections size is not a multiple of %ld\n", | ||
128 | (long)sizeof(Elf_Shdr)); | ||
129 | PUT_LE(&hdr->e_shoff, fake_sections_value); | ||
130 | PUT_LE(&hdr->e_shentsize, fake_sections_value ? sizeof(Elf_Shdr) : 0); | ||
131 | PUT_LE(&hdr->e_shnum, fake_sections_size / sizeof(Elf_Shdr)); | ||
132 | PUT_LE(&hdr->e_shstrndx, SHN_UNDEF); | ||
133 | |||
134 | if (!name) { | 276 | if (!name) { |
135 | fwrite(addr, load_size, 1, outfile); | 277 | fwrite(addr, load_size, 1, outfile); |
136 | return; | 278 | return; |
@@ -168,9 +310,9 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) | |||
168 | (unsigned long)GET_LE(&alt_sec->sh_size)); | 310 | (unsigned long)GET_LE(&alt_sec->sh_size)); |
169 | } | 311 | } |
170 | for (i = 0; i < NSYMS; i++) { | 312 | for (i = 0; i < NSYMS; i++) { |
171 | if (syms[i]) | 313 | if (required_syms[i].export && syms[i]) |
172 | fprintf(outfile, "\t.sym_%s = 0x%" PRIx64 ",\n", | 314 | fprintf(outfile, "\t.sym_%s = 0x%" PRIx64 ",\n", |
173 | required_syms[i], syms[i]); | 315 | required_syms[i].name, syms[i]); |
174 | } | 316 | } |
175 | fprintf(outfile, "};\n"); | 317 | fprintf(outfile, "};\n"); |
176 | } | 318 | } |