From: jont AT harlequin DOT co DOT uk (Jon Thackray) Subject: b18 linker problems 5 Sep 1997 22:08:28 -0700 Approved: cygnus DOT gnu-win32 AT cygnus DOT com Distribution: cygnus Message-ID: <199709031333.OAA03966.cygnus.gnu-win32@zaphod.long.harlequin.co.uk> Original-To: gnu-win32 AT cygnus DOT com Original-Sender: owner-gnu-win32 AT cygnus DOT com It seems that the b18 linker does incorrect things with pc relative relocation to symbols not in the .text section (or more specifically not in section 0, as will be revealed later). The following email contains a number of exhibits demonstrating the problem, and where I think it lies. First is a small assembler program demonstrating the problem. The problem can be shown under as if there is a pc relative relocation to a symbol not in the .text section, as in the example below. If one rearranges the sections by replaacing .text with .text$i and vice versa then all is ok. A language translator which does not place its text in section 0 may also encounter the problem. I include three outputs from objdump of the link of this program. The first link was done with a raw b18 and demonstrates the problem. The second link was done using b17 ld, and shows that the problem is a regression. The third link is done with a version of ld that I have modified to produce a fix for the problem. The problem seems to lie in a combination of _bfd_coff_generic_relocate_section within cofflink.c and coff_i386_rtype_to_howto in coff-i386.c I include annotated code from cofflink.c and coff-i386.c demonstrating where I think the problem lies. .section .text$i .global foo1 .global foo2 foo1: mov $0, %eax ret foo2: mov $1, %eax ret .text .global main .global foo1 .global foo2 main: call foo1 call foo2 mov $0, %eax ret with b18 ld bash$ objdump --disassemble foo.exe foo.exe: file format pei-i386 Disassembly of section .text: 00401000
: 0: e8 1b 00 00 00 call 00401020 5: e8 1c 00 00 00 call 00401026 a: b8 00 00 00 00 movl $0x0,%eax f: c3 ret 00401010 : 0: b8 00 00 00 00 movl $0x0,%eax 5: c3 ret 00401016 : 0: b8 01 00 00 00 movl $0x1,%eax 5: c3 ret 0040101c : ... with b17 ld objdump --disassemble foo.exe foo.exe: file format pei-i386 Disassembly of section .text: 00401000
: 0: e8 0b 00 00 00 call 00401010 5: e8 0c 00 00 00 call 00401016 a: b8 00 00 00 00 movl $0x0,%eax f: c3 ret 00401010 : 0: b8 00 00 00 00 movl $0x0,%eax 5: c3 ret 00401016 : 0: b8 01 00 00 00 movl $0x1,%eax 5: c3 ret 0040101c : ... After modifying b18 objdump --disassemble foo.exe foo.exe: file format pei-i386 Disassembly of section .text: 00401000
: 0: e8 0b 00 00 00 call 00401010 5: e8 0c 00 00 00 call 00401016 a: b8 00 00 00 00 movl $0x0,%eax f: c3 ret 00401010 : 0: b8 00 00 00 00 movl $0x0,%eax 5: c3 ret 00401016 : 0: b8 01 00 00 00 movl $0x1,%eax 5: c3 ret 0040101c : ... Annotated excerpt from bfd/coff-i386.c (annotations within C style comments) static reloc_howto_type * coff_i386_rtype_to_howto (abfd, sec, rel, h, sym, addendp) bfd *abfd; asection *sec; struct internal_reloc *rel; struct coff_link_hash_entry *h; struct internal_syment *sym; bfd_vma *addendp; { reloc_howto_type *howto; howto = howto_table + rel->r_type; #ifdef COFF_WITH_PE *addendp = 0; /* For gun-win32 on the PC, this will happen */ #endif if (howto->pc_relative) *addendp += sec->vma; if (sym != NULL && sym->n_scnum == 0 && sym->n_value != 0) { /* This is a common symbol. The section contents include the size (sym->n_value) as an addend. The relocate_section function will be adding in the final value of the symbol. We need to subtract out the current size in order to get the correct result. */ BFD_ASSERT (h != NULL); #ifndef COFF_WITH_PE /* I think we *do* want to bypass this. If we don't, I have seen some data parameters get the wrong relcation address. If I link two versions with and without this section bypassed and then do a binary comparison, the addresses which are different can be looked up in the map. The case in which this section has been bypassed has addresses which correspond to values I can find in the map */ *addendp -= sym->n_value; #endif } /* If the output symbol is common (in which case this must be a relocateable link), we need to add in the final size of the common symbol. */ if (h != NULL && h->root.type == bfd_link_hash_common) *addendp += h->root.u.c.size; #ifdef COFF_WITH_PE if (howto->pc_relative) *addendp -= 4; /* This will also happen */ if (rel->r_type == R_IMAGEBASE) { *addendp -= pe_data(sec->output_section->owner)->pe_opthdr.ImageBase; } #endif return howto; } Annotated excerpt from bfd/cofflink.c (annotations within C style comments) if (sym != NULL && sym->n_scnum != 0) addend = - sym->n_value; else addend = 0; /* In the example case, addend is assigned the value -sym->n_value. * However, this is irrelevant because howto overwites the value of * addend to be -4 (to deal with the fact that the 386 computes * pc-relative addresses from the start of the next instruction, * whereas the address we'll subtract out later will be the address at * which the relocation takes place, ie 4 less). In the modified * example, where the section names are switched, addend is simply set * to zero. */ howto = bfd_coff_rtype_to_howto (input_bfd, input_section, rel, h, sym, &addend); if (howto == NULL) return false; /* If we are doing a relocateable link, then we can just ignore a PC relative reloc that is pcrel_offset. It will already have the correct value. If this is not a relocateable link, then we should ignore the symbol value. */ if (howto->pc_relative && howto->pcrel_offset) { if (info->relocateable) continue; if (sym != NULL && sym->n_scnum != 0) addend += sym->n_value; /* This would undo the earlier subtraction, except that howto has * junked the result. The overall effect is that we end up pointing * to an address which is too large by an amount equal to the symbol * value. */ } A simple fix for the above is to leave out the two modifications of addend by sym->n_value. This produces the corect result for the example code above, and also for the code I was originally trying to link. However, I suspect there is more to this than meets my eye. - For help on using this list (especially unsubscribing), send a message to "gnu-win32-request AT cygnus DOT com" with one line of text: "help".