~ubuntu-branches/ubuntu/lucid/ndiswrapper/lucid

« back to all changes in this revision

Viewing changes to driver/pe_linker.c

  • Committer: Bazaar Package Importer
  • Author(s): Andres Salomon
  • Date: 2005-01-09 19:40:16 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20050109194016-q0wfc6kf43uog0bb
Tags: 0.12+1.0rc2-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Copyright (C) 2003-2004 Pontus Fuchs, Giridhar Pemmasani
 
3
 *
 
4
 *  This program is free software; you can redistribute it and/or modify
 
5
 *  it under the terms of the GNU General Public License as published by
 
6
 *  the Free Software Foundation; either version 2 of the License, or
 
7
 *  (at your option) any later version.
 
8
 *
 
9
 *  This program is distributed in the hope that it will be useful,
 
10
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
12
 *  GNU General Public License for more details.
 
13
 *
 
14
 */
 
15
 
 
16
#ifdef TEST_LOADER
 
17
 
 
18
#include <stdio.h>
 
19
#include <stdint.h>
 
20
#include <string.h>
 
21
#include <linux/types.h>
 
22
#include <asm/errno.h>
 
23
 
 
24
#include "pe_linker.h"
 
25
 
 
26
#else
 
27
 
 
28
#include <linux/types.h>
 
29
#include <asm/errno.h>
 
30
 
 
31
#include "pe_linker.h"
 
32
 
 
33
#endif
 
34
 
 
35
static struct exports exports[40];
 
36
static int num_exports;
 
37
 
 
38
#define RVA2VA(image, rva, type) (type)rva_to_va(image, rva)
 
39
 
 
40
#ifdef TEST_LOADER
 
41
#define WRAP_EXPORT_FUNC void
 
42
static WRAP_EXPORT_FUNC get_export(char *name)
 
43
{
 
44
        return name;
 
45
}
 
46
#else
 
47
extern struct wrap_export ntoskernel_exports[], ndis_exports[],
 
48
        misc_funcs_exports[], hal_exports[];
 
49
#ifdef CONFIG_USB
 
50
extern struct wrap_export usb_exports[];
 
51
#endif
 
52
 
 
53
static WRAP_EXPORT_FUNC get_export(char *name)
 
54
{
 
55
        int i;
 
56
 
 
57
        for (i = 0 ; ntoskernel_exports[i].name != NULL; i++)
 
58
                if (strcmp(ntoskernel_exports[i].name, name) == 0)
 
59
                        return ntoskernel_exports[i].func;
 
60
 
 
61
        for (i = 0 ; ndis_exports[i].name != NULL; i++)
 
62
                if (strcmp(ndis_exports[i].name, name) == 0)
 
63
                        return ndis_exports[i].func;
 
64
 
 
65
        for (i = 0 ; misc_funcs_exports[i].name != NULL; i++)
 
66
                if (strcmp(misc_funcs_exports[i].name, name) == 0)
 
67
                        return misc_funcs_exports[i].func;
 
68
 
 
69
        for (i = 0 ; hal_exports[i].name != NULL; i++)
 
70
                if (strcmp(hal_exports[i].name, name) == 0)
 
71
                        return hal_exports[i].func;
 
72
 
 
73
#ifdef CONFIG_USB
 
74
        for (i = 0 ; usb_exports[i].name != NULL; i++)
 
75
                if (strcmp(usb_exports[i].name, name) == 0)
 
76
                        return usb_exports[i].func;
 
77
#endif
 
78
 
 
79
        for (i = 0; i < num_exports; i++)
 
80
                if (strcmp(exports[i].name, name) == 0)
 
81
                        return (void *)exports[i].addr;
 
82
 
 
83
        return NULL;
 
84
}
 
85
#endif // TEST_LOADER
 
86
 
 
87
static void *get_dll_init(char *name)
 
88
{
 
89
        int i;
 
90
        for (i = 0; i < num_exports; i++)
 
91
                if ((strcmp(exports[i].dll, name) == 0) &&
 
92
                    (strcmp(exports[i].name, "DllInitialize") == 0))
 
93
                        return (void *)exports[i].addr;
 
94
        return NULL;
 
95
}
 
96
 
 
97
/* rva_to_va, get_section and fixup_relocs are heavily based on
 
98
 * Bill Paul's PE loader for ndisulator (Project Evil).
 
99
 */
 
100
 
 
101
static ULONG_PTR rva_to_va(void *image, uint32_t rva)
 
102
{
 
103
        struct optional_header *opt_hdr;
 
104
        struct section_header *sect_hdr;
 
105
        struct nt_header *nt_hdr;
 
106
        int i, sections;
 
107
        ULONG_PTR nt_hdr_offset, ret;
 
108
 
 
109
        nt_hdr_offset =  *(ULONG_PTR *)(image+0x3c);
 
110
        nt_hdr = (struct nt_header *)((char *)image + nt_hdr_offset);
 
111
 
 
112
        sections = nt_hdr->file_hdr.num_sections;
 
113
        opt_hdr = &nt_hdr->opt_hdr;
 
114
        sect_hdr = (struct section_header *)((void *)nt_hdr +
 
115
                                             sizeof(struct nt_header));
 
116
 
 
117
        for (i = 0; i < sections; i++, sect_hdr++) {
 
118
                int fixedlen = sect_hdr->virt_size;
 
119
                fixedlen += ((opt_hdr->opt_nt_hdr.section_alignment - 1) -
 
120
                             sect_hdr->virt_size) &
 
121
                        (opt_hdr->opt_nt_hdr.section_alignment - 1);
 
122
 
 
123
                if (sect_hdr->virt_addr <= (uint32_t)rva &&
 
124
                    (sect_hdr->virt_addr + fixedlen) > (uint32_t)rva)
 
125
                        break;
 
126
        }
 
127
 
 
128
        if (i > sections)
 
129
                return 0;
 
130
 
 
131
        ret = ((ULONG_PTR)(image + rva - sect_hdr->virt_addr +
 
132
                           sect_hdr->rawdata_addr));
 
133
        return ret;
 
134
}
 
135
 
 
136
static struct section_header *get_section(struct nt_header *nt_hdr,
 
137
                                          const char *name)
 
138
{
 
139
        int i, sections;
 
140
        struct section_header *sect_hdr;
 
141
 
 
142
        sections = nt_hdr->file_hdr.num_sections;
 
143
        sect_hdr = (struct section_header *)((char *)nt_hdr +
 
144
                                             sizeof(struct nt_header));
 
145
        for (i = 0; i < sections; i++)
 
146
                if (!strcmp(sect_hdr->name, name))
 
147
                        return sect_hdr;
 
148
                else
 
149
                        sect_hdr++;
 
150
 
 
151
        return NULL;
 
152
}
 
153
 
 
154
/*
 
155
 * Find and validate the coff header
 
156
 *
 
157
 */
 
158
static int check_nt_hdr(struct nt_header *nt_hdr)
 
159
{
 
160
        const char pe_sign[4] = {'P', 'E', 0, 0};
 
161
        LONG char_must;
 
162
 
 
163
        /* Validate the pe signature */
 
164
        if (memcmp(pe_sign, nt_hdr->magic, sizeof(pe_sign)) != 0)
 
165
                return -EINVAL;
 
166
 
 
167
        /* Make sure Image is PE32 */
 
168
        if (nt_hdr->opt_hdr.opt_std_hdr.magic != COFF_MAGIC_PE32 &&
 
169
            nt_hdr->opt_hdr.opt_std_hdr.magic != COFF_MAGIC_PE32PLUS)
 
170
                return -EINVAL;
 
171
        
 
172
        if (nt_hdr->file_hdr.machine != COFF_MACHINE_I386 &&
 
173
            nt_hdr->file_hdr.machine != COFF_MACHINE_AMD64) {
 
174
                ERROR("driver for machine %x is not supported (yet)",
 
175
                        nt_hdr->file_hdr.machine);
 
176
                return -EINVAL;
 
177
        }
 
178
 
 
179
        /* Make sure this is executable image */
 
180
        char_must = COFF_CHAR_IMAGE;
 
181
        if ((nt_hdr->file_hdr.characteristics & char_must) != char_must)
 
182
                return -EINVAL;
 
183
 
 
184
        /* Must be relocatable */
 
185
        if ((nt_hdr->file_hdr.characteristics & COFF_CHAR_RELOCS_STRIPPED))
 
186
                return -EINVAL;
 
187
 
 
188
        /* Make sure we have at least one section */
 
189
        if (nt_hdr->file_hdr.num_sections <= 0)
 
190
                return -EINVAL;
 
191
 
 
192
        if (nt_hdr->opt_hdr.opt_nt_hdr.section_alignment <
 
193
           nt_hdr->opt_hdr.opt_nt_hdr.file_alignment) {
 
194
                ERROR("Alignment mismatch: secion: 0x%x, file: 0x%x",
 
195
                      nt_hdr->opt_hdr.opt_nt_hdr.section_alignment,
 
196
                      nt_hdr->opt_hdr.opt_nt_hdr.file_alignment);
 
197
                return -EINVAL;
 
198
        }
 
199
 
 
200
        if ((nt_hdr->file_hdr.characteristics & COFF_CHAR_DLL))
 
201
                return COFF_CHAR_DLL;
 
202
        if ((nt_hdr->file_hdr.characteristics & COFF_CHAR_IMAGE))
 
203
                return COFF_CHAR_IMAGE;
 
204
        return -EINVAL;
 
205
}
 
206
 
 
207
static int import(void *image, struct coffpe_import_dirent *dirent, char *dll)
 
208
{
 
209
        ULONG_PTR *lookup_tbl, *address_tbl;
 
210
        char *symname = 0;
 
211
        int i;
 
212
        int ret = 0;
 
213
        void *adr;
 
214
 
 
215
        lookup_tbl  = RVA2VA(image, dirent->import_lookup_tbl, ULONG_PTR *);
 
216
        address_tbl = RVA2VA(image, dirent->import_address_table, ULONG_PTR *);
 
217
 
 
218
        for (i = 0; lookup_tbl[i]; i++) {
 
219
                if (lookup_tbl[i] & 0x80000000) {
 
220
                        ERROR("ordinal import not supported: %d",
 
221
                              (int) lookup_tbl[i]);
 
222
                        return -1;
 
223
                }
 
224
                else {
 
225
                        symname = RVA2VA(image,
 
226
                                         ((lookup_tbl[i] & 0x7fffffff) + 2),
 
227
                                         char *);
 
228
                }
 
229
 
 
230
                adr = get_export(symname);
 
231
                if (adr != NULL)
 
232
                        DBGTRACE1("found symbol: %s:%s, rva = %lu",
 
233
                                  dll, symname, (unsigned long)address_tbl[i]);
 
234
                if (adr == NULL) {
 
235
                        ERROR("Unknown symbol: %s:%s", dll, symname);
 
236
                        ret = -1;
 
237
                }
 
238
                DBGTRACE1("Importing rva: %p, %08X: %s : %s",
 
239
                          (void *)(address_tbl[i]), (UINT)adr, dll, symname);
 
240
                address_tbl[i] = (ULONG_PTR)adr;
 
241
        }
 
242
        return ret;
 
243
}
 
244
 
 
245
static int read_exports(void *image, struct nt_header *nt_hdr, char *dll)
 
246
{
 
247
        struct section_header *export_section;
 
248
        struct export_dir_table *export_dir_table;
 
249
        uint32_t *export_addr_table;
 
250
        int i;
 
251
        uint32_t *name_table;
 
252
 
 
253
        export_section = get_section(nt_hdr, ".edata");
 
254
        if (export_section)
 
255
                DBGTRACE1("%s", "found exports section");
 
256
        else
 
257
                return 0;
 
258
 
 
259
        export_dir_table = (struct export_dir_table *)
 
260
                (image + export_section->rawdata_addr);
 
261
        name_table = (unsigned int *)(image + export_dir_table->name_addr_rva);
 
262
        export_addr_table = (uint32_t *)
 
263
                (image + export_dir_table->export_table_rva);
 
264
 
 
265
        for (i = 0; i < export_dir_table->num_name_addr; i++) {
 
266
                if (nt_hdr->opt_hdr.export_tbl.rva <= *export_addr_table ||
 
267
                    *export_addr_table >= (nt_hdr->opt_hdr.export_tbl.rva +
 
268
                                           nt_hdr->opt_hdr.export_tbl.size))
 
269
                        DBGTRACE1("%s", "forwarder rva");
 
270
 
 
271
                DBGTRACE1("export symbol: %s, at %p",
 
272
                     (char *)(image + *name_table),
 
273
                     (void *)(image + *export_addr_table));
 
274
                     
 
275
                exports[num_exports].dll = dll;
 
276
                exports[num_exports].name = (char *)(image + *name_table);
 
277
                exports[num_exports].addr = (ULONG_PTR)(image +
 
278
                                                        *export_addr_table);
 
279
 
 
280
                num_exports++;
 
281
                name_table++;
 
282
                export_addr_table++;
 
283
        }
 
284
        return 0;
 
285
}
 
286
 
 
287
static int fixup_imports(void *image, struct nt_header *nt_hdr)
 
288
{
 
289
        int i;
 
290
        char *name;
 
291
        int ret = 0;
 
292
        struct coffpe_import_dirent *dirent;
 
293
 
 
294
        dirent = RVA2VA(image, nt_hdr->opt_hdr.import_tbl.rva,
 
295
                          struct coffpe_import_dirent *);
 
296
 
 
297
        for (i = 0; dirent[i].name_rva; i++) {
 
298
                name = RVA2VA(image, dirent[i].name_rva, char*);
 
299
 
 
300
                //printk("Imports from dll: %s\n", name);
 
301
                ret += import(image, &dirent[i], name);
 
302
        }
 
303
        return ret;
 
304
}
 
305
 
 
306
static int fixup_reloc(void *image, struct nt_header *nt_hdr)
 
307
{
 
308
        struct section_header *sect_hdr;
 
309
        ULONG_PTR base = nt_hdr->opt_hdr.opt_nt_hdr.image_base;
 
310
        ULONG_PTR size;
 
311
        struct coffpe_relocs *fixup_block;
 
312
 
 
313
        sect_hdr = get_section(nt_hdr, ".reloc");
 
314
        if (sect_hdr == NULL)
 
315
                return -EINVAL;
 
316
        fixup_block = (struct coffpe_relocs *)(image + sect_hdr->rawdata_addr);
 
317
 
 
318
        do {
 
319
                int i;
 
320
                uint16_t fixup, offset;
 
321
 
 
322
                size = (fixup_block->block_size - (2 * sizeof(uint32_t))) /
 
323
                        sizeof(uint16_t);
 
324
                for (i = 0; i < size; i++) {
 
325
                        uint32_t *loc;
 
326
                        uint32_t addr;
 
327
                        fixup = fixup_block->fixup[i];
 
328
                        offset = fixup & 0xfff;
 
329
                        loc = RVA2VA(image, fixup_block->page_rva + offset,
 
330
                                   uint32_t *);
 
331
 
 
332
                        switch ((fixup >> 12) & 0x0f) {
 
333
                        case COFF_FIXUP_ABSOLUTE:
 
334
                                break;
 
335
                        case COFF_FIXUP_HIGHLOW:
 
336
                                addr = RVA2VA(image, (*loc - base), uint32_t);
 
337
                                *loc = addr;
 
338
                                break;
 
339
                        default:
 
340
                                ERROR("unknown fixup: %08X", fixup);
 
341
                                return -EOPNOTSUPP;
 
342
                                break;
 
343
                        }
 
344
                }
 
345
                fixup_block = (struct coffpe_relocs *)
 
346
                        ((ULONG_PTR)fixup_block + fixup_block->block_size);
 
347
        } while (fixup_block->block_size);
 
348
 
 
349
        return 0;
 
350
}
 
351
 
 
352
int load_pe_images(struct pe_image *pe_image, int n)
 
353
{
 
354
        struct nt_header *nt_hdr;
 
355
        UINT nt_hdr_offset;
 
356
        int i = 0;
 
357
        void *image;
 
358
        int size;
 
359
        struct optional_header *opt_hdr;
 
360
 
 
361
        for (i = 0; i < n; i++) {
 
362
                image = pe_image[i].image;
 
363
                size = pe_image[i].size;
 
364
 
 
365
                /* The PE header is found at the RVA specified at offset 3c. */
 
366
                if (size < 0x3c + 4)
 
367
                        return -EINVAL;
 
368
                nt_hdr_offset =  *(unsigned int *)(image+0x3c);
 
369
                nt_hdr = (struct nt_header *)((char *)image + nt_hdr_offset);
 
370
                pe_image[i].type = check_nt_hdr(nt_hdr);
 
371
                if (pe_image[i].type <= 0)
 
372
                        return -EINVAL;
 
373
 
 
374
                if (read_exports(image, nt_hdr, pe_image[i].name))
 
375
                        return -EINVAL;
 
376
        }
 
377
 
 
378
        for (i = 0; i < n; i++) {
 
379
                image = pe_image[i].image;
 
380
                size = pe_image[i].size;
 
381
 
 
382
                nt_hdr_offset =  *(UINT *)(image+0x3c);
 
383
                nt_hdr = (struct nt_header *)((char *)image + nt_hdr_offset);
 
384
                opt_hdr = &nt_hdr->opt_hdr;
 
385
 
 
386
                if (fixup_reloc(image, nt_hdr))
 
387
                        return -EINVAL;
 
388
                if (fixup_imports(image, nt_hdr))
 
389
                        return -EINVAL;
 
390
                flush_icache_range(image, pe_image[i].size);
 
391
 
 
392
                pe_image[i].entry = RVA2VA(image,
 
393
                                           opt_hdr->opt_std_hdr.entry_rva,
 
394
                                           void *);
 
395
                DBGTRACE1("entry is at %p, rva at %08X", pe_image[i].entry, 
 
396
                     (unsigned int)opt_hdr->opt_std_hdr.entry_rva);
 
397
        } for (i = 0; i < n; i++) {
 
398
                image = pe_image[i].image;
 
399
                size = pe_image[i].size;
 
400
 
 
401
                nt_hdr_offset =  *(UINT *)(image+0x3c);
 
402
                nt_hdr = (struct nt_header *)((char *)image + nt_hdr_offset);
 
403
                opt_hdr = &nt_hdr->opt_hdr;
 
404
 
 
405
                if (pe_image[i].type == COFF_CHAR_DLL) {
 
406
                        struct ustring ustring;
 
407
                        char *buf = "0\0t0m0p00";
 
408
                        int (*dll_entry)(struct ustring *ustring) STDCALL;
 
409
 
 
410
                        memset(&ustring, 0, sizeof(ustring));
 
411
                        ustring.buf = buf;
 
412
                        dll_entry = (void *)get_dll_init(pe_image[i].name);
 
413
 
 
414
                        DBGTRACE1("calling dll_init at %p", dll_entry);
 
415
                        if (!dll_entry || dll_entry(&ustring))
 
416
                                ERROR("DLL initialize failed for %s",
 
417
                                      pe_image[i].name);
 
418
                }
 
419
                else if (pe_image[i].type == COFF_CHAR_IMAGE)
 
420
                        ;
 
421
                else
 
422
                        ERROR("illegal image type: %d", pe_image[i].type);
 
423
        }
 
424
        return 0;
 
425
}