aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfd/cofflink.c368
1 files changed, 351 insertions, 17 deletions
diff --git a/bfd/cofflink.c b/bfd/cofflink.c
index 8776964..2e6fe26 100644
--- a/bfd/cofflink.c
+++ b/bfd/cofflink.c
@@ -1,5 +1,5 @@
/* COFF specific linker code.
- Copyright 1994 Free Software Foundation, Inc.
+ Copyright 1994, 1995 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support.
This file is part of BFD, the Binary File Descriptor library.
@@ -78,6 +78,9 @@ struct coff_final_link_info
bfd_byte *external_relocs;
/* Buffer large enough to hold swapped relocs of any input section. */
struct internal_reloc *internal_relocs;
+
+enum bfd_link_subsystem subsystem;
+bfd_link_stack_heap stack_heap_parameters;
};
static struct bfd_hash_entry *coff_link_hash_newfunc
@@ -100,6 +103,45 @@ static boolean coff_reloc_link_order
PARAMS ((bfd *, struct coff_final_link_info *, asection *,
struct bfd_link_order *));
+
+/* These new data and data types are used to keep track of the .idata$4 and
+ .idata$5 relocations which are put into the .idata section for all of the
+ *.dll input libraries linked in. This is not a great solution, and may
+ break in the future if MS changes the format of its libraries, but it
+ does work for the collection of mstools libraries we are currently working
+ with. The main problem is that there are some new majic symbols defined
+ in the libraries which are non-standard coff and simply aren't handled
+ completely by ld. What has been included here will help finish up the job.
+ Basically, during the link, .idata$4 and .idata$5 pointers are correctly
+ relocated to the image. At the very end of the link, the .idata$2
+ information is written. This data appears at the beginning of the .idata
+ section and a 'set' of information appears for each *.dll passed in.
+ Each set of information consists of 3 addresses, a pointer to the .idata$4
+ start, a pointer to .idata$6 (which has the name of the dll), and a pointer
+ to .idata$5 start. The idata$4 and 5 information is a list of pointers
+ which appear to point to the name of various functions found within the dll.
+ When invoked, the loader will write over these names with the correct
+ addresses to use for these functions.
+ Without this 'fix', all information appears correctly except for the
+ addresses of the .idata$4 and 5 starts within the .idata$2 portion of the
+ .idata section. What we will do is to keep track of the dll's processed
+ and the number of functions needed from each dll. From this information
+ we can correctly compute the start of the idata$4 and 5 lists for each
+ dll in the idata section */
+static int num_DLLs_done = 0;
+static int num_DLLs = 0;
+static int all_entries = 0;
+struct DLL_struct {
+ const char * DLL_name;
+ int num_entries;
+};
+struct DLL_struct MS_DLL[10];
+static bfd_vma idata_4_prev = 0;
+static bfd_vma idata_5_prev = 0;
+static bfd_vma add_to_val = 0;
+
+
+
/* Create an entry in a COFF linker hash table. */
static struct bfd_hash_entry *
@@ -304,7 +346,7 @@ coff_read_string_table (abfd)
else
{
#if STRING_SIZE_SIZE == 4
- strsize = bfd_h_get_32 (abfd, extstrsize);
+ strsize = bfd_h_get_32 (abfd, (bfd_byte *) extstrsize);
#else
#error Change bfd_h_get_32
#endif
@@ -566,9 +608,10 @@ coff_link_add_symbols (abfd, info)
bfd_byte *eaux;
union internal_auxent *iaux;
- alloc = bfd_hash_allocate (&info->hash->table,
- (sym.n_numaux
- * sizeof (*alloc)));
+ alloc = ((union internal_auxent *)
+ bfd_hash_allocate (&info->hash->table,
+ (sym.n_numaux
+ * sizeof (*alloc))));
if (alloc == NULL)
{
bfd_set_error (bfd_error_no_memory);
@@ -593,6 +636,126 @@ coff_link_add_symbols (abfd, info)
return true;
}
+/* parse out a -heap <reserved>,<commit> line */
+
+static char *
+dores_com (ptr, def,res, com)
+ char *ptr;
+ int *def;
+ int *res;
+ int *com;
+{
+ *def = 1;
+ *res = strtoul (ptr, &ptr, 0);
+ if (ptr[0] == ',')
+ *com = strtoul (ptr+1, &ptr, 0);
+ return ptr;
+}
+
+static char *get_name(ptr, dst)
+char *ptr;
+char **dst;
+{
+ while (*ptr == ' ')
+ ptr++;
+ *dst = ptr;
+ while (*ptr && *ptr != ' ')
+ ptr++;
+ *ptr = 0;
+ return ptr+1;
+}
+/* Process any magic embedded commands in a section called .drectve */
+
+static int
+process_embedded_commands (abfd)
+ bfd *abfd;
+{
+ asection *sec = bfd_get_section_by_name (abfd, ".drectve");
+ char *s;
+ char *e;
+ char *copy;
+ if (!s)
+ return 1;
+
+ copy = malloc (sec->_raw_size);
+ if (!copy)
+ {
+ bfd_set_error (bfd_error_no_memory);
+ return 0;
+ }
+ if (! bfd_get_section_contents(abfd, sec, copy, 0, sec->_raw_size))
+ {
+ free (copy);
+ return 0;
+ }
+ e = copy + sec->_raw_size;
+ for (s = copy; s < e ; )
+ {
+ if (s[0]!= '-') {
+ s++;
+ continue;
+ }
+ if (strncmp (s,"-attr", 5) == 0)
+ {
+ char *name;
+ char *attribs;
+ asection *asec;
+
+ int loop = 1;
+ int had_write = 0;
+ int had_read = 0;
+ int had_exec= 0;
+ int had_shared= 0;
+ s += 5;
+ s = get_name(s, &name);
+ s = get_name(s, &attribs);
+ while (loop) {
+ switch (*attribs++)
+ {
+ case 'W':
+ had_write = 1;
+ break;
+ case 'R':
+ had_read = 1;
+ break;
+ case 'S':
+ had_shared = 1;
+ break;
+ case 'X':
+ had_exec = 1;
+ break;
+ default:
+ loop = 0;
+ }
+ }
+ asec = bfd_get_section_by_name (abfd, name);
+ if (asec) {
+ if (had_exec)
+ asec->flags |= SEC_CODE;
+ if (!had_write)
+ asec->flags |= SEC_READONLY;
+ }
+ }
+ else if (strncmp (s,"-heap", 5) == 0)
+ {
+ s = dores_com (s+5,
+ &NT_stack_heap.heap_defined,
+ &NT_stack_heap.heap_reserve,
+ &NT_stack_heap.heap_commit);
+ }
+ else if (strncmp (s,"-stack", 6) == 0)
+ {
+ s = dores_com (s+6,
+ &NT_stack_heap.heap_defined,
+ &NT_stack_heap.heap_reserve,
+ &NT_stack_heap.heap_commit);
+ }
+ else
+ s++;
+ }
+ free (copy);
+ return 1;
+}
/* Do the final link step. */
boolean
@@ -633,6 +796,15 @@ _bfd_coff_final_link (abfd, info)
finfo.external_relocs = NULL;
finfo.internal_relocs = NULL;
+ if (obj_pe(abfd))
+ {
+ /* store the subsystem, stack and heap parameters in variables defined
+ in internal.h so that when they are needed to write the NT optional
+ file header (coffcode.h), they will be available */
+ NT_subsystem = info->subsystem;
+ NT_stack_heap = info->stack_heap_parameters;
+ }
+
finfo.strtab = _bfd_stringtab_init ();
if (finfo.strtab == NULL)
goto error_return;
@@ -989,7 +1161,7 @@ _bfd_coff_final_link (abfd, info)
#if STRING_SIZE_SIZE == 4
bfd_h_put_32 (abfd,
_bfd_stringtab_size (finfo.strtab) + STRING_SIZE_SIZE,
- strbuf);
+ (bfd_byte *) strbuf);
#else
#error Change bfd_h_put_32
#endif
@@ -1054,6 +1226,9 @@ coff_link_input_bfd (finfo, input_bfd)
bfd *input_bfd;
{
boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
+ boolean (*adjust_symndx) PARAMS ((bfd *, struct bfd_link_info *, bfd *,
+ asection *, struct internal_reloc *,
+ boolean *));
bfd *output_bfd;
const char *strings;
bfd_size_type syment_base;
@@ -1109,6 +1284,13 @@ coff_link_input_bfd (finfo, input_bfd)
indexp = finfo->sym_indices;
output_index = syment_base;
outsym = finfo->outsyms;
+
+ if (obj_pe (output_bfd))
+ {
+ if (!process_embedded_commands (input_bfd))
+ return false;
+ }
+
while (esym < esym_end)
{
struct internal_syment isym;
@@ -1387,7 +1569,8 @@ coff_link_input_bfd (finfo, input_bfd)
{
/* If this is a long filename, we must put it in the
string table. */
- if (auxp->x_file.x_n.x_zeroes == 0)
+ if (auxp->x_file.x_n.x_zeroes == 0
+ && auxp->x_file.x_n.x_offset != 0)
{
const char *filename;
bfd_size_type indx;
@@ -1570,13 +1753,14 @@ coff_link_input_bfd (finfo, input_bfd)
symbol will be the first symbol in the next input file. In the
normal case, this will save us from writing out the C_FILE symbol
again. */
- if (finfo->last_file_index >= syment_base)
+ if (finfo->last_file_index != -1
+ && finfo->last_file_index >= syment_base)
{
finfo->last_file.n_value = output_index;
bfd_coff_swap_sym_out (output_bfd, (PTR) &finfo->last_file,
(PTR) (finfo->outsyms
- + ((finfo->last_file_index - syment_base)
- * osymesz)));
+ + ((finfo->last_file_index - syment_base)
+ * osymesz)));
}
/* Write the modified symbols to the output file. */
@@ -1598,6 +1782,7 @@ coff_link_input_bfd (finfo, input_bfd)
/* Relocate the contents of each section. */
relsz = bfd_coff_relsz (input_bfd);
+ adjust_symndx = coff_backend_info (input_bfd)->_bfd_coff_adjust_symndx;
for (o = input_bfd->sections; o != NULL; o = o->next)
{
if ((o->flags & SEC_HAS_CONTENTS) == 0)
@@ -1605,7 +1790,7 @@ coff_link_input_bfd (finfo, input_bfd)
if (! bfd_get_section_contents (input_bfd, o, finfo->contents,
(file_ptr) 0, o->_raw_size))
- return false;
+ return false;
if ((o->flags & SEC_RELOC) != 0)
{
@@ -1655,7 +1840,6 @@ coff_link_input_bfd (finfo, input_bfd)
struct coff_link_hash_entry **rel_hash;
offset = o->output_section->vma + o->output_offset - o->vma;
-
irel = internal_relocs;
irelend = irel + o->reloc_count;
rel_hash = (finfo->section_info[target_index].rel_hashes
@@ -1663,6 +1847,7 @@ coff_link_input_bfd (finfo, input_bfd)
for (; irel < irelend; irel++, rel_hash++)
{
struct coff_link_hash_entry *h;
+ boolean adjusted;
*rel_hash = NULL;
@@ -1673,6 +1858,16 @@ coff_link_input_bfd (finfo, input_bfd)
if (irel->r_symndx == -1)
continue;
+ if (adjust_symndx)
+ {
+ if (! (*adjust_symndx) (output_bfd, finfo->info,
+ input_bfd, o, irel,
+ &adjusted))
+ return false;
+ if (adjusted)
+ continue;
+ }
+
h = obj_coff_sym_hashes (input_bfd)[irel->r_symndx];
if (h != NULL)
{
@@ -1792,12 +1987,13 @@ coff_write_global_sym (h, data)
return false;
case bfd_link_hash_undefined:
- case bfd_link_hash_weak:
+ case bfd_link_hash_undefweak:
isym.n_scnum = N_UNDEF;
isym.n_value = 0;
break;
case bfd_link_hash_defined:
+ case bfd_link_hash_defweak:
{
asection *sec;
@@ -1897,7 +2093,7 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
asection *output_section;
struct bfd_link_order *link_order;
{
- const reloc_howto_type *howto;
+ reloc_howto_type *howto;
struct internal_reloc *irel;
struct coff_link_hash_entry **rel_hash_ptr;
@@ -2039,6 +2235,7 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
struct internal_reloc *rel;
struct internal_reloc *relend;
+
rel = relocs;
relend = rel + input_section->reloc_count;
for (; rel < relend; rel++)
@@ -2048,7 +2245,7 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
struct internal_syment *sym;
bfd_vma addend;
bfd_vma val;
- const reloc_howto_type *howto;
+ reloc_howto_type *howto;
bfd_reloc_status_type rstat;
symndx = rel->r_symndx;
@@ -2068,21 +2265,33 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
size of the symbol is included in the section contents, or it
is not. We assume that the size is not included, and force
the rtype_to_howto function to adjust the addend as needed. */
+
if (sym != NULL && sym->n_scnum != 0)
addend = - sym->n_value;
else
addend = 0;
+
howto = bfd_coff_rtype_to_howto (input_bfd, input_section, rel, h,
sym, &addend);
if (howto == NULL)
return false;
+ /* WINDOWS_NT; in this next section, the value of 'val' will be computed.
+ With respect to the .idata and .rsrc sections, the NT_IMAGE_BASE
+ must be removed from the value that is to be relocated (NT_IMAGE_BASE
+ is currently defined in internal.h and has value 400000). Now this
+ value should only be removed from addresses being relocated in the
+ .idata and .rsrc sections, not the .text section which should have
+ the 'real' address. In addition, the .rsrc val's must also be
+ adjusted by the input_section->vma. */
+
val = 0;
if (h == NULL)
{
asection *sec;
+ int i;
if (symndx == -1)
{
@@ -2092,15 +2301,30 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
else
{
sec = sections[symndx];
- val = (sec->output_section->vma
+ val = (sec->output_section->vma
+ sec->output_offset
+ sym->n_value
- sec->vma);
+ if (obj_pe(output_bfd)) {
+ /* Make a correction here to val if the sec is either .rsrc
+ or .idata */
+ if (strcmp (input_section->name, ".text") != 0)
+ {
+ if (strncmp (sec->name, ".idata$", 7) == 0)
+ val -= NT_IMAGE_BASE;
+ else if (strncmp (sec->name, ".rsrc$", 6) == 0)
+ {
+ val -= NT_IMAGE_BASE;
+ val += sec->vma;
+ }
+ }
+ }
}
}
else
{
- if (h->root.type == bfd_link_hash_defined)
+ if (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
{
asection *sec;
@@ -2108,6 +2332,20 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
val = (h->root.u.def.value
+ sec->output_section->vma
+ sec->output_offset);
+ if (obj_pe (output_bfd)) {
+ /* Make a correction here to val if the sec is either .rsrc
+ or .idata */
+ if (strcmp (input_section->name, ".text") != 0)
+ {
+ if (strncmp (sec->name, ".idata$", 7) == 0)
+ val -= NT_IMAGE_BASE;
+ else if (strncmp (sec->name, ".rsrc$", 6) == 0)
+ {
+ val -= NT_IMAGE_BASE;
+ val += sec->vma;
+ }
+ }
+ }
}
else if (! info->relocateable)
{
@@ -2118,6 +2356,102 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
}
}
+ if (obj_pe (output_bfd)) {
+
+ /* Here's where we will collect the information about the dll .idata$4
+ and 5 entries and fix up the vals for .idata$2 information. When
+ we encounter processing for .idata$5 (this could also be done for
+ .idata$4) we will keep track of the number of entries made for a
+ particular dll. Now if we are processing .idata$2 input_section,
+ then we know how many entries have been made from each dll and we
+ have to fix up the .idata$2 start addresses for .idata$4 and
+ .idata$5. */
+ add_to_val = 0;
+ if (strncmp (input_section->name, ".idata$5", 8) == 0)
+ {
+ if (num_DLLs == 0) /* this is the first one */
+ {
+ num_DLLs += 1;
+ MS_DLL[num_DLLs].DLL_name = input_bfd->filename;
+ MS_DLL[num_DLLs].num_entries += 1;
+ }
+ else if (!strcmp (input_bfd->filename, MS_DLL[num_DLLs].DLL_name))
+ {
+ /* this is just another entry */
+ MS_DLL[num_DLLs].num_entries += 1;
+ }
+ else
+ {
+ /* This is a new DLL */
+ num_DLLs += 1;
+ MS_DLL[num_DLLs].DLL_name = input_bfd->filename;
+ MS_DLL[num_DLLs].num_entries += 1;
+ }
+ all_entries += 1;
+ }
+
+ else if (strncmp (input_section->name, ".idata$2", 8) == 0)
+ {
+ /* All information about the number of entries needed from each
+ DLL has been collected at this point. Now we actually want to
+ make and adjustment to the val's for .idata$4 and .idata$5
+ which are part of the .idata$2 section. */
+ /* first we have to get the symbol name from sym. This will be
+ either .idata$4, .idata$5 or .idata$6. A 'fixup' is computed for
+ .idata$4 and .idata$5 but not for .idata$6 (this value is handled
+ correctly already and doesn't have to be fixed) */
+ const char *name;
+ char buf[SYMNMLEN + 1];
+
+ if (sym->_n._n_n._n_zeroes == 0 && sym->_n._n_n._n_offset != 0)
+ name = obj_coff_strings (input_bfd) + sym->_n._n_n._n_offset;
+ else
+ {
+ strncpy (buf, sym->_n._n_name, SYMNMLEN);
+ buf[SYMNMLEN] = '\0';
+ name = buf;
+ }
+
+ if (num_DLLs_done)
+ {
+ /* we have done at least one. The val fixups are based on the
+ previous fixups */
+ if (strncmp (name, ".idata$4", 8) == 0)
+ {
+ add_to_val = idata_4_prev +
+ ((MS_DLL[num_DLLs_done].num_entries + 1) * 4);
+ idata_4_prev = add_to_val;
+ }
+ else if (strncmp (name, ".idata$5", 8) == 0)
+ {
+ add_to_val = idata_5_prev +
+ ((MS_DLL[num_DLLs_done].num_entries + 1) * 4);
+ idata_5_prev = add_to_val;
+ num_DLLs_done += 1; /* assuming that idata$5 is done after $4*/
+ }
+ }
+ else
+ {
+ /* This is the first one. The other idata$4 and 5 entries will be
+ computed from these */
+ if (strncmp (name, ".idata$4", 8) == 0)
+ {
+ add_to_val = ((num_DLLs - 1) * 0x14) + 0x28;
+ idata_4_prev = add_to_val;
+ }
+ else if (strncmp (name, ".idata$5", 8) == 0)
+ {
+ add_to_val = idata_4_prev + (all_entries + num_DLLs) * 4;
+ idata_5_prev = add_to_val;
+ num_DLLs_done += 1; /* assuming that idata$5 is done after $4*/
+ }
+
+ }
+ }
+ val = val + add_to_val;
+
+ }
+
rstat = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents,
rel->r_vaddr - input_section->vma,