# This shell script emits a C file. -*- C -*-
# It does some substitutions.
if [ -z "$MACHINE" ]; then
OUTPUT_ARCH=${ARCH}
else
OUTPUT_ARCH=${ARCH}:${MACHINE}
fi
cat >e${EMULATION_NAME}.c <<EOF
#define TARGET_IS_${EMULATION_NAME}
#include "bfd.h"
#include "sysdep.h"
#include "bfdlink.h"
#include "libiberty.h"
#include "safe-ctype.h"
#include "ld.h"
#include "ldmain.h"
#include "ldmisc.h"
#include "ldexp.h"
#include "ldlang.h"
#include "ldfile.h"
#include "ldemul.h"
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#else
# define dirent direct
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
static void gld${EMULATION_NAME}_find_so
(lang_input_statement_type *);
static char *gld${EMULATION_NAME}_search_dir
(const char *, const char *, bfd_boolean *);
static void gld${EMULATION_NAME}_check_needed
(lang_input_statement_type *);
static bfd_boolean gld${EMULATION_NAME}_search_needed
(const char *, const char *);
static bfd_boolean gld${EMULATION_NAME}_try_needed
(const char *, const char *);
static void gld${EMULATION_NAME}_find_assignment
(lang_statement_union_type *);
static void gld${EMULATION_NAME}_find_exp_assignment
(etree_type *);
static void gld${EMULATION_NAME}_count_need
(lang_input_statement_type *);
static void gld${EMULATION_NAME}_set_need
(lang_input_statement_type *);
static void
gld${EMULATION_NAME}_before_parse (void)
{
ldfile_set_output_arch ("${OUTPUT_ARCH}", bfd_arch_`echo ${ARCH} | sed -e 's/:.* config.dynamic_link = TRUE;
config.has_shared = TRUE;
}
static void
gld${EMULATION_NAME}_set_symbols (void)
{
EOF
if [ "x${host}" = "x${target}" ] ; then
case " ${EMULATION_LIBPATH} " in
*" ${EMULATION_NAME} "*)
cat >>e${EMULATION_NAME}.c <<EOF
const char *env;
env = (const char *) getenv ("LD_LIBRARY_PATH");
if (env != NULL)
{
char *l;
l = xstrdup (env);
while (1)
{
char *c;
c = strchr (l, ':');
if (c != NULL)
*c++ = '\0';
if (*l != '\0')
ldfile_add_library_path (l, FALSE);
if (c == NULL)
break;
l = c;
}
}
EOF
;;
esac
fi
cat >>e${EMULATION_NAME}.c <<EOF
}
static void
gld${EMULATION_NAME}_create_output_section_statements (void)
{
lang_for_each_input_file (gld${EMULATION_NAME}_find_so);
}
static void
gld${EMULATION_NAME}_find_so (lang_input_statement_type *inp)
{
search_dirs_type *search;
char *found = NULL;
char *alc;
struct stat st;
if (! inp->search_dirs_flag
|| ! inp->is_archive
|| ! inp->dynamic)
return;
ASSERT (strncmp (inp->local_sym_name, "-l", 2) == 0);
for (search = search_head; search != NULL; search = search->next)
{
bfd_boolean found_static;
found = gld${EMULATION_NAME}_search_dir (search->name, inp->filename,
&found_static);
if (found != NULL || found_static)
break;
}
if (found == NULL)
{
return;
}
alc = (char *) xmalloc (strlen (search->name) + strlen (found) + 2);
sprintf (alc, "%s/%s", search->name, found);
inp->filename = alc;
inp->search_dirs_flag = FALSE;
free (found);
alc = (char *) xmalloc (strlen (inp->filename) + 1);
strcpy (alc, inp->filename);
strstr (alc + strlen (search->name), ".so")[2] = 'a';
if (stat (alc, &st) != 0)
free (alc);
else
{
lang_input_statement_type *sa;
sa = ((lang_input_statement_type *)
xmalloc (sizeof (lang_input_statement_type)));
*sa = *inp;
inp->filename = alc;
inp->local_sym_name = alc;
inp->header.next = (lang_statement_union_type *) sa;
inp->next_real_file = (lang_statement_union_type *) sa;
}
}
static char *
gld${EMULATION_NAME}_search_dir
(const char *dirname, const char *filename, bfd_boolean *found_static)
{
int force_maj, force_min;
const char *dot;
unsigned int len;
char *alc;
char *found;
int max_maj, max_min;
DIR *dir;
struct dirent *entry;
unsigned int dirnamelen;
char *full_path;
int statval;
struct stat st;
*found_static = FALSE;
force_maj = -1;
force_min = -1;
dot = strchr (filename, '.');
if (dot == NULL)
{
len = strlen (filename);
alc = NULL;
}
else
{
force_maj = atoi (dot + 1);
len = dot - filename;
alc = (char *) xmalloc (len + 1);
strncpy (alc, filename, len);
alc[len] = '\0';
filename = alc;
dot = strchr (dot + 1, '.');
if (dot != NULL)
force_min = atoi (dot + 1);
}
found = NULL;
max_maj = max_min = 0;
dir = opendir (dirname);
if (dir == NULL)
return NULL;
dirnamelen = strlen (dirname);
while ((entry = readdir (dir)) != NULL)
{
const char *s;
int found_maj, found_min;
if (strncmp (entry->d_name, "lib", 3) != 0
|| strncmp (entry->d_name + 3, filename, len) != 0)
continue;
if (dot == NULL
&& strcmp (entry->d_name + 3 + len, ".a") == 0)
{
*found_static = TRUE;
continue;
}
if (strncmp (entry->d_name + 3 + len, ".so", 3) != 0)
continue;
if (entry->d_name[6 + len] == '\0')
;
else if (entry->d_name[6 + len] == '.'
&& ISDIGIT (entry->d_name[7 + len]))
;
else
continue;
for (s = entry->d_name + 6 + len; *s != '\0'; s++)
if (*s != '.' && ! ISDIGIT (*s))
break;
if (*s != '\0')
continue;
found_maj = 0;
found_min = 0;
sscanf (entry->d_name + 3 + len, ".so.%d.%d",
&found_maj, &found_min);
if ((force_maj != -1 && force_maj != found_maj)
|| (force_min != -1 && force_min != found_min))
continue;
full_path = xmalloc (dirnamelen + 1 + strlen (entry->d_name) + 1);
sprintf (full_path, "%s/%s", dirname, entry->d_name);
statval = stat (full_path, &st);
free (full_path);
if (statval != 0)
continue;
if (found == NULL
|| (found_maj > max_maj)
|| (found_maj == max_maj
&& (found_min > max_min
|| (found_min == max_min
&& strcmp (entry->d_name, found) > 0))))
{
if (found != NULL)
free (found);
found = (char *) xmalloc (strlen (entry->d_name) + 1);
strcpy (found, entry->d_name);
max_maj = found_maj;
max_min = found_min;
}
}
closedir (dir);
if (alc != NULL)
free (alc);
return found;
}
static struct bfd_link_needed_list *global_needed;
static bfd_boolean global_found;
static void
gld${EMULATION_NAME}_after_open (void)
{
struct bfd_link_needed_list *needed, *l;
if (link_info.relocatable || link_info.shared)
return;
needed = bfd_sunos_get_needed_list (output_bfd, &link_info);
for (l = needed; l != NULL; l = l->next)
{
struct bfd_link_needed_list *ll;
const char *lname;
search_dirs_type *search;
lname = l->name;
for (ll = needed; ll != l; ll = ll->next)
if (strcmp (ll->name, lname) == 0)
break;
if (ll != l)
continue;
global_needed = l;
global_found = FALSE;
lang_for_each_input_file (gld${EMULATION_NAME}_check_needed);
if (global_found)
continue;
if (strncmp (lname, "-l", 2) != 0)
{
bfd *abfd;
abfd = bfd_openr (lname, bfd_get_target (output_bfd));
if (abfd != NULL)
{
if (! bfd_check_format (abfd, bfd_object))
{
(void) bfd_close (abfd);
abfd = NULL;
}
}
if (abfd != NULL)
{
if ((bfd_get_file_flags (abfd) & DYNAMIC) == 0)
{
(void) bfd_close (abfd);
abfd = NULL;
}
}
if (abfd != NULL)
{
if (! bfd_link_add_symbols (abfd, &link_info))
einfo ("%F%B: could not read symbols: %E\n", abfd);
}
else
{
einfo ("%P: warning: %s, needed by %B, not found\n",
lname, l->by);
}
continue;
}
lname += 2;
if (gld${EMULATION_NAME}_search_needed (command_line.rpath_link,
lname))
continue;
if (command_line.rpath != NULL)
{
if (gld${EMULATION_NAME}_search_needed (command_line.rpath, lname))
continue;
}
else
{
for (search = search_head; search != NULL; search = search->next)
if (gld${EMULATION_NAME}_try_needed (search->name, lname))
break;
if (search != NULL)
continue;
}
EOF
if [ "x${host}" = "x${target}" ] ; then
case " ${EMULATION_LIBPATH} " in
*" ${EMULATION_NAME} "*)
cat >>e${EMULATION_NAME}.c <<EOF
{
const char *lib_path;
lib_path = (const char *) getenv ("LD_LIBRARY_PATH");
if (gld${EMULATION_NAME}_search_needed (lib_path, lname))
continue;
}
EOF
;;
esac
fi
cat >>e${EMULATION_NAME}.c <<EOF
if (command_line.rpath != NULL)
{
for (search = search_head; search != NULL; search = search->next)
{
if (search->cmdline)
continue;
if (gld${EMULATION_NAME}_try_needed (search->name, lname))
break;
}
if (search != NULL)
continue;
}
einfo ("%P: warning: %s, needed by %B, not found\n",
l->name, l->by);
}
}
static bfd_boolean
gld${EMULATION_NAME}_search_needed (const char *path, const char *name)
{
const char *s;
if (path == NULL || *path == '\0')
return FALSE;
while (1)
{
const char *dir;
char *dircopy;
s = strchr (path, ':');
if (s == NULL)
{
dircopy = NULL;
dir = path;
}
else
{
dircopy = (char *) xmalloc (s - path + 1);
memcpy (dircopy, path, s - path);
dircopy[s - path] = '\0';
dir = dircopy;
}
if (gld${EMULATION_NAME}_try_needed (dir, name))
return TRUE;
if (dircopy != NULL)
free (dircopy);
if (s == NULL)
break;
path = s + 1;
}
return FALSE;
}
static bfd_boolean
gld${EMULATION_NAME}_try_needed (const char *dir, const char *name)
{
char *file;
char *alc;
bfd_boolean ignore;
bfd *abfd;
file = gld${EMULATION_NAME}_search_dir (dir, name, &ignore);
if (file == NULL)
return FALSE;
alc = (char *) xmalloc (strlen (dir) + strlen (file) + 2);
sprintf (alc, "%s/%s", dir, file);
free (file);
abfd = bfd_openr (alc, bfd_get_target (output_bfd));
if (abfd == NULL)
return FALSE;
if (! bfd_check_format (abfd, bfd_object))
{
(void) bfd_close (abfd);
return FALSE;
}
if ((bfd_get_file_flags (abfd) & DYNAMIC) == 0)
{
(void) bfd_close (abfd);
return FALSE;
}
if (! bfd_link_add_symbols (abfd, &link_info))
einfo ("%F%B: could not read symbols: %E\n", abfd);
return TRUE;
}
static void
gld${EMULATION_NAME}_check_needed (lang_input_statement_type *s)
{
if (s->filename == NULL)
return;
if (strncmp (global_needed->name, "-l", 2) != 0)
{
if (strcmp (s->filename, global_needed->name) == 0)
global_found = TRUE;
}
else
{
const char *sname, *lname;
const char *sdot, *ldot;
int lmaj, lmin, smaj, smin;
lname = global_needed->name + 2;
sname = strrchr (s->filename, '/');
if (sname == NULL)
sname = s->filename;
else
++sname;
if (strncmp (sname, "lib", 3) != 0)
return;
sname += 3;
ldot = strchr (lname, '.');
if (ldot == NULL)
ldot = lname + strlen (lname);
sdot = strstr (sname, ".so.");
if (sdot == NULL)
return;
if (sdot - sname != ldot - lname
|| strncmp (lname, sname, sdot - sname) != 0)
return;
lmaj = lmin = -1;
sscanf (ldot, ".%d.%d", &lmaj, &lmin);
smaj = smin = -1;
sscanf (sdot, ".so.%d.%d", &smaj, &smin);
if ((smaj != lmaj && smaj != -1 && lmaj != -1)
|| (smin != lmin && smin != -1 && lmin != -1))
return;
global_found = TRUE;
}
}
static const char *find_assign;
static bfd_boolean found_assign;
static bfd_size_type need_size;
static bfd_size_type need_entries;
static bfd_byte *need_contents;
static bfd_byte *need_pinfo;
static bfd_byte *need_pnames;
#define NEED_ENTRY_SIZE (16)
static void
gld${EMULATION_NAME}_before_allocation (void)
{
struct bfd_link_hash_entry *hdyn = NULL;
asection *sneed;
asection *srules;
asection *sdyn;
if (! link_info.shared && ! link_info.relocatable && ! entry_from_cmdline)
{
struct bfd_link_hash_entry *h;
for (h = link_info.hash->undefs; h != NULL; h = h->u.undef.next)
{
if (h->type == bfd_link_hash_undefined
&& h->u.undef.abfd != NULL
&& (h->u.undef.abfd->flags & DYNAMIC) == 0
&& strcmp (h->root.string, "__DYNAMIC") != 0
&& strcmp (h->root.string, "__GLOBAL_OFFSET_TABLE_") != 0)
{
find_assign = h->root.string;
found_assign = FALSE;
lang_for_each_statement (gld${EMULATION_NAME}_find_assignment);
if (! found_assign)
{
link_info.shared = TRUE;
break;
}
}
}
}
if (link_info.shared)
{
lang_output_section_statement_type *os;
os = lang_output_section_statement_lookup (".text");
if (os->addr_tree == NULL)
os->addr_tree = exp_intop (0x20);
}
if (! link_info.relocatable)
{
hdyn = bfd_link_hash_lookup (link_info.hash, "__DYNAMIC", TRUE, FALSE,
FALSE);
if (hdyn == NULL)
einfo ("%P%F: bfd_link_hash_lookup: %E\n");
if (! bfd_sunos_record_link_assignment (output_bfd, &link_info,
"__DYNAMIC"))
einfo ("%P%F: failed to record assignment to __DYNAMIC: %E\n");
}
lang_for_each_statement (gld${EMULATION_NAME}_find_assignment);
if (! bfd_sunos_size_dynamic_sections (output_bfd, &link_info, &sdyn,
&sneed, &srules))
einfo ("%P%F: failed to set dynamic section sizes: %E\n");
if (sneed != NULL)
{
need_entries = 0;
need_size = 0;
lang_for_each_input_file (gld${EMULATION_NAME}_count_need);
ASSERT (need_entries != 0);
sneed->size = need_size;
sneed->contents = (bfd_byte *) xmalloc (need_size);
need_contents = sneed->contents;
need_pinfo = sneed->contents;
need_pnames = sneed->contents + need_entries * 16;
lang_for_each_input_file (gld${EMULATION_NAME}_set_need);
ASSERT ((bfd_size_type) (need_pnames - sneed->contents) == need_size);
}
if (srules != NULL)
{
if (command_line.rpath)
{
srules->size = strlen (command_line.rpath);
srules->contents = (bfd_byte *) command_line.rpath;
}
else
{
unsigned int size;
search_dirs_type *search;
size = 0;
for (search = search_head; search != NULL; search = search->next)
if (search->cmdline)
size += strlen (search->name) + 1;
srules->size = size;
if (size > 0)
{
char *p;
srules->contents = (bfd_byte *) xmalloc (size);
p = (char *) srules->contents;
*p = '\0';
for (search = search_head; search != NULL; search = search->next)
{
if (search->cmdline)
{
if (p != (char *) srules->contents)
*p++ = ':';
strcpy (p, search->name);
p += strlen (p);
}
}
}
}
}
if (! link_info.relocatable)
{
hdyn->type = bfd_link_hash_defined;
hdyn->u.def.value = 0;
if (sdyn != NULL)
hdyn->u.def.section = sdyn;
else
hdyn->u.def.section = bfd_abs_section_ptr;
}
}
static void
gld${EMULATION_NAME}_find_assignment (lang_statement_union_type *s)
{
if (s->header.type == lang_assignment_statement_enum
&& (find_assign == NULL || ! found_assign))
gld${EMULATION_NAME}_find_exp_assignment (s->assignment_statement.exp);
}
static void
gld${EMULATION_NAME}_find_exp_assignment (etree_type *exp)
{
switch (exp->type.node_class)
{
case etree_assign:
if (find_assign != NULL)
{
if (strcmp (find_assign, exp->assign.dst) == 0)
found_assign = TRUE;
return;
}
if (strcmp (exp->assign.dst, ".") != 0)
{
if (! bfd_sunos_record_link_assignment (output_bfd, &link_info,
exp->assign.dst))
einfo ("%P%F: failed to record assignment to %s: %E\n",
exp->assign.dst);
}
gld${EMULATION_NAME}_find_exp_assignment (exp->assign.src);
break;
case etree_binary:
gld${EMULATION_NAME}_find_exp_assignment (exp->binary.lhs);
gld${EMULATION_NAME}_find_exp_assignment (exp->binary.rhs);
break;
case etree_trinary:
gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.cond);
gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.lhs);
gld${EMULATION_NAME}_find_exp_assignment (exp->trinary.rhs);
break;
case etree_unary:
gld${EMULATION_NAME}_find_exp_assignment (exp->unary.child);
break;
default:
break;
}
}
static void
gld${EMULATION_NAME}_count_need (lang_input_statement_type *inp)
{
if (inp->the_bfd != NULL
&& (inp->the_bfd->flags & DYNAMIC) != 0)
{
++need_entries;
need_size += NEED_ENTRY_SIZE;
if (! inp->is_archive)
need_size += strlen (inp->filename) + 1;
else
{
ASSERT (inp->local_sym_name[0] == '-'
&& inp->local_sym_name[1] == 'l');
need_size += strlen (inp->local_sym_name + 2) + 1;
}
}
}
static void
gld${EMULATION_NAME}_set_need (lang_input_statement_type *inp)
{
if (inp->the_bfd != NULL
&& (inp->the_bfd->flags & DYNAMIC) != 0)
{
bfd_size_type c;
bfd_put_32 (output_bfd, need_pnames - need_contents, need_pinfo);
if (! inp->is_archive)
{
bfd_put_32 (output_bfd, (bfd_vma) 0, need_pinfo + 4);
bfd_put_16 (output_bfd, (bfd_vma) 0, need_pinfo + 8);
bfd_put_16 (output_bfd, (bfd_vma) 0, need_pinfo + 10);
strcpy ((char *) need_pnames, inp->filename);
}
else
{
char *verstr;
int maj, min;
bfd_put_32 (output_bfd, (bfd_vma) 0x80000000, need_pinfo + 4);
maj = 0;
min = 0;
verstr = strstr (inp->filename, ".so.");
if (verstr != NULL)
sscanf (verstr, ".so.%d.%d", &maj, &min);
bfd_put_16 (output_bfd, (bfd_vma) maj, need_pinfo + 8);
bfd_put_16 (output_bfd, (bfd_vma) min, need_pinfo + 10);
strcpy ((char *) need_pnames, inp->local_sym_name + 2);
}
c = (need_pinfo - need_contents) / NEED_ENTRY_SIZE;
if (c + 1 >= need_entries)
bfd_put_32 (output_bfd, (bfd_vma) 0, need_pinfo + 12);
else
bfd_put_32 (output_bfd, (bfd_vma) (c + 1) * NEED_ENTRY_SIZE,
need_pinfo + 12);
need_pinfo += NEED_ENTRY_SIZE;
need_pnames += strlen ((char *) need_pnames) + 1;
}
}
static char *
gld${EMULATION_NAME}_get_script (int *isfile)
EOF
if test -n "$COMPILE_IN"
then
# Scripts compiled in.
# sed commands to quote an ld script as a C string.
sc="-f stringify.sed"
cat >>e${EMULATION_NAME}.c <<EOF
{
*isfile = 0;
if (link_info.relocatable && config.build_constructors)
return
EOF
sed $sc ldscripts/${EMULATION_NAME}.xu >> e${EMULATION_NAME}.c
echo ' ; else if (link_info.relocatable) return' >> e${EMULATION_NAME}.c
sed $sc ldscripts/${EMULATION_NAME}.xr >> e${EMULATION_NAME}.c
echo ' ; else if (!config.text_read_only) return' >> e${EMULATION_NAME}.c
sed $sc ldscripts/${EMULATION_NAME}.xbn >> e${EMULATION_NAME}.c
echo ' ; else if (!config.magic_demand_paged) return' >> e${EMULATION_NAME}.c
sed $sc ldscripts/${EMULATION_NAME}.xn >> e${EMULATION_NAME}.c
echo ' ; else return' >> e${EMULATION_NAME}.c
sed $sc ldscripts/${EMULATION_NAME}.x >> e${EMULATION_NAME}.c
echo '; }' >> e${EMULATION_NAME}.c
else
# Scripts read from the filesystem.
cat >>e${EMULATION_NAME}.c <<EOF
{
*isfile = 1;
if (link_info.relocatable && config.build_constructors)
return "ldscripts/${EMULATION_NAME}.xu";
else if (link_info.relocatable)
return "ldscripts/${EMULATION_NAME}.xr";
else if (!config.text_read_only)
return "ldscripts/${EMULATION_NAME}.xbn";
else if (!config.magic_demand_paged)
return "ldscripts/${EMULATION_NAME}.xn";
else
return "ldscripts/${EMULATION_NAME}.x";
}
EOF
fi
cat >>e${EMULATION_NAME}.c <<EOF
struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
{
gld${EMULATION_NAME}_before_parse,
syslib_default,
hll_default,
after_parse_default,
gld${EMULATION_NAME}_after_open,
after_allocation_default,
set_output_arch_default,
ldemul_default_target,
gld${EMULATION_NAME}_before_allocation,
gld${EMULATION_NAME}_get_script,
"${EMULATION_NAME}",
"${OUTPUT_FORMAT}",
NULL,
gld${EMULATION_NAME}_create_output_section_statements,
NULL,
NULL,
gld${EMULATION_NAME}_set_symbols,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
EOF