1
+ /*
2
+ * vdso2c - A vdso image preparation tool
3
+ * Copyright (c) 2014 Andy Lutomirski and others
4
+ * Licensed under the GPL v2
5
+ *
6
+ * vdso2c requires stripped and unstripped input. It would be trivial
7
+ * to fully strip the input in here, but, for reasons described below,
8
+ * we need to write a section table. Doing this is more or less
9
+ * equivalent to dropping all non-allocatable sections, but it's
10
+ * easier to let objcopy handle that instead of doing it ourselves.
11
+ * If we ever need to do something fancier than what objcopy provides,
12
+ * it would be straightforward to add here.
13
+ *
14
+ * We're keep a section table for a few reasons:
15
+ *
16
+ * The Go runtime had a couple of bugs: it would read the section
17
+ * table to try to figure out how many dynamic symbols there were (it
18
+ * shouldn't have looked at the section table at all) and, if there
19
+ * were no SHT_SYNDYM section table entry, it would use an
20
+ * uninitialized value for the number of symbols. An empty DYNSYM
21
+ * table would work, but I see no reason not to write a valid one (and
22
+ * keep full performance for old Go programs). This hack is only
23
+ * needed on x86_64.
24
+ *
25
+ * The bug was introduced on 2012-08-31 by:
26
+ * https://code.google.com/p/go/source/detail?r=56ea40aac72b
27
+ * and was fixed on 2014-06-13 by:
28
+ * https://code.google.com/p/go/source/detail?r=fc1cd5e12595
29
+ *
30
+ * Binutils has issues debugging the vDSO: it reads the section table to
31
+ * find SHT_NOTE; it won't look at PT_NOTE for the in-memory vDSO, which
32
+ * would break build-id if we removed the section table. Binutils
33
+ * also requires that shstrndx != 0. See:
34
+ * https://sourceware.org/bugzilla/show_bug.cgi?id=17064
35
+ *
36
+ * elfutils might not look for PT_NOTE if there is a section table at
37
+ * all. I don't know whether this matters for any practical purpose.
38
+ *
39
+ * For simplicity, rather than hacking up a partial section table, we
40
+ * just write a mostly complete one. We omit non-dynamic symbols,
41
+ * though, since they're rather large.
42
+ *
43
+ * Once binutils gets fixed, we might be able to drop this for all but
44
+ * the 64-bit vdso, since build-id only works in kernel RPMs, and
45
+ * systems that update to new enough kernel RPMs will likely update
46
+ * binutils in sync. build-id has never worked for home-built kernel
47
+ * RPMs without manual symlinking, and I suspect that no one ever does
48
+ * that.
49
+ */
50
+
1
51
#include <inttypes.h>
2
52
#include <stdint.h>
3
53
#include <unistd.h>
@@ -61,7 +111,8 @@ static void fail(const char *format, ...)
61
111
va_start (ap , format );
62
112
fprintf (stderr , "Error: " );
63
113
vfprintf (stderr , format , ap );
64
- unlink (outfilename );
114
+ if (outfilename )
115
+ unlink (outfilename );
65
116
exit (1 );
66
117
va_end (ap );
67
118
}
@@ -114,38 +165,61 @@ extern void bad_put_le(void);
114
165
#include "vdso2c.h"
115
166
#undef ELF_BITS
116
167
117
- static void go (void * addr , size_t len , FILE * outfile , const char * name )
168
+ static void go (void * raw_addr , size_t raw_len ,
169
+ void * stripped_addr , size_t stripped_len ,
170
+ FILE * outfile , const char * name )
118
171
{
119
- Elf64_Ehdr * hdr = (Elf64_Ehdr * )addr ;
172
+ Elf64_Ehdr * hdr = (Elf64_Ehdr * )raw_addr ;
120
173
121
174
if (hdr -> e_ident [EI_CLASS ] == ELFCLASS64 ) {
122
- go64 (addr , len , outfile , name );
175
+ go64 (raw_addr , raw_len , stripped_addr , stripped_len ,
176
+ outfile , name );
123
177
} else if (hdr -> e_ident [EI_CLASS ] == ELFCLASS32 ) {
124
- go32 (addr , len , outfile , name );
178
+ go32 (raw_addr , raw_len , stripped_addr , stripped_len ,
179
+ outfile , name );
125
180
} else {
126
181
fail ("unknown ELF class\n" );
127
182
}
128
183
}
129
184
185
+ static void map_input (const char * name , void * * addr , size_t * len , int prot )
186
+ {
187
+ off_t tmp_len ;
188
+
189
+ int fd = open (name , O_RDONLY );
190
+ if (fd == -1 )
191
+ err (1 , "%s" , name );
192
+
193
+ tmp_len = lseek (fd , 0 , SEEK_END );
194
+ if (tmp_len == (off_t )- 1 )
195
+ err (1 , "lseek" );
196
+ * len = (size_t )tmp_len ;
197
+
198
+ * addr = mmap (NULL , tmp_len , prot , MAP_PRIVATE , fd , 0 );
199
+ if (* addr == MAP_FAILED )
200
+ err (1 , "mmap" );
201
+
202
+ close (fd );
203
+ }
204
+
130
205
int main (int argc , char * * argv )
131
206
{
132
- int fd ;
133
- off_t len ;
134
- void * addr ;
207
+ size_t raw_len , stripped_len ;
208
+ void * raw_addr , * stripped_addr ;
135
209
FILE * outfile ;
136
210
char * name , * tmp ;
137
211
int namelen ;
138
212
139
- if (argc != 3 ) {
140
- printf ("Usage: vdso2c INPUT OUTPUT\n" );
213
+ if (argc != 4 ) {
214
+ printf ("Usage: vdso2c RAW_INPUT STRIPPED_INPUT OUTPUT\n" );
141
215
return 1 ;
142
216
}
143
217
144
218
/*
145
219
* Figure out the struct name. If we're writing to a .so file,
146
220
* generate raw output insted.
147
221
*/
148
- name = strdup (argv [2 ]);
222
+ name = strdup (argv [3 ]);
149
223
namelen = strlen (name );
150
224
if (namelen >= 3 && !strcmp (name + namelen - 3 , ".so" )) {
151
225
name = NULL ;
@@ -161,26 +235,18 @@ int main(int argc, char **argv)
161
235
* tmp = '_' ;
162
236
}
163
237
164
- fd = open (argv [1 ], O_RDONLY );
165
- if (fd == -1 )
166
- err (1 , "%s" , argv [1 ]);
167
-
168
- len = lseek (fd , 0 , SEEK_END );
169
- if (len == (off_t )- 1 )
170
- err (1 , "lseek" );
171
-
172
- addr = mmap (NULL , len , PROT_READ | PROT_WRITE , MAP_PRIVATE , fd , 0 );
173
- if (addr == MAP_FAILED )
174
- err (1 , "mmap" );
238
+ map_input (argv [1 ], & raw_addr , & raw_len , PROT_READ );
239
+ map_input (argv [2 ], & stripped_addr , & stripped_len , PROT_READ );
175
240
176
- outfilename = argv [2 ];
241
+ outfilename = argv [3 ];
177
242
outfile = fopen (outfilename , "w" );
178
243
if (!outfile )
179
244
err (1 , "%s" , argv [2 ]);
180
245
181
- go (addr , ( size_t ) len , outfile , name );
246
+ go (raw_addr , raw_len , stripped_addr , stripped_len , outfile , name );
182
247
183
- munmap (addr , len );
248
+ munmap (raw_addr , raw_len );
249
+ munmap (stripped_addr , stripped_len );
184
250
fclose (outfile );
185
251
186
252
return 0 ;
0 commit comments