ELF64: actually generate relative GOT/PLT references correctly
authorH. Peter Anvin <hpa@zytor.com>
Sat, 18 Oct 2008 06:06:46 +0000 (23:06 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Sat, 18 Oct 2008 06:06:46 +0000 (23:06 -0700)
Fix the arithmetic for relative GOT/PLT references.

We still can't enable exactitude, because of the assumption that
"size" is always the proper adjustment for the offset of the
displacement inside the instruction, which is wrong in the case of
displacements that are followed by an immediate.  This also affects
the list file, so it really should be fixed.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
output/outelf64.c

index fa4a631..f6dc617 100644 (file)
@@ -1067,7 +1067,7 @@ static void elf_out(int32_t segto, const void *data,
        }
        elf_sect_writeaddr(s, addr, size);
     } else if (type == OUT_REL2ADR) {
-       addr = *(int64_t *)data - size;
+       addr = *(int64_t *)data;
         if (segment == segto)
             error(ERR_PANIC, "intra-segment OUT_REL2ADR");
         if (segment == NO_SEG) {
@@ -1077,7 +1077,7 @@ static void elf_out(int32_t segto, const void *data,
                   " segment base references");
         } else {
             if (wrt == NO_SEG) {
-                elf_add_reloc(s, segment, addr, R_X86_64_PC16);
+                elf_add_reloc(s, segment, addr-size, R_X86_64_PC16);
                addr = 0;
             } else {
                 error(ERR_NONFATAL,
@@ -1086,7 +1086,7 @@ static void elf_out(int32_t segto, const void *data,
         }
        elf_sect_writeaddr(s, addr, size);
     } else if (type == OUT_REL4ADR) {
-       addr = *(int64_t *)data - size;
+       addr = *(int64_t *)data;
         if (segment == segto)
             error(ERR_PANIC, "intra-segment OUT_REL4ADR");
         if (segment == NO_SEG) {
@@ -1096,17 +1096,16 @@ static void elf_out(int32_t segto, const void *data,
                   " segment base references");
         } else {
             if (wrt == NO_SEG) {
-                elf_add_reloc(s, segment, addr, R_X86_64_PC32);
+                elf_add_reloc(s, segment, addr-size, R_X86_64_PC32);
                addr = 0;
             } else if (wrt == elf_plt_sect + 1) {
-               int64_t pcrel = s->len + size;
-                elf_add_gsym_reloc(s, segment, addr+pcrel, pcrel,
+                elf_add_gsym_reloc(s, segment, addr, size,
                                   R_X86_64_PLT32, false);
                addr = 0;
             } else if (wrt == elf_gotpc_sect + 1 ||
                       wrt == elf_got_sect + 1) {
-               int64_t pcrel = s->len + size;
-                elf_add_gsym_reloc(s, segment, addr+pcrel, pcrel,
+               printf("addr = %ld, pcrel = %ld\n", addr, size);
+                elf_add_gsym_reloc(s, segment, addr, size,
                                   R_X86_64_GOTPCREL, false);
                addr = 0;
             } else if (wrt == elf_gotoff_sect + 1 ||
@@ -1120,7 +1119,7 @@ static void elf_out(int32_t segto, const void *data,
         }
        elf_sect_writeaddr(s, addr, size);
     } else if (type == OUT_REL8ADR) {
-       addr = *(int64_t *)data - size;
+       addr = *(int64_t *)data;
         if (segment == segto)
             error(ERR_PANIC, "intra-segment OUT_REL8ADR");
         if (segment == NO_SEG) {
@@ -1130,12 +1129,11 @@ static void elf_out(int32_t segto, const void *data,
                   " segment base references");
         } else {
             if (wrt == NO_SEG) {
-                elf_add_reloc(s, segment, addr, R_X86_64_PC64);
+                elf_add_reloc(s, segment, addr-size, R_X86_64_PC64);
                addr = 0;
             } else if (wrt == elf_gotpc_sect + 1 ||
                       wrt == elf_got_sect + 1) {
-               int64_t pcrel = s->len + size;
-                elf_add_gsym_reloc(s, segment, addr+pcrel, pcrel,
+                elf_add_gsym_reloc(s, segment, addr, size,
                                   R_X86_64_GOTPCREL64, false);
                addr = 0;
             } else if (wrt == elf_gotoff_sect + 1 ||