aboutsummaryrefslogtreecommitdiff
path: root/binutils-1.9/strip.c
diff options
context:
space:
mode:
Diffstat (limited to 'binutils-1.9/strip.c')
-rw-r--r--binutils-1.9/strip.c915
1 files changed, 915 insertions, 0 deletions
diff --git a/binutils-1.9/strip.c b/binutils-1.9/strip.c
new file mode 100644
index 0000000..6008382
--- /dev/null
+++ b/binutils-1.9/strip.c
@@ -0,0 +1,915 @@
+/* strip certain symbols from a rel file.
+ Copyright (C) 1986, 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <errno.h>
+extern int errno;
+#include "getopt.h"
+
+#ifdef USG
+#include <fcntl.h>
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#if !defined(A_OUT) && !defined(MACH_O)
+#define A_OUT
+#endif
+
+#ifdef A_OUT
+#ifdef COFF_ENCAPSULATE
+#include "a.out.encap.h"
+#else
+/* On native BSD systems, use the system's own a.out.h. */
+#include <a.out.h>
+#endif
+#endif
+
+#ifdef MACH_O
+#ifndef A_OUT
+#include <nlist.h>
+#include <reloc.h>
+#endif
+#include <sys/loader.h>
+#endif
+
+#ifdef nounderscore
+#define LPREFIX '.'
+#else
+#define LPREFIX 'L'
+#endif
+
+#if defined (sun) && defined (sparc)
+/* On the sparc, the name of the relocation info structure is
+ different (on SunOS4, "struct relocation_info" does not exist).
+ The meaning of the r_index field is the same as r_symbolnum
+ in normal relocation_info's for external symbols. Fortunately,
+ we only use the field for external symbols. */
+typedef struct reloc_info_sparc *relocation_info_ptr;
+#define RELOCATION_INFO_SYMBOL_NUM(ri) (ri)->r_index
+#else /* not Sun and sparc. */
+typedef struct relocation_info *relocation_info_ptr;
+#define RELOCATION_INFO_SYMBOL_NUM(ri) (ri)->r_symbolnum
+#endif /* not Sun and sparc. */
+
+/* If BSD or HP-UX, we can use `ftruncate' and `rename'. */
+
+#if !defined(USG) || defined(hpux)
+#define HAVE_FTRUNCATE
+#define HAVE_RENAME
+#endif /* !USG || hpux */
+
+/* Number of nlist entries that are for local symbols. */
+int local_sym_count;
+
+/* Number of nlist entries that are for local symbols
+ whose names don't start with L. */
+int non_L_local_sym_count;
+
+/* Number of nlist entries for debugger info. */
+int debugger_sym_count;
+
+/* Number of global symbols referenced or defined. */
+int global_sym_count;
+
+/* Total number of symbols to be preserved in the current file. */
+int nsyms;
+
+/* Number of files specified in the command line. */
+int number_of_files;
+
+/* Kinds of files understood. */
+enum file_type { IS_UNKNOWN, IS_A_OUT, IS_MACH_O };
+
+/* Each specified file has a file_entry structure for it.
+ These are contained in the vector which file_table points to. */
+
+struct file_entry
+{
+ char *filename;
+ enum file_type filetype; /* what kind of file it is */
+
+ /* Things obtained from the file's header. */
+ long int trel_offset; /* offset to text relocation */
+ unsigned int trel_size; /* size of text relocation */
+ long int drel_offset; /* offset to data relocation */
+ unsigned int drel_size; /* size of data relocation */
+ long int syms_offset; /* offset to the symbol table */
+ unsigned int syms_size; /* size of the symbol table */
+ long int strs_offset; /* offset to the string table */
+ unsigned int strs_size; /* size of the string table */
+
+ int ss_size; /* size, in bytes, of symbols_and_strings data */
+ struct nlist *symbols_and_strings;
+
+ /* offset of the symtab_command in a mach-O file's header */
+ long int symtab_cmd_offset;
+};
+
+struct file_entry *file_table;
+
+/* Descriptor on which current file is open. */
+int input_desc;
+
+/* Stream for writing that file using stdio. */
+FILE *outstream;
+
+enum strip_action
+{
+ strip_undef,
+ strip_all, /* strip all symbols */
+ strip_debug /* strip all debugger symbols */
+};
+
+/* Which symbols to remove. */
+enum strip_action strip_symbols;
+
+enum locals_action
+{
+ locals_undef,
+ locals_start_L, /* discard locals starting with L */
+ locals_all /* discard all locals */
+};
+
+/* Which local symbols to remove. */
+enum locals_action discard_locals;
+
+/* The name this program was run with. */
+char *program_name;
+
+char *malloc ();
+
+char *concat ();
+char *xmalloc ();
+int file_open ();
+int read_entry_symbols ();
+int read_file_symbols ();
+int read_header ();
+void count_file_symbols ();
+void error ();
+void file_close ();
+void rewrite_file_symbols ();
+void strip_file ();
+void usage ();
+
+struct option long_options[] =
+{
+ {"strip-all", 0, 0, 's'},
+ {"strip-debug", 0, 0, 'S'},
+ {"discard-all", 0, 0, 'x'},
+ {"discard-locals", 0, 0, 'X'},
+ {0, 0, 0, 0}
+};
+
+void
+main (argc, argv)
+ char **argv;
+ int argc;
+{
+ int c;
+ int ind;
+ int i;
+ struct file_entry *p;
+
+ program_name = argv[0];
+
+ strip_symbols = strip_undef; /* default is to strip everything. */
+ discard_locals = locals_undef;
+
+ while ((c = getopt_long (argc, argv, "gsSxX", long_options, &ind)) != EOF)
+ {
+ switch (c)
+ {
+ case 0 :
+ break;
+ case 's':
+ strip_symbols = strip_all;
+ break;
+ case 'g':
+ case 'S':
+ strip_symbols = strip_debug;
+ break;
+ case 'x':
+ discard_locals = locals_all;
+ break;
+ case 'X':
+ discard_locals = locals_start_L;
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ /* Default is to strip all symbols. */
+ if (strip_symbols == strip_undef && discard_locals == locals_undef)
+ strip_symbols = strip_all;
+
+ number_of_files = argc - optind;
+
+ if (!number_of_files)
+ usage ();
+
+ p = file_table
+ = (struct file_entry *) xmalloc (number_of_files * sizeof (struct file_entry));
+
+ /* Now fill in file_table */
+
+ for (i = 0; i < number_of_files; i++)
+ {
+ p->filename = argv[i + optind];
+ p->filetype = IS_UNKNOWN;
+ p->trel_offset = p->trel_size = p->drel_offset = p->drel_size = 0;
+ p->syms_offset = p->syms_size = p->strs_offset = p->strs_size = 0;
+ p->symbols_and_strings = 0;
+ p->symtab_cmd_offset = 0;
+ p++;
+ }
+
+ for (i = 0; i < number_of_files; i++)
+ strip_file (&file_table[i]);
+ exit (0);
+}
+
+int delayed_signal;
+
+void
+delay_signal (signo)
+ int signo;
+{
+ delayed_signal = signo;
+ signal (signo, delay_signal);
+}
+
+/* process one input file */
+
+void
+strip_file (entry)
+ struct file_entry *entry;
+{
+ int val;
+ int sigint_handled = 0;
+ int sighup_handled = 0;
+ int sigterm_handled = 0;
+
+ local_sym_count = 0;
+ non_L_local_sym_count = 0;
+ debugger_sym_count = 0;
+ global_sym_count = 0;
+
+ val = file_open (entry);
+ if (val < 0)
+ return;
+
+ if (strip_symbols != strip_all)
+ /* Read in the existing symbols unless we are discarding everything. */
+ {
+ if (read_file_symbols (entry) < 0)
+ return;
+ }
+
+ /* Effectively defer handling of asynchronous kill signals. */
+ delayed_signal = 0;
+ if (signal (SIGINT, SIG_IGN) != SIG_IGN)
+ sigint_handled = 1, signal (SIGINT, delay_signal);
+ if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
+ sighup_handled = 1, signal (SIGHUP, delay_signal);
+ if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
+ sigterm_handled = 1, signal (SIGTERM, delay_signal);
+
+ /* Change the file. */
+
+ rewrite_file_symbols (entry);
+ if (strip_symbols != strip_all)
+ free (entry->symbols_and_strings);
+
+ file_close ();
+
+ /* Effectively undefer handling. */
+ if (sigint_handled)
+ signal (SIGINT, SIG_DFL);
+ if (sighup_handled)
+ signal (SIGHUP, SIG_DFL);
+ if (sigterm_handled)
+ signal (SIGTERM, SIG_DFL);
+
+ /* Handle any signal that came in while they were deferred. */
+ if (delayed_signal)
+ kill (getpid (), delayed_signal);
+}
+
+/** Convenient functions for operating on one or all files being processed. */
+
+/* Close the file that is now open. */
+
+void
+file_close ()
+{
+ if (input_desc != -1)
+ close (input_desc);
+ input_desc = -1;
+}
+
+/* Open the file specified by 'entry', and return a descriptor,
+ or -1 if the file cannot be opened or is not in rel format.
+ The descriptor is also saved in input_desc. */
+
+int
+file_open (entry)
+ struct file_entry *entry;
+{
+ int desc;
+
+ desc = open (entry->filename, O_RDWR, 0);
+
+ if (desc > 0)
+ {
+ input_desc = desc;
+ if (read_header (desc, entry) < 0)
+ {
+ close (desc);
+ return -1;
+ }
+ return desc;
+ }
+
+ error (0, errno, "%s", entry->filename);
+ return -1;
+}
+
+/* Validate file ENTRY and read its symbol and string sections into core.
+ Return 0 if ok, -1 if error. */
+
+int
+read_file_symbols (entry)
+ struct file_entry *entry;
+{
+ if (read_entry_symbols (input_desc, entry) < 0)
+ {
+ file_close ();
+ return -1;
+ }
+ count_file_symbols (entry);
+ return 0;
+}
+
+/* Read a file's header and fill in various fields of a file's entry.
+ Return -1 on failure, 0 if successful. */
+
+int
+read_header (desc, entry)
+ int desc;
+ struct file_entry *entry;
+{
+ int len;
+
+#ifdef A_OUT
+ {
+ struct exec hdr;
+
+ lseek (desc, 0, 0);
+#ifdef HEADER_SEEK_FD
+ /* Skip the headers that encapsulate our data in some other format
+ such as COFF. */
+ HEADER_SEEK_FD (desc);
+#endif
+ len = read (desc, (char *) &hdr, sizeof (struct exec));
+ if (len == sizeof (struct exec) && !N_BADMAG (hdr))
+ {
+ entry->filetype = IS_A_OUT;
+#ifdef N_TRELOFF
+ entry->trel_offset = N_TRELOFF (hdr);
+#else
+#ifdef N_DATOFF
+ entry->trel_offset = N_DATOFF (hdr) + hdr.a_data;
+#else
+ entry->trel_offset = N_TXTOFF (hdr) + hdr.a_text + hdr.a_data;
+#endif
+#endif
+ entry->trel_size = hdr.a_trsize;
+#ifdef N_DRELOFF
+ entry->drel_offset = N_DRELOFF (hdr);
+#else
+ entry->drel_offset = entry->trel_offset + entry->trel_size;
+#endif
+ entry->drel_size = hdr.a_drsize;
+ entry->syms_offset = N_SYMOFF(hdr);
+ entry->syms_size = hdr.a_syms;
+ entry->strs_offset = N_STROFF(hdr);
+ lseek(desc, entry->strs_offset, 0);
+ if (read (desc, (char *) &entry->strs_size, sizeof entry->strs_size)
+ != sizeof entry->strs_size)
+ {
+ error (0, errno, "%s: cannot read string table size",
+ entry->filename);
+ return -1;
+ }
+ return 0;
+ }
+ }
+#endif
+
+#ifdef MACH_O
+ {
+ struct mach_header mach_header;
+ char *hdrbuf;
+ struct load_command *load_command;
+ struct segment_command *segment_command;
+ struct section *section;
+ struct symtab_command *symtab_command;
+ int symtab_seen;
+ int len, cmd, seg;
+
+ symtab_seen = 0;
+
+ lseek (desc, 0L, 0);
+ len = read (desc, (char *) &mach_header, sizeof (struct mach_header));
+ if (len == sizeof (struct mach_header) && mach_header.magic == MH_MAGIC)
+ {
+ entry->filetype = IS_MACH_O;
+ hdrbuf = xmalloc (mach_header.sizeofcmds);
+ len = read (desc, hdrbuf, mach_header.sizeofcmds);
+ if (len != mach_header.sizeofcmds)
+ {
+ error (0, errno, "%s: cannot read Mach-O load commands",
+ entry->filename);
+ return -1;
+ }
+ load_command = (struct load_command *) hdrbuf;
+ for (cmd = 0; cmd < mach_header.ncmds; ++cmd)
+ {
+ switch (load_command->cmd)
+ {
+ case LC_SEGMENT:
+ segment_command = (struct segment_command *) load_command;
+ section = (struct section *) ((char *) (segment_command + 1));
+ for (seg = 0; seg < segment_command->nsects; ++seg, ++section)
+ {
+ if (!strncmp(SECT_TEXT, section->sectname, sizeof section->sectname))
+ {
+ entry->trel_offset = section->reloff;
+ entry->trel_size = section->nreloc * sizeof (struct relocation_info);
+ }
+ else if (!strncmp(SECT_DATA, section->sectname, sizeof section->sectname))
+ {
+ entry->drel_offset = section->reloff;
+ entry->drel_size = section->nreloc * sizeof (struct relocation_info);
+ }
+ }
+ break;
+ case LC_SYMTAB:
+ if (symtab_seen)
+ error (0, 0, "%s: more than one LC_SYMTAB", entry->filename);
+ else
+ {
+ symtab_seen = 1;
+ symtab_command = (struct symtab_command *) load_command;
+ entry->syms_offset = symtab_command->symoff;
+ entry->syms_size = symtab_command->nsyms * sizeof (struct nlist);
+ entry->strs_offset = symtab_command->stroff;
+ entry->strs_size = symtab_command->strsize;
+ entry->symtab_cmd_offset = (char *) load_command - hdrbuf
+ + sizeof (struct mach_header);
+ }
+ break;
+ }
+ load_command = (struct load_command *)
+ ((char *) load_command + load_command->cmdsize);
+ }
+
+ free (hdrbuf);
+
+ if (!symtab_seen)
+ {
+ error (0, 0, "%s: no symbol table", entry->filename);
+ return -1;
+ }
+
+ return 0;
+ }
+ }
+#endif
+
+ error (0, 0, "%s: not an executable or object file", entry->filename);
+ return -1;
+}
+
+/* Read the symbols and strings of file ENTRY into core.
+ Assume it is already open, on descriptor DESC.
+ Return -1 on failure, 0 if successful. */
+
+int
+read_entry_symbols (desc, entry)
+ struct file_entry *entry;
+ int desc;
+{
+ entry->ss_size = entry->syms_size + entry->strs_size;
+ entry->symbols_and_strings = (struct nlist *) xmalloc (entry->ss_size);
+
+ lseek (desc, entry->syms_offset, 0);
+ if (entry->ss_size != read (desc, entry->symbols_and_strings, entry->ss_size))
+ {
+ error (0, errno, "%s: premature end of file in symbols/strings",
+ entry->filename);
+ return -1;
+ }
+ return 0;
+}
+
+/* Count the number of symbols of various categories in the file of ENTRY. */
+
+void
+count_file_symbols (entry)
+ struct file_entry *entry;
+{
+ struct nlist *p, *end = entry->symbols_and_strings + entry->syms_size / sizeof (struct nlist);
+ char *name_base = entry->syms_size + (char *) entry->symbols_and_strings;
+
+ for (p = entry->symbols_and_strings; p < end; p++)
+ if (p->n_type & N_EXT)
+ global_sym_count++;
+ else if (p->n_un.n_strx && !(p->n_type & (N_STAB | N_EXT)))
+ {
+ if ((p->n_un.n_strx + name_base)[0] != LPREFIX)
+ non_L_local_sym_count++;
+ local_sym_count++;
+ }
+ else
+ debugger_sym_count++;
+}
+
+void modify_relocation ();
+void write_file_syms ();
+
+/* Total size of string table strings allocated so far */
+int strtab_size;
+
+/* Vector whose elements are the strings to go in the string table */
+char **strtab_vector;
+
+/* Index in strtab_vector at which the next string will be stored */
+int strtab_index;
+
+int sym_written_count;
+
+int
+assign_string_table_index (name)
+ char *name;
+{
+ int index = strtab_size;
+
+ strtab_size += strlen (name) + 1;
+ strtab_vector[strtab_index++] = name;
+
+ return index;
+}
+
+void
+rewrite_file_symbols (entry)
+ struct file_entry *entry;
+{
+ int i;
+ struct nlist *newsyms;
+
+ /* Calculate number of symbols to be preserved. */
+
+ if (strip_symbols == strip_all)
+ nsyms = 0;
+ else
+ {
+ nsyms = global_sym_count;
+ if (discard_locals == locals_start_L)
+ nsyms += non_L_local_sym_count;
+ else if (discard_locals == locals_undef)
+ nsyms += local_sym_count;
+ }
+
+ if (strip_symbols == strip_undef)
+ nsyms += debugger_sym_count;
+
+ strtab_vector = (char **) xmalloc (nsyms * sizeof (char *));
+ strtab_index = 0;
+
+ strtab_size = 4;
+
+ /* Accumulate in 'newsyms' the symbol table to be written. */
+
+ newsyms = (struct nlist *) xmalloc (nsyms * sizeof (struct nlist));
+
+ sym_written_count = 0;
+
+ if (strip_symbols != strip_all)
+ /* Write into newsyms the symbols we want to keep. */
+ write_file_syms (entry, newsyms);
+
+ if (sym_written_count != nsyms)
+ {
+ fprintf (stderr, "written = %d, expected = %d\n",
+ sym_written_count, nsyms);
+ abort ();
+ }
+
+ /* Modify the symbol-numbers in the relocation in the file,
+ to preserve its meaning */
+ modify_relocation (input_desc, entry);
+
+#ifndef HAVE_FTRUNCATE
+ {
+ int size = entry->syms_offset, mode;
+ int old_desc;
+ char *renamed;
+ char *copy_buffer = (char *)xmalloc (size);
+ struct stat statbuf;
+
+ renamed = (char *) xmalloc(strlen(entry->filename) + 3);
+ strcpy(renamed, entry->filename);
+ {
+ char *file_p, *renamed_p;
+ file_p = strrchr(entry->filename, '/');
+ file_p = file_p ? ++file_p : entry->filename;
+ renamed_p = renamed + (file_p - entry->filename);
+ *renamed_p++ = '~';
+ while (*renamed_p++ = *file_p++)
+ ;
+ *renamed_p++ = '~';
+ *renamed_p = '\0';
+ }
+
+ lseek (input_desc, 0, 0);
+ if (read (input_desc, copy_buffer, size) != size)
+ {
+ error (0, errno, "%s: cannot read up to symbol table",
+ entry->filename);
+ return;
+ }
+ mode = fstat (input_desc, &statbuf) ? 0666 : statbuf.st_mode;
+ if (rename (entry->filename, renamed))
+ {
+ error (0, errno, "%s", entry->filename);
+ return;
+ }
+ old_desc = input_desc;
+ input_desc = open (entry->filename, O_RDWR | O_CREAT | O_TRUNC, mode);
+ if (input_desc < 0)
+ {
+ error (0, errno, "%s", entry->filename);
+ return;
+ }
+ if (write (input_desc, copy_buffer, size) != size)
+ error (0, errno, "%s", entry->filename);
+ if (unlink (renamed))
+ error (0, errno, "%s", renamed);
+ if (close (old_desc))
+ error (0, errno, "%s", renamed);
+ free (copy_buffer);
+ free (renamed);
+ }
+#endif /* not HAVE_FTRUNCATE */
+
+ /* Now write contents of NEWSYMS into the file. */
+
+ lseek (input_desc, entry->syms_offset, 0);
+ write (input_desc, newsyms, nsyms * sizeof (struct nlist));
+ free (newsyms);
+
+ /* Now write the string table. */
+
+ {
+ char *strvec = (char *) xmalloc (strtab_size);
+ char *p;
+
+ *((long *) strvec) = strtab_size;
+
+ p = strvec + sizeof (long);
+
+ for (i = 0; i < strtab_index; i++)
+ {
+ int len = strlen (strtab_vector[i]);
+ strcpy (p, strtab_vector[i]);
+ *(p+len) = 0;
+ p += len + 1;
+ }
+
+ write (input_desc, strvec, strtab_size);
+ free (strvec);
+ }
+
+ /* Adjust file to be smaller */
+
+#ifdef HAVE_FTRUNCATE
+ if (ftruncate (input_desc, lseek (input_desc, 0L, 1)) < 0)
+ error (0, errno, "%s", entry->filename);
+#endif
+
+ /* Write new symbol table size into file header. */
+
+#ifdef A_OUT
+ if (entry->filetype == IS_A_OUT)
+ {
+ struct exec hdr;
+
+ lseek (input_desc, 0L, 0);
+#ifdef HEADER_SEEK_FD
+ HEADER_SEEK_FD (input_desc);
+#endif
+ read (input_desc, (char *) &hdr, sizeof hdr);
+ hdr.a_syms = nsyms * sizeof (struct nlist);
+ lseek (input_desc, -(long) sizeof hdr, 1);
+ write (input_desc, (char *) &hdr, sizeof hdr);
+ }
+#endif
+
+#ifdef MACH_O
+ if (entry->filetype == IS_MACH_O)
+ {
+ struct symtab_command cmd;
+
+ lseek (input_desc, entry->symtab_cmd_offset, 0);
+ read (input_desc, (char *) &cmd, sizeof cmd);
+ cmd.nsyms = nsyms;
+ cmd.stroff = cmd.symoff + cmd.nsyms * sizeof (struct nlist);
+ cmd.strsize = strtab_size;
+ lseek (input_desc, entry->symtab_cmd_offset, 0);
+ write (input_desc, (char *) &cmd, sizeof cmd);
+ }
+#endif
+
+ free (strtab_vector);
+}
+
+/* Copy into NEWSYMS the symbol entries to be preserved.
+ Count them in sym_written_count.
+
+ We record, for each symbol written, its symbol number in the resulting file.
+ This is so that the relocation can be updated later.
+ Since the symbol names will not be needed again,
+ this index goes in the `n_strx' field.
+ If a symbol is not written, -1 is stored there. */
+
+void
+write_file_syms (entry, newsyms)
+ struct file_entry *entry;
+ struct nlist *newsyms;
+{
+ struct nlist *p = entry->symbols_and_strings;
+ struct nlist *end = p + entry->syms_size / sizeof (struct nlist);
+ char *string_base = (char *) end; /* address of start of file's string table */
+ struct nlist *outp = newsyms;
+
+ for (; p < end; p++)
+ {
+ int write;
+
+ if (p->n_type & N_EXT)
+ write = 1;
+ else if (p->n_un.n_strx && !(p->n_type & (N_STAB | N_EXT)))
+ /* ordinary local symbol */
+ write = (discard_locals != locals_all)
+ && !(discard_locals == locals_start_L &&
+ (p->n_un.n_strx + string_base)[0] == LPREFIX);
+ else
+ /* debugger symbol */
+ write = (strip_symbols == strip_undef);
+
+ if (write)
+ {
+ if (p->n_un.n_strx)
+ p->n_un.n_strx = assign_string_table_index (p->n_un.n_strx + string_base);
+
+ *outp++ = *p;
+
+ p->n_un.n_strx = sym_written_count++;
+ }
+ else p->n_un.n_strx = -1;
+ }
+}
+
+/* Read in ENTRY's relocation, alter the symbolnums in it,
+ and write it out again. */
+
+void
+modify_relocation (desc, entry)
+ int desc;
+ struct file_entry *entry;
+{
+ relocation_info_ptr reloc, p, end;
+ int size;
+ struct nlist *sym_base = (struct nlist *) entry->symbols_and_strings;
+ int losing = 0;
+ long int offsets[2];
+ unsigned int sizes[2];
+ int i;
+
+ offsets[0] = entry->trel_offset;
+ sizes[0] = entry->trel_size;
+ offsets[1] = entry->drel_offset;
+ sizes[1] = entry->drel_size;
+
+ for (i = 0; i < 2; ++i)
+ {
+ size = sizes[i];
+ reloc = (relocation_info_ptr) xmalloc (size);
+ lseek (desc, offsets[i], 0);
+ read (desc, reloc, size);
+
+ p = reloc;
+ end = (relocation_info_ptr) (size + (char *) reloc);
+ while (p < end)
+ {
+ if (p->r_extern)
+ {
+ int newnum = (sym_base == 0 ? -1
+ :((sym_base + RELOCATION_INFO_SYMBOL_NUM(p))
+ -> n_un.n_strx));
+ if (newnum < 0)
+ {
+ if (losing == 0)
+ error (0, 0, "%s: warning: file is now unlinkable",
+ entry->filename);
+ losing = 1;
+ }
+ RELOCATION_INFO_SYMBOL_NUM(p) = newnum;
+ }
+ p++;
+ }
+
+ lseek (desc, offsets[i], 0);
+ write (desc, reloc, size);
+ free ((char *) reloc);
+ }
+}
+
+/* Return a newly-allocated string whose contents
+ concatenate those of S1, S2, S3. */
+
+char *
+concat (s1, s2, s3)
+ char *s1, *s2, *s3;
+{
+ int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
+ char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
+
+ strcpy (result, s1);
+ strcpy (result + len1, s2);
+ strcpy (result + len1 + len2, s3);
+ *(result + len1 + len2 + len3) = 0;
+
+ return result;
+}
+
+/* Like malloc but get fatal error if memory is exhausted. */
+
+char *
+xmalloc (size)
+ unsigned size;
+{
+ /* Some implementations of malloc get unhappy if size==0.
+ * Given that the code sometimes "wants" a 0-length array,
+ * it seems cleaner to put a work-around here
+ * than clutter up the code logic in various other places. */
+ char *result = malloc (size==0 ? 1 : size);
+
+ if (!result)
+ error (1, 0, "virtual memory exhausted");
+ return result;
+}
+
+#ifndef HAVE_RENAME
+int
+rename (from, to)
+ char *from, *to;
+{
+ unlink (to);
+ if (link (from, to) < 0 || unlink (from) < 0)
+ return -1;
+ else
+ return 0;
+}
+#endif
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-gsxSX] [+strip-all] [+strip-debug] [+discard-all]\n\
+ [+discard-locals] file...\n", program_name);
+ exit (1);
+}