/* BFD backend for PDP-11, running 2.11BSD in particular.
This file was hacked up by looking hard at the existing vaxnetbsd
- back end and the header files in 2.11BSD.
+ back end and the header files in 2.11BSD. The symbol table format
+ of 2.11BSD has been extended to accommodate .stab symbols. See
+ struct pdp11_external_nlist below for details.
TODO
* support for V7 file formats
#define A_FLAG_RELOC_STRIPPED 0x0001
+/* The following struct defines the format of an entry in the object file
+ symbol table. In the original 2.11BSD struct the index into the string
+ table is stored as a long, but the PDP11 C convention for storing a long in
+ memory placed the most significant word first even though the bytes within a
+ word are stored least significant first. So here the string table index is
+ considered to be just 16 bits and the first two bytes of the struct were
+ previously named e_unused. To extend the symbol table format to accommodate
+ .stab symbols, the e_unused bytes are renamed e_desc to store the desc field
+ of the .stab symbol. The GDP Project's STABS document says that the "other"
+ field is almost always unused and can be set to zero; the only nonzero cases
+ identified were for stabs in their own sections, which does not apply for
+ pdp11 a.out format, and for a special case of GNU Modula2 which is not
+ supported for the PDP11. */
#define external_nlist pdp11_external_nlist
struct pdp11_external_nlist
{
- bfd_byte e_unused[2]; /* Unused. */
+ bfd_byte e_desc[2]; /* The desc field for .stab symbols, else 0. */
bfd_byte e_strx[2]; /* Index into string table of name. */
bfd_byte e_type[1]; /* Type of symbol. */
bfd_byte e_ovly[1]; /* Overlay number. */
#include "aout/stab_gnu.h"
#include "aout/ar.h"
+/* The symbol type numbers for the 16-bit a.out format from 2.11BSD differ from
+ those defined in aout64.h so we must redefine them here. N_EXT changes from
+ 0x01 to 0x20 which creates a conflict with some .stab values, in particular
+ between undefined externals (N_UNDF+N_EXT) vs. global variables (N_GYSM) and
+ between external bss symbols (N_BSS+N_EXT) vs. function names (N_FUN). We
+ disambiguate those conflicts with a hack in is_stab() to look for the ':' in
+ the global variable or function name string. */
#undef N_TYPE
#undef N_UNDF
#undef N_ABS
#define N_REG 0x14 /* Register symbol. */
#define N_FN 0x1f /* File name. */
#define N_EXT 0x20 /* External flag. */
-#define N_STAB 0xc0 /* Not relevant; modified aout64.h's 0xe0 to avoid N_EXT. */
+/* Type numbers from .stab entries that could conflict:
+ N_GSYM 0x20 Global variable [conflict with external undef]
+ N_FNAME 0x22 Function name (for BSD Fortran) [ignored]
+ N_FUN 0x24 Function name [conflict with external BSS]
+ N_NOMAP 0x34 No DST map for sym. [ext. reg. doesn't exist]
+*/
#define RELOC_SIZE 2
return NULL;
}
+/* Disambiguate conflicts between normal symbol types and .stab symbol types
+ (undefined externals N_UNDF+N_EXT vs. global variables N_GYSM and external
+ bss symbols N_BSS+N_EXT vs. function names N_FUN) with a hack to look for
+ the ':' in the global variable or function name string. */
+
+static int
+is_stab (int type, const char *name)
+{
+ if (type == N_GSYM || type == N_FUN)
+ return strchr (name, ':') != NULL;
+ return type > N_FUN;
+}
+
static int
pdp11_aout_write_headers (bfd *abfd, struct internal_exec *execp)
{
sets the entry point, and that is likely to be non-zero for most systems. */
if (execp->a_entry != 0
- || (execp->a_entry >= obj_textsec(abfd)->vma
- && execp->a_entry < obj_textsec(abfd)->vma + obj_textsec(abfd)->size))
+ || (execp->a_entry >= obj_textsec (abfd)->vma
+ && execp->a_entry < (obj_textsec (abfd)->vma
+ + obj_textsec (abfd)->size)
+ && execp->a_trsize == 0
+ && execp->a_drsize == 0))
abfd->flags |= EXEC_P;
#ifdef STAT_FOR_EXEC
else
syms = (struct external_nlist *)
_bfd_malloc_and_read (abfd, count * EXTERNAL_NLIST_SIZE,
count * EXTERNAL_NLIST_SIZE);
- if (syms == NULL && count != 0)
+ if (syms == NULL)
return FALSE;
#endif
unsigned char string_chars[BYTES_IN_LONG];
bfd_size_type stringsize;
char *strings;
+ bfd_size_type amt = BYTES_IN_LONG;
/* Get the size of the strings. */
if (bfd_seek (abfd, obj_str_filepos (abfd), SEEK_SET) != 0
- || (bfd_bread ((void *) string_chars, (bfd_size_type) BYTES_IN_LONG,
- abfd) != BYTES_IN_LONG))
+ || bfd_bread ((void *) string_chars, amt, abfd) != amt)
return FALSE;
stringsize = H_GET_32 (abfd, string_chars);
+ if (stringsize == 0)
+ stringsize = 1;
+ else if (stringsize < BYTES_IN_LONG
+ || (size_t) stringsize != stringsize)
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
#ifdef USE_MMAP
- if (! bfd_get_file_window (abfd, obj_str_filepos (abfd), stringsize,
- &obj_aout_string_window (abfd), TRUE))
- return FALSE;
- strings = (char *) obj_aout_string_window (abfd).data;
-#else
- strings = bfd_malloc (stringsize + 1);
- if (strings == NULL)
- return FALSE;
-
- /* Skip space for the string count in the buffer for convenience
- when using indexes. */
- if (bfd_bread (strings + 4, stringsize - 4, abfd) != stringsize - 4)
+ if (stringsize >= BYTES_IN_LONG)
{
- free (strings);
- return FALSE;
+ if (! bfd_get_file_window (abfd, obj_str_filepos (abfd), stringsize + 1,
+ &obj_aout_string_window (abfd), TRUE))
+ return FALSE;
+ strings = (char *) obj_aout_string_window (abfd).data;
}
+ else
#endif
+ {
+ strings = (char *) bfd_malloc (stringsize + 1);
+ if (strings == NULL)
+ return FALSE;
+
+ if (stringsize >= BYTES_IN_LONG)
+ {
+ /* Keep the string count in the buffer for convenience
+ when indexing with e_strx. */
+ amt = stringsize - BYTES_IN_LONG;
+ if (bfd_bread (strings + BYTES_IN_LONG, amt, abfd) != amt)
+ {
+ free (strings);
+ return FALSE;
+ }
+ }
+ }
/* Ensure that a zero index yields an empty string. */
strings[0] = '\0';
- strings[stringsize - 1] = 0;
+ /* Ensure that the string buffer is NUL terminated. */
+ strings[stringsize] = 0;
obj_aout_external_strings (abfd) = strings;
obj_aout_external_string_size (abfd) = stringsize;
{
flagword visible;
- if (cache_ptr->type == N_FN)
+ if (is_stab (cache_ptr->type, cache_ptr->symbol.name))
{
asection *sec;
cache_ptr->symbol.flags = BSF_DEBUGGING;
/* Work out the symbol section. */
- switch (cache_ptr->type & N_TYPE)
+ switch (cache_ptr->type)
{
- case N_TEXT:
+ case N_SO:
+ case N_SOL:
+ case N_FUN:
+ case N_ENTRY:
+ case N_SLINE:
case N_FN:
sec = obj_textsec (abfd);
break;
- case N_DATA:
+ case N_STSYM:
+ case N_DSLINE:
sec = obj_datasec (abfd);
break;
- case N_BSS:
+ case N_LCSYM:
+ case N_BSLINE:
sec = obj_bsssec (abfd);
break;
default:
- case N_ABS:
sec = bfd_abs_section_ptr;
break;
}
bfd_vma value = cache_ptr->value;
asection *sec;
bfd_vma off;
+ const char *name = cache_ptr->name != NULL ? cache_ptr->name : "*unknown*";
/* Mask out any existing type bits in case copying from one section
to another. */
- sym_pointer->e_type[0] &= ~N_TYPE;
+ if (!is_stab (sym_pointer->e_type[0], name))
+ sym_pointer->e_type[0] &= ~N_TYPE;
sec = bfd_asymbol_section (cache_ptr);
off = 0;
_bfd_error_handler
/* xgettext:c-format */
(_("%pB: can not represent section for symbol `%s' in a.out object file format"),
- abfd, cache_ptr->name != NULL ? cache_ptr->name : "*unknown*");
+ abfd, name);
bfd_set_error (bfd_error_nonrepresentable_section);
return FALSE;
}
return &new_symbol_type->symbol;
}
-/* Translate a set of internal symbols into external symbols. */
+/* Translate a set of external symbols into internal symbols. */
bfd_boolean
NAME (aout, translate_symbol_table) (bfd *abfd,
for (; ext < ext_end; ext++, in++)
{
bfd_vma x;
+ int ovly;
x = GET_WORD (abfd, ext->e_strx);
in->symbol.the_bfd = abfd;
else if (x < strsize)
in->symbol.name = str + x;
else
- return FALSE;
+ {
+ _bfd_error_handler
+ (_("%pB: invalid string offset %" PRIu64 " >= %" PRIu64),
+ abfd, (uint64_t) x, (uint64_t) strsize);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ ovly = H_GET_8 (abfd, ext->e_ovly);
+ if (ovly != 0)
+ {
+ _bfd_error_handler
+ (_("%pB: symbol indicates overlay (not supported)"), abfd);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
in->symbol.value = GET_WORD (abfd, ext->e_value);
- /* TODO: is 0 a safe value here? */
- in->desc = 0;
+ /* e_desc is zero for normal symbols but for .stab symbols it
+ carries the desc field in our extended 2.11BSD format. */
+ in->desc = H_GET_16 (abfd, ext->e_desc);
in->other = 0;
in->type = H_GET_8 (abfd, ext->e_type);
in->symbol.udata.p = NULL;
/* Get the index of a string in a strtab, adding it if it is not
already present. */
-static INLINE bfd_size_type
+static inline bfd_size_type
add_to_stringtab (bfd *abfd,
struct bfd_strtab_hash *tab,
const char *str,
bfd_size_type indx;
struct external_nlist nsp;
- PUT_WORD (abfd, 0, nsp.e_unused);
-
indx = add_to_stringtab (abfd, strtab, g->name, FALSE);
if (indx == (bfd_size_type) -1)
goto error_return;
PUT_WORD (abfd, indx, nsp.e_strx);
if (bfd_asymbol_flavour(g) == abfd->xvec->flavour)
- H_PUT_8 (abfd, aout_symbol(g)->type, nsp.e_type);
+ {
+ H_PUT_16 (abfd, aout_symbol (g)->desc, nsp.e_desc);
+ H_PUT_8 (abfd, 0, nsp.e_ovly);
+ H_PUT_8 (abfd, aout_symbol (g)->type, nsp.e_type);
+ }
else
- H_PUT_8 (abfd, 0, nsp.e_type);
+ {
+ H_PUT_16 (abfd, 0, nsp.e_desc);
+ H_PUT_8 (abfd, 0, nsp.e_ovly);
+ H_PUT_8 (abfd, 0, nsp.e_type);
+ }
if (! translate_to_native_sym_flags (abfd, g, &nsp))
goto error_return;
- H_PUT_8 (abfd, 0, nsp.e_ovly);
-
if (bfd_bwrite ((void *)&nsp, (bfd_size_type) EXTERNAL_NLIST_SIZE, abfd)
!= EXTERNAL_NLIST_SIZE)
goto error_return;
local or global. */
r_extern = (reloc_entry & RTYPE) == REXT;
- if (r_extern && r_index > symcount)
+ if (r_extern && r_index >= symcount)
{
/* We could arrange to return an error, but it might be useful
- to see the file even if it is bad. */
+ to see the file even if it is bad. FIXME: Of course this
+ means that objdump -r *doesn't* see the actual reloc, and
+ objcopy silently writes a different reloc. */
r_extern = 0;
r_index = N_ABS;
}
{
bfd_byte *r;
+ if ((*generic)->howto == NULL
+ || (*generic)->sym_ptr_ptr == NULL)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ _bfd_error_handler (_("%pB: attempt to write out "
+ "unknown reloc type"), abfd);
+ bfd_release (abfd, native);
+ return FALSE;
+ }
r = native + (*generic)->address;
pdp11_aout_swap_reloc_out (abfd, *generic, r);
count--;
char *buf;
*filename_ptr = bfd_get_filename (abfd);
- *functionname_ptr = 0;
+ *functionname_ptr = NULL;
*line_ptr = 0;
if (discriminator_ptr)
*discriminator_ptr = 0;
const char * symname;
symname = q->symbol.name;
- if (strcmp (symname + strlen (symname) - 2, ".o") == 0)
+
+ if (symname != NULL
+ && strlen (symname) > 2
+ && strcmp (symname + strlen (symname) - 2, ".o") == 0)
{
if (q->symbol.value > low_line_vma)
{
/* Look ahead to next symbol to check if that too is an N_SO. */
p++;
if (*p == NULL)
- break;
+ goto done;
q = (aout_symbol_type *)(*p);
if (q->type != (int) N_SO)
goto next;
*filename_ptr = main_file_name;
else
{
- sprintf (buf, "%s%s", directory_name, main_file_name);
- *filename_ptr = buf;
- buf += filelen + 1;
+ if (buf == NULL)
+ /* PR binutils/20891: In a corrupt input file both
+ main_file_name and directory_name can be empty... */
+ * filename_ptr = NULL;
+ else
+ {
+ snprintf (buf, filelen + 1, "%s%s", directory_name,
+ main_file_name);
+ *filename_ptr = buf;
+ buf += filelen + 1;
+ }
}
}
const char *function = func->name;
char *colon;
+ if (buf == NULL)
+ {
+ /* PR binutils/20892: In a corrupt input file func can be empty. */
+ * functionname_ptr = NULL;
+ return TRUE;
+ }
/* The caller expects a symbol name. We actually have a
function name, without the leading underscore. Put the
underscore back in, so that the caller gets a symbol name. */
for (; p < pend; p++)
{
int type = H_GET_8 (abfd, p->e_type);
- const char *name;
+ const char *name = strings + GET_WORD (abfd, p->e_strx);
struct bfd_link_hash_entry *h;
/* Ignore symbols that are not externally visible. This is an
optimization only, as we check the type more thoroughly
below. */
if ((type & N_EXT) == 0
+ || is_stab(type, name)
|| type == N_FN)
continue;
- name = strings + GET_WORD (abfd, p->e_strx);
h = bfd_link_hash_lookup (info->hash, name, FALSE, FALSE, TRUE);
/* We are only interested in symbols that are currently
type = H_GET_8 (abfd, p->e_type);
+ /* PR 19629: Corrupt binaries can contain illegal string offsets. */
+ if (GET_WORD (abfd, p->e_strx) >= obj_aout_external_string_size (abfd))
+ return FALSE;
name = strings + GET_WORD (abfd, p->e_strx);
+
+ /* Ignore debugging symbols. */
+ if (is_stab (type, name))
+ continue;
+
value = GET_WORD (abfd, p->e_value);
flags = BSF_GLOBAL;
string = NULL;
switch (type)
{
default:
- /* Anything else should be a debugging symbol. */
- BFD_ASSERT ((type & N_STAB) != 0);
+ /* Shouldn't be any types not covered. */
+ BFD_ASSERT (0);
continue;
case N_UNDF:
return (struct bfd_hash_entry *) ret;
}
+/* Write out a symbol that was not associated with an a.out input
+ object. */
+
static bfd_boolean
aout_link_write_other_symbol (struct bfd_hash_entry *bh, void *data)
{
}
H_PUT_8 (output_bfd, type, outsym.e_type);
+ H_PUT_8 (output_bfd, 0, outsym.e_ovly);
indx = add_to_stringtab (output_bfd, flaginfo->strtab, h->root.root.string,
FALSE);
if (indx == (bfd_size_type) -1)
/* FIXME: No way to handle errors. */
abort ();
+ PUT_WORD (output_bfd, 0, outsym.e_desc);
PUT_WORD (output_bfd, indx, outsym.e_strx);
PUT_WORD (output_bfd, val, outsym.e_value);
r_extern = (r_type == REXT);
howto_idx = r_pcrel;
- BFD_ASSERT (howto_idx < TABLE_SIZE (howto_table_pdp11));
- howto = howto_table_pdp11 + howto_idx;
+ if (howto_idx < TABLE_SIZE (howto_table_pdp11))
+ howto = howto_table_pdp11 + howto_idx;
+ else
+ {
+ _bfd_error_handler (_("%pB: unsupported relocation type"),
+ input_bfd);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
}
if (relocatable)
&& discard != discard_all)
{
H_PUT_8 (output_bfd, N_TEXT, outsym->e_type);
+ H_PUT_8 (output_bfd, 0, outsym->e_ovly);
+ H_PUT_16 (output_bfd, 0, outsym->e_desc);
strtab_index = add_to_stringtab (output_bfd, flaginfo->strtab,
bfd_get_filename (input_bfd), FALSE);
if (strtab_index == (bfd_size_type) -1)
case strip_none:
break;
case strip_debugger:
- if ((type & N_STAB) != 0)
+ if (is_stab (type, name))
skip = TRUE;
break;
case strip_some:
}
/* Get the value of the symbol. */
- if ((type & N_TYPE) == N_TEXT
+ if (is_stab (type, name))
+ {
+ switch (type)
+ {
+ default:
+ symsec = bfd_abs_section_ptr;
+ break;
+ case N_SO:
+ case N_SOL:
+ case N_FUN:
+ case N_ENTRY:
+ case N_SLINE:
+ case N_FN:
+ symsec = obj_textsec (input_bfd);
+ break;
+ case N_STSYM:
+ case N_DSLINE:
+ symsec = obj_datasec (input_bfd);
+ break;
+ case N_LCSYM:
+ case N_BSLINE:
+ symsec = obj_bsssec (input_bfd);
+ break;
+ }
+ val = GET_WORD (input_bfd, sym->e_value);
+ }
+ else if ((type & N_TYPE) == N_TEXT
|| type == N_WEAKT)
symsec = obj_textsec (input_bfd);
else if ((type & N_TYPE) == N_DATA
val = GET_WORD (input_bfd, sym->e_value);
symsec = NULL;
}
- else if ((type & N_STAB) != 0)
- {
- val = GET_WORD (input_bfd, sym->e_value);
- symsec = NULL;
- }
else
{
/* If we get here with an indirect symbol, it means that
case discard_sec_merge:
break;
case discard_l:
- if ((type & N_STAB) == 0
+ if (!is_stab (type, name)
&& bfd_is_local_label_name (input_bfd, name))
skip = TRUE;
break;
/* Copy this symbol into the list of symbols we are going to
write out. */
H_PUT_8 (output_bfd, type, outsym->e_type);
+ H_PUT_8 (output_bfd, H_GET_8 (input_bfd, sym->e_ovly), outsym->e_ovly);
+ H_PUT_16 (output_bfd, H_GET_16 (input_bfd, sym->e_desc), outsym->e_desc);
copy = FALSE;
if (! flaginfo->info->keep_memory)
{