Mail Archives: cygwin/1997/09/05/22:08:28
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 <main>:
0: e8 1b 00 00 00 call 00401020 <etext+4>
5: e8 1c 00 00 00 call 00401026 <etext+a>
a: b8 00 00 00 00 movl $0x0,%eax
f: c3 ret
00401010 <foo1>:
0: b8 00 00 00 00 movl $0x0,%eax
5: c3 ret
00401016 <foo2>:
0: b8 01 00 00 00 movl $0x1,%eax
5: c3 ret
0040101c <etext>:
...
with b17 ld
objdump --disassemble foo.exe
foo.exe: file format pei-i386
Disassembly of section .text:
00401000 <main>:
0: e8 0b 00 00 00 call 00401010 <foo1>
5: e8 0c 00 00 00 call 00401016 <foo2>
a: b8 00 00 00 00 movl $0x0,%eax
f: c3 ret
00401010 <foo1>:
0: b8 00 00 00 00 movl $0x0,%eax
5: c3 ret
00401016 <foo2>:
0: b8 01 00 00 00 movl $0x1,%eax
5: c3 ret
0040101c <etext>:
...
After modifying b18
objdump --disassemble foo.exe
foo.exe: file format pei-i386
Disassembly of section .text:
00401000 <main>:
0: e8 0b 00 00 00 call 00401010 <foo1>
5: e8 0c 00 00 00 call 00401016 <foo2>
a: b8 00 00 00 00 movl $0x0,%eax
f: c3 ret
00401010 <foo1>:
0: b8 00 00 00 00 movl $0x0,%eax
5: c3 ret
00401016 <foo2>:
0: b8 01 00 00 00 movl $0x1,%eax
5: c3 ret
0040101c <etext>:
...
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".
- Raw text -