diff options
Diffstat (limited to 'arch/parisc/kernel/module.c')
-rw-r--r-- | arch/parisc/kernel/module.c | 216 |
1 files changed, 135 insertions, 81 deletions
diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c index 44138c3e6ea7..9013243cecca 100644 --- a/arch/parisc/kernel/module.c +++ b/arch/parisc/kernel/module.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * | 6 | * |
7 | * Linux/PA-RISC Project (http://www.parisc-linux.org/) | 7 | * Linux/PA-RISC Project (http://www.parisc-linux.org/) |
8 | * Copyright (C) 2003 Randolph Chung <tausq at debian . org> | 8 | * Copyright (C) 2003 Randolph Chung <tausq at debian . org> |
9 | * Copyright (C) 2008 Helge Deller <deller@gmx.de> | ||
9 | * | 10 | * |
10 | * | 11 | * |
11 | * This program is free software; you can redistribute it and/or modify | 12 | * This program is free software; you can redistribute it and/or modify |
@@ -24,6 +25,19 @@ | |||
24 | * | 25 | * |
25 | * | 26 | * |
26 | * Notes: | 27 | * Notes: |
28 | * - PLT stub handling | ||
29 | * On 32bit (and sometimes 64bit) and with big kernel modules like xfs or | ||
30 | * ipv6 the relocation types R_PARISC_PCREL17F and R_PARISC_PCREL22F may | ||
31 | * fail to reach their PLT stub if we only create one big stub array for | ||
32 | * all sections at the beginning of the core or init section. | ||
33 | * Instead we now insert individual PLT stub entries directly in front of | ||
34 | * of the code sections where the stubs are actually called. | ||
35 | * This reduces the distance between the PCREL location and the stub entry | ||
36 | * so that the relocations can be fulfilled. | ||
37 | * While calculating the final layout of the kernel module in memory, the | ||
38 | * kernel module loader calls arch_mod_section_prepend() to request the | ||
39 | * to be reserved amount of memory in front of each individual section. | ||
40 | * | ||
27 | * - SEGREL32 handling | 41 | * - SEGREL32 handling |
28 | * We are not doing SEGREL32 handling correctly. According to the ABI, we | 42 | * We are not doing SEGREL32 handling correctly. According to the ABI, we |
29 | * should do a value offset, like this: | 43 | * should do a value offset, like this: |
@@ -58,9 +72,13 @@ | |||
58 | #define DEBUGP(fmt...) | 72 | #define DEBUGP(fmt...) |
59 | #endif | 73 | #endif |
60 | 74 | ||
75 | #define RELOC_REACHABLE(val, bits) \ | ||
76 | (( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 ) || \ | ||
77 | ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) ? \ | ||
78 | 0 : 1) | ||
79 | |||
61 | #define CHECK_RELOC(val, bits) \ | 80 | #define CHECK_RELOC(val, bits) \ |
62 | if ( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 ) || \ | 81 | if (!RELOC_REACHABLE(val, bits)) { \ |
63 | ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) { \ | ||
64 | printk(KERN_ERR "module %s relocation of symbol %s is out of range (0x%lx in %d bits)\n", \ | 82 | printk(KERN_ERR "module %s relocation of symbol %s is out of range (0x%lx in %d bits)\n", \ |
65 | me->name, strtab + sym->st_name, (unsigned long)val, bits); \ | 83 | me->name, strtab + sym->st_name, (unsigned long)val, bits); \ |
66 | return -ENOEXEC; \ | 84 | return -ENOEXEC; \ |
@@ -92,13 +110,6 @@ static inline int in_local(struct module *me, void *loc) | |||
92 | return in_init(me, loc) || in_core(me, loc); | 110 | return in_init(me, loc) || in_core(me, loc); |
93 | } | 111 | } |
94 | 112 | ||
95 | static inline int in_local_section(struct module *me, void *loc, void *dot) | ||
96 | { | ||
97 | return (in_init(me, loc) && in_init(me, dot)) || | ||
98 | (in_core(me, loc) && in_core(me, dot)); | ||
99 | } | ||
100 | |||
101 | |||
102 | #ifndef CONFIG_64BIT | 113 | #ifndef CONFIG_64BIT |
103 | struct got_entry { | 114 | struct got_entry { |
104 | Elf32_Addr addr; | 115 | Elf32_Addr addr; |
@@ -258,23 +269,42 @@ static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n) | |||
258 | /* Free memory returned from module_alloc */ | 269 | /* Free memory returned from module_alloc */ |
259 | void module_free(struct module *mod, void *module_region) | 270 | void module_free(struct module *mod, void *module_region) |
260 | { | 271 | { |
272 | kfree(mod->arch.section); | ||
273 | mod->arch.section = NULL; | ||
274 | |||
261 | vfree(module_region); | 275 | vfree(module_region); |
262 | /* FIXME: If module_region == mod->init_region, trim exception | 276 | /* FIXME: If module_region == mod->init_region, trim exception |
263 | table entries. */ | 277 | table entries. */ |
264 | } | 278 | } |
265 | 279 | ||
280 | /* Additional bytes needed in front of individual sections */ | ||
281 | unsigned int arch_mod_section_prepend(struct module *mod, | ||
282 | unsigned int section) | ||
283 | { | ||
284 | /* size needed for all stubs of this section (including | ||
285 | * one additional for correct alignment of the stubs) */ | ||
286 | return (mod->arch.section[section].stub_entries + 1) | ||
287 | * sizeof(struct stub_entry); | ||
288 | } | ||
289 | |||
266 | #define CONST | 290 | #define CONST |
267 | int module_frob_arch_sections(CONST Elf_Ehdr *hdr, | 291 | int module_frob_arch_sections(CONST Elf_Ehdr *hdr, |
268 | CONST Elf_Shdr *sechdrs, | 292 | CONST Elf_Shdr *sechdrs, |
269 | CONST char *secstrings, | 293 | CONST char *secstrings, |
270 | struct module *me) | 294 | struct module *me) |
271 | { | 295 | { |
272 | unsigned long gots = 0, fdescs = 0, stubs = 0, init_stubs = 0; | 296 | unsigned long gots = 0, fdescs = 0, len; |
273 | unsigned int i; | 297 | unsigned int i; |
274 | 298 | ||
299 | len = hdr->e_shnum * sizeof(me->arch.section[0]); | ||
300 | me->arch.section = kzalloc(len, GFP_KERNEL); | ||
301 | if (!me->arch.section) | ||
302 | return -ENOMEM; | ||
303 | |||
275 | for (i = 1; i < hdr->e_shnum; i++) { | 304 | for (i = 1; i < hdr->e_shnum; i++) { |
276 | const Elf_Rela *rels = (void *)hdr + sechdrs[i].sh_offset; | 305 | const Elf_Rela *rels = (void *)sechdrs[i].sh_addr; |
277 | unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels); | 306 | unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels); |
307 | unsigned int count, s; | ||
278 | 308 | ||
279 | if (strncmp(secstrings + sechdrs[i].sh_name, | 309 | if (strncmp(secstrings + sechdrs[i].sh_name, |
280 | ".PARISC.unwind", 14) == 0) | 310 | ".PARISC.unwind", 14) == 0) |
@@ -290,11 +320,23 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr, | |||
290 | */ | 320 | */ |
291 | gots += count_gots(rels, nrels); | 321 | gots += count_gots(rels, nrels); |
292 | fdescs += count_fdescs(rels, nrels); | 322 | fdescs += count_fdescs(rels, nrels); |
293 | if(strncmp(secstrings + sechdrs[i].sh_name, | 323 | |
294 | ".rela.init", 10) == 0) | 324 | /* XXX: By sorting the relocs and finding duplicate entries |
295 | init_stubs += count_stubs(rels, nrels); | 325 | * we could reduce the number of necessary stubs and save |
296 | else | 326 | * some memory. */ |
297 | stubs += count_stubs(rels, nrels); | 327 | count = count_stubs(rels, nrels); |
328 | if (!count) | ||
329 | continue; | ||
330 | |||
331 | /* so we need relocation stubs. reserve necessary memory. */ | ||
332 | /* sh_info gives the section for which we need to add stubs. */ | ||
333 | s = sechdrs[i].sh_info; | ||
334 | |||
335 | /* each code section should only have one relocation section */ | ||
336 | WARN_ON(me->arch.section[s].stub_entries); | ||
337 | |||
338 | /* store number of stubs we need for this section */ | ||
339 | me->arch.section[s].stub_entries += count; | ||
298 | } | 340 | } |
299 | 341 | ||
300 | /* align things a bit */ | 342 | /* align things a bit */ |
@@ -306,18 +348,8 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr, | |||
306 | me->arch.fdesc_offset = me->core_size; | 348 | me->arch.fdesc_offset = me->core_size; |
307 | me->core_size += fdescs * sizeof(Elf_Fdesc); | 349 | me->core_size += fdescs * sizeof(Elf_Fdesc); |
308 | 350 | ||
309 | me->core_size = ALIGN(me->core_size, 16); | ||
310 | me->arch.stub_offset = me->core_size; | ||
311 | me->core_size += stubs * sizeof(struct stub_entry); | ||
312 | |||
313 | me->init_size = ALIGN(me->init_size, 16); | ||
314 | me->arch.init_stub_offset = me->init_size; | ||
315 | me->init_size += init_stubs * sizeof(struct stub_entry); | ||
316 | |||
317 | me->arch.got_max = gots; | 351 | me->arch.got_max = gots; |
318 | me->arch.fdesc_max = fdescs; | 352 | me->arch.fdesc_max = fdescs; |
319 | me->arch.stub_max = stubs; | ||
320 | me->arch.init_stub_max = init_stubs; | ||
321 | 353 | ||
322 | return 0; | 354 | return 0; |
323 | } | 355 | } |
@@ -380,23 +412,27 @@ enum elf_stub_type { | |||
380 | }; | 412 | }; |
381 | 413 | ||
382 | static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, | 414 | static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, |
383 | enum elf_stub_type stub_type, int init_section) | 415 | enum elf_stub_type stub_type, Elf_Addr loc0, unsigned int targetsec) |
384 | { | 416 | { |
385 | unsigned long i; | ||
386 | struct stub_entry *stub; | 417 | struct stub_entry *stub; |
387 | 418 | ||
388 | if(init_section) { | 419 | /* initialize stub_offset to point in front of the section */ |
389 | i = me->arch.init_stub_count++; | 420 | if (!me->arch.section[targetsec].stub_offset) { |
390 | BUG_ON(me->arch.init_stub_count > me->arch.init_stub_max); | 421 | loc0 -= (me->arch.section[targetsec].stub_entries + 1) * |
391 | stub = me->module_init + me->arch.init_stub_offset + | 422 | sizeof(struct stub_entry); |
392 | i * sizeof(struct stub_entry); | 423 | /* get correct alignment for the stubs */ |
393 | } else { | 424 | loc0 = ALIGN(loc0, sizeof(struct stub_entry)); |
394 | i = me->arch.stub_count++; | 425 | me->arch.section[targetsec].stub_offset = loc0; |
395 | BUG_ON(me->arch.stub_count > me->arch.stub_max); | ||
396 | stub = me->module_core + me->arch.stub_offset + | ||
397 | i * sizeof(struct stub_entry); | ||
398 | } | 426 | } |
399 | 427 | ||
428 | /* get address of stub entry */ | ||
429 | stub = (void *) me->arch.section[targetsec].stub_offset; | ||
430 | me->arch.section[targetsec].stub_offset += sizeof(struct stub_entry); | ||
431 | |||
432 | /* do not write outside available stub area */ | ||
433 | BUG_ON(0 == me->arch.section[targetsec].stub_entries--); | ||
434 | |||
435 | |||
400 | #ifndef CONFIG_64BIT | 436 | #ifndef CONFIG_64BIT |
401 | /* for 32-bit the stub looks like this: | 437 | /* for 32-bit the stub looks like this: |
402 | * ldil L'XXX,%r1 | 438 | * ldil L'XXX,%r1 |
@@ -489,15 +525,19 @@ int apply_relocate_add(Elf_Shdr *sechdrs, | |||
489 | Elf32_Addr val; | 525 | Elf32_Addr val; |
490 | Elf32_Sword addend; | 526 | Elf32_Sword addend; |
491 | Elf32_Addr dot; | 527 | Elf32_Addr dot; |
528 | Elf_Addr loc0; | ||
529 | unsigned int targetsec = sechdrs[relsec].sh_info; | ||
492 | //unsigned long dp = (unsigned long)$global$; | 530 | //unsigned long dp = (unsigned long)$global$; |
493 | register unsigned long dp asm ("r27"); | 531 | register unsigned long dp asm ("r27"); |
494 | 532 | ||
495 | DEBUGP("Applying relocate section %u to %u\n", relsec, | 533 | DEBUGP("Applying relocate section %u to %u\n", relsec, |
496 | sechdrs[relsec].sh_info); | 534 | targetsec); |
497 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | 535 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { |
498 | /* This is where to make the change */ | 536 | /* This is where to make the change */ |
499 | loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | 537 | loc = (void *)sechdrs[targetsec].sh_addr |
500 | + rel[i].r_offset; | 538 | + rel[i].r_offset; |
539 | /* This is the start of the target section */ | ||
540 | loc0 = sechdrs[targetsec].sh_addr; | ||
501 | /* This is the symbol it is referring to */ | 541 | /* This is the symbol it is referring to */ |
502 | sym = (Elf32_Sym *)sechdrs[symindex].sh_addr | 542 | sym = (Elf32_Sym *)sechdrs[symindex].sh_addr |
503 | + ELF32_R_SYM(rel[i].r_info); | 543 | + ELF32_R_SYM(rel[i].r_info); |
@@ -569,19 +609,32 @@ int apply_relocate_add(Elf_Shdr *sechdrs, | |||
569 | break; | 609 | break; |
570 | case R_PARISC_PCREL17F: | 610 | case R_PARISC_PCREL17F: |
571 | /* 17-bit PC relative address */ | 611 | /* 17-bit PC relative address */ |
572 | val = get_stub(me, val, addend, ELF_STUB_GOT, in_init(me, loc)); | 612 | /* calculate direct call offset */ |
613 | val += addend; | ||
573 | val = (val - dot - 8)/4; | 614 | val = (val - dot - 8)/4; |
574 | CHECK_RELOC(val, 17) | 615 | if (!RELOC_REACHABLE(val, 17)) { |
616 | /* direct distance too far, create | ||
617 | * stub entry instead */ | ||
618 | val = get_stub(me, sym->st_value, addend, | ||
619 | ELF_STUB_DIRECT, loc0, targetsec); | ||
620 | val = (val - dot - 8)/4; | ||
621 | CHECK_RELOC(val, 17); | ||
622 | } | ||
575 | *loc = (*loc & ~0x1f1ffd) | reassemble_17(val); | 623 | *loc = (*loc & ~0x1f1ffd) | reassemble_17(val); |
576 | break; | 624 | break; |
577 | case R_PARISC_PCREL22F: | 625 | case R_PARISC_PCREL22F: |
578 | /* 22-bit PC relative address; only defined for pa20 */ | 626 | /* 22-bit PC relative address; only defined for pa20 */ |
579 | val = get_stub(me, val, addend, ELF_STUB_GOT, in_init(me, loc)); | 627 | /* calculate direct call offset */ |
580 | DEBUGP("STUB FOR %s loc %lx+%lx at %lx\n", | 628 | val += addend; |
581 | strtab + sym->st_name, (unsigned long)loc, addend, | ||
582 | val) | ||
583 | val = (val - dot - 8)/4; | 629 | val = (val - dot - 8)/4; |
584 | CHECK_RELOC(val, 22); | 630 | if (!RELOC_REACHABLE(val, 22)) { |
631 | /* direct distance too far, create | ||
632 | * stub entry instead */ | ||
633 | val = get_stub(me, sym->st_value, addend, | ||
634 | ELF_STUB_DIRECT, loc0, targetsec); | ||
635 | val = (val - dot - 8)/4; | ||
636 | CHECK_RELOC(val, 22); | ||
637 | } | ||
585 | *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); | 638 | *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); |
586 | break; | 639 | break; |
587 | 640 | ||
@@ -610,13 +663,17 @@ int apply_relocate_add(Elf_Shdr *sechdrs, | |||
610 | Elf64_Addr val; | 663 | Elf64_Addr val; |
611 | Elf64_Sxword addend; | 664 | Elf64_Sxword addend; |
612 | Elf64_Addr dot; | 665 | Elf64_Addr dot; |
666 | Elf_Addr loc0; | ||
667 | unsigned int targetsec = sechdrs[relsec].sh_info; | ||
613 | 668 | ||
614 | DEBUGP("Applying relocate section %u to %u\n", relsec, | 669 | DEBUGP("Applying relocate section %u to %u\n", relsec, |
615 | sechdrs[relsec].sh_info); | 670 | targetsec); |
616 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | 671 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { |
617 | /* This is where to make the change */ | 672 | /* This is where to make the change */ |
618 | loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | 673 | loc = (void *)sechdrs[targetsec].sh_addr |
619 | + rel[i].r_offset; | 674 | + rel[i].r_offset; |
675 | /* This is the start of the target section */ | ||
676 | loc0 = sechdrs[targetsec].sh_addr; | ||
620 | /* This is the symbol it is referring to */ | 677 | /* This is the symbol it is referring to */ |
621 | sym = (Elf64_Sym *)sechdrs[symindex].sh_addr | 678 | sym = (Elf64_Sym *)sechdrs[symindex].sh_addr |
622 | + ELF64_R_SYM(rel[i].r_info); | 679 | + ELF64_R_SYM(rel[i].r_info); |
@@ -672,42 +729,40 @@ int apply_relocate_add(Elf_Shdr *sechdrs, | |||
672 | DEBUGP("PCREL22F Symbol %s loc %p val %lx\n", | 729 | DEBUGP("PCREL22F Symbol %s loc %p val %lx\n", |
673 | strtab + sym->st_name, | 730 | strtab + sym->st_name, |
674 | loc, val); | 731 | loc, val); |
732 | val += addend; | ||
675 | /* can we reach it locally? */ | 733 | /* can we reach it locally? */ |
676 | if(!in_local_section(me, (void *)val, (void *)dot)) { | 734 | if (in_local(me, (void *)val)) { |
677 | 735 | /* this is the case where the symbol is local | |
678 | if (in_local(me, (void *)val)) | 736 | * to the module, but in a different section, |
679 | /* this is the case where the | 737 | * so stub the jump in case it's more than 22 |
680 | * symbol is local to the | 738 | * bits away */ |
681 | * module, but in a different | 739 | val = (val - dot - 8)/4; |
682 | * section, so stub the jump | 740 | if (!RELOC_REACHABLE(val, 22)) { |
683 | * in case it's more than 22 | 741 | /* direct distance too far, create |
684 | * bits away */ | 742 | * stub entry instead */ |
685 | val = get_stub(me, val, addend, ELF_STUB_DIRECT, | 743 | val = get_stub(me, sym->st_value, |
686 | in_init(me, loc)); | 744 | addend, ELF_STUB_DIRECT, |
687 | else if (strncmp(strtab + sym->st_name, "$$", 2) | 745 | loc0, targetsec); |
746 | } else { | ||
747 | /* Ok, we can reach it directly. */ | ||
748 | val = sym->st_value; | ||
749 | val += addend; | ||
750 | } | ||
751 | } else { | ||
752 | val = sym->st_value; | ||
753 | if (strncmp(strtab + sym->st_name, "$$", 2) | ||
688 | == 0) | 754 | == 0) |
689 | val = get_stub(me, val, addend, ELF_STUB_MILLI, | 755 | val = get_stub(me, val, addend, ELF_STUB_MILLI, |
690 | in_init(me, loc)); | 756 | loc0, targetsec); |
691 | else | 757 | else |
692 | val = get_stub(me, val, addend, ELF_STUB_GOT, | 758 | val = get_stub(me, val, addend, ELF_STUB_GOT, |
693 | in_init(me, loc)); | 759 | loc0, targetsec); |
694 | } | 760 | } |
695 | DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n", | 761 | DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n", |
696 | strtab + sym->st_name, loc, sym->st_value, | 762 | strtab + sym->st_name, loc, sym->st_value, |
697 | addend, val); | 763 | addend, val); |
698 | /* FIXME: local symbols work as long as the | ||
699 | * core and init pieces aren't separated too | ||
700 | * far. If this is ever broken, you will trip | ||
701 | * the check below. The way to fix it would | ||
702 | * be to generate local stubs to go between init | ||
703 | * and core */ | ||
704 | if((Elf64_Sxword)(val - dot - 8) > 0x800000 -1 || | ||
705 | (Elf64_Sxword)(val - dot - 8) < -0x800000) { | ||
706 | printk(KERN_ERR "Module %s, symbol %s is out of range for PCREL22F relocation\n", | ||
707 | me->name, strtab + sym->st_name); | ||
708 | return -ENOEXEC; | ||
709 | } | ||
710 | val = (val - dot - 8)/4; | 764 | val = (val - dot - 8)/4; |
765 | CHECK_RELOC(val, 22); | ||
711 | *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); | 766 | *loc = (*loc & ~0x3ff1ffd) | reassemble_22(val); |
712 | break; | 767 | break; |
713 | case R_PARISC_DIR64: | 768 | case R_PARISC_DIR64: |
@@ -794,12 +849,8 @@ int module_finalize(const Elf_Ehdr *hdr, | |||
794 | addr = (u32 *)entry->addr; | 849 | addr = (u32 *)entry->addr; |
795 | printk("INSNS: %x %x %x %x\n", | 850 | printk("INSNS: %x %x %x %x\n", |
796 | addr[0], addr[1], addr[2], addr[3]); | 851 | addr[0], addr[1], addr[2], addr[3]); |
797 | printk("stubs used %ld, stubs max %ld\n" | 852 | printk("got entries used %ld, gots max %ld\n" |
798 | "init_stubs used %ld, init stubs max %ld\n" | ||
799 | "got entries used %ld, gots max %ld\n" | ||
800 | "fdescs used %ld, fdescs max %ld\n", | 853 | "fdescs used %ld, fdescs max %ld\n", |
801 | me->arch.stub_count, me->arch.stub_max, | ||
802 | me->arch.init_stub_count, me->arch.init_stub_max, | ||
803 | me->arch.got_count, me->arch.got_max, | 854 | me->arch.got_count, me->arch.got_max, |
804 | me->arch.fdesc_count, me->arch.fdesc_max); | 855 | me->arch.fdesc_count, me->arch.fdesc_max); |
805 | #endif | 856 | #endif |
@@ -829,7 +880,10 @@ int module_finalize(const Elf_Ehdr *hdr, | |||
829 | me->name, me->arch.got_count, MAX_GOTS); | 880 | me->name, me->arch.got_count, MAX_GOTS); |
830 | return -EINVAL; | 881 | return -EINVAL; |
831 | } | 882 | } |
832 | 883 | ||
884 | kfree(me->arch.section); | ||
885 | me->arch.section = NULL; | ||
886 | |||
833 | /* no symbol table */ | 887 | /* no symbol table */ |
834 | if(symhdr == NULL) | 888 | if(symhdr == NULL) |
835 | return 0; | 889 | return 0; |