diff options
author | Andy Lutomirski <luto@amacapital.net> | 2014-06-18 18:59:48 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2014-06-19 18:45:12 -0400 |
commit | bfad381c0d1e19cae8461e105d8d4387dd2a14fe (patch) | |
tree | c4f4fba23ad53bfe9c6f99c4c9156716fa3fa5c5 /arch/x86/vdso/vdso2c.h | |
parent | c1979c370273fd9f7326ffa27a63b9ddb0f495f4 (diff) |
x86/vdso: Improve the fake section headers
Fully stripping the vDSO has other unfortunate side effects:
- binutils is unable to find ELF notes without a SHT_NOTE section.
- Even elfutils has trouble: it can find ELF notes without a section
table at all, but if a section table is present, it won't look for
PT_NOTE.
- gdb wants section names to match between stripped DSOs and their
symbols; otherwise it will corrupt symbol addresses.
We're also breaking the rules: section 0 is supposed to be SHT_NULL.
Fix these problems by building a better fake section table. While
we're at it, we might as well let buggy Go versions keep working well
by giving the SHT_DYNSYM entry the correct size.
This is a bit unfortunate: it adds quite a bit of size to the vdso
image.
If/when binutils improves and the improved versions become widespread,
it would be worth considering dropping most of this.
Signed-off-by: Andy Lutomirski <luto@amacapital.net>
Link: http://lkml.kernel.org/r/0e546a5eeaafdf1840e6ee654a55c1e727c26663.1403129369.git.luto@amacapital.net
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86/vdso/vdso2c.h')
-rw-r--r-- | arch/x86/vdso/vdso2c.h | 180 |
1 files changed, 158 insertions, 22 deletions
diff --git a/arch/x86/vdso/vdso2c.h b/arch/x86/vdso/vdso2c.h index 8e185ce39e69..f01ed4bde880 100644 --- a/arch/x86/vdso/vdso2c.h +++ b/arch/x86/vdso/vdso2c.h | |||
@@ -4,6 +4,116 @@ | |||
4 | * are built for 32-bit userspace. | 4 | * are built for 32-bit userspace. |
5 | */ | 5 | */ |
6 | 6 | ||
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 | |||
97 | if (!copy) | ||
98 | return; | ||
99 | |||
100 | if (out->count >= out->max_count) | ||
101 | fail("too many copied sections (max = %d)\n", out->max_count); | ||
102 | |||
103 | if (in_idx == out->in_shstrndx) | ||
104 | out->out_shstrndx = out->count; | ||
105 | |||
106 | out->table[out->count] = *in; | ||
107 | PUT_LE(&out->table[out->count].sh_name, | ||
108 | BITSFUNC(find_shname)(out, name)); | ||
109 | |||
110 | /* elfutils requires that a strtab have the correct type. */ | ||
111 | if (!strcmp(name, ".fake_shstrtab")) | ||
112 | PUT_LE(&out->table[out->count].sh_type, SHT_STRTAB); | ||
113 | |||
114 | out->count++; | ||
115 | } | ||
116 | |||
7 | static void BITSFUNC(go)(void *addr, size_t len, | 117 | static void BITSFUNC(go)(void *addr, size_t len, |
8 | FILE *outfile, const char *name) | 118 | FILE *outfile, const char *name) |
9 | { | 119 | { |
@@ -19,7 +129,7 @@ static void BITSFUNC(go)(void *addr, size_t len, | |||
19 | const char *secstrings; | 129 | const char *secstrings; |
20 | uint64_t syms[NSYMS] = {}; | 130 | uint64_t syms[NSYMS] = {}; |
21 | 131 | ||
22 | uint64_t fake_sections_value = 0, fake_sections_size = 0; | 132 | struct BITSFUNC(fake_sections) fake_sections = {}; |
23 | 133 | ||
24 | ELF(Phdr) *pt = (ELF(Phdr) *)(addr + GET_LE(&hdr->e_phoff)); | 134 | ELF(Phdr) *pt = (ELF(Phdr) *)(addr + GET_LE(&hdr->e_phoff)); |
25 | 135 | ||
@@ -89,23 +199,57 @@ static void BITSFUNC(go)(void *addr, size_t len, | |||
89 | GET_LE(&sym->st_name); | 199 | GET_LE(&sym->st_name); |
90 | 200 | ||
91 | for (k = 0; k < NSYMS; k++) { | 201 | for (k = 0; k < NSYMS; k++) { |
92 | if (!strcmp(name, required_syms[k])) { | 202 | if (!strcmp(name, required_syms[k].name)) { |
93 | if (syms[k]) { | 203 | if (syms[k]) { |
94 | fail("duplicate symbol %s\n", | 204 | fail("duplicate symbol %s\n", |
95 | required_syms[k]); | 205 | required_syms[k].name); |
96 | } | 206 | } |
97 | syms[k] = GET_LE(&sym->st_value); | 207 | syms[k] = GET_LE(&sym->st_value); |
98 | } | 208 | } |
99 | } | 209 | } |
100 | 210 | ||
101 | if (!strcmp(name, "vdso_fake_sections")) { | 211 | if (!strcmp(name, "fake_shstrtab")) { |
102 | if (fake_sections_value) | 212 | ELF(Shdr) *sh; |
103 | fail("duplicate vdso_fake_sections\n"); | 213 | |
104 | fake_sections_value = GET_LE(&sym->st_value); | 214 | fake_sections.in_shstrndx = GET_LE(&sym->st_shndx); |
105 | fake_sections_size = GET_LE(&sym->st_size); | 215 | fake_sections.shstrtab = addr + GET_LE(&sym->st_value); |
216 | fake_sections.shstrtab_len = GET_LE(&sym->st_size); | ||
217 | sh = addr + GET_LE(&hdr->e_shoff) + | ||
218 | GET_LE(&hdr->e_shentsize) * | ||
219 | fake_sections.in_shstrndx; | ||
220 | fake_sections.shstr_offset = GET_LE(&sym->st_value) - | ||
221 | GET_LE(&sh->sh_addr); | ||
106 | } | 222 | } |
107 | } | 223 | } |
108 | 224 | ||
225 | /* Build the output section table. */ | ||
226 | if (!syms[sym_VDSO_FAKE_SECTION_TABLE_START] || | ||
227 | !syms[sym_VDSO_FAKE_SECTION_TABLE_END]) | ||
228 | fail("couldn't find fake section table\n"); | ||
229 | if ((syms[sym_VDSO_FAKE_SECTION_TABLE_END] - | ||
230 | syms[sym_VDSO_FAKE_SECTION_TABLE_START]) % sizeof(ELF(Shdr))) | ||
231 | fail("fake section table size isn't a multiple of sizeof(Shdr)\n"); | ||
232 | fake_sections.table = addr + syms[sym_VDSO_FAKE_SECTION_TABLE_START]; | ||
233 | fake_sections.table_offset = syms[sym_VDSO_FAKE_SECTION_TABLE_START]; | ||
234 | fake_sections.max_count = (syms[sym_VDSO_FAKE_SECTION_TABLE_END] - | ||
235 | syms[sym_VDSO_FAKE_SECTION_TABLE_START]) / | ||
236 | sizeof(ELF(Shdr)); | ||
237 | |||
238 | BITSFUNC(init_sections)(&fake_sections); | ||
239 | for (i = 0; i < GET_LE(&hdr->e_shnum); i++) { | ||
240 | ELF(Shdr) *sh = addr + GET_LE(&hdr->e_shoff) + | ||
241 | GET_LE(&hdr->e_shentsize) * i; | ||
242 | BITSFUNC(copy_section)(&fake_sections, i, sh, | ||
243 | secstrings + GET_LE(&sh->sh_name)); | ||
244 | } | ||
245 | if (!fake_sections.out_shstrndx) | ||
246 | fail("didn't generate shstrndx?!?\n"); | ||
247 | |||
248 | PUT_LE(&hdr->e_shoff, fake_sections.table_offset); | ||
249 | PUT_LE(&hdr->e_shentsize, sizeof(ELF(Shdr))); | ||
250 | PUT_LE(&hdr->e_shnum, fake_sections.count); | ||
251 | PUT_LE(&hdr->e_shstrndx, fake_sections.out_shstrndx); | ||
252 | |||
109 | /* Validate mapping addresses. */ | 253 | /* Validate mapping addresses. */ |
110 | for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) { | 254 | for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) { |
111 | if (!syms[i]) | 255 | if (!syms[i]) |
@@ -113,25 +257,17 @@ static void BITSFUNC(go)(void *addr, size_t len, | |||
113 | 257 | ||
114 | if (syms[i] % 4096) | 258 | if (syms[i] % 4096) |
115 | fail("%s must be a multiple of 4096\n", | 259 | fail("%s must be a multiple of 4096\n", |
116 | required_syms[i]); | 260 | required_syms[i].name); |
117 | if (syms[i] < data_size) | 261 | if (syms[i] < data_size) |
118 | fail("%s must be after the text mapping\n", | 262 | fail("%s must be after the text mapping\n", |
119 | required_syms[i]); | 263 | required_syms[i].name); |
120 | if (syms[sym_end_mapping] < syms[i] + 4096) | 264 | if (syms[sym_end_mapping] < syms[i] + 4096) |
121 | fail("%s overruns end_mapping\n", required_syms[i]); | 265 | fail("%s overruns end_mapping\n", |
266 | required_syms[i].name); | ||
122 | } | 267 | } |
123 | if (syms[sym_end_mapping] % 4096) | 268 | if (syms[sym_end_mapping] % 4096) |
124 | fail("end_mapping must be a multiple of 4096\n"); | 269 | fail("end_mapping must be a multiple of 4096\n"); |
125 | 270 | ||
126 | /* Remove sections or use fakes */ | ||
127 | if (fake_sections_size % sizeof(ELF(Shdr))) | ||
128 | fail("vdso_fake_sections size is not a multiple of %ld\n", | ||
129 | (long)sizeof(ELF(Shdr))); | ||
130 | PUT_LE(&hdr->e_shoff, fake_sections_value); | ||
131 | PUT_LE(&hdr->e_shentsize, fake_sections_value ? sizeof(ELF(Shdr)) : 0); | ||
132 | PUT_LE(&hdr->e_shnum, fake_sections_size / sizeof(ELF(Shdr))); | ||
133 | PUT_LE(&hdr->e_shstrndx, SHN_UNDEF); | ||
134 | |||
135 | if (!name) { | 271 | if (!name) { |
136 | fwrite(addr, load_size, 1, outfile); | 272 | fwrite(addr, load_size, 1, outfile); |
137 | return; | 273 | return; |
@@ -169,9 +305,9 @@ static void BITSFUNC(go)(void *addr, size_t len, | |||
169 | (unsigned long)GET_LE(&alt_sec->sh_size)); | 305 | (unsigned long)GET_LE(&alt_sec->sh_size)); |
170 | } | 306 | } |
171 | for (i = 0; i < NSYMS; i++) { | 307 | for (i = 0; i < NSYMS; i++) { |
172 | if (syms[i]) | 308 | if (required_syms[i].export && syms[i]) |
173 | fprintf(outfile, "\t.sym_%s = 0x%" PRIx64 ",\n", | 309 | fprintf(outfile, "\t.sym_%s = 0x%" PRIx64 ",\n", |
174 | required_syms[i], syms[i]); | 310 | required_syms[i].name, syms[i]); |
175 | } | 311 | } |
176 | fprintf(outfile, "};\n"); | 312 | fprintf(outfile, "};\n"); |
177 | } | 313 | } |