aboutsummaryrefslogtreecommitdiff
path: root/binutils/nlmconv.c
diff options
context:
space:
mode:
Diffstat (limited to 'binutils/nlmconv.c')
-rw-r--r--binutils/nlmconv.c327
1 files changed, 288 insertions, 39 deletions
diff --git a/binutils/nlmconv.c b/binutils/nlmconv.c
index 9c1755e..b6d4229 100644
--- a/binutils/nlmconv.c
+++ b/binutils/nlmconv.c
@@ -28,6 +28,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <ansidecl.h>
#include <stdio.h>
+#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
@@ -43,6 +44,14 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
since it will handle undefined values. */
#undef strerror
extern char *strerror ();
+
+#ifndef localtime
+extern struct tm *localtime ();
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
/* Global variables. */
@@ -52,19 +61,22 @@ char *program_name;
/* The version number. */
extern char *program_version;
-/* The symbols table. */
+/* Local variables. */
+
+/* The symbol table. */
static asymbol **symbols;
-/* Local variables. */
+/* The total amount of bss space. */
+static bfd_size_type total_bss_size;
/* The list of long options. */
static struct option long_options[] =
{
{ "header-info", required_argument, 0, 'T' },
- { "help", no_argument, 0, 'H' },
+ { "help", no_argument, 0, 'h' },
{ "input-format", required_argument, 0, 'I' },
{ "output-format", required_argument, 0, 'O' },
- { "version", no_argument, 0, 'v' },
+ { "version", no_argument, 0, 'V' },
{ NULL, no_argument, 0, 0 }
};
@@ -76,11 +88,12 @@ static const char *select_output_format PARAMS ((enum bfd_architecture,
long, boolean));
static void setup_sections PARAMS ((bfd *, asection *, PTR));
static void copy_sections PARAMS ((bfd *, asection *, PTR));
-static void mangle_relocs PARAMS ((bfd *, arelent **,
- bfd_size_type, char *,
+static void mangle_relocs PARAMS ((bfd *, asection *, arelent **,
+ bfd_size_type *, char *,
bfd_size_type));
-static void i386_mangle_relocs PARAMS ((bfd *, arelent **, bfd_size_type,
- char *, bfd_size_type));
+static void i386_mangle_relocs PARAMS ((bfd *, asection *, arelent **,
+ bfd_size_type *, char *,
+ bfd_size_type));
/* The main routine. */
@@ -95,27 +108,33 @@ main (argc, argv)
const char *header_file = NULL;
bfd *inbfd;
bfd *outbfd;
- unsigned int symcount;
+ asymbol **newsyms, **outsyms;
+ unsigned int symcount, newsymalloc, newsymcount;
+ asection *bss_sec;
+ asymbol *endsym;
unsigned int i;
char inlead, outlead;
boolean gotstart, gotexit, gotcheck;
struct stat st;
- FILE *custom_data, *help_data, *message_data, *rpc_data;
+ FILE *custom_data, *help_data, *message_data, *rpc_data, *shared_data;
bfd_size_type custom_size, help_size, message_size, module_size, rpc_size;
asection *custom_section, *help_section, *message_section, *module_section;
- asection *rpc_section;
+ asection *rpc_section, *shared_section;
+ bfd *sharedbfd;
+ bfd_size_type shared_offset, shared_size;
+ Nlm_Internal_Fixed_Header sharedhdr;
int len;
program_name = argv[0];
bfd_init ();
- while ((opt = getopt_long (argc, argv, "HI:O:T:v", long_options, (int *) 0))
+ while ((opt = getopt_long (argc, argv, "hI:O:T:V", long_options, (int *) 0))
!= EOF)
{
switch (opt)
{
- case 'H':
+ case 'h':
show_help ();
/*NOTREACHED*/
case 'I':
@@ -127,7 +146,7 @@ main (argc, argv)
case 'T':
header_file = optarg;
break;
- case 'v':
+ case 'V':
printf ("GNU %s version %s\n", program_name, program_version);
exit (0);
/*NOTREACHED*/
@@ -215,12 +234,31 @@ main (argc, argv)
symbols = (asymbol **) xmalloc (get_symtab_upper_bound (inbfd));
symcount = bfd_canonicalize_symtab (inbfd, symbols);
+ /* Make sure we have a .bss section. Doing this first is an attempt
+ to ensure that it will be the first bss section. */
+ bss_sec = bfd_get_section_by_name (outbfd, NLM_UNINITIALIZED_DATA_NAME);
+ if (bss_sec == NULL)
+ {
+ bss_sec = bfd_make_section (outbfd, NLM_UNINITIALIZED_DATA_NAME);
+ if (bss_sec == NULL)
+ bfd_fatal ("make .bss section");
+ bss_sec->flags = SEC_ALLOC;
+ bss_sec->alignment_power = bfd_log2 (0); /* FIXME */
+ }
+
+ /* Set up the sections. */
+ bfd_map_over_sections (inbfd, setup_sections, (PTR) outbfd);
+
/* Adjust symbol information. */
inlead = bfd_get_symbol_leading_char (inbfd);
outlead = bfd_get_symbol_leading_char (outbfd);
gotstart = false;
gotexit = false;
gotcheck = false;
+ newsymalloc = 10;
+ newsyms = (asymbol **) xmalloc (newsymalloc * sizeof (asymbol *));
+ newsymcount = 0;
+ endsym = NULL;
for (i = 0; i < symcount; i++)
{
register asymbol *sym;
@@ -258,17 +296,55 @@ main (argc, argv)
}
}
- /* If this is a global symbol, see if it's in the export list.
- Change the prefix if necessary. */
- if ((sym->flags & (BSF_EXPORT | BSF_GLOBAL)) != 0
- && export_symbols != NULL)
+ /* NLM's have an uninitialized data section, but they do not
+ have a common section in the Unix sense. Move all common
+ symbols into the .bss section, and mark them as exported. */
+ if (bfd_is_com_section (bfd_get_section (sym)))
+ {
+ bfd_vma size;
+ bfd_size_type align;
+
+ sym->section = bss_sec;
+ size = sym->value;
+ sym->value = bss_sec->_raw_size;
+ bss_sec->_raw_size += size;
+ align = 1 << bss_sec->alignment_power;
+ bss_sec->_raw_size = (bss_sec->_raw_size + align - 1) &~ (align - 1);
+ total_bss_size += bss_sec->_raw_size - sym->value;
+ sym->flags |= BSF_EXPORT | BSF_GLOBAL;
+ }
+
+ /* Force _edata and _end to be defined. This would normally be
+ done by the linker, but the manipulation of the common
+ symbols will confuse it. */
+ if (bfd_asymbol_name (sym)[0] == '_'
+ && bfd_get_section (sym) == &bfd_und_section)
+ {
+ if (strcmp (bfd_asymbol_name (sym), "_edata") == 0)
+ {
+ sym->section = bss_sec;
+ sym->value = 0;
+ }
+ if (strcmp (bfd_asymbol_name (sym), "_end") == 0)
+ {
+ sym->section = bss_sec;
+ endsym = sym;
+ }
+ }
+
+ /* If this is a global symbol, check the export list. */
+ if ((sym->flags & (BSF_EXPORT | BSF_GLOBAL)) != 0)
{
register struct string_list *l;
+ int found_simple;
+ /* Unfortunately, a symbol can appear multiple times on the
+ export list, with and without prefixes. */
+ found_simple = 0;
for (l = export_symbols; l != NULL; l = l->next)
{
if (strcmp (l->string, bfd_asymbol_name (sym)) == 0)
- break;
+ found_simple = 1;
else
{
char *zbase;
@@ -277,15 +353,30 @@ main (argc, argv)
if (zbase != NULL
&& strcmp (zbase + 1, bfd_asymbol_name (sym)) == 0)
{
- sym->name = l->string;
- break;
+ /* We must add a symbol with this prefix. */
+ if (newsymcount >= newsymalloc)
+ {
+ newsymalloc += 10;
+ newsyms = ((asymbol **)
+ xrealloc (newsyms,
+ (newsymalloc
+ * sizeof (asymbol *))));
+ }
+ newsyms[newsymcount] =
+ (asymbol *) xmalloc (sizeof (asymbol));
+ *newsyms[newsymcount] = *sym;
+ newsyms[newsymcount]->name = l->string;
+ ++newsymcount;
}
}
}
- if (l == NULL)
- fprintf (stderr,
- "%s: warning: symbol %s exported but not in export list\n",
- program_name, bfd_asymbol_name (sym));
+ if (! found_simple)
+ {
+ /* The unmodified symbol is actually not exported at
+ all. */
+ sym->flags &=~ (BSF_GLOBAL | BSF_EXPORT);
+ sym->flags |= BSF_LOCAL;
+ }
}
/* If it's an undefined symbol, see if it's on the import list.
@@ -340,7 +431,21 @@ main (argc, argv)
}
}
- bfd_set_symtab (outbfd, symbols, symcount);
+ if (endsym != NULL)
+ endsym->value = total_bss_size;
+
+ if (newsymcount == 0)
+ outsyms = symbols;
+ else
+ {
+ outsyms = (asymbol **) xmalloc ((symcount + newsymcount + 1)
+ * sizeof (asymbol *));
+ memcpy (outsyms, symbols, symcount * sizeof (asymbol *));
+ memcpy (outsyms + symcount, newsyms, newsymcount * sizeof (asymbol *));
+ outsyms[symcount + newsymcount] = NULL;
+ }
+
+ bfd_set_symtab (outbfd, outsyms, symcount + newsymcount);
if (! gotstart)
fprintf (stderr, "%s: warning: START procedure %s not defined\n",
@@ -353,9 +458,6 @@ main (argc, argv)
fprintf (stderr, "%s: warning: CHECK procedure %s not defined\n",
program_name, check_procedure);
- /* Set up the sections. */
- bfd_map_over_sections (inbfd, setup_sections, (PTR) outbfd);
-
/* Add additional sections required for the header information. */
if (custom_file != NULL)
{
@@ -458,6 +560,83 @@ main (argc, argv)
strncpy (nlm_extended_header (outbfd)->stamp, "MeSsAgEs", 8);
}
}
+ if (sharelib_file != NULL)
+ {
+ sharedbfd = bfd_openr (sharelib_file, output_format);
+ if (sharedbfd == NULL
+ || ! bfd_check_format (sharedbfd, bfd_object))
+ {
+ fprintf (stderr, "%s:%s: %s\n", program_name, sharelib_file,
+ bfd_errmsg (bfd_error));
+ sharelib_file = NULL;
+ }
+ else
+ {
+ sharedhdr = *nlm_fixed_header (sharedbfd);
+ bfd_close (sharedbfd);
+ shared_data = fopen (sharelib_file, "r");
+ if (shared_data == NULL
+ || (fstat (fileno (shared_data), &st) < 0))
+ {
+ fprintf (stderr, "%s:%s: %s\n", program_name, sharelib_file,
+ strerror (errno));
+ sharelib_file = NULL;
+ }
+ else
+ {
+ /* If we were clever, we could just copy out the
+ sections of the shared library which we actually
+ need. However, we would have to figure out the sizes
+ of the external and public information, and that can
+ not be done without reading through them. */
+ shared_offset = st.st_size;
+ if (shared_offset > sharedhdr.codeImageOffset)
+ shared_offset = sharedhdr.codeImageOffset;
+ if (shared_offset > sharedhdr.dataImageOffset)
+ shared_offset = sharedhdr.dataImageOffset;
+ if (shared_offset > sharedhdr.relocationFixupOffset)
+ shared_offset = sharedhdr.relocationFixupOffset;
+ if (shared_offset > sharedhdr.externalReferencesOffset)
+ shared_offset = sharedhdr.externalReferencesOffset;
+ if (shared_offset > sharedhdr.publicsOffset)
+ shared_offset = sharedhdr.publicsOffset;
+ shared_size = st.st_size - shared_offset;
+ shared_section = bfd_make_section (outbfd, ".nlmshared");
+ if (shared_section == NULL
+ || ! bfd_set_section_size (outbfd, shared_section,
+ shared_size)
+ || ! bfd_set_section_flags (outbfd, shared_section,
+ SEC_HAS_CONTENTS))
+ bfd_fatal ("shared section");
+ strncpy (nlm_extended_header (outbfd)->stamp, "MeSsAgEs", 8);
+ }
+ }
+ }
+
+ /* Check whether a version was given. */
+ if (strncmp (version_hdr->stamp, "VeRsIoN#", 8) != 0)
+ fprintf (stderr, "%s: warning: No version number given\n",
+ program_name);
+
+ /* At least for now, always create an extended header, because that
+ is what NLMLINK does. */
+ strncpy (nlm_extended_header (outbfd)->stamp, "MeSsAgEs", 8);
+
+ /* If the date was not given, force it in. */
+ if (nlm_version_header (outbfd)->month == 0
+ && nlm_version_header (outbfd)->day == 0
+ && nlm_version_header (outbfd)->year == 0)
+ {
+ unsigned long now; /* FIXME: should be time_t. */
+ struct tm *ptm;
+
+ time (&now);
+ ptm = localtime (&now);
+ nlm_version_header (outbfd)->month = ptm->tm_mon + 1;
+ nlm_version_header (outbfd)->day = ptm->tm_mday;
+ nlm_version_header (outbfd)->year = ptm->tm_year + 1900;
+ strncpy (version_hdr->stamp, "VeRsIoN#", 8);
+ }
/* Copy over the sections. */
bfd_map_over_sections (inbfd, copy_sections, (PTR) outbfd);
@@ -583,6 +762,51 @@ main (argc, argv)
}
free (data);
}
+ if (sharelib_file != NULL)
+ {
+ PTR data;
+
+ data = xmalloc (shared_size);
+ if (fseek (shared_data, shared_offset, SEEK_SET) != 0
+ || fread (data, 1, shared_size, shared_data) != shared_size)
+ fprintf (stderr, "%s:%s: read: %s\n", program_name, sharelib_file,
+ strerror (errno));
+ else
+ {
+ if (! bfd_set_section_contents (outbfd, shared_section, data,
+ (file_ptr) 0, shared_size))
+ bfd_fatal ("shared section");
+ }
+ nlm_extended_header (outbfd)->sharedCodeOffset =
+ sharedhdr.codeImageOffset - shared_offset + shared_section->filepos;
+ nlm_extended_header (outbfd)->sharedCodeLength =
+ sharedhdr.codeImageSize;
+ nlm_extended_header (outbfd)->sharedDataOffset =
+ sharedhdr.dataImageOffset - shared_offset + shared_section->filepos;
+ nlm_extended_header (outbfd)->sharedDataLength =
+ sharedhdr.dataImageSize;
+ nlm_extended_header (outbfd)->sharedRelocationFixupOffset =
+ (sharedhdr.relocationFixupOffset
+ - shared_offset
+ + shared_section->filepos);
+ nlm_extended_header (outbfd)->sharedRelocationFixupCount =
+ sharedhdr.numberOfRelocationFixups;
+ nlm_extended_header (outbfd)->sharedExternalReferenceOffset =
+ (sharedhdr.externalReferencesOffset
+ - shared_offset
+ + shared_section->filepos);
+ nlm_extended_header (outbfd)->sharedExternalReferenceCount =
+ sharedhdr.numberOfExternalReferences;
+ nlm_extended_header (outbfd)->sharedPublicsOffset =
+ sharedhdr.publicsOffset - shared_offset + shared_section->filepos;
+ nlm_extended_header (outbfd)->sharedPublicsCount =
+ sharedhdr.numberOfPublics;
+ nlm_extended_header (outbfd)->SharedInitializationOffset =
+ sharedhdr.codeStartOffset;
+ nlm_extended_header (outbfd)->SharedExitProcedureOffset =
+ sharedhdr.exitProcedureOffset;
+ free (data);
+ }
len = strlen (argv[optind + 1]);
if (len > NLM_MODULE_NAME_SIZE - 2)
len = NLM_MODULE_NAME_SIZE - 2;
@@ -619,7 +843,7 @@ show_usage (file, status)
int status;
{
fprintf (file, "\
-Usage: %s [-Hv] [-I format] [-O format] [-T header-file]\n\
+Usage: %s [-hV] [-I format] [-O format] [-T header-file]\n\
[--input-format=format] [--output-format=format]\n\
[--header-file=file] [--help] [--version]\n\
in-file out-file\n",
@@ -661,6 +885,7 @@ setup_sections (inbfd, insec, data_ptr)
{
bfd *outbfd = (bfd *) data_ptr;
asection *outsec;
+ flagword f;
outsec = bfd_get_section_by_name (outbfd, bfd_section_name (inbfd, insec));
if (outsec == NULL)
@@ -685,9 +910,12 @@ setup_sections (inbfd, insec, data_ptr)
bfd_section_alignment (inbfd, insec)))
bfd_fatal ("set section alignment");
- if (! bfd_set_section_flags (outbfd, outsec,
- bfd_get_section_flags (inbfd, insec)))
+ f = bfd_get_section_flags (inbfd, insec);
+ if (! bfd_set_section_flags (outbfd, outsec, f))
bfd_fatal ("set section flags");
+
+ if ((f & SEC_LOAD) == 0 && (f & SEC_ALLOC) != 0)
+ total_bss_size += bfd_section_size (inbfd, insec);
}
/* Copy the section contents. */
@@ -735,7 +963,8 @@ copy_sections (inbfd, insec, data_ptr)
relocs = (arelent **) xmalloc (reloc_size);
reloc_count = bfd_canonicalize_reloc (inbfd, insec, relocs, symbols);
- mangle_relocs (outbfd, relocs, reloc_count, (char *) contents, size);
+ mangle_relocs (outbfd, insec, relocs, &reloc_count, (char *) contents,
+ size);
bfd_set_reloc (outbfd, outsec, relocs, reloc_count);
}
@@ -752,17 +981,18 @@ copy_sections (inbfd, insec, data_ptr)
by the input formats. */
static void
-mangle_relocs (outbfd, relocs, reloc_count, contents, contents_size)
+mangle_relocs (outbfd, insec, relocs, reloc_count_ptr, contents, contents_size)
bfd *outbfd;
+ asection *insec;
arelent **relocs;
- bfd_size_type reloc_count;
+ bfd_size_type *reloc_count_ptr;
char *contents;
bfd_size_type contents_size;
{
switch (bfd_get_arch (outbfd))
{
case bfd_arch_i386:
- i386_mangle_relocs (outbfd, relocs, reloc_count, contents,
+ i386_mangle_relocs (outbfd, insec, relocs, reloc_count_ptr, contents,
contents_size);
break;
default:
@@ -792,14 +1022,19 @@ static reloc_howto_type nlm_i386_pcrel_howto =
true); /* pcrel_offset */
static void
-i386_mangle_relocs (outbfd, relocs, reloc_count, contents, contents_size)
+i386_mangle_relocs (outbfd, insec, relocs, reloc_count_ptr, contents,
+ contents_size)
bfd *outbfd;
+ asection *insec;
arelent **relocs;
- bfd_size_type reloc_count;
+ bfd_size_type *reloc_count_ptr;
char *contents;
bfd_size_type contents_size;
{
- while (reloc_count-- != 0)
+ bfd_size_type reloc_count, i;
+
+ reloc_count = *reloc_count_ptr;
+ for (i = 0; i < reloc_count; i++)
{
arelent *rel;
asymbol *sym;
@@ -816,6 +1051,20 @@ i386_mangle_relocs (outbfd, relocs, reloc_count, contents, contents_size)
if (rel->address + 4 > contents_size)
continue;
+ /* A PC relative reloc entirely within a single section is
+ completely unnecessary. This can be generated by ld -r. */
+ if (sym == insec->symbol
+ && rel->howto != NULL
+ && rel->howto->pc_relative
+ && ! rel->howto->pcrel_offset)
+ {
+ --*reloc_count_ptr;
+ --relocs;
+ memmove (relocs, relocs + 1,
+ (reloc_count - i) * sizeof (arelent *));
+ continue;
+ }
+
/* NetWare doesn't support reloc addends, so we get rid of them
here by simply adding them into the object data. We handle
the symbol value, if any, the same way. */