summaryrefslogtreecommitdiffstats
path: root/src/i386_ld.c
diff options
context:
space:
mode:
authorUlrich Drepper <[email protected]>2005-07-26 05:00:05 +0000
committerUlrich Drepper <[email protected]>2005-07-26 05:00:05 +0000
commitb08d5a8fb42f4586d756068065186b5af7e48dad (patch)
tree9f05f86be7877ed461b4dc05f53b29ea4fc0d2a1 /src/i386_ld.c
Adjust for monotone.
Diffstat (limited to 'src/i386_ld.c')
-rw-r--r--src/i386_ld.c894
1 files changed, 894 insertions, 0 deletions
diff --git a/src/i386_ld.c b/src/i386_ld.c
new file mode 100644
index 00000000..ee1cb966
--- /dev/null
+++ b/src/i386_ld.c
@@ -0,0 +1,894 @@
+/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
+ Written by Ulrich Drepper <[email protected]>, 2001.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <error.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+
+// XXX For debugging
+#include <stdio.h>
+
+#include <system.h>
+#include "ld.h"
+#include "list.h"
+/* x86 is little endian. */
+#define UNALIGNED_ACCESS_CLASS LITTLE_ENDIAN
+#include "unaligned.h"
+#include "xelf.h"
+
+
+/* The old callbacks. */
+static int (*old_open_outfile) (struct ld_state *, int, int, int);
+
+
+static int
+elf_i386_open_outfile (struct ld_state *statep,
+ int machine __attribute__ ((unused)),
+ int klass __attribute__ ((unused)),
+ int data __attribute__ ((unused)))
+{
+ /* This backend only handles 32-bit object files. */
+ /* XXX For now just use the generic backend. */
+ return old_open_outfile (statep, EM_386, ELFCLASS32, ELFDATA2LSB);
+}
+
+
+/* Process relocations for the output in a relocatable file. This
+ only means adjusting offset and symbol indices. */
+static void
+elf_i386_relocate_section (struct ld_state *statep __attribute__ ((unused)),
+ Elf_Scn *outscn, struct scninfo *firstp,
+ const Elf32_Word *dblindirect)
+{
+ struct scninfo *runp;
+ Elf_Data *data;
+
+ /* Iterate over all the input sections. Appropriate data buffers in the
+ output sections were already created. I get them iteratively, too. */
+ runp = firstp;
+ data = NULL;
+ do
+ {
+ Elf_Data *reltgtdata;
+ Elf_Data *insymdata;
+ Elf_Data *inxndxdata = NULL;
+ size_t maxcnt;
+ size_t cnt;
+ const Elf32_Word *symindirect;
+ struct symbol **symref;
+ struct usedfiles *file = runp->fileinfo;
+ XElf_Shdr *shdr = &SCNINFO_SHDR (runp->shdr);
+
+ /* Get the output section data buffer for this input section. */
+ data = elf_getdata (outscn, data);
+ assert (data != NULL);
+
+ /* Get the data for section in the input file this relocation
+ section is relocating. Since these buffers are reused in the
+ output modifying these buffers has the correct result. */
+ reltgtdata = elf_getdata (file->scninfo[shdr->sh_info].scn, NULL);
+
+ /* Get the data for the input section symbol table for this
+ relocation section. */
+ insymdata = elf_getdata (file->scninfo[shdr->sh_link].scn, NULL);
+ assert (insymdata != NULL);
+
+ /* And the extended section index table. */
+ inxndxdata = runp->fileinfo->xndxdata;
+
+ /* Number of relocations. */
+ maxcnt = shdr->sh_size / shdr->sh_entsize;
+
+ /* Array directing local symbol table offsets to output symbol
+ table offsets. */
+ symindirect = file->symindirect;
+
+ /* References to the symbol records. */
+ symref = file->symref;
+
+ /* Iterate over all the relocations in the section. */
+ for (cnt = 0; cnt < maxcnt; ++cnt)
+ {
+ XElf_Rel_vardef (rel);
+ Elf32_Word si;
+ XElf_Sym_vardef (sym);
+ Elf32_Word xndx;
+
+ /* Get the relocation data itself. x86 uses Rel
+ relocations. In case we have to handle Rela as well the
+ whole loop probably should be duplicated. */
+ xelf_getrel (data, cnt, rel);
+ assert (rel != NULL);
+
+ /* Compute the symbol index in the output file. */
+ si = symindirect[XELF_R_SYM (rel->r_info)];
+ if (si == 0)
+ {
+ /* This happens if the symbol is locally undefined or
+ superceded by some other definition. */
+ assert (symref[XELF_R_SYM (rel->r_info)] != NULL);
+ si = symref[XELF_R_SYM (rel->r_info)]->outsymidx;
+ }
+ /* Take reordering performed to sort the symbol table into
+ account. */
+ si = dblindirect[si];
+
+ /* Get the symbol table entry. */
+ xelf_getsymshndx (insymdata, inxndxdata, XELF_R_SYM (rel->r_info),
+ sym, xndx);
+ if (sym->st_shndx != SHN_XINDEX)
+ xndx = sym->st_shndx;
+ assert (xndx < SHN_LORESERVE || xndx > SHN_HIRESERVE);
+
+ /* We fortunately don't have to do much. The relocations
+ mostly get only updates of the offset. Only is a
+ relocation referred to a section do we have to do
+ something. In this case the reference to the sections
+ has no direct equivalent since the part the input section
+ contributes need not start at the same offset as in the
+ input file. Therefore we have to adjust the addend which
+ in the case of Rel relocations is in the target section
+ itself. */
+ if (XELF_ST_TYPE (sym->st_info) == STT_SECTION)
+ {
+ Elf32_Word toadd;
+
+ /* We expect here on R_386_32 relocations. */
+ assert (XELF_R_TYPE (rel->r_info) == R_386_32);
+
+ /* Avoid writing to the section memory if this is
+ effectively a no-op since it might save a
+ copy-on-write operation. */
+ toadd = file->scninfo[xndx].offset;
+ if (toadd != 0)
+ add_4ubyte_unaligned (reltgtdata->d_buf + rel->r_offset,
+ toadd);
+ }
+
+ /* Adjust the offset for the position of the input section
+ content in the output section. */
+ rel->r_offset += file->scninfo[shdr->sh_info].offset;
+
+ /* And finally adjust the index of the symbol in the output
+ symbol table. */
+ rel->r_info = XELF_R_INFO (si, XELF_R_TYPE (rel->r_info));
+
+ /* Store the result. */
+ (void) xelf_update_rel (data, cnt, rel);
+ }
+
+ runp = runp->next;
+ }
+ while (runp != firstp);
+}
+
+
+/* Each PLT entry has 16 bytes. We need one entry as overhead for
+ the code to set up the call into the runtime relocation. */
+#define PLT_ENTRY_SIZE 16
+
+static void
+elf_i386_initialize_plt (struct ld_state *statep, Elf_Scn *scn)
+{
+ Elf_Data *data;
+ XElf_Shdr_vardef (shdr);
+
+ /* Change the entry size in the section header. */
+ xelf_getshdr (scn, shdr);
+ assert (shdr != NULL);
+ shdr->sh_entsize = PLT_ENTRY_SIZE;
+ (void) xelf_update_shdr (scn, shdr);
+
+ data = elf_newdata (scn);
+ if (data == NULL)
+ error (EXIT_FAILURE, 0, gettext ("cannot allocate PLT section: %s"),
+ elf_errmsg (-1));
+
+ /* We need one special PLT entry (performing the jump to the runtime
+ relocation routines) and one for each function we call in a DSO. */
+ data->d_size = (1 + statep->nplt) * PLT_ENTRY_SIZE;
+ data->d_buf = xcalloc (1, data->d_size);
+ data->d_align = 8;
+ data->d_off = 0;
+
+ statep->nplt_used = 1;
+}
+
+
+static void
+elf_i386_initialize_pltrel (struct ld_state *statep, Elf_Scn *scn)
+{
+ Elf_Data *data;
+
+ data = elf_newdata (scn);
+ if (data == NULL)
+ error (EXIT_FAILURE, 0, gettext ("cannot allocate PLTREL section: %s"),
+ elf_errmsg (-1));
+
+ /* One relocation per PLT entry. */
+ data->d_size = statep->nplt * sizeof (Elf32_Rel);
+ data->d_buf = xcalloc (1, data->d_size);
+ data->d_type = ELF_T_REL;
+ data->d_align = 4;
+ data->d_off = 0;
+}
+
+
+static void
+elf_i386_initialize_got (struct ld_state *statep, Elf_Scn *scn)
+{
+ Elf_Data *data;
+
+ /* If we have no .plt we don't need the special entries we normally
+ create for it. The other contents is created later. */
+ if (statep->ngot + statep->nplt == 0)
+ return;
+
+ data = elf_newdata (scn);
+ if (data == NULL)
+ error (EXIT_FAILURE, 0, gettext ("cannot allocate GOT section: %s"),
+ elf_errmsg (-1));
+
+ /* We construct the .got section in pieces. Here we only add the data
+ structures which are used by the PLT. This includes three reserved
+ entries at the beginning (the first will contain a pointer to the
+ .dynamic section), and one word for each PLT entry. */
+ data->d_size = (3 + statep->ngot + statep->nplt) * sizeof (Elf32_Addr);
+ data->d_buf = xcalloc (1, data->d_size);
+ data->d_align = sizeof (Elf32_Addr);
+ data->d_off = 0;
+}
+
+
+/* The first entry in an absolute procedure linkage table looks like
+ this. See the SVR4 ABI i386 supplement to see how this works. */
+static const unsigned char elf_i386_plt0_entry[PLT_ENTRY_SIZE] =
+{
+ 0xff, 0x35, /* pushl contents of address */
+ 0, 0, 0, 0, /* replaced with address of .got + 4. */
+ 0xff, 0x25, /* jmp indirect */
+ 0, 0, 0, 0, /* replaced with address of .got + 8. */
+ 0, 0, 0, 0 /* pad out to 16 bytes. */
+};
+
+/* Type describing the first PLT entry in non-PIC. */
+struct plt0_entry
+{
+ /* First a 'push' of the second GOT entry. */
+ unsigned char push_instr[2];
+ uint32_t gotp4_addr;
+ /* Second, a 'jmp indirect' to the third GOT entry. */
+ unsigned char jmp_instr[2];
+ uint32_t gotp8_addr;
+ /* Padding. */
+ unsigned char padding[4];
+} __attribute__ ((packed));
+
+/* The first entry in a PIC procedure linkage table look like this. */
+static const unsigned char elf_i386_pic_plt0_entry[PLT_ENTRY_SIZE] =
+{
+ 0xff, 0xb3, 4, 0, 0, 0, /* pushl 4(%ebx) */
+ 0xff, 0xa3, 8, 0, 0, 0, /* jmp *8(%ebx) */
+ 0, 0, 0, 0 /* pad out to 16 bytes. */
+};
+
+/* Contents of all but the first PLT entry in executable. */
+static const unsigned char elf_i386_plt_entry[PLT_ENTRY_SIZE] =
+{
+ 0xff, 0x25, /* jmp indirect */
+ 0, 0, 0, 0, /* replaced with address of this symbol in .got. */
+ 0x68, /* pushl immediate */
+ 0, 0, 0, 0, /* replaced with offset into relocation table. */
+ 0xe9, /* jmp relative */
+ 0, 0, 0, 0 /* replaced with offset to start of .plt. */
+};
+
+/* Contents of all but the first PLT entry in DSOs. */
+static const unsigned char elf_i386_pic_plt_entry[PLT_ENTRY_SIZE] =
+{
+ 0xff, 0xa3, /* jmp *offset(%ebx) */
+ 0, 0, 0, 0, /* replaced with offset of this symbol in .got. */
+ 0x68, /* pushl immediate */
+ 0, 0, 0, 0, /* replaced with offset into relocation table. */
+ 0xe9, /* jmp relative */
+ 0, 0, 0, 0 /* replaced with offset to start of .plt. */
+};
+
+/* Type describing a PLT entry. */
+struct plt_entry
+{
+ /* The first instruction is 'jmp indirect' or 'jmp *offset(%ebs)'. */
+ unsigned char jmp_instr[2];
+ uint32_t offset_got;
+ /* The second instruction is 'push immediate'. */
+ unsigned char push_instr;
+ uint32_t push_imm;
+ /* Finally a 'jmp relative'. */
+ unsigned char jmp_instr2;
+ uint32_t plt0_offset;
+} __attribute__ ((packed));
+
+
+static void
+elf_i386_finalize_plt (struct ld_state *statep, size_t nsym,
+ size_t nsym_dyn __attribute__ ((unused)))
+{
+ Elf_Scn *scn;
+ XElf_Shdr_vardef (shdr);
+ Elf_Data *data;
+ Elf_Data *symdata = NULL;
+ Elf_Data *dynsymdata;
+ size_t cnt;
+ const bool build_dso = statep->file_type == dso_file_type;
+
+ if (unlikely (statep->nplt + statep->ngot == 0))
+ /* Nothing to be done. */
+ return;
+
+ /* Get the address of the got section. */
+ scn = elf_getscn (statep->outelf, statep->gotscnidx);
+ xelf_getshdr (scn, shdr);
+ data = elf_getdata (scn, NULL);
+ assert (shdr != NULL && data != NULL);
+ Elf32_Addr gotaddr = shdr->sh_addr;
+
+ /* Now create the initial values for the .got section. The first
+ word contains the address of the .dynamic section. */
+ xelf_getshdr (elf_getscn (statep->outelf, statep->dynamicscnidx), shdr);
+ assert (shdr != NULL);
+ ((Elf32_Word *) data->d_buf)[0] = shdr->sh_addr;
+
+ /* The second and third entry are left empty for use by the dynamic
+ linker. The following entries are pointers to the instructions
+ following the initial jmp instruction in the corresponding PLT
+ entry. Since the first PLT entry is special the first used one
+ has the index 1. */
+ scn = elf_getscn (statep->outelf, statep->pltscnidx);
+ xelf_getshdr (scn, shdr);
+ assert (shdr != NULL);
+
+ dynsymdata = elf_getdata (elf_getscn (statep->outelf, statep->dynsymscnidx),
+ NULL);
+ assert (dynsymdata != NULL);
+
+ if (statep->symscnidx != 0)
+ {
+ symdata = elf_getdata (elf_getscn (statep->outelf, statep->symscnidx),
+ NULL);
+ assert (symdata != NULL);
+ }
+
+ for (cnt = 0; cnt < statep->nplt; ++cnt)
+ {
+ assert ((4 + cnt) * sizeof (Elf32_Word) <= data->d_size);
+
+ /* Address in the PLT. */
+ Elf32_Addr pltentryaddr = shdr->sh_addr + (1 + cnt) * PLT_ENTRY_SIZE;
+
+ /* Point the GOT entry at the PLT entry, after the initial jmp. */
+ ((Elf32_Word *) data->d_buf)[3 + cnt] = pltentryaddr + 6;
+
+ /* The value of the symbol is the address of the corresponding PLT
+ entry. Store the address, also for the normal symbol table if
+ this is necessary. */
+ ((Elf32_Sym *) dynsymdata->d_buf)[1 + cnt].st_value = pltentryaddr;
+
+ if (symdata != NULL)
+ ((Elf32_Sym *) symdata->d_buf)[nsym - statep->nplt + cnt].st_value
+ = pltentryaddr;
+ }
+
+ /* Create the .plt section. */
+ scn = elf_getscn (statep->outelf, statep->pltscnidx);
+ data = elf_getdata (scn, NULL);
+ assert (data != NULL);
+
+ /* Create the first entry. */
+ assert (data->d_size >= PLT_ENTRY_SIZE);
+ if (build_dso)
+ /* Copy the entry. It's complete, no relocation needed. */
+ memcpy (data->d_buf, elf_i386_pic_plt0_entry, PLT_ENTRY_SIZE);
+ else
+ {
+ /* Copy the skeleton. */
+ memcpy (data->d_buf, elf_i386_plt0_entry, PLT_ENTRY_SIZE);
+
+ /* And fill in the addresses. */
+ struct plt0_entry *addr = (struct plt0_entry *) data->d_buf;
+ addr->gotp4_addr = target_bswap_32 (gotaddr + 4);
+ addr->gotp8_addr = target_bswap_32 (gotaddr + 8);
+ }
+
+ /* For DSOs we need GOT offsets, otherwise the GOT address. */
+ Elf32_Addr gotaddr_off = build_dso ? 0 : gotaddr;
+
+ /* Create the remaining entries. */
+ const unsigned char *plt_template
+ = build_dso ? elf_i386_pic_plt_entry : elf_i386_plt_entry;
+
+ for (cnt = 0; cnt < statep->nplt; ++cnt)
+ {
+ struct plt_entry *addr;
+
+ /* Copy the template. */
+ assert (data->d_size >= (2 + cnt) * PLT_ENTRY_SIZE);
+ addr = (struct plt_entry *) ((char *) data->d_buf
+ + (1 + cnt) * PLT_ENTRY_SIZE);
+ memcpy (addr, plt_template, PLT_ENTRY_SIZE);
+
+ /* And once more, fill in the addresses. First the address of
+ this symbol in .got. */
+ addr->offset_got = target_bswap_32 (gotaddr_off
+ + (3 + cnt) * sizeof (Elf32_Addr));
+ /* Offset into relocation table. */
+ addr->push_imm = target_bswap_32 (cnt * sizeof (Elf32_Rel));
+ /* Offset to start of .plt. */
+ addr->plt0_offset = target_bswap_32 (-(2 + cnt) * PLT_ENTRY_SIZE);
+ }
+
+ /* Create the .rel.plt section data. It simply means relocations
+ addressing the corresponding entry in the .got section. The
+ section name is misleading. */
+ scn = elf_getscn (statep->outelf, statep->pltrelscnidx);
+ xelf_getshdr (scn, shdr);
+ data = elf_getdata (scn, NULL);
+ assert (shdr != NULL && data != NULL);
+
+ /* Update the sh_link to point to the section being modified. We
+ point it here (correctly) to the .got section. Some linkers
+ (e.g., the GNU binutils linker) point to the .plt section. This
+ is wrong since the .plt section isn't modified even though the
+ name .rel.plt suggests that this is correct. */
+ shdr->sh_link = statep->dynsymscnidx;
+ shdr->sh_info = statep->gotscnidx;
+ (void) xelf_update_shdr (scn, shdr);
+
+ for (cnt = 0; cnt < statep->nplt; ++cnt)
+ {
+ XElf_Rel_vardef (rel);
+
+ assert ((1 + cnt) * sizeof (Elf32_Rel) <= data->d_size);
+ xelf_getrel_ptr (data, cnt, rel);
+ rel->r_offset = gotaddr + (3 + cnt) * sizeof (Elf32_Addr);
+ /* The symbol table entries for the functions from DSOs are at
+ the end of the symbol table. */
+ rel->r_info = XELF_R_INFO (1 + cnt, R_386_JMP_SLOT);
+ (void) xelf_update_rel (data, cnt, rel);
+ }
+}
+
+
+static int
+elf_i386_rel_type (struct ld_state *statep __attribute__ ((__unused__)))
+{
+ /* ELF/i386 uses REL. */
+ return DT_REL;
+}
+
+
+static void
+elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo)
+{
+ /* We go through the list of input sections and count those relocations
+ which are not handled by the linker. At the same time we have to
+ see how many GOT entries we need and how much .bss space is needed
+ for copy relocations. */
+ Elf_Data *data = elf_getdata (scninfo->scn, NULL);
+ XElf_Shdr *shdr = &SCNINFO_SHDR (scninfo->shdr);
+ size_t maxcnt = shdr->sh_size / shdr->sh_entsize;
+ size_t relsize = 0;
+ size_t cnt;
+ struct symbol *sym;
+
+ assert (shdr->sh_type == SHT_REL);
+
+ for (cnt = 0; cnt < maxcnt; ++cnt)
+ {
+ XElf_Rel_vardef (rel);
+
+ xelf_getrel (data, cnt, rel);
+ /* XXX Should we complain about failing accesses? */
+ if (rel != NULL)
+ {
+ Elf32_Word r_sym = XELF_R_SYM (rel->r_info);
+
+ switch (XELF_R_TYPE (rel->r_info))
+ {
+ case R_386_GOT32:
+ if (! scninfo->fileinfo->symref[r_sym]->defined)
+ relsize += sizeof (Elf32_Rel);
+
+ /* This relocation is not emitted in the output file but
+ requires a GOT entry. */
+ ++statep->ngot;
+ ++statep->nrel_got;
+
+ /* FALLTHROUGH */
+
+ case R_386_GOTOFF:
+ case R_386_GOTPC:
+ statep->need_got = true;
+ break;
+
+ case R_386_32:
+ case R_386_PC32:
+ /* These relocations cause text relocations in DSOs. */
+ if (linked_from_dso_p (scninfo, r_sym))
+ {
+ if (statep->file_type == dso_file_type)
+ {
+ relsize += sizeof (Elf32_Rel);
+ statep->dt_flags |= DF_TEXTREL;
+ }
+ else
+ {
+ /* Non-function objects from a DSO need to get a
+ copy relocation. */
+ sym = scninfo->fileinfo->symref[r_sym];
+
+ /* Only do this if we have not requested a copy
+ relocation already. */
+ if (unlikely (sym->type != STT_FUNC) && ! sym->need_copy)
+ {
+ sym->need_copy = 1;
+ ++statep->ncopy;
+ relsize += sizeof (Elf32_Rel);
+ }
+ }
+ }
+ else if (statep->file_type == dso_file_type
+ && r_sym >= SCNINFO_SHDR (scninfo->fileinfo->scninfo[shdr->sh_link].shdr).sh_info
+ && scninfo->fileinfo->symref[r_sym]->outdynsymidx != 0
+ && XELF_R_TYPE (rel->r_info) == R_386_32)
+ relsize += sizeof (Elf32_Rel);
+ break;
+
+ case R_386_PLT32:
+ /* We might need a PLT entry. But we cannot say for sure
+ here since one of the symbols might turn up being
+ defined in the executable (if we create such a thing).
+ If a DSO is created we still might use a local
+ definition.
+
+ If the symbol is not defined and we are not creating
+ a statically linked binary, then we need in any case
+ a PLT entry. */
+ if (! scninfo->fileinfo->symref[r_sym]->defined)
+ {
+ assert (!statep->statically);
+
+ sym = scninfo->fileinfo->symref[r_sym];
+ sym->type = STT_FUNC;
+ sym->in_dso = 1;
+ sym->defined = 1;
+
+ /* Remove from the list of unresolved symbols. */
+ --statep->nunresolved;
+ if (! sym->weak)
+ --statep->nunresolved_nonweak;
+ CDBL_LIST_DEL (statep->unresolved, sym);
+
+ /* Add to the list of symbols we expect from a DSO. */
+ ++statep->nplt;
+ ++statep->nfrom_dso;
+ CDBL_LIST_ADD_REAR (statep->from_dso, sym);
+ }
+ break;
+
+ case R_386_TLS_GD:
+ case R_386_TLS_LDM:
+ case R_386_TLS_GD_32:
+ case R_386_TLS_GD_PUSH:
+ case R_386_TLS_GD_CALL:
+ case R_386_TLS_GD_POP:
+ case R_386_TLS_LDM_32:
+ case R_386_TLS_LDM_PUSH:
+ case R_386_TLS_LDM_CALL:
+ case R_386_TLS_LDM_POP:
+ case R_386_TLS_LDO_32:
+ case R_386_TLS_IE_32:
+ case R_386_TLS_LE_32:
+ /* XXX */
+ abort ();
+ break;
+
+ case R_386_NONE:
+ /* Nothing to be done. */
+ break;
+
+ /* These relocation should never be generated by an
+ assembler. */
+ case R_386_COPY:
+ case R_386_GLOB_DAT:
+ case R_386_JMP_SLOT:
+ case R_386_RELATIVE:
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ case R_386_TLS_TPOFF32:
+ /* Unknown relocation. */
+ default:
+ abort ();
+ }
+ }
+ }
+
+ scninfo->relsize = relsize;
+}
+
+
+static void
+elf_i386_create_relocations (struct ld_state *statep,
+ const Elf32_Word *dblindirect __attribute__ ((unused)))
+{
+ /* Get the address of the got section. */
+ Elf_Scn *pltscn = elf_getscn (statep->outelf, statep->pltscnidx);
+ Elf32_Shdr *shdr = elf32_getshdr (pltscn);
+ assert (shdr != NULL);
+ Elf32_Addr pltaddr = shdr->sh_addr;
+
+ Elf_Scn *gotscn = elf_getscn (statep->outelf, statep->gotscnidx);
+ shdr = elf32_getshdr (gotscn);
+ assert (shdr != NULL);
+ Elf32_Addr gotaddr = shdr->sh_addr;
+
+ Elf_Scn *reldynscn = elf_getscn (statep->outelf, statep->reldynscnidx);
+ Elf_Data *reldyndata = elf_getdata (reldynscn, NULL);
+
+ size_t nreldyn = 0;
+#define ngot_used (3 + statep->nplt + nreldyn)
+
+ struct scninfo *first = statep->rellist->next;
+ struct scninfo *runp = first;
+ do
+ {
+ XElf_Shdr *rshdr = &SCNINFO_SHDR (runp->shdr);
+ Elf_Data *reldata = elf_getdata (runp->scn, NULL);
+ int nrels = rshdr->sh_size / rshdr->sh_entsize;
+
+ /* We will need the following vlaues a couple of times. Help
+ the compiler and improve readability. */
+ struct symbol **symref = runp->fileinfo->symref;
+ struct scninfo *scninfo = runp->fileinfo->scninfo;
+
+ /* This is the offset of the input section we are looking at in
+ the output file. */
+ XElf_Addr inscnoffset = scninfo[rshdr->sh_info].offset;
+
+ /* The target section. We use the data from the input file. */
+ Elf_Data *data = elf_getdata (scninfo[rshdr->sh_info].scn, NULL);
+
+ /* We cannot handle relocations against merge-able sections. */
+ assert ((SCNINFO_SHDR (scninfo[rshdr->sh_link].shdr).sh_flags
+ & SHF_MERGE) == 0);
+
+ /* Cache the access to the symbol table data. */
+ Elf_Data *symdata = elf_getdata (scninfo[rshdr->sh_link].scn, NULL);
+
+ int cnt;
+ for (cnt = 0; cnt < nrels; ++cnt)
+ {
+ XElf_Rel_vardef (rel);
+ XElf_Rel *rel2;
+ xelf_getrel (reldata, cnt, rel);
+ assert (rel != NULL);
+ XElf_Addr reladdr = inscnoffset + rel->r_offset;
+ XElf_Addr value;
+
+ size_t idx = XELF_R_SYM (rel->r_info);
+ if (idx < runp->fileinfo->nlocalsymbols)
+ {
+ XElf_Sym_vardef (sym);
+ xelf_getsym (symdata, idx, sym);
+
+ /* The value just depends on the position of the referenced
+ section in the output file and the addend. */
+ value = scninfo[sym->st_shndx].offset + sym->st_value;
+ }
+ else if (symref[idx]->in_dso)
+ {
+ /* MERGE.VALUE contains the PLT index. We have to add 1 since
+ there is this one special PLT entry at the beginning. */
+ assert (symref[idx]->merge.value != 0
+ || symref[idx]->type != STT_FUNC);
+ value = pltaddr + symref[idx]->merge.value * PLT_ENTRY_SIZE;
+ }
+ else
+ value = symref[idx]->merge.value;
+
+ /* Address of the relocated memory in the data buffer. */
+ void *relloc = (char *) data->d_buf + rel->r_offset;
+
+ switch (XELF_R_TYPE (rel->r_info))
+ {
+ /* These three cases can be handled together since the
+ symbol associated with the R_386_GOTPC relocation is
+ _GLOBAL_OFFSET_TABLE_ which has a value corresponding
+ to the address of the GOT and the address of the PLT
+ entry required for R_386_PLT32 is computed above. */
+ case R_386_PC32:
+ case R_386_GOTPC:
+ case R_386_PLT32:
+ value -= reladdr;
+ /* FALLTHROUGH */
+
+ case R_386_32:
+ if (linked_from_dso_p (scninfo, idx)
+ && statep->file_type != dso_file_type
+ && symref[idx]->type != STT_FUNC)
+ {
+ value = (ld_state.copy_section->offset
+ + symref[idx]->merge.value);
+
+ if (unlikely (symref[idx]->need_copy))
+ {
+ /* Add a relocation to initialize the GOT entry. */
+ assert (symref[idx]->outdynsymidx != 0);
+#if NATIVE_ELF != 0
+ xelf_getrel_ptr (reldyndata, nreldyn, rel2);
+#else
+ rel2 = &rel_mem;
+#endif
+ rel2->r_offset = value;
+ rel2->r_info
+ = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_COPY);
+ (void) xelf_update_rel (reldyndata, nreldyn, rel2);
+ ++nreldyn;
+
+ /* Update the symbol table record for the new
+ address. */
+ Elf32_Word symidx = symref[idx]->outdynsymidx;
+ Elf_Scn *symscn = elf_getscn (statep->outelf,
+ statep->dynsymscnidx);
+ Elf_Data *outsymdata = elf_getdata (symscn, NULL);
+ assert (outsymdata != NULL);
+ XElf_Sym_vardef (sym);
+ xelf_getsym (outsymdata, symidx, sym);
+ sym->st_value = value;
+ sym->st_shndx = statep->copy_section->outscnndx;
+ (void) xelf_update_sym (outsymdata, symidx, sym);
+
+ symidx = symref[idx]->outsymidx;
+ if (symidx != 0)
+ {
+ symidx = statep->dblindirect[symidx];
+ symscn = elf_getscn (statep->outelf,
+ statep->symscnidx);
+ outsymdata = elf_getdata (symscn, NULL);
+ assert (outsymdata != NULL);
+ xelf_getsym (outsymdata, symidx, sym);
+ sym->st_value = value;
+ sym->st_shndx = statep->copy_section->outscnndx;
+ (void) xelf_update_sym (outsymdata, symidx, sym);
+ }
+
+ /* Remember that we set up the copy relocation. */
+ symref[idx]->need_copy = 0;
+ }
+ }
+ else if (statep->file_type == dso_file_type
+ && idx >= SCNINFO_SHDR (scninfo[rshdr->sh_link].shdr).sh_info
+ && symref[idx]->outdynsymidx != 0)
+ {
+#if NATIVE_ELF != 0
+ xelf_getrel_ptr (reldyndata, nreldyn, rel2);
+#else
+ rel2 = &rel_mem;
+#endif
+ rel2->r_offset = value;
+ rel2->r_info
+ = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_32);
+ (void) xelf_update_rel (reldyndata, nreldyn, rel2);
+ ++nreldyn;
+
+ value = 0;
+ }
+ add_4ubyte_unaligned (relloc, value);
+ break;
+
+ case R_386_GOT32:
+ store_4ubyte_unaligned (relloc, ngot_used * sizeof (Elf32_Addr));
+
+ /* Add a relocation to initialize the GOT entry. */
+#if NATIVE_ELF != 0
+ xelf_getrel_ptr (reldyndata, nreldyn, rel2);
+#else
+ rel2 = &rel_mem;
+#endif
+ rel2->r_offset = gotaddr + ngot_used * sizeof (Elf32_Addr);
+ rel2->r_info
+ = XELF_R_INFO (symref[idx]->outdynsymidx, R_386_GLOB_DAT);
+ (void) xelf_update_rel (reldyndata, nreldyn, rel2);
+ ++nreldyn;
+ break;
+
+ case R_386_GOTOFF:
+ add_4ubyte_unaligned (relloc, value - gotaddr);
+ break;
+
+ case R_386_32PLT:
+ case R_386_TLS_TPOFF:
+ case R_386_TLS_IE:
+ case R_386_TLS_GOTIE:
+ case R_386_TLS_LE:
+ case R_386_TLS_GD:
+ case R_386_TLS_LDM:
+ case R_386_16:
+ case R_386_PC16:
+ case R_386_8:
+ case R_386_PC8:
+ case R_386_TLS_GD_32:
+ case R_386_TLS_GD_PUSH:
+ case R_386_TLS_GD_CALL:
+ case R_386_TLS_GD_POP:
+ case R_386_TLS_LDM_32:
+ case R_386_TLS_LDM_PUSH:
+ case R_386_TLS_LDM_CALL:
+ case R_386_TLS_LDM_POP:
+ case R_386_TLS_LDO_32:
+ case R_386_TLS_IE_32:
+ case R_386_TLS_LE_32:
+ // XXX For now fall through
+ printf("ignored relocation %d\n", (int) XELF_R_TYPE (rel->r_info));
+ break;
+
+ case R_386_NONE:
+ /* Nothing to do. */
+ break;
+
+ case R_386_COPY:
+ case R_386_JMP_SLOT:
+ case R_386_RELATIVE:
+ case R_386_GLOB_DAT:
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ case R_386_TLS_TPOFF32:
+ default:
+ /* Should not happen. */
+ abort ();
+ }
+ }
+ }
+ while ((runp = runp->next) != first);
+}
+
+
+int
+elf_i386_ld_init (struct ld_state *statep)
+{
+ /* We have a few callbacks available. */
+ old_open_outfile = statep->callbacks.open_outfile;
+ statep->callbacks.open_outfile = elf_i386_open_outfile;
+
+ statep->callbacks.relocate_section = elf_i386_relocate_section;
+
+ statep->callbacks.initialize_plt = elf_i386_initialize_plt;
+ statep->callbacks.initialize_pltrel = elf_i386_initialize_pltrel;
+
+ statep->callbacks.initialize_got = elf_i386_initialize_got;
+
+ statep->callbacks.finalize_plt = elf_i386_finalize_plt;
+
+ statep->callbacks.rel_type = elf_i386_rel_type;
+
+ statep->callbacks.count_relocations = elf_i386_count_relocations;
+
+ statep->callbacks.create_relocations = elf_i386_create_relocations;
+
+ return 0;
+}