From: David Henkel-Wallace Date: Thu, 21 Mar 1991 21:29:06 +0000 (+0000) Subject: Initial revision X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=2fa0b342a5cd580781d2b9348a87f33a92d363fa;p=binutils-gdb.git Initial revision --- diff --git a/binutils/ar.c b/binutils/ar.c new file mode 100644 index 00000000000..9a8a5281be0 --- /dev/null +++ b/binutils/ar.c @@ -0,0 +1,850 @@ + + + +/* ar.c - Archive modify and extract. */ +/* + Bugs: should use getopt the way tar does (complete w/optional -) and + should have long options too. GNU ar used to check file against filesystem + in quick_update and replace operations (would check mtime). Doesn't warn + when name truncated. No way to specify pos_end. Error messages should be + more consistant. +*/ +#include "sysdep.h" +#include "bfd.h" +#include "ar.h" +#include +#include +#include +#define BUFSIZE 8192 +/* Not great to have these here. Should they be exported or not? */ +PROTO(size_t, bfd_read, (void *ptr, size_t size, size_t nitems, bfd * abfd)); +PROTO(size_t, bfd_write, (void *ptr, size_t size, size_t nitems, bfd * abfd)); +/* PROTO (void, open_inarch, (char *archive_filename)); */ +#ifdef __STDC__ +static void open_inarch(char *archive_filename); +#else +static void open_inarch(); +#endif /* __STDC__ */ + +PROTO(void, map_over_members, (void (*function) (), char **files, int count)); +PROTO(void, print_contents, (bfd * member)); +PROTO(void, extract_file, (bfd * abfd)); +PROTO(void, delete_members, (char **files_to_delete)); +PROTO(void, do_quick_append, (char *archive_filename, char **files_to_append)); +PROTO(void, move_members, (char **files_to_move)); +PROTO(void, replace_members, (char **files_to_replace)); +PROTO(void, print_descr, (bfd * abfd)); +PROTO(void, ranlib_only, (char *archname)); + +/** Globals and flags */ + +char *program_name = NULL; +bfd bogus_archive; +bfd *inarch; /* The input arch we're manipulating */ + +/* Nonzero means don't warn about creating the archive file if necessary. */ +int silent_create = 0; +/* Nonzero means describe each action performed. */ +int verbose = 0; +/* Nonzero means preserve dates of members when extracting them. */ +int preserve_dates = 0; +/* + Nonzero means don't replace existing members whose dates are more recent + than the corresponding files. +*/ +int newer_only = 0; +/* write a __.SYMDEF member into the modified archive. */ +boolean write_armap = false; +/* + Nonzero means don't update __.SYMDEF unless command line explicitly + requested it +*/ +int ignore_symdef = 0; +/* + Nonzero means it's the name of an existing member; position new or moved + files with respect to this one. +*/ +char *posname = NULL; +/* + Sez how to use `posname': pos_before means position before that member. + pos_after means position after that member. pos_end means always at end. + pos_default means default appropriately. For the latter two, `posname' + should also be zero. +*/ +enum pos { + pos_default, pos_before, pos_after, pos_end +} postype = pos_default; + +/* + The option parsing should be in its own function. It will be when I have + getopt working. +*/ +int +main(argc, argv) + int argc; + char **argv; +{ + char *arg_ptr; + char c; + enum { + none = 0, delete, replace, print_table, + print_files, extract, move, quick_append + } operation = none; + int arg_index; + char **files; + char *inarch_filename; + char *temp; + program_name = argv[0]; + + + temp = strrchr(program_name, '/'); + if (temp == (char *) NULL) + temp = program_name; /* shouldn't happen, but... */ + else + ++temp; + if (!strcmp(temp, "ranlib")) { + if (argc < 2) + fatal("Too few command arguments."); + ranlib_only(argv[1]); + } + + + if (argc < 3) + fatal("Too few command arguments."); + + arg_ptr = argv[1]; + + if (*arg_ptr == '-') + ++arg_ptr; /* compatibility */ + + while (c = *arg_ptr++) { + switch (c) { + case 'd': + case 'm': + case 'p': + case 'q': + case 'r': + case 't': + case 'x': + if (operation != none) + fatal("two different operation switches specified"); + switch (c) { + case 'd': + operation = delete; + break; + case 'm': + operation = move; + break; + case 'p': + operation = print_files; + break; + case 'q': + operation = quick_append; + break; + case 'r': + operation = replace; + break; + case 't': + operation = print_table; + break; + case 'x': + operation = extract; + break; + } + case 'l': + break; + case 'c': + silent_create = 1; + break; + case 'o': + preserve_dates = 1; + break; + case 's': + write_armap = true; + break; + case 'u': + newer_only = 1; + break; + case 'v': + verbose = 1; + break; + case 'a': + postype = pos_after; + break; + case 'b': + postype = pos_before; + break; + case 'i': + postype = pos_before; + break; + default: + fatal("invalid option %c", c); + } + } + + if (operation == none && write_armap) + ranlib_only(argv[2]); + + if (operation == none) + fatal("no operation specified"); + + if (newer_only && operation != replace) + fatal("'u' only meaningful with 'r' option."); + + arg_index = 2; + + if (postype != pos_default) + posname = argv[arg_index++]; + + inarch_filename = argv[arg_index++]; + + if (arg_index < argc) { + files = argv + arg_index; + while (arg_index < argc) + if (!strcmp(argv[arg_index++], "__.SYMDEF")) { + ignore_symdef = 1; + break; + } + } + else + files = NULL; + + if (operation == quick_append) { + if (files != NULL) + do_quick_append(inarch_filename, files); + exit(0); + } + + + open_inarch(inarch_filename); + /* + If we have no archive, and we've been asked to replace then create one + */ + + if (operation == replace && + inarch == &bogus_archive) { + silent_create = 1; + do_quick_append(inarch_filename, 0); + open_inarch(inarch_filename); + } + + switch (operation) { + + case print_table: + map_over_members(print_descr, files, argc - 3); + break; + + case print_files: + map_over_members(print_contents, files, argc - 3); + break; + + case extract: + map_over_members(extract_file, files, argc - 3); + break; + + case delete: + if (files != NULL) + delete_members(files); + break; + + case move: + if (files != NULL) + move_members(files); + break; + + case replace: + if (files != NULL || write_armap) + replace_members(files); + break; + + /* Shouldn't happen! */ + default: + fprintf(stderr, "Sorry; this option not implemented.\n"); + } + + return (0); +} /* main() */ + +static +char *normalize(file) +char *file; +{ + char * filename = strrchr(file, '/'); + if (filename != (char *)NULL) { + filename ++; + } + else { + filename = file; + } + return filename; +} + +static void +open_inarch(archive_filename) + char *archive_filename; +{ + bfd **last_one; + bfd *next_one; + struct stat sbuf; + bfd_error = no_error; + if (stat(archive_filename, &sbuf) != 0) { + if (errno != ENOENT) + bfd_fatal(archive_filename); + if (!silent_create) + fprintf(stderr, + "%s: creating %s\n", program_name, archive_filename); + + inarch = &bogus_archive; + inarch->filename = archive_filename; + inarch->has_armap = true; + + } + else { + inarch = bfd_openr(archive_filename, NULL); + if (inarch == NULL) { + bloser: + bfd_perror(archive_filename); + exit(1); + } + + if (bfd_check_format(inarch, bfd_archive) != true) + fatal("File %s is not an archive.", archive_filename); + last_one = &(inarch->next); + /* Read all the contents right away, regardless. */ + for (next_one = bfd_openr_next_archived_file(inarch, NULL); + next_one; + next_one = bfd_openr_next_archived_file(inarch, next_one)) { + *last_one = next_one; + last_one = &next_one->next; + } + *last_one = (bfd *) NULL; + if (bfd_error != no_more_archived_files) + goto bloser; + } +} + + + +/* + If count is 0, then function is called once on each entry. if nonzero, + count is the length of the files chain; function is called on each entry + whose name matches one in files +*/ +void +map_over_members(function, files, count) + void (*function) (); + char **files; + int count; +{ + bfd *head; + + + + + if (count == 0) { + for (head = inarch->next; head; head = head->next) + function(head); + return; + } + /* + This may appear to be a baroque way of accomplishing what we want. + however we have to iterate over the filenames in order to notice where + a filename is requested but does not exist in the archive. Ditto + mapping over each file each time -- we want to hack multiple + references. + */ + + for (; count > 0; files++, count--) { + boolean found = false; + for (head = inarch->next; head; head = head->next) + if ((head->filename != NULL) && + (!strcmp(*files, head->filename))) { + found = true; + function(head); + } + if (!found) + fprintf(stderr, "No entry %s in archive.\n", *files); + } +} + + +/* Things which are interesting to map over all or some of the files: */ + +void +print_descr(abfd) + bfd *abfd; +{ + print_arelt_descr(abfd, verbose); +} + +void +print_contents(abfd) + bfd *abfd; +{ + int ncopied = 0; + struct stat buf; + long size; + if (bfd_stat_arch_elt(abfd, &buf) != 0) + fatal("Internal stat error on %s", abfd->filename); + + if (verbose) + printf("\n\n\n", abfd->filename); + + bfd_seek(abfd, 0, SEEK_SET); + + size = buf.st_size; + while (ncopied < size) { + char cbuf[BUFSIZE]; + int nread; + int tocopy = size - ncopied; + if (tocopy > BUFSIZE) + tocopy = BUFSIZE; + + nread = bfd_read(cbuf, 1, tocopy, abfd); /* oops -- broke + abstraction! */ + + if (nread != tocopy) + fatal("file %s not a valid archive", abfd->my_archive->filename); + fwrite(cbuf, 1, nread, stdout); + ncopied += tocopy; + } +} + + +/* + Extract a member of the archive into its own file. + +We defer opening the new file until after we have read a BUFSIZ chunk of the + old one, since we know we have just read the archive header for the old + one. Since most members are shorter than BUFSIZ, this means we will read + the old header, read the old data, write a new inode for the new file, and + write the new data, and be done. This 'optimization' is what comes from + sitting next to a bare disk and hearing it every time it seeks. -- Gnu + Gilmore +*/ + +void +extract_file(abfd) + bfd *abfd; +{ + FILE *ostream; + char cbuf[BUFSIZE]; + int nread, + tocopy; + int ncopied = 0; + long size; + struct stat buf; + if (bfd_stat_arch_elt(abfd, &buf) != 0) + fatal("Internal stat error on %s", abfd->filename); + size = buf.st_size; + + if (verbose) + printf("x - %s\n", abfd->filename); + + bfd_seek(abfd, 0, SEEK_SET); + + ostream = 0; + while (ncopied < size) { + tocopy = size - ncopied; + if (tocopy > BUFSIZE) + tocopy = BUFSIZE; + + nread = bfd_read(cbuf, 1, tocopy, abfd); + if (nread != tocopy) + fatal("file %s not a valid archive", abfd->my_archive->filename); + + /* See comment above; this saves disk arm motion */ + if (!ostream) { + /* Seems like an abstraction violation, eh? Well it's OK! */ + ostream = fopen(abfd->filename, "w"); + if (!ostream) { + perror(abfd->filename); + exit(1); + } + } + /* no need to byte-swap; the two formats are presumably compatible(!) */ + fwrite(cbuf, 1, nread, ostream); + ncopied += tocopy; + } + + fclose(ostream); + chmod(abfd->filename, buf.st_mode); + + if (preserve_dates) { +#ifdef USG + long tb[2]; + tb[0] = buf.st_mtime; + tb[1] = buf.st_mtime; + utime(abfd->filename, tb); /* FIXME check result */ +#else + struct timeval tv[2]; + tv[0].tv_sec = buf.st_mtime; + tv[0].tv_usec = 0; + tv[1].tv_sec = buf.st_mtime; + tv[1].tv_usec = 0; + utimes(abfd->filename, tv); /* FIXME check result */ +#endif + } +} + + +/* Just do it quickly; don't worry about dups, armap, or anything like that */ + +/* This is ugly! XXX */ + +PROTO(struct ar_hdr *, bfd_special_undocumented_glue, (char *filename)); + +void +do_quick_append(archive_filename, files_to_append) + char *archive_filename; + char **files_to_append; + +{ + FILE *ofile, + *ifile; + char buf[BUFSIZE]; + long tocopy, + thistime; + bfd *temp; + struct stat sbuf; + boolean newfile = false; + bfd_error = no_error; + + if (stat(archive_filename, &sbuf) != 0) { + if (errno != ENOENT) + bfd_fatal(archive_filename); + newfile = true; + } + + + ofile = fopen(archive_filename, "a+"); + if (ofile == NULL) { + perror(program_name); + exit(1); + } + + /* bletch */ + temp = bfd_openr(archive_filename, NULL); + if (temp == NULL) { + bfd_perror(archive_filename); + exit(1); + } + if (newfile == false) { + if (bfd_check_format(temp, bfd_archive) != true) + fatal("File %s is not an archive.", archive_filename); + } + else { + fwrite(ARMAG, 1, SARMAG, ofile); + if (!silent_create) + fprintf(stderr, "%s: creating %s\n", program_name, archive_filename); + } + + /* assume it's an achive, go straight to the end, sans $200 */ + fseek(ofile, 0, 2); + + for (; files_to_append && *files_to_append; ++files_to_append) { + struct ar_hdr *hdr = bfd_special_undocumented_glue(*files_to_append); + if (hdr == NULL) { + bfd_perror(*files_to_append); + exit(1); + } + + BFD_SEND(temp, _bfd_truncate_arname, (temp, *files_to_append, (char *) hdr)); + + ifile = fopen(*files_to_append, "r"); + if (ifile == NULL) + bfd_perror(program_name); + + if (stat(*files_to_append, &sbuf) != 0) + bfd_perror(*files_to_append); + + tocopy = sbuf.st_size; + + /* XXX should do error-checking! */ + fwrite(hdr, 1, sizeof(struct ar_hdr), ofile); + + + while (tocopy > 0) { + thistime = tocopy; + if (thistime > BUFSIZE) + thistime = BUFSIZE; + fread(buf, 1, thistime, ifile); + fwrite(buf, 1, thistime, ofile); + tocopy -= thistime; + } + fclose(ifile); + if ((sbuf.st_size % 2) == 1) + putc('\n', ofile); + } + fclose(ofile); + bfd_close(temp); +} + + +void +write_archive() +{ + bfd *obfd; + char *xmalloc(); + int namelen = strlen(inarch->filename); + char *new_name = xmalloc(namelen + 6); + bfd *contents_head = inarch->next; + if (inarch == &bogus_archive) { + /* How can this be ? */ + return; + } + else { + + strcpy(new_name, inarch->filename); + strcpy(new_name + namelen, ".art"); + obfd = bfd_openw(new_name, bfd_get_target(inarch)); + + if (obfd == NULL) + bfd_fatal(inarch->filename); + + bfd_set_format(obfd, bfd_archive); + obfd->has_armap = write_armap; + + if (bfd_set_archive_head(obfd, contents_head) != true) + bfd_fatal(inarch->filename); + + if (!bfd_close(obfd)) + bfd_fatal(inarch->filename); + if (rename(new_name, inarch->filename) != 0) + bfd_fatal(inarch->filename); + } +} + + + +/* + returns a pointer to the pointer to the entry which should be rplacd'd + into when altering. default_pos should be how to interpret pos_default, + and should be a pos value. +*/ + +bfd ** +get_pos_bfd(contents, default_pos) + bfd **contents; + enum pos default_pos; +{ + bfd **after_bfd; + + enum pos realpos = (postype == pos_default ? default_pos : postype); + switch (realpos) { + + case pos_end: + after_bfd = contents; + while (*after_bfd) { + after_bfd = &((*after_bfd)->next); + } + + break; +#if 0 + case pos_after: + for (after_bfd = contents; after_bfd; after_bfd = after_bfd->next) + if (!strcpy(after_bfd->filename, posname)) + break; + break; + case pos_before: + for (after_bfd = contents; after_bfd; after_bfd = after_bfd->next) + if (after_bfd->next && (!strcpy(after_bfd->next->filename, posname))) + break; +#endif + } + + return after_bfd; +} + + +void +delete_members(files_to_delete) + char **files_to_delete; +{ + bfd **current_ptr_ptr; + boolean found; + boolean something_changed = false; + for (; *files_to_delete != NULL; ++files_to_delete) { + /* + In a.out systems, the armap is optional. It's also called + __.SYMDEF. So if the user asked to delete it, we should remember + that fact. The name is NULL in COFF archives, so using this as a + key is as good as anything I suppose + */ + if (!strcmp(*files_to_delete, "__.SYMDEF")) { + inarch->has_armap = false; + write_armap = false; + continue; + } + + found = false; + current_ptr_ptr = &(inarch->next); + while (*current_ptr_ptr) { + if (strcmp(*files_to_delete, (*current_ptr_ptr)->filename) == 0) { + found = true; + something_changed = true; + if (verbose) + printf("d - %s\n", + *files_to_delete); + *current_ptr_ptr = ((*current_ptr_ptr)->next); + goto next_file; + + } + else { + current_ptr_ptr = &((*current_ptr_ptr)->next); + } + } + + if (verbose && found == false) { + printf("No member named `%s'\n", *files_to_delete); + } +next_file:; + + } + + if (something_changed == true) { + write_archive(); + } +} + + +/* Reposition existing members within an archive */ + +void +move_members(files_to_move) + char **files_to_move; +{ + bfd **after_bfd; /* New entries go after this one */ + bfd **current_ptr_ptr; /* cdr pointer into contents */ + + + + + for (; *files_to_move; ++files_to_move) { + current_ptr_ptr = &(inarch->next); + while (*current_ptr_ptr) { + bfd *current_ptr = *current_ptr_ptr; + if (strcmp(normalize(*files_to_move), current_ptr->filename) == 0) { + /* + Move this file to the end of the list - first cut from + where it is. + */ + *current_ptr_ptr = current_ptr->next; + + /* Now glue to end */ + after_bfd = get_pos_bfd(&inarch->next, pos_end); + *after_bfd = current_ptr; + current_ptr->next = (bfd *) NULL; + + if (verbose) + printf("m - %s\n", *files_to_move); + + goto next_file; + } + current_ptr_ptr = &((*current_ptr_ptr)->next); + } + fprintf(stderr, "No entry %s in archive %s!\n", + *files_to_move, inarch->filename); + exit(1); +next_file:; + } + + write_archive(); +} + + +/* Ought to default to replacing in place, but this is existing practice! */ + +void +replace_members(files_to_move) + char **files_to_move; +{ + bfd **after_bfd; /* New entries go after this one */ + bfd *current; + bfd **current_ptr; + bfd *temp; + /* + If the first item in the archive is an __.SYMDEF then remove it + */ + if (inarch->next && + strcmp(inarch->next->filename, "__.SYMDEF") == 0) { + inarch->next = inarch->next->next; + } + + + + while (files_to_move && *files_to_move) { + current_ptr = &inarch->next; + while (*current_ptr) { + current = *current_ptr; + + if (!strcmp(normalize(*files_to_move), current->filename)) { + /* snip out this entry from the chain */ + *current_ptr = current->next; + if (newer_only) { + struct stat fsbuf, + asbuf; + if (stat(*files_to_move, &fsbuf) != 0) { + if (errno != ENOENT) + bfd_fatal(*files_to_move); + goto next_file; + } + if (bfd_stat_arch_elt(current, &asbuf) != 0) + fatal("Internal stat error on %s", current->filename); + + if (fsbuf.st_mtime <= asbuf.st_mtime) + goto next_file; + } + + + after_bfd = get_pos_bfd(&inarch->next, pos_end); + temp = *after_bfd; + *after_bfd = bfd_openr(*files_to_move, NULL); + if (*after_bfd == (bfd *) NULL) { + fprintf(stderr, "Can't open file %s\n", *files_to_move); + exit(1); + } + (*after_bfd)->next = temp; + + if (verbose) { + printf("%c - %s\n", (postype == pos_after ? 'r' : 'a'), + *files_to_move); + } + goto next_file; + } + current_ptr = &(current->next); + } + + /* It isn't in there, so add to end */ + + after_bfd = get_pos_bfd(&inarch->next, pos_end); + temp = *after_bfd; + *after_bfd = bfd_openr(*files_to_move, NULL); + if (*after_bfd == (bfd *) NULL) { + fprintf(stderr, "Can't open file %s\n", *files_to_move); + exit(1); + } + if (verbose) { + printf("c - %s\n", *files_to_move); + } + + (*after_bfd)->next = temp; + +next_file:; + + files_to_move++; + } + + + write_archive(); +} + +void +ranlib_only(archname) + char *archname; +{ + write_armap = true; + open_inarch(archname); + write_archive(); + exit(0); +} diff --git a/binutils/bucomm.c b/binutils/bucomm.c new file mode 100644 index 00000000000..00f379f2293 --- /dev/null +++ b/binutils/bucomm.c @@ -0,0 +1,151 @@ +/*** bucomm.c -- Bin Utils COMmon code. + + We might put this in a library someday so it could be dynamically + loaded, but for now it's not necessary */ + +#include "sysdep.h" +#include "bfd.h" +#include + +char *target = NULL; /* default as late as possible */ + +/* Yes, this is what atexit is for, but that isn't guaranteed yet. + And yes, I know this isn't as good, but it does what is needed just fine */ +void (*exit_handler) (); + +/** Memory hackery */ + +PROTO (char *, malloc, (unsigned size)); +PROTO (char *, realloc, (char *ptr, unsigned size)); + + +/* Error reporting */ + +char *program_name; + +void +bfd_fatal (string) + char *string; +{ + char *errmsg = bfd_errmsg (bfd_error); + + if (string) + fprintf (stderr, "%s: %s: %s\n", program_name, string, errmsg); + else + fprintf (stderr, "%s: %s\n", program_name, errmsg); + + if (NULL != exit_handler) (*exit_handler) (); + exit (1); +} + +#ifndef NO_STDARG +void +fatal (Format) + const char *Format; +{ + va_list args; + + va_start (args, Format); + vfprintf (stderr, Format, args); + va_end (args); + (void) putc ('\n', stderr); + if (NULL != exit_handler) (*exit_handler) (); + exit (1); +} +#else +#ifndef NO_VARARGS +void fatal (va_alist) + va_dcl +{ + char *Format; + va_list args; + + va_start (args); + Format = va_arg(args, char *); + vfprintf (stderr, Format, args); + va_end (args); + (void) putc ('\n', stderr); + if (NULL != exit_handler) (*exit_handler) (); + exit (1); +} /* fatal() */ +#else +/*VARARGS1 */ +fatal (Format, args) + char *Format; +{ + as_where (); + _doprnt (Format, &args, stderr); /* not terribly portable, but... */ + (void) putc ('\n', stderr); + if (NULL != exit_handler) (*exit_handler) (); + exit (1); +} +#endif /* not NO_VARARGS */ +#endif /* not NO_STDARG */ + + +/** Display the archive header for an element as if it were an ls -l listing */ + +/* Mode User\tGroup\tSize\tDate Name */ + +void +print_arelt_descr (abfd, verbose) + bfd *abfd; + boolean verbose; +{ + struct stat buf; + char modebuf[11]; + char timebuf[40]; + long when; + long current_time = time ((long *) 0); + + if (verbose) { + + if (bfd_stat_arch_elt (abfd, &buf) == 0) { /* if not, huh? */ + + mode_string (buf.st_mode, modebuf); + modebuf[10] = '\0'; + fputs (modebuf, stdout); + + when = buf.st_mtime; + strcpy (timebuf, ctime (&when)); + + /* This code comes from gnu ls. */ + if ((current_time - when > 6 * 30 * 24 * 60 * 60) + || (current_time - when < 0)) { + /* The file is fairly old or in the future. + POSIX says the cutoff is 6 months old; + approximate this by 6*30 days. + Show the year instead of the time of day. */ + strcpy (timebuf + 11, timebuf + 19); + } + timebuf[16] = 0; + + printf (" %d\t%d\t%ld\t%s ", buf.st_uid, buf.st_gid, buf.st_size, timebuf); + } + } + + puts (abfd->filename); +} + +/* Like malloc but get fatal error if memory is exhausted. */ +char * +xmalloc (size) + unsigned size; +{ + register char *result = malloc (size); + if (result == NULL && size != NULL) fatal ("virtual memory exhausted"); + + return result; +} + +/* Like realloc but get fatal error if memory is exhausted. */ +char * +xrealloc (ptr, size) + char *ptr; + unsigned size; +{ + register char *result = realloc (ptr, size); + if (result == 0 && size != 0) fatal ("virtual memory exhausted"); + + return result; +} diff --git a/binutils/filemode.c b/binutils/filemode.c new file mode 100644 index 00000000000..1bb5e642120 --- /dev/null +++ b/binutils/filemode.c @@ -0,0 +1,193 @@ +/* filemode.c -- make a string describing file modes + Copyright (C) 1985, 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 +#include + +void mode_string (); +static char ftypelet (); +static void rwx (); +static void setst (); + +/* filemodestring - fill in string STR with an ls-style ASCII + representation of the st_mode field of file stats block STATP. + 10 characters are stored in STR; no terminating null is added. + The characters stored in STR are: + + 0 File type. 'd' for directory, 'c' for character + special, 'b' for block special, 'm' for multiplex, + 'l' for symbolic link, 's' for socket, 'p' for fifo, + '-' for any other file type + + 1 'r' if the owner may read, '-' otherwise. + + 2 'w' if the owner may write, '-' otherwise. + + 3 'x' if the owner may execute, 's' if the file is + set-user-id, '-' otherwise. + 'S' if the file is set-user-id, but the execute + bit isn't set. + + 4 'r' if group members may read, '-' otherwise. + + 5 'w' if group members may write, '-' otherwise. + + 6 'x' if group members may execute, 's' if the file is + set-group-id, '-' otherwise. + 'S' if it is set-group-id but not executable. + + 7 'r' if any user may read, '-' otherwise. + + 8 'w' if any user may write, '-' otherwise. + + 9 'x' if any user may execute, 't' if the file is "sticky" + (will be retained in swap space after execution), '-' + otherwise. + 'T' if the file is sticky but not executable. */ + +void +filemodestring (statp, str) + struct stat *statp; + char *str; +{ + mode_string (statp->st_mode, str); +} + +/* Like filemodestring, but only the relevant part of the `struct stat' + is given as an argument. */ + +void +mode_string (mode, str) + unsigned short mode; + char *str; +{ + str[0] = ftypelet (mode); + rwx ((mode & 0700) << 0, &str[1]); + rwx ((mode & 0070) << 3, &str[4]); + rwx ((mode & 0007) << 6, &str[7]); + setst (mode, str); +} + +/* Return a character indicating the type of file described by + file mode BITS: + 'd' for directories + 'b' for block special files + 'c' for character special files + 'm' for multiplexor files + 'l' for symbolic links + 's' for sockets + 'p' for fifos + '-' for any other file type. */ + +static char +ftypelet (bits) + unsigned short bits; +{ + switch (bits & S_IFMT) + { + default: + return '-'; + case S_IFDIR: + return 'd'; +#ifdef S_IFLNK + case S_IFLNK: + return 'l'; +#endif +#ifdef S_IFCHR + case S_IFCHR: + return 'c'; +#endif +#ifdef S_IFBLK + case S_IFBLK: + return 'b'; +#endif +#ifdef S_IFMPC + case S_IFMPC: + case S_IFMPB: + return 'm'; +#endif +#ifdef S_IFSOCK + case S_IFSOCK: + return 's'; +#endif +#ifdef S_IFIFO +#if S_IFIFO != S_IFSOCK + case S_IFIFO: + return 'p'; +#endif +#endif +#ifdef S_IFNWK /* HP-UX */ + case S_IFNWK: + return 'n'; +#endif + } +} + +/* Look at read, write, and execute bits in BITS and set + flags in CHARS accordingly. */ + +static void +rwx (bits, chars) + unsigned short bits; + char *chars; +{ + chars[0] = (bits & S_IREAD) ? 'r' : '-'; + chars[1] = (bits & S_IWRITE) ? 'w' : '-'; + chars[2] = (bits & S_IEXEC) ? 'x' : '-'; +} + +/* Set the 's' and 't' flags in file attributes string CHARS, + according to the file mode BITS. */ + +static void +setst (bits, chars) + unsigned short bits; + char *chars; +{ +#ifdef S_ISUID + if (bits & S_ISUID) + { + if (chars[3] != 'x') + /* Set-uid, but not executable by owner. */ + chars[3] = 'S'; + else + chars[3] = 's'; + } +#endif +#ifdef S_ISGID + if (bits & S_ISGID) + { + if (chars[6] != 'x') + /* Set-gid, but not executable by group. */ + chars[6] = 'S'; + else + chars[6] = 's'; + } +#endif +#ifdef S_ISVTX + if (bits & S_ISVTX) + { + if (chars[9] != 'x') + /* Sticky, but not executable by others. */ + chars[9] = 'T'; + else + chars[9] = 't'; + } +#endif +} + + diff --git a/binutils/i960-pinsn.c b/binutils/i960-pinsn.c new file mode 100644 index 00000000000..1c7b719112b --- /dev/null +++ b/binutils/i960-pinsn.c @@ -0,0 +1,820 @@ +/* Disassemble i80960 instructions. + */ + +/* Copyright (C) 1990, 1991 Free Software Foundation, Inc. + +This file is part of BFD, the Binary File Diddler. + +BFD 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. + +BFD 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 BFD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ + $Log +*/ +#include +#include "sysdep.h" +#include "bfd.h" + +extern char *xmalloc(); +extern int fputs(); + +static char *reg_names[] = { +/* 0 */ "pfp", "sp", "rip", "r3", "r4", "r5", "r6", "r7", +/* 8 */ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", +/* 16 */ "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", +/* 24 */ "g8", "g9", "g10", "g11", "g12", "g13", "g14", "fp", +/* 32 */ "pc", "ac", "ip", "tc", "fp0", "fp1", "fp2", "fp3" +}; + + +static FILE *stream; /* Output goes here */ +static void print_addr(); +static void ctrl(); +static void cobr(); +static void reg(); +static int mem(); +static void ea(); +static void dstop(); +static void regop(); +static void invalid(); +static int pinsn(); +static void put_abs(); + + +/* Print the i960 instruction at address 'memaddr' in debugged memory, + * on stream 's'. Returns length of the instruction, in bytes. + */ +int +print_insn_i960( memaddr, buffer, s ) + bfd_vma memaddr; +uint8e_type *buffer; + FILE *s; +{ + unsigned int word1, word2; + + stream = s; + word1 =buffer [0] |( buffer[1]<< 8) | (buffer[2] << 16) | ( buffer[3] <<24); + word2 =buffer [4] |( buffer[5]<< 8) | (buffer[6] << 16) | ( buffer[7] <<24); + return pinsn( memaddr, word1, word2 ); +} + +#define IN_GDB + +/***************************************************************************** + * All code below this point should be identical with that of + * the disassembler in gdmp960. + *****************************************************************************/ + +struct tabent { + char *name; + char numops; +}; + +static int +pinsn( memaddr, word1, word2 ) + unsigned long memaddr; + unsigned long word1, word2; +{ + int instr_len; + + instr_len = 4; + put_abs( word1, word2 ); + + /* Divide instruction set into classes based on high 4 bits of opcode*/ + switch ( (word1 >> 28) & 0xf ){ + case 0x0: + case 0x1: + ctrl( memaddr, word1, word2 ); + break; + case 0x2: + case 0x3: + cobr( memaddr, word1, word2 ); + break; + case 0x5: + case 0x6: + case 0x7: + reg( word1 ); + break; + case 0x8: + case 0x9: + case 0xa: + case 0xb: + case 0xc: + instr_len = mem( memaddr, word1, word2, 0 ); + break; + default: + /* invalid instruction, print as data word */ + invalid( word1 ); + break; + } + return instr_len; +} + +/****************************************/ +/* CTRL format */ +/****************************************/ +static void +ctrl( memaddr, word1, word2 ) + unsigned long memaddr; + unsigned long word1, word2; +{ + int i; + static struct tabent ctrl_tab[] = { + NULL, 0, /* 0x00 */ + NULL, 0, /* 0x01 */ + NULL, 0, /* 0x02 */ + NULL, 0, /* 0x03 */ + NULL, 0, /* 0x04 */ + NULL, 0, /* 0x05 */ + NULL, 0, /* 0x06 */ + NULL, 0, /* 0x07 */ + "b", 1, /* 0x08 */ + "call", 1, /* 0x09 */ + "ret", 0, /* 0x0a */ + "bal", 1, /* 0x0b */ + NULL, 0, /* 0x0c */ + NULL, 0, /* 0x0d */ + NULL, 0, /* 0x0e */ + NULL, 0, /* 0x0f */ + "bno", 1, /* 0x10 */ + "bg", 1, /* 0x11 */ + "be", 1, /* 0x12 */ + "bge", 1, /* 0x13 */ + "bl", 1, /* 0x14 */ + "bne", 1, /* 0x15 */ + "ble", 1, /* 0x16 */ + "bo", 1, /* 0x17 */ + "faultno", 0, /* 0x18 */ + "faultg", 0, /* 0x19 */ + "faulte", 0, /* 0x1a */ + "faultge", 0, /* 0x1b */ + "faultl", 0, /* 0x1c */ + "faultne", 0, /* 0x1d */ + "faultle", 0, /* 0x1e */ + "faulto", 0, /* 0x1f */ + }; + + i = (word1 >> 24) & 0xff; + if ( (ctrl_tab[i].name == NULL) || ((word1 & 1) != 0) ){ + invalid( word1 ); + return; + } + + fputs( ctrl_tab[i].name, stream ); + if ( word1 & 2 ){ /* Predicts branch not taken */ + fputs( ".f", stream ); + } + + if ( ctrl_tab[i].numops == 1 ){ + /* EXTRACT DISPLACEMENT AND CONVERT TO ADDRESS */ + word1 &= 0x00ffffff; + if ( word1 & 0x00800000 ){ /* Sign bit is set */ + word1 |= (-1 & ~0xffffff); /* Sign extend */ + } + putc( '\t', stream ); + print_addr( word1 + memaddr ); + } +} + +/****************************************/ +/* COBR format */ +/****************************************/ +static void +cobr( memaddr, word1, word2 ) + unsigned long memaddr; + unsigned long word1, word2; +{ + int src1; + int src2; + int i; + + static struct tabent cobr_tab[] = { + "testno", 1, /* 0x20 */ + "testg", 1, /* 0x21 */ + "teste", 1, /* 0x22 */ + "testge", 1, /* 0x23 */ + "testl", 1, /* 0x24 */ + "testne", 1, /* 0x25 */ + "testle", 1, /* 0x26 */ + "testo", 1, /* 0x27 */ + NULL, 0, /* 0x28 */ + NULL, 0, /* 0x29 */ + NULL, 0, /* 0x2a */ + NULL, 0, /* 0x2b */ + NULL, 0, /* 0x2c */ + NULL, 0, /* 0x2d */ + NULL, 0, /* 0x2e */ + NULL, 0, /* 0x2f */ + "bbc", 3, /* 0x30 */ + "cmpobg", 3, /* 0x31 */ + "cmpobe", 3, /* 0x32 */ + "cmpobge", 3, /* 0x33 */ + "cmpobl", 3, /* 0x34 */ + "cmpobne", 3, /* 0x35 */ + "cmpoble", 3, /* 0x36 */ + "bbs", 3, /* 0x37 */ + "cmpibno", 3, /* 0x38 */ + "cmpibg", 3, /* 0x39 */ + "cmpibe", 3, /* 0x3a */ + "cmpibge", 3, /* 0x3b */ + "cmpibl", 3, /* 0x3c */ + "cmpibne", 3, /* 0x3d */ + "cmpible", 3, /* 0x3e */ + "cmpibo", 3, /* 0x3f */ + }; + + i = ((word1 >> 24) & 0xff) - 0x20; + if ( cobr_tab[i].name == NULL ){ + invalid( word1 ); + return; + } + + fputs( cobr_tab[i].name, stream ); + if ( word1 & 2 ){ /* Predicts branch not taken */ + fputs( ".f", stream ); + } + putc( '\t', stream ); + + src1 = (word1 >> 19) & 0x1f; + src2 = (word1 >> 14) & 0x1f; + + if ( word1 & 0x02000 ){ /* M1 is 1 */ + fprintf( stream, "%d", src1 ); + } else { /* M1 is 0 */ + fputs( reg_names[src1], stream ); + } + + if ( cobr_tab[i].numops > 1 ){ + if ( word1 & 1 ){ /* S2 is 1 */ + fprintf( stream, ",sf%d,", src2 ); + } else { /* S1 is 0 */ + fprintf( stream, ",%s,", reg_names[src2] ); + } + + /* Extract displacement and convert to address + */ + word1 &= 0x00001ffc; + if ( word1 & 0x00001000 ){ /* Negative displacement */ + word1 |= (-1 & ~0x1fff); /* Sign extend */ + } + print_addr( memaddr + word1 ); + } +} + +/****************************************/ +/* MEM format */ +/****************************************/ +static int /* returns instruction length: 4 or 8 */ +mem( memaddr, word1, word2, noprint ) + unsigned long memaddr; + unsigned long word1, word2; + int noprint; /* If TRUE, return instruction length, but + * don't output any text. + */ +{ + int i, j; + int len; + int mode; + int offset; + char *reg1, *reg2, *reg3; + + /* This lookup table is too sparse to make it worth typing in, but not + * so large as to make a sparse array necessary. We allocate the + * table at runtime, initialize all entries to empty, and copy the + * real ones in from an initialization table. + * + * NOTE: In this table, the meaning of 'numops' is: + * 1: single operand + * 2: 2 operands, load instruction + * -2: 2 operands, store instruction + */ + static struct tabent *mem_tab = NULL; + static struct { int opcode; char *name; char numops; } mem_init[] = { +#define MEM_MIN 0x80 + 0x80, "ldob", 2, + 0x82, "stob", -2, + 0x84, "bx", 1, + 0x85, "balx", 2, + 0x86, "callx", 1, + 0x88, "ldos", 2, + 0x8a, "stos", -2, + 0x8c, "lda", 2, + 0x90, "ld", 2, + 0x92, "st", -2, + 0x98, "ldl", 2, + 0x9a, "stl", -2, + 0xa0, "ldt", 2, + 0xa2, "stt", -2, + 0xb0, "ldq", 2, + 0xb2, "stq", -2, + 0xc0, "ldib", 2, + 0xc2, "stib", -2, + 0xc8, "ldis", 2, + 0xca, "stis", -2, +#define MEM_MAX 0xca +#define MEM_SIZ ((MEM_MAX-MEM_MIN+1) * sizeof(struct tabent)) + 0, NULL, 0 + }; + + if ( mem_tab == NULL ){ + mem_tab = (struct tabent *) xmalloc( MEM_SIZ ); + bzero( (void *) mem_tab, MEM_SIZ ); + for ( i = 0; mem_init[i].opcode != 0; i++ ){ + j = mem_init[i].opcode - MEM_MIN; + mem_tab[j].name = mem_init[i].name; + mem_tab[j].numops = mem_init[i].numops; + } + } + + i = ((word1 >> 24) & 0xff) - MEM_MIN; + mode = (word1 >> 10) & 0xf; + + if ( (mem_tab[i].name != NULL) /* Valid instruction */ + && ((mode == 5) || (mode >=12)) ){ /* With 32-bit displacement */ + len = 8; + } else { + len = 4; + } + + if ( noprint ){ + return len; + } + + if ( (mem_tab[i].name == NULL) || (mode == 6) ){ + invalid( word1 ); + return len; + } + + fprintf( stream, "%s\t", mem_tab[i].name ); + + reg1 = reg_names[ (word1 >> 19) & 0x1f ]; /* MEMB only */ + reg2 = reg_names[ (word1 >> 14) & 0x1f ]; + reg3 = reg_names[ word1 & 0x1f ]; /* MEMB only */ + offset = word1 & 0xfff; /* MEMA only */ + + switch ( mem_tab[i].numops ){ + + case 2: /* LOAD INSTRUCTION */ + if ( mode & 4 ){ /* MEMB FORMAT */ + ea( memaddr, mode, reg2, reg3, word1, word2 ); + fprintf( stream, ",%s", reg1 ); + } else { /* MEMA FORMAT */ + fprintf( stream, "0x%x", (unsigned) offset ); + if (mode & 8) { + fprintf( stream, "(%s)", reg2 ); + } + fprintf( stream, ",%s", reg1 ); + } + break; + + case -2: /* STORE INSTRUCTION */ + if ( mode & 4 ){ /* MEMB FORMAT */ + fprintf( stream, "%s,", reg1 ); + ea( memaddr, mode, reg2, reg3, word1, word2 ); + } else { /* MEMA FORMAT */ + fprintf( stream, "%s,0x%x", reg1, (unsigned) offset ); + if (mode & 8) { + fprintf( stream, "(%s)", reg2 ); + } + } + break; + + case 1: /* BX/CALLX INSTRUCTION */ + if ( mode & 4 ){ /* MEMB FORMAT */ + ea( memaddr, mode, reg2, reg3, word1, word2 ); + } else { /* MEMA FORMAT */ + fprintf( stream, "0x%x", (unsigned) offset ); + if (mode & 8) { + fprintf( stream, "(%s)", reg2 ); + } + } + break; + } + + return len; +} + +/****************************************/ +/* REG format */ +/****************************************/ +static void +reg( word1 ) + unsigned long word1; +{ + int i, j; + int opcode; + int fp; + int m1, m2, m3; + int s1, s2; + int src, src2, dst; + char *mnemp; + + /* This lookup table is too sparse to make it worth typing in, but not + * so large as to make a sparse array necessary. We allocate the + * table at runtime, initialize all entries to empty, and copy the + * real ones in from an initialization table. + * + * NOTE: In this table, the meaning of 'numops' is: + * 1: single operand, which is NOT a destination. + * -1: single operand, which IS a destination. + * 2: 2 operands, the 2nd of which is NOT a destination. + * -2: 2 operands, the 2nd of which IS a destination. + * 3: 3 operands + * + * If an opcode mnemonic begins with "F", it is a floating-point + * opcode (the "F" is not printed). + */ + + static struct tabent *reg_tab = NULL; + static struct { int opcode; char *name; char numops; } reg_init[] = { +#define REG_MIN 0x580 + 0x580, "notbit", 3, + 0x581, "and", 3, + 0x582, "andnot", 3, + 0x583, "setbit", 3, + 0x584, "notand", 3, + 0x586, "xor", 3, + 0x587, "or", 3, + 0x588, "nor", 3, + 0x589, "xnor", 3, + 0x58a, "not", -2, + 0x58b, "ornot", 3, + 0x58c, "clrbit", 3, + 0x58d, "notor", 3, + 0x58e, "nand", 3, + 0x58f, "alterbit", 3, + 0x590, "addo", 3, + 0x591, "addi", 3, + 0x592, "subo", 3, + 0x593, "subi", 3, + 0x598, "shro", 3, + 0x59a, "shrdi", 3, + 0x59b, "shri", 3, + 0x59c, "shlo", 3, + 0x59d, "rotate", 3, + 0x59e, "shli", 3, + 0x5a0, "cmpo", 2, + 0x5a1, "cmpi", 2, + 0x5a2, "concmpo", 2, + 0x5a3, "concmpi", 2, + 0x5a4, "cmpinco", 3, + 0x5a5, "cmpinci", 3, + 0x5a6, "cmpdeco", 3, + 0x5a7, "cmpdeci", 3, + 0x5ac, "scanbyte", 2, + 0x5ae, "chkbit", 2, + 0x5b0, "addc", 3, + 0x5b2, "subc", 3, + 0x5cc, "mov", -2, + 0x5d8, "eshro", 3, + 0x5dc, "movl", -2, + 0x5ec, "movt", -2, + 0x5fc, "movq", -2, + 0x600, "synmov", 2, + 0x601, "synmovl", 2, + 0x602, "synmovq", 2, + 0x603, "cmpstr", 3, + 0x604, "movqstr", 3, + 0x605, "movstr", 3, + 0x610, "atmod", 3, + 0x612, "atadd", 3, + 0x613, "inspacc", -2, + 0x614, "ldphy", -2, + 0x615, "synld", -2, + 0x617, "fill", 3, + 0x630, "sdma", 3, + 0x631, "udma", 0, + 0x640, "spanbit", -2, + 0x641, "scanbit", -2, + 0x642, "daddc", 3, + 0x643, "dsubc", 3, + 0x644, "dmovt", -2, + 0x645, "modac", 3, + 0x646, "condrec", -2, + 0x650, "modify", 3, + 0x651, "extract", 3, + 0x654, "modtc", 3, + 0x655, "modpc", 3, + 0x656, "receive", -2, + 0x659, "sysctl", 3, + 0x660, "calls", 1, + 0x662, "send", 3, + 0x663, "sendserv", 1, + 0x664, "resumprcs", 1, + 0x665, "schedprcs", 1, + 0x666, "saveprcs", 0, + 0x668, "condwait", 1, + 0x669, "wait", 1, + 0x66a, "signal", 1, + 0x66b, "mark", 0, + 0x66c, "fmark", 0, + 0x66d, "flushreg", 0, + 0x66f, "syncf", 0, + 0x670, "emul", 3, + 0x671, "ediv", 3, + 0x673, "ldtime", -1, + 0x674, "Fcvtir", -2, + 0x675, "Fcvtilr", -2, + 0x676, "Fscalerl", 3, + 0x677, "Fscaler", 3, + 0x680, "Fatanr", 3, + 0x681, "Flogepr", 3, + 0x682, "Flogr", 3, + 0x683, "Fremr", 3, + 0x684, "Fcmpor", 2, + 0x685, "Fcmpr", 2, + 0x688, "Fsqrtr", -2, + 0x689, "Fexpr", -2, + 0x68a, "Flogbnr", -2, + 0x68b, "Froundr", -2, + 0x68c, "Fsinr", -2, + 0x68d, "Fcosr", -2, + 0x68e, "Ftanr", -2, + 0x68f, "Fclassr", 1, + 0x690, "Fatanrl", 3, + 0x691, "Flogeprl", 3, + 0x692, "Flogrl", 3, + 0x693, "Fremrl", 3, + 0x694, "Fcmporl", 2, + 0x695, "Fcmprl", 2, + 0x698, "Fsqrtrl", -2, + 0x699, "Fexprl", -2, + 0x69a, "Flogbnrl", -2, + 0x69b, "Froundrl", -2, + 0x69c, "Fsinrl", -2, + 0x69d, "Fcosrl", -2, + 0x69e, "Ftanrl", -2, + 0x69f, "Fclassrl", 1, + 0x6c0, "Fcvtri", -2, + 0x6c1, "Fcvtril", -2, + 0x6c2, "Fcvtzri", -2, + 0x6c3, "Fcvtzril", -2, + 0x6c9, "Fmovr", -2, + 0x6d9, "Fmovrl", -2, + 0x6e1, "Fmovre", -2, + 0x6e2, "Fcpysre", 3, + 0x6e3, "Fcpyrsre", 3, + 0x701, "mulo", 3, + 0x708, "remo", 3, + 0x70b, "divo", 3, + 0x741, "muli", 3, + 0x748, "remi", 3, + 0x749, "modi", 3, + 0x74b, "divi", 3, + 0x78b, "Fdivr", 3, + 0x78c, "Fmulr", 3, + 0x78d, "Fsubr", 3, + 0x78f, "Faddr", 3, + 0x79b, "Fdivrl", 3, + 0x79c, "Fmulrl", 3, + 0x79d, "Fsubrl", 3, + 0x79f, "Faddrl", 3, +#define REG_MAX 0x79f +#define REG_SIZ ((REG_MAX-REG_MIN+1) * sizeof(struct tabent)) + 0, NULL, 0 + }; + + if ( reg_tab == NULL ){ + reg_tab = (struct tabent *) xmalloc( REG_SIZ ); + bzero( (void *) reg_tab, REG_SIZ ); + for ( i = 0; reg_init[i].opcode != 0; i++ ){ + j = reg_init[i].opcode - REG_MIN; + reg_tab[j].name = reg_init[i].name; + reg_tab[j].numops = reg_init[i].numops; + } + } + + opcode = ((word1 >> 20) & 0xff0) | ((word1 >> 7) & 0xf); + i = opcode - REG_MIN; + + if ( (opcodeREG_MAX) || (reg_tab[i].name==NULL) ){ + invalid( word1 ); + return; + } + + mnemp = reg_tab[i].name; + if ( *mnemp == 'F' ){ + fp = 1; + mnemp++; + } else { + fp = 0; + } + + fputs( mnemp, stream ); + + s1 = (word1 >> 5) & 1; + s2 = (word1 >> 6) & 1; + m1 = (word1 >> 11) & 1; + m2 = (word1 >> 12) & 1; + m3 = (word1 >> 13) & 1; + src = word1 & 0x1f; + src2 = (word1 >> 14) & 0x1f; + dst = (word1 >> 19) & 0x1f; + + if ( reg_tab[i].numops != 0 ){ + putc( '\t', stream ); + + switch ( reg_tab[i].numops ){ + case 1: + regop( m1, s1, src, fp ); + break; + case -1: + dstop( m3, dst, fp ); + break; + case 2: + regop( m1, s1, src, fp ); + putc( ',', stream ); + regop( m2, s2, src2, fp ); + break; + case -2: + regop( m1, s1, src, fp ); + putc( ',', stream ); + dstop( m3, dst, fp ); + break; + case 3: + regop( m1, s1, src, fp ); + putc( ',', stream ); + regop( m2, s2, src2, fp ); + putc( ',', stream ); + dstop( m3, dst, fp ); + break; + } + } +} + + +/* + * Print out effective address for memb instructions. + */ +static void +ea( memaddr, mode, reg2, reg3, word1, word2 ) + unsigned long memaddr; + int mode; + char *reg2, *reg3; +int word1; + unsigned int word2; +{ + int scale; + static int scale_tab[] = { 1, 2, 4, 8, 16 }; + + scale = (word1 >> 7) & 0x07; + if ( (scale > 4) || ((word1 >> 5) & 0x03 != 0) ){ + invalid( word1 ); + return; + } + scale = scale_tab[scale]; + + switch (mode) { + case 4: /* (reg) */ + fprintf( stream, "(%s)", reg2 ); + break; + case 5: /* displ+8(ip) */ + print_addr( word2+8+memaddr ); + break; + case 7: /* (reg)[index*scale] */ + if (scale == 1) { + fprintf( stream, "(%s)[%s]", reg2, reg3 ); + } else { + fprintf( stream, "(%s)[%s*%d]",reg2,reg3,scale); + } + break; + case 12: /* displacement */ + print_addr( word2 ); + break; + case 13: /* displ(reg) */ + print_addr( word2 ); + fprintf( stream, "(%s)", reg2 ); + break; + case 14: /* displ[index*scale] */ + print_addr( word2 ); + if (scale == 1) { + fprintf( stream, "[%s]", reg3 ); + } else { + fprintf( stream, "[%s*%d]", reg3, scale ); + } + break; + case 15: /* displ(reg)[index*scale] */ + print_addr( word2 ); + if (scale == 1) { + fprintf( stream, "(%s)[%s]", reg2, reg3 ); + } else { + fprintf( stream, "(%s)[%s*%d]",reg2,reg3,scale ); + } + break; + default: + invalid( word1 ); + return; + } +} + + +/************************************************/ +/* Register Instruction Operand */ +/************************************************/ +static void +regop( mode, spec, reg, fp ) + int mode, spec, reg, fp; +{ + if ( fp ){ /* FLOATING POINT INSTRUCTION */ + if ( mode == 1 ){ /* FP operand */ + switch ( reg ){ + case 0: fputs( "fp0", stream ); break; + case 1: fputs( "fp1", stream ); break; + case 2: fputs( "fp2", stream ); break; + case 3: fputs( "fp3", stream ); break; + case 16: fputs( "0f0.0", stream ); break; + case 22: fputs( "0f1.0", stream ); break; + default: putc( '?', stream ); break; + } + } else { /* Non-FP register */ + fputs( reg_names[reg], stream ); + } + } else { /* NOT FLOATING POINT */ + if ( mode == 1 ){ /* Literal */ + fprintf( stream, "%d", reg ); + } else { /* Register */ + if ( spec == 0 ){ + fputs( reg_names[reg], stream ); + } else { + fprintf( stream, "sf%d", reg ); + } + } + } +} + +/************************************************/ +/* Register Instruction Destination Operand */ +/************************************************/ +static void +dstop( mode, reg, fp ) + int mode, reg, fp; +{ + /* 'dst' operand can't be a literal. On non-FP instructions, register + * mode is assumed and "m3" acts as if were "s3"; on FP-instructions, + * sf registers are not allowed so m3 acts normally. + */ + if ( fp ){ + regop( mode, 0, reg, fp ); + } else { + regop( 0, mode, reg, fp ); + } +} + + +static void +invalid( word1 ) + int word1; +{ + fprintf( stream, ".word\t0x%08x", (unsigned) word1 ); +} + +static void +print_addr(a) +int a; +{ + fprintf( stream, "0x%x", (unsigned) a ); +} + +static void +put_abs( word1, word2 ) + unsigned long word1, word2; +{ +#ifdef IN_GDB + return; +#else + int len; + + switch ( (word1 >> 28) & 0xf ){ + case 0x8: + case 0x9: + case 0xa: + case 0xb: + case 0xc: + /* MEM format instruction */ + len = mem( 0, word1, word2, 1 ); + break; + default: + len = 4; + break; + } + + if ( len == 8 ){ + fprintf( stream, "%08x %08x\t", word1, word2 ); + } else { + fprintf( stream, "%08x \t", word1 ); + } +; + +#endif +} diff --git a/binutils/m68k-pinsn.c b/binutils/m68k-pinsn.c new file mode 100644 index 00000000000..284f3353199 --- /dev/null +++ b/binutils/m68k-pinsn.c @@ -0,0 +1,806 @@ +/* Print m68k instructions for objdump + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + + +This file is part of the binutils. + +The binutils are 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. + +The binutils are distributed in the hope that they 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 the binutils; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ + $Log$ + Revision 1.1 1991/03/21 21:26:45 gumby + Initial revision + + * Revision 1.1 1991/03/13 00:34:06 chrisb + * Initial revision + * + * Revision 1.4 1991/03/09 04:36:34 rich + * Modified Files: + * sparc-pinsn.c ostrip.c objdump.c m68k-pinsn.c i960-pinsn.c + * binutils.h + * + * Pulled sysdep.h out of bfd.h. + * + * Revision 1.3 1991/03/08 21:54:45 rich + * Modified Files: + * Makefile ar.c binutils.h bucomm.c copy.c cplus-dem.c getopt.c + * i960-pinsn.c m68k-pinsn.c nm.c objdump.c sparc-opcode.h + * sparc-pinsn.c strip.c + * + * Verifying Portland tree with steve's last changes. Also, some partial + * porting. + * + * Revision 1.2 1991/03/08 07:46:24 sac + * Added -l option to disassembly - prints line numbers too. + * + * Revision 1.1 1991/02/22 16:48:02 sac + * Initial revision + * +*/ + +#include +#include "sysdep.h" +#include "bfd.h" +#include "m68k-opcode.h" + +extern int fputs(); +extern void print_address(); + +/* 68k instructions are never longer than this many bytes. */ +#define MAXLEN 22 + +/* Number of elements in the opcode table. */ +#define NOPCODES (sizeof m68k_opcodes / sizeof m68k_opcodes[0]) + +extern char *reg_names[]; +char *fpcr_names[] = { "", "fpiar", "fpsr", "fpiar/fpsr", "fpcr", + "fpiar/fpcr", "fpsr/fpcr", "fpiar-fpcr"}; + +char *reg_names[] = {"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "a0", "a1", "a2", "a3", "a4", "a5", "fp", "sp", "ps", "pc"}; +static unsigned char *print_insn_arg (); +static unsigned char *print_indexed (); +static void print_base (); +static int fetch_arg (); + +#define NEXTBYTE(p) (p += 2, ((char *)p)[-1]) + +#define NEXTWORD(p) \ + (p += 2, ((((char *)p)[-2]) << 8) + p[-1]) + +#define NEXTLONG(p) \ + (p += 4, (((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1]) + +#define NEXTSINGLE(p) \ + (p += 4, *((float *)(p - 4))) + +#define NEXTDOUBLE(p) \ + (p += 8, *((double *)(p - 8))) + +#define NEXTEXTEND(p) \ + (p += 12, 0.0) /* Need a function to convert from extended to double + precision... */ + +#define NEXTPACKED(p) \ + (p += 12, 0.0) /* Need a function to convert from packed to double + precision. Actually, it's easier to print a + packed number than a double anyway, so maybe + there should be a special case to handle this... */ + +/* Print the m68k instruction at address MEMADDR in debugged memory, + on STREAM. Returns length of the instruction, in bytes. */ + +int +print_insn_m68k(addr, buffer, stream) + bfd_vma addr; +unsigned char *buffer; + FILE *stream; +{ + register unsigned int i; + register unsigned char *p; + register char *d; + register unsigned int bestmask; + int best; + + + + bestmask = 0; + best = -1; + for (i = 0; i < NOPCODES; i++) + { + register unsigned int opcode = m68k_opcodes[i].opcode; + register unsigned int match = m68k_opcodes[i].match; + if (((0xff & buffer[0] & (match >> 24)) == (0xff & (opcode >> 24))) + && ((0xff & buffer[1] & (match >> 16)) == (0xff & (opcode >> 16))) + && ((0xff & buffer[2] & (match >> 8)) == (0xff & (opcode >> 8))) + && ((0xff & buffer[3] & match) == (0xff & opcode))) + { + /* Don't use for printout the variants of divul and divsl + that have the same register number in two places. + The more general variants will match instead. */ + for (d = m68k_opcodes[i].args; *d; d += 2) + if (d[1] == 'D') + break; + + /* Don't use for printout the variants of most floating + point coprocessor instructions which use the same + register number in two places, as above. */ + if (*d == 0) + for (d = m68k_opcodes[i].args; *d; d += 2) + if (d[1] == 't') + break; + + if (*d == 0 && match > bestmask) + { + best = i; + bestmask = match; + } + } + } + + /* Handle undefined instructions. */ + if (best < 0) + { + fprintf (stream, "0%o", (unsigned) (buffer[0] << 8) + buffer[1]); + return 2; + } + + fprintf (stream, "%s", m68k_opcodes[best].name); + + /* Point at first word of argument data, + and at descriptor for first argument. */ + p = buffer + 2; + + /* Why do this this way? -MelloN */ + for (d = m68k_opcodes[best].args; *d; d += 2) + { + if (d[0] == '#') + { + if (d[1] == 'l' && p - buffer < 6) + p = buffer + 6; + else if (p - buffer < 4 && d[1] != 'C' && d[1] != '8' ) + p = buffer + 4; + } + if (d[1] >= '1' && d[1] <= '3' && p - buffer < 4) + p = buffer + 4; + if (d[1] >= '4' && d[1] <= '6' && p - buffer < 6) + p = buffer + 6; + if ((d[0] == 'L' || d[0] == 'l') && d[1] == 'w' && p - buffer < 4) + p = buffer + 4; + } + + d = m68k_opcodes[best].args; + + if (*d) + fputs (" ", stream); + + while (*d) + { + p = print_insn_arg (d, buffer, p, addr + p - buffer, stream); + d += 2; + if (*d && *(d - 2) != 'I' && *d != 'k') + fputs (",", stream); + } + return p - buffer; +} + +static unsigned char * +print_insn_arg (d, buffer, p, addr, stream) + char *d; + unsigned char *buffer; + register unsigned char *p; + bfd_vma addr; /* PC for this arg to be relative to */ + FILE *stream; +{ + register int val; + register int place = d[1]; + int regno; + register char *regname; + register unsigned char *p1; + register double flval; + int flt_p; + + switch (*d) + { + case 'C': + fprintf (stream, "ccr"); + break; + + case 'S': + fprintf (stream, "sr"); + break; + + case 'U': + fprintf (stream, "usp"); + break; + + case 'J': + { + static struct { char *name; int value; } names[] + = {{"sfc", 0x000}, {"dfc", 0x001}, {"cacr", 0x002}, + {"usp", 0x800}, {"vbr", 0x801}, {"caar", 0x802}, + {"msp", 0x803}, {"isp", 0x804}}; + + val = fetch_arg (buffer, place, 12); + for (regno = sizeof names / sizeof names[0] - 1; regno >= 0; regno--) + if (names[regno].value == val) + { + fprintf (stream, names[regno].name); + break; + } + if (regno < 0) + fprintf (stream, "%d", val); + } + break; + + case 'Q': + val = fetch_arg (buffer, place, 3); + if (val == 0) val = 8; + fprintf (stream, "#%d", val); + break; + + case 'M': + val = fetch_arg (buffer, place, 8); + if (val & 0x80) + val = val - 0x100; + fprintf (stream, "#%d", val); + break; + + case 'T': + val = fetch_arg (buffer, place, 4); + fprintf (stream, "#%d", val); + break; + + case 'D': + fprintf (stream, "%s", reg_names[fetch_arg (buffer, place, 3)]); + break; + + case 'A': + fprintf (stream, "%s", + reg_names[fetch_arg (buffer, place, 3) + 010]); + break; + + case 'R': + fprintf (stream, "%s", reg_names[fetch_arg (buffer, place, 4)]); + break; + + case 'F': + fprintf (stream, "fp%d", fetch_arg (buffer, place, 3)); + break; + + case 'O': + val = fetch_arg (buffer, place, 6); + if (val & 0x20) + fprintf (stream, "%s", reg_names [val & 7]); + else + fprintf (stream, "%d", val); + break; + + case '+': + fprintf (stream, "%s@+", + reg_names[fetch_arg (buffer, place, 3) + 8]); + break; + + case '-': + fprintf (stream, "%s@-", + reg_names[fetch_arg (buffer, place, 3) + 8]); + break; + + case 'k': + if (place == 'k') + fprintf (stream, "{%s}", reg_names[fetch_arg (buffer, place, 3)]); + else if (place == 'C') + { + val = fetch_arg (buffer, place, 7); + if ( val > 63 ) /* This is a signed constant. */ + val -= 128; + fprintf (stream, "{#%d}", val); + } + else + fprintf(stderr, "Invalid arg format in opcode table: \"%c%c\".", + *d, place); + break; + + case '#': + case '^': + p1 = buffer + (*d == '#' ? 2 : 4); + if (place == 's') + val = fetch_arg (buffer, place, 4); + else if (place == 'C') + val = fetch_arg (buffer, place, 7); + else if (place == '8') + val = fetch_arg (buffer, place, 3); + else if (place == '3') + val = fetch_arg (buffer, place, 8); + else if (place == 'b') + val = NEXTBYTE (p1); + else if (place == 'w') + val = NEXTWORD (p1); + else if (place == 'l') + val = NEXTLONG (p1); + else + fprintf(stderr, "Invalid arg format in opcode table: \"%c%c\".", + *d, place); + fprintf (stream, "#%d", val); + break; + + case 'B': + if (place == 'b') + val = NEXTBYTE (p); + else if (place == 'w') + val = NEXTWORD (p); + else if (place == 'l') + val = NEXTLONG (p); + else if (place == 'g') + { + val = ((char *)buffer)[1]; + if (val == 0) + val = NEXTWORD (p); + else if (val == -1) + val = NEXTLONG (p); + } + else if (place == 'c') + { + if (buffer[1] & 0x40) /* If bit six is one, long offset */ + val = NEXTLONG (p); + else + val = NEXTWORD (p); + } + else + fprintf(stderr, "Invalid arg format in opcode table: \"%c%c\".", + *d, place); + print_address (addr + val, stream); + break; + + case 'd': + val = NEXTWORD (p); + fprintf (stream, "%s@(%d)", + reg_names[fetch_arg (buffer, place, 3)], val); + break; + + case 's': + fprintf (stream, "%s", + fpcr_names[fetch_arg (buffer, place, 3)]); + break; + + case 'I': + val = fetch_arg (buffer, 'd', 3); /* Get coprocessor ID... */ + if (val != 1) /* Unusual coprocessor ID? */ + fprintf (stream, "(cpid=%d) ", val); + if (place == 'i') + p += 2; /* Skip coprocessor extended operands */ + break; + + case '*': + case '~': + case '%': + case ';': + case '@': + case '!': + case '$': + case '?': + case '/': + case '&': + + if (place == 'd') + { + val = fetch_arg (buffer, 'x', 6); + val = ((val & 7) << 3) + ((val >> 3) & 7); + } + else + val = fetch_arg (buffer, 's', 6); + + /* Get register number assuming address register. */ + regno = (val & 7) + 8; + regname = reg_names[regno]; + switch (val >> 3) + { + case 0: + fprintf (stream, "%s", reg_names[val]); + break; + + case 1: + fprintf (stream, "%s", regname); + break; + + case 2: + fprintf (stream, "%s@", regname); + break; + + case 3: + fprintf (stream, "%s@+", regname); + break; + + case 4: + fprintf (stream, "%s@-", regname); + break; + + case 5: + val = NEXTWORD (p); + fprintf (stream, "%s@(%d)", regname, val); + break; + + case 6: + p = print_indexed (regno, p, addr, stream); + break; + + case 7: + switch (val & 7) + { + case 0: + val = NEXTWORD (p); + fprintf (stream, "@#"); + print_address (val, stream); + break; + + case 1: + val = NEXTLONG (p); + fprintf (stream, "@#"); + print_address (val, stream); + break; + + case 2: + val = NEXTWORD (p); + print_address (addr + val, stream); + break; + + case 3: + p = print_indexed (-1, p, addr, stream); + break; + + case 4: + flt_p = 1; /* Assume it's a float... */ + switch( place ) + { + case 'b': + val = NEXTBYTE (p); + flt_p = 0; + break; + + case 'w': + val = NEXTWORD (p); + flt_p = 0; + break; + + case 'l': + val = NEXTLONG (p); + flt_p = 0; + break; + + case 'f': + flval = NEXTSINGLE(p); + break; + + case 'F': + flval = NEXTDOUBLE(p); + break; + + case 'x': + flval = NEXTEXTEND(p); + break; + + case 'p': + flval = NEXTPACKED(p); + break; + + default: + fprintf(stderr, "Invalid arg format in opcode table: \"%c%c\".", + *d, place); + } + if ( flt_p ) /* Print a float? */ + fprintf (stream, "#%g", flval); + else + fprintf (stream, "#%d", val); + break; + + default: + fprintf (stream, "", (unsigned) val); + } + } + break; + + case 'L': + case 'l': + if (place == 'w') + { + char doneany; + p1 = buffer + 2; + val = NEXTWORD (p1); + /* Move the pointer ahead if this point is farther ahead + than the last. */ + p = p1 > p ? p1 : p; + if (val == 0) + { + fputs ("#0", stream); + break; + } + if (*d == 'l') + { + register int newval = 0; + for (regno = 0; regno < 16; ++regno) + if (val & (0x8000 >> regno)) + newval |= 1 << regno; + val = newval; + } + val &= 0xffff; + doneany = 0; + for (regno = 0; regno < 16; ++regno) + if (val & (1 << regno)) + { + int first_regno; + if (doneany) + fputs ("/", stream); + doneany = 1; + fprintf (stream, "%s", reg_names[regno]); + first_regno = regno; + while (val & (1 << (regno + 1))) + ++regno; + if (regno > first_regno) + fprintf (stream, "-%s", reg_names[regno]); + } + } + else if (place == '3') + { + /* `fmovem' insn. */ + char doneany; + val = fetch_arg (buffer, place, 8); + if (val == 0) + { + fputs ("#0", stream); + break; + } + if (*d == 'l') + { + register int newval = 0; + for (regno = 0; regno < 8; ++regno) + if (val & (0x80 >> regno)) + newval |= 1 << regno; + val = newval; + } + val &= 0xff; + doneany = 0; + for (regno = 0; regno < 8; ++regno) + if (val & (1 << regno)) + { + int first_regno; + if (doneany) + fputs ("/", stream); + doneany = 1; + fprintf (stream, "fp%d", regno); + first_regno = regno; + while (val & (1 << (regno + 1))) + ++regno; + if (regno > first_regno) + fprintf (stream, "-fp%d", regno); + } + } + else + abort (); + break; + + default: + fprintf(stderr, "Invalid arg format in opcode table: \"%c\".", *d); + } + + return (unsigned char *) p; +} + +/* Fetch BITS bits from a position in the instruction specified by CODE. + CODE is a "place to put an argument", or 'x' for a destination + that is a general address (mode and register). + BUFFER contains the instruction. */ + +static int +fetch_arg (buffer, code, bits) + unsigned char *buffer; + char code; + int bits; +{ + register int val; + switch (code) + { + case 's': + val = buffer[1]; + break; + + case 'd': /* Destination, for register or quick. */ + val = (buffer[0] << 8) + buffer[1]; + val >>= 9; + break; + + case 'x': /* Destination, for general arg */ + val = (buffer[0] << 8) + buffer[1]; + val >>= 6; + break; + + case 'k': + val = (buffer[3] >> 4); + break; + + case 'C': + val = buffer[3]; + break; + + case '1': + val = (buffer[2] << 8) + buffer[3]; + val >>= 12; + break; + + case '2': + val = (buffer[2] << 8) + buffer[3]; + val >>= 6; + break; + + case '3': + case 'j': + val = (buffer[2] << 8) + buffer[3]; + break; + + case '4': + val = (buffer[4] << 8) + buffer[5]; + val >>= 12; + break; + + case '5': + val = (buffer[4] << 8) + buffer[5]; + val >>= 6; + break; + + case '6': + val = (buffer[4] << 8) + buffer[5]; + break; + + case '7': + val = (buffer[2] << 8) + buffer[3]; + val >>= 7; + break; + + case '8': + val = (buffer[2] << 8) + buffer[3]; + val >>= 10; + break; + + default: + abort (); + } + + switch (bits) + { + case 3: + return val & 7; + case 4: + return val & 017; + case 5: + return val & 037; + case 6: + return val & 077; + case 7: + return val & 0177; + case 8: + return val & 0377; + case 12: + return val & 07777; + default: + abort (); + return(0); + } +} /* fetch_arg() */ + +/* Print an indexed argument. The base register is BASEREG (-1 for pc). + P points to extension word, in buffer. + ADDR is the nominal core address of that extension word. */ + +static unsigned char * +print_indexed (basereg, p, addr, stream) + int basereg; + unsigned char *p; + FILE *stream; +bfd_vma addr; +{ + register int word; + static char *scales[] = {"", "*2", "*4", "*8"}; + register int base_disp; + register int outer_disp; + char buf[40]; + + word = NEXTWORD (p); + + /* Generate the text for the index register. + Where this will be output is not yet determined. */ + sprintf (buf, "[%s.%c%s]", + reg_names[(word >> 12) & 0xf], + (word & 0x800) ? 'l' : 'w', + scales[(word >> 9) & 3]); + + /* Handle the 68000 style of indexing. */ + + if ((word & 0x100) == 0) + { + print_base (basereg, + ((word & 0x80) ? word | 0xff00 : word & 0xff) + + ((basereg == -1) ? addr : 0), + stream); + fputs (buf, stream); + return p; + } + + /* Handle the generalized kind. */ + /* First, compute the displacement to add to the base register. */ + + if (word & 0200) + basereg = -2; + if (word & 0100) + buf[0] = 0; + base_disp = 0; + switch ((word >> 4) & 3) + { + case 2: + base_disp = NEXTWORD (p); + break; + case 3: + base_disp = NEXTLONG (p); + } + if (basereg == -1) + base_disp += addr; + + /* Handle single-level case (not indirect) */ + + if ((word & 7) == 0) + { + print_base (basereg, base_disp, stream); + fputs (buf, stream); + return p; + } + + /* Two level. Compute displacement to add after indirection. */ + + outer_disp = 0; + switch (word & 3) + { + case 2: + outer_disp = NEXTWORD (p); + break; + case 3: + outer_disp = NEXTLONG (p); + } + + fprintf (stream, "%d(", outer_disp); + print_base (basereg, base_disp, stream); + + /* If postindexed, print the closeparen before the index. */ + if (word & 4) + fprintf (stream, ")%s", buf); + /* If preindexed, print the closeparen after the index. */ + else + fprintf (stream, "%s)", buf); + + return p; +} + +/* Print a base register REGNO and displacement DISP, on STREAM. + REGNO = -1 for pc, -2 for none (suppressed). */ + +static void +print_base (regno, disp, stream) + int regno; + int disp; + FILE *stream; +{ + if (regno == -2) + fprintf (stream, "%d", disp); + else if (regno == -1) + fprintf (stream, "0x%x", (unsigned) disp); + else + fprintf (stream, "%d(%s)", disp, reg_names[regno]); +} diff --git a/binutils/nm.c b/binutils/nm.c new file mode 100644 index 00000000000..ac490f72596 --- /dev/null +++ b/binutils/nm.c @@ -0,0 +1,387 @@ +/*** nm.c -- Describe symbol table of a rel file. */ +#include "sysdep.h" +#include "bfd.h" +#include "getopt.h" +#include "stab.gnu.h" +#include + + + +PROTO(static boolean, display_file, (char *filename)); +PROTO(static boolean, do_one_rel_file, (bfd *file)); +PROTO(static unsigned int, filter_symbols, (bfd *file, asymbol **syms, + unsigned long symcount)); + +PROTO(static void, print_symbols, (bfd *file, asymbol **syms, + unsigned long symcount)); +extern PROTO(int, (*sorters[2][2]), (char *x, char *y)); +PROTO(static void, print_symdef_entry, (bfd * abfd)); + +/* Command options. */ + +int external_only = 0; /* print external symbols only */ +int file_on_each_line = 0; /* print file name on each line */ +int no_sort = 0; /* don't sort; print syms in order found */ +int print_debug_syms = 0; /* print debugger-only symbols too */ +int print_armap = 0; /* describe __.SYMDEF data in archive files. */ +int reverse_sort = 0; /* sort in downward(alpha or numeric) order */ +int sort_numerically = 0; /* sort in numeric rather than alpha order */ +int undefined_only = 0; /* print undefined symbols only */ + +boolean print_each_filename = false; /* Ick. Used in archives. */ + +/* IMPORT */ +extern char *program_name; +extern char *program_version; +extern char *target; + +struct option long_options[] = { + {"debug-syms", 0, &print_debug_syms, 1}, + {"extern-only", 0, &external_only, 1}, + {"no-sort", 0, &no_sort, 1}, + {"numeric-sort", 0, &sort_numerically, 1}, + {"print-armap", 0, &print_armap, 1}, + {"print-file-name", 0, &file_on_each_line, 1}, + {"reverse-sort", 0, &reverse_sort, 1}, + {"target", 2, NULL, NULL}, + {"undefined-only", 0, &undefined_only, 1}, + {0, 0, 0, 0} +}; + +/* Some error-reporting functions */ + +void +usage () +{ + fprintf(stderr, "nm %s\nUsage: %s [-agnoprsu] filename...\n", + program_version, program_name); + exit(0); +} + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; /* sez which option char */ + int ind = 0; /* used by getopt and ignored by us */ + extern int optind; /* steps thru options */ + + program_name = *argv; + + while ((c = getopt_long(argc, argv, "agnoprsu", long_options, &ind)) != EOF) { + switch (c) { + case 'a': print_debug_syms = 1; break; + case 'g': external_only = 1; break; + case 'n': sort_numerically = 1; break; + case 'o': file_on_each_line = 1; break; + case 'p': no_sort = 1; break; + case 'r': reverse_sort = 1; break; + case 's': print_armap = 1; break; + case 'u': undefined_only = 1; break; + + case 0: + if (!strcmp("target",(long_options[option_index]).name)) { + target = optarg; + } + + break; /* we've been given a long option */ + + default: + usage (); + } + } + + /* Strangely, for the shell you should return only a nonzero value + on sucess -- the inverse of the C sense. */ + + /* OK, all options now parsed. If no filename specified, do a.out. */ + if (optind == argc) return !display_file ("a.out"); + + /* We were given several filenames to do: */ + while (optind < argc) + if (!display_file (argv[optind++])) return 1; + + return 0; +} + +/** Display a file's stats */ + +/* goto here is marginally cleaner than the nested if syntax */ + +static boolean +display_file (filename) + char *filename; +{ + boolean retval = false; + bfd *file; + bfd *arfile = NULL; + + file = bfd_openr(filename, target); + if (file == NULL) { + bfd_fatal (filename); + } + + if (bfd_check_format(file, bfd_object)) { + retval = do_one_rel_file (file); + goto closer; + } + + if (!bfd_check_format (file, bfd_archive)) { + fprintf (stderr, "%s: %s: unknown format.\n", program_name, filename); + retval = false; + goto closer; + } + + printf("In archive %s:\n", filename); + if (print_armap) print_symdef_entry (file); + for (;;) { + arfile = bfd_openr_next_archived_file (file, arfile); + + if (arfile == NULL) { + if (bfd_error != no_more_archived_files) + bfd_fatal (filename); + goto closer; + } + + if (!bfd_check_format(arfile, bfd_object)) + printf("%s: not an object file\n", arfile->filename); + else { + printf ("\n%s:\n", arfile->filename); + if (!do_one_rel_file (arfile)) return false; + } + } + + closer: + if (bfd_close(file) == false) + bfd_fatal (filename); + + return retval; +} + + +static boolean +do_one_rel_file (abfd) + bfd *abfd; +{ + unsigned int storage; + asymbol **syms; + unsigned int symcount = 0; + + if (!(bfd_get_file_flags (abfd) & HAS_SYMS)) { + (void) printf ("No symbols in \"%s\".\n", bfd_get_filename (abfd)); + return true; + } + + + storage = get_symtab_upper_bound (abfd); + if (storage == 0) { + nosymz: + fprintf (stderr, "%s: Symflags set but there are none?\n", + bfd_get_filename (abfd)); + exit (1); + } + + syms = (asymbol **) xmalloc (storage); + + symcount = bfd_canonicalize_symtab (abfd, syms); + if (symcount == 0) goto nosymz; + + /* Discard the symbols we don't want to print. + It's OK to do this in place; we'll free the storage anyway + (after printing) */ + + symcount = filter_symbols (abfd, syms, symcount); + + if (!no_sort) + qsort((char *) syms, symcount, sizeof (asymbol *), + sorters[sort_numerically][reverse_sort]); + + if (print_each_filename && !file_on_each_line) + printf("\n%s:\n", bfd_get_filename(abfd)); + + print_symbols (abfd, syms, symcount); + free (syms); + return true; +} + +/* Symbol-sorting predicates */ +#define valueof(x) ((x)->section ? (x)->section->vma + (x)->value : (x)->value) +int +numeric_forward (x, y) + char *x; + char *y; +{ + + return (valueof(*(asymbol **)x) - valueof(*(asymbol **) y));; +} + +int +numeric_reverse (x, y) + char *x; + char *y; +{ + return (valueof(*(asymbol **)y) - valueof(*(asymbol **) x)); + +} + +int +non_numeric_forward (x, y) + char *x; + char *y; +{ + char *xn = (*(asymbol **) x)->name; + char *yn = (*(asymbol **) y)->name; + + return ((xn == NULL) ? ((yn == NULL) ? 0 : -1) : + ((yn == NULL) ? 1 : strcmp (xn, yn))); +} + +int +non_numeric_reverse (x, y) + char *x; + char *y; +{ + return -(non_numeric_forward (x, y)); +} + +int (*sorters[2][2])() = { + {non_numeric_forward, non_numeric_reverse}, + {numeric_forward, numeric_reverse}, +}; + + +/* Choose which symbol entries to print; + compact them downward to get rid of the rest. + Return the number of symbols to be printed. */ +static unsigned int +filter_symbols (abfd, syms, symcount) + bfd *abfd; + asymbol **syms; + unsigned long symcount; +{ + asymbol **from, **to; + unsigned int dst_count = 0; + unsigned int src_count; + for (from = to = syms, src_count = 0; src_count flags; + + if (undefined_only) { + keep = (flags & BSF_UNDEFINED); + } else if (external_only) { + keep = ((flags & BSF_GLOBAL) || (flags & BSF_UNDEFINED) || + (flags & BSF_FORT_COMM)); + } else { + keep = 1; + } + + if (!print_debug_syms && ((flags & BSF_DEBUGGING) != 0)) { + keep = 0; + } + + if (keep) { + to[dst_count++] = from[src_count]; + } + } + + return dst_count; +} + + +/* Return a lower-case character corresponding to the symbol class of sym */ +char +decode_symclass (sym) + asymbol *sym; +{ + flagword flags = sym->flags; + + if ((sym->value == 0) && (sym->section != NULL)) + /* Huh? All section names don't begin with "." */ + return (sym->section->name)[1]; + + if (flags & BSF_FORT_COMM) return 'C'; + if (flags & BSF_UNDEFINED) return 'U'; + if (flags & BSF_ABSOLUTE) return 'a'; + + + if ( (flags & BSF_GLOBAL) || (flags & BSF_LOCAL) ){ + if ( !strcmp(sym->section->name, ".text") ){ + return 't'; + } else if ( !strcmp(sym->section->name, ".data") ){ + return 'd'; + } else if ( !strcmp(sym->section->name, ".bss") ){ + return 'b'; + } else { + return 'o'; + } + } + + /* We don't have to handle these cases just yet, but we will soon: + N_SETV: 'v'; + N_SETA: 'l'; + N_SETT: 'x'; + N_SETD: 'z'; + N_SETB: 's'; + N_INDR: 'i'; + */ + + return '?'; +} + +static void +print_symbols (abfd, syms, symcount) + bfd *abfd; + asymbol **syms; + unsigned long symcount; +{ + asymbol **sym = syms, **end = syms + symcount; + char class; + + for (; sym < end; ++sym) { + if (file_on_each_line) printf("%s:", bfd_get_filename(abfd)); + + if (undefined_only) { + if ((*sym)->flags & BSF_UNDEFINED) + puts ((*sym)->name); + } + else { + asymbol *p = *sym; + if (p) { + class = decode_symclass (p); + + if (p->flags & BSF_GLOBAL) + class = toupper (class); + + if (p->value || ((p->flags & BSF_UNDEFINED) != BSF_UNDEFINED)) + printf ("%08lx ", (p->section ? p->value + p->section->vma : p->value)); + else fputs (" ", stdout); + + printf ("%c %s\n", class, p->name); + } + } + } +} + +static void +print_symdef_entry (abfd) + bfd * abfd; +{ + symindex idx = BFD_NO_MORE_SYMBOLS; + carsym *thesym; + boolean everprinted = false; + + for (idx = bfd_get_next_mapent (abfd, idx, &thesym); + idx != BFD_NO_MORE_SYMBOLS; + idx = bfd_get_next_mapent (abfd, idx, &thesym)) { + bfd *elt; + if (!everprinted) { + printf ("\nArchive index:\n"); + everprinted = true; + } + elt = bfd_get_elt_at_index (abfd, idx); + if (thesym->name != (char *)NULL) { + printf ("%s in %s\n", thesym->name, bfd_get_filename (elt)); +} + } +} diff --git a/binutils/objdump.c b/binutils/objdump.c new file mode 100644 index 00000000000..352adc3a5d3 --- /dev/null +++ b/binutils/objdump.c @@ -0,0 +1,714 @@ +/*** objdump.c -- dump information about an object file. */ + +/* Copyright (C) 1990, 1991 Free Software Foundation, Inc. + +This file is part of BFD, the Binary File Diddler. + +BFD 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. + +BFD 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 BFD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + $Id$ + $Log$ + Revision 1.1 1991/03/21 21:26:48 gumby + Initial revision + + * Revision 1.2 1991/03/15 18:34:14 rich + * foo + * + * Revision 1.1 1991/03/13 00:34:19 chrisb + * Initial revision + * + * Revision 1.9 1991/03/09 04:36:33 rich + * Modified Files: + * sparc-pinsn.c ostrip.c objdump.c m68k-pinsn.c i960-pinsn.c + * binutils.h + * + * Pulled sysdep.h out of bfd.h. + * + * Revision 1.8 1991/03/09 03:42:01 rich + * Modified Files: + * Makefile alloca.c ar.c i960-pinsn.c nm.c objdump.c ostrip.c + * strip.c + * + * Ports for intel960 group Portland. + * + * Revision 1.7 1991/03/08 21:54:47 rich + * Modified Files: + * Makefile ar.c binutils.h bucomm.c copy.c cplus-dem.c getopt.c + * i960-pinsn.c m68k-pinsn.c nm.c objdump.c sparc-opcode.h + * sparc-pinsn.c strip.c + * + * Verifying Portland tree with steve's last changes. Also, some partial + * porting. + * + * Revision 1.6 1991/03/08 07:46:26 sac + * Added -l option to disassembly - prints line numbers too. + * + * Revision 1.5 1991/03/07 21:50:24 sac + * More intelligent reloc printing + * + * Revision 1.4 1991/03/05 16:36:54 sac + * Fixed bug where empty symbols would print (null) on suns and crash elsewhere. + * +*/ +/* + * Until there is other documentation, refer to the manual page dump(1) in + * the system 5 program's reference manual + */ + +#include "sysdep.h" +#include "bfd.h" +#include "getopt.h" +#include +#include + +char *malloc(); +char *realloc(); +char *xmalloc(); + +char *default_target = NULL; /* default at runtime */ + +char *program_name = NULL; + +int dump_section_contents; /* -s */ +int dump_section_headers; /* -h */ +boolean dump_file_header; /* -f */ +int dump_symtab; /* -t */ +int dump_reloc_info; /* -r */ +int dump_ar_hdrs; /* -a */ +int with_line_numbers; /* -l */ +boolean disassemble; /* -d */ +char *only; + +PROTO (void, display_file, (char *filename, char *target)); +PROTO (void, dump_data, (bfd *abfd)); +PROTO (void, dump_relocs, (bfd *abfd)); +PROTO (void, dump_symbols, (bfd *abfd)); +PROTO (void, print_arelt_descr, (bfd *abfd, boolean verbose)); + + + + + + + +char *machine = (char *)NULL; + asymbol **syms; + asymbol **syms2; + + +unsigned int storage; + +unsigned int symcount = 0; + +void +usage () +{ + fprintf (stderr, + "usage: %s [-ahfdrtxsl] [-m machine] [-j section_name] obj ...\n", + program_name); + exit (1); +} + +static struct option long_options[] = + {{"syms", 0, &dump_symtab, 1}, + {"reloc", 0, &dump_reloc_info, 1}, + {"header", 0, &dump_section_headers, 1}, + {0, 0, 0, 0}}; + + + +static void +dump_headers(abfd) +bfd *abfd; +{ + asection *section; + for (section = abfd->sections; + section != (asection *) NULL; + section = section->next) + { + char *comma = ""; +#define PF(x,y) \ + if (section->flags & x) { printf("%s%s",comma,y); comma = ", "; } + + printf("SECTION %d [%s]\t: size %08x", + section->index, + section->name, +(unsigned) section->size); + printf(" vma %08lx align 2**%2u\n ", + section->vma, + section->alignment_power); + PF(SEC_ALLOC,"ALLOC"); + PF(SEC_LOAD,"LOAD"); + PF(SEC_RELOC,"RELOC"); + PF(SEC_BALIGN,"BALIGN"); + PF(SEC_READONLY,"READONLY"); + PF(SEC_CODE,"CODE"); + PF(SEC_DATA,"DATA"); + PF(SEC_ROM,"ROM"); + printf("\n"); +#undef PF + } +} + +static asymbol ** +slurp_symtab(abfd) +bfd *abfd; +{ + asymbol **sy; + if (!(bfd_get_file_flags (abfd) & HAS_SYMS)) { + (void) printf ("No symbols in \"%s\".\n", bfd_get_filename (abfd)); + return(NULL); + } + + storage = get_symtab_upper_bound (abfd); + if (storage) { + sy = (asymbol **) malloc (storage); + if (sy == NULL) { + fprintf (stderr, "%s: out of memory.\n", program_name); + exit (1); + } + } + symcount = bfd_canonicalize_symtab (abfd, sy); +return sy; +} +/* Sort symbols into value order */ +static int comp(ap,bp) +asymbol **ap; +asymbol **bp; +{ + asymbol *a = *ap; + asymbol *b = *bp; + int diff; + + if ( a->name== (char *)NULL || (a->flags &( BSF_DEBUGGING| BSF_UNDEFINED) )) + a->the_bfd = 0; + if ( b->name== (char *)NULL || (b->flags &( BSF_DEBUGGING|BSF_UNDEFINED))) + b->the_bfd =0; + + diff = a->the_bfd - b->the_bfd; + if (diff) { + return -diff; + } + diff = a->value - b->value; + if (diff) { + return diff; + } + return a->section - b->section; +} + +/* Print the supplied address symbolically if possible */ +void +print_address(vma, stream) +bfd_vma vma; +FILE *stream; +{ + /* Perform a binary search looking for the closest symbol to + the required value */ + + unsigned int min = 0; + unsigned int max = symcount; + + unsigned int thisplace = 1; + unsigned int oldthisplace ; + + int vardiff; + if (symcount == 0) + fprintf(stream,"%08lx", vma); + else { + while (true) { + oldthisplace = thisplace; + thisplace = (max + min )/2 ; + if (thisplace == oldthisplace) break; + + + vardiff = syms[thisplace]->value - vma; + + if (vardiff) { + if (vardiff > 0) { + max = thisplace; + } + else { + min = thisplace; + } + } + else { + /* Totally awesome! the exact right symbol */ + fprintf(stream,"%08lx (%s)", vma, syms[thisplace]->name); + return; + } + } + /* We've run out of places to look, print the symbol before this one */ + /* see if this or the symbol before describes this location the best */ + + if (thisplace != 0) { + if (syms[thisplace-1]->value - vma > + syms[thisplace]->value-vma) { + /* Previous symbol is in correct section and is closer */ + thisplace --; + } + } + + { + char *section_name; + asection *sec = syms[thisplace]->section; + if (sec) { + section_name = sec->name; + } + else { + section_name = "abs"; + } + } + if (syms[thisplace]->value > vma) { + fprintf(stream,"%08lx (%s-%lx)", vma, syms[thisplace]->name, + syms[thisplace]->value - vma); + + } + else { + fprintf(stream,"%08lx (%s+%lx)", vma, + syms[thisplace]->name, + vma - syms[thisplace]->value); + } + } +} + +void +disassemble_data(abfd) +bfd *abfd; +{ + bfd_byte *data = NULL; + unsigned int datasize = 0; + unsigned int i; + int (*print)() ; + int print_insn_m68k(); + int print_insn_i960(); + int print_insn_sparc(); + enum bfd_architecture a; + unsigned long m; + asection *section; + /* Replace symbol section relative values with abs values */ + + + for (i = 0; i < symcount; i++) { + if (syms[i]->section != (asection *)NULL) { + syms[i]->value += syms[i]->section->vma; + } + } + + /* We keep a copy of the symbols in the original order */ + syms2 = slurp_symtab(abfd); + + /* Sort the symbols into section and symbol order */ + (void) qsort(syms, symcount, sizeof(asymbol *), comp); + + /* Find the first useless symbol */ + { unsigned int i; + for (i =0; i < symcount; i++) { + if (syms[i]->the_bfd == 0) { + symcount =i; + break; + } + } + } + + + if (machine!= (char *)NULL) { + if (bfd_scan_arch_mach(machine, &a, &m) == false) { + fprintf(stderr,"%s: Can't use supplied machine %s\n", + program_name, + machine); + exit(1); + } + } + else { + a = bfd_get_architecture(abfd); + } + switch (a) { + + case bfd_arch_sparc: + print = print_insn_sparc; + break; + case bfd_arch_m68k: + print = print_insn_m68k; + break; + case bfd_arch_i960: + print = print_insn_i960; + break; + default: + fprintf(stderr,"%s: Can't disassemble for architecture %s\n", + program_name, + bfd_printable_arch_mach(bfd_get_architecture(abfd),0)); + exit(1); + } + + + for (section = abfd->sections; + section != (asection *)NULL; + section = section->next) { + + if (only == (char *)NULL || strcmp(only,section->name) == 0){ + printf("Disassembly of section %s:\n", section->name); + + if (section->size == 0) continue; + + data = (bfd_byte *)malloc(section->size); + + if (data == (bfd_byte *)NULL) { + fprintf (stderr, "%s: memory exhausted.\n", program_name); + exit (1); + } + datasize = section->size; + + + bfd_get_section_contents (abfd, section, data, 0, section->size); + + i = 0; + while ((size_t)i size) { + if (with_line_numbers) { + static prevline; + char *filename; + char *functionname; + int line; + bfd_find_nearest_line(abfd, + section, + syms, + section->vma + i, + &filename, + &functionname, + &line); + + if (filename && functionname && line && line != prevline) { + printf("%s:%d\n", filename, line); + prevline = line; + } + } + print_address(section->vma + i, stdout); + printf(" "); + + i += print(section->vma + i, + data + i, + stdout); + putchar ('\n') ; + } + + + + + free(data); + } + } +} + +void +display_bfd (abfd) + bfd *abfd; +{ + + if (!bfd_check_format (abfd, bfd_object)) { + fprintf (stderr,"%s: %s not an object file\n", program_name, + abfd->filename); + return; + } + printf ("\n%s: file format %s\n", abfd->filename, abfd->xvec->name); + if (dump_ar_hdrs) print_arelt_descr (abfd, true); + + if (dump_file_header) { + char *comma = ""; + + printf("architecture: %s, ", + bfd_printable_arch_mach (bfd_get_architecture (abfd), + bfd_get_machine (abfd))); + printf("flags 0x%08x:\n", abfd->flags); + +#define PF(x, y) if (abfd->flags & x) {printf("%s%s", comma, y); comma=", ";} + PF(HAS_RELOC, "HAS_RELOC"); + PF(EXEC_P, "EXEC_P"); + PF(HAS_LINENO, "HAS_LINENO"); + PF(HAS_DEBUG, "HAS_DEBUG"); + PF(HAS_SYMS, "HAS_SYMS"); + PF(HAS_LOCALS, "HAS_LOCALS"); + PF(DYNAMIC, "DYNAMIC"); + PF(WP_TEXT, "WP_TEXT"); + PF(D_PAGED, "D_PAGED"); + printf("\nstart address 0x%08lx", abfd->start_address); + } + printf("\n"); + + if (dump_section_headers) + dump_headers(abfd); + if (dump_symtab || dump_reloc_info || disassemble) { +syms = slurp_symtab(abfd); + } + if (dump_symtab) dump_symbols (abfd); + if (dump_reloc_info) dump_relocs(abfd); + if (dump_section_contents) dump_data (abfd); + if (disassemble) disassemble_data(abfd); +} + +void +display_file (filename, target) + char *filename; + char *target; +{ + bfd *file, *arfile = (bfd *) NULL; + + file = bfd_openr (filename, target); + if (file == NULL) { + bfd_perror (filename); + return; + } + + if (bfd_check_format (file, bfd_archive) == true) { + printf ("In archive %s:\n", bfd_get_filename (file)); + for(;;) { + bfd_error = no_error; + + arfile = bfd_openr_next_archived_file (file, arfile); + if (arfile == NULL) { + if (bfd_error != no_more_archived_files) + bfd_perror (bfd_get_filename(file)); + return; + } + + display_bfd (arfile); + /* Don't close the archive elements; we need them for next_archive */ + } + } + else + display_bfd(file); + + bfd_close(file); +} + +/* Actually display the various requested regions */ + + + + + + + + + + +void +dump_data (abfd) + bfd *abfd; +{ + asection *section; + bfd_byte *data ; + unsigned int datasize = 0; + size_t i; + + for (section = abfd->sections; section != NULL; section = + section->next) { + int onaline = 16; + + if (only == (char *)NULL || + strcmp(only,section->name) == 0){ + + + + printf("Contents of section %s:\n", section->name); + + if (section->size == 0) continue; + data = (bfd_byte *)malloc(section->size); + if (data == (bfd_byte *)NULL) { + fprintf (stderr, "%s: memory exhausted.\n", program_name); + exit (1); + } + datasize = section->size; + + + bfd_get_section_contents (abfd, section, data, 0, section->size); + + for (i= 0; i < section->size; i += onaline) { + size_t j; + printf(" %04lx ", i + section->vma); + for (j = i; j < i+ onaline; j++) { + if (j < section->size) + printf("%02x", (unsigned)(data[j])); + else + printf(" "); + if ((j & 3 ) == 3) printf(" "); + } + + printf(" "); + for (j = i; j < i+onaline ; j++) { + if (j >= section->size) + printf(" "); + else + printf("%c", isprint(data[j]) ?data[j] : '.'); + } + putchar ('\n'); + } + } + + free (data); + } +} + + + +/* Should perhaps share code and display with nm? */ +void +dump_symbols (abfd) + bfd *abfd; +{ + + unsigned int count; + asymbol **current = syms; + printf("SYMBOL TABLE:\n"); + + for (count = 0; count < symcount; count++) { + if ((*current)->the_bfd) { + bfd_print_symbol((*current)->the_bfd, + stdout, + *current, bfd_print_symbol_all_enum); + + printf("\n"); + } + current++; + } + printf("\n"); + printf("\n"); +} + + +void +dump_relocs(abfd) +bfd *abfd; +{ + arelent **relpp; + unsigned int relcount; + asection *a; + for (a = abfd->sections; a != (asection *)NULL; a = a->next) { + printf("RELOCATION RECORDS FOR [%s]:",a->name); + + if (get_reloc_upper_bound(abfd, a) == 0) { + printf(" (none)\n\n"); + } + else { + arelent **p; + + relpp = (arelent **) xmalloc( get_reloc_upper_bound(abfd,a) ); + relcount = bfd_canonicalize_reloc(abfd,a,relpp, syms); + if (relcount == 0) { + printf(" (none)\n\n"); + } + else { + printf("\n"); + printf("OFFSET TYPE VALUE \n"); + + for (p =relpp; *p != (arelent *)NULL; p++) { + arelent *q = *p; + char *sym_name; + char *section_name = q->section == (asection *)NULL ? "*abs" : + q->section->name; + if (q->sym_ptr_ptr && *q->sym_ptr_ptr) { + sym_name = (*(q->sym_ptr_ptr))->name ; + } + else { + sym_name = 0; + } + if (sym_name) { + printf("%08lx %-8s %s", + q->address, + q->howto->name, + sym_name); + } + else { + printf("%08lx %-8s [%s]", + q->address, + q->howto->name, + section_name); + } + if (q->addend) { + printf("+0x%lx(%ld)", q->addend, (long) q->addend); + } + printf("\n"); + } + printf("\n\n"); + free(relpp); + } + } + + } +} + + +/** main and like trivia */ +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + extern int optind; + extern char *optarg; + char *target = default_target; + boolean seenflag = false; + int ind = 0; + + program_name = *argv; + + while ((c = getopt_long (argc, argv, "b:m:dlfahrtxsj:", long_options, &ind)) + != EOF) { + seenflag = true; + switch (c) { + case 'm': + machine = optarg; + break; + case 'j': + only = optarg; + break; + case 'l': + with_line_numbers = 1; + break; + case 'b': + target = optarg; + break; + case 'f': + dump_file_header = true; + break; + case 'x': + dump_symtab = 1; + dump_reloc_info = 1; + dump_file_header = true; + dump_ar_hdrs = 1; + dump_section_headers = 1; + break; + case 0 : break; /* we've been given a long option */ + case 't': dump_symtab = 1; break; + case 'd': disassemble = true ; break; + case 's': dump_section_contents = 1; break; + case 'r': dump_reloc_info = 1; break; + case 'a': dump_ar_hdrs = 1; break; + case 'h': dump_section_headers = 1; break; + default: + usage (); + } + } + + if (seenflag == false) + usage (); + + if (optind == argc) + display_file ("a.out", target); + else + for (; optind < argc;) + display_file (argv[optind++], target); + return 0; +} diff --git a/binutils/size.c b/binutils/size.c new file mode 100644 index 00000000000..9f6800c9d33 --- /dev/null +++ b/binutils/size.c @@ -0,0 +1,320 @@ +/*** size.c -- report size of various sections of an executable file */ +/* Extensions/incompatibilities: + o - BSD output has filenames at the end. + o - BSD output can appear in different radicies. + o - SysV output has less redundant whitespace. Filename comes at end. + o - SysV output doesn't show VMA which is always the same as the PMA. + o - We also handle core files. + o - We also handle archives. + If you write shell scripts which manipulate this info then you may be + out of luck; there's no +predantic switch. +*/ +#include "sysdep.h" +#include "bfd.h" +#include "getopt.h" + + +#ifndef BSD_DEFAULT +#define BSD_DEFAULT 1 +#endif + +PROTO(void, display_file, (char *filename)); +PROTO(void, print_sizes, (bfd *file)); + +/* Various program options */ + +enum {decimal, octal, hex} radix = decimal; +int berkeley_format = BSD_DEFAULT; /* 0 means use AT&T-style output */ +int show_version = 0; +int show_help = 0; + +/* IMPORTS */ +extern char *program_version; +extern char *program_name; +extern char *target; + +/** main and like trivia */ + +void +usage () +{ + fprintf (stderr, "size %s\nUsage: %s -{dox}{AB}V files ...\n", + program_version, program_name); + fputs("\t+radix={8|10|16} -- select appropriate output radix.\n\ +\t-d -- output in decimal\n\ +\t-o -- output in octal\n\ +\t-x -- output in hex", stderr); + fputs("\t+format={Berkeley|SysV} -- select display format.\n\ +\t-A -- SysV(AT&T) format\n\ +\t-B -- BSD format", stderr); +#if BSD_DEFAULT + fputs("\t (Default is +format=Berkeley)", stderr); +#else + fputs("\t (Default is +format=SysV)", stderr); +#endif + fputs("\t-V, +version -- display program version, etc.\n\ +\t+help -- this message\n", stderr); + exit(1); +} + +struct option long_options[] = {{"radix", 1, 0, 0}, + {"format", 1, 0, 0}, + {"version", 0, &show_version, 1}, + {"target", 2, NULL, NULL}, + {"help", 0, &show_help, 1}, + {0, 0, 0, 0}}; + +int +main (argc, argv) + int argc; + char **argv; +{ + int temp; + int c; /* sez which option char */ + int option_index = 0; + extern int optind; /* steps thru options */ + program_name = *argv; + + while ((c = getopt_long(argc, argv, "ABVdox", long_options, + &option_index)) != EOF) + switch(c) { + case 0: + if (!strcmp("format",(long_options[option_index]).name)) { + switch(*optarg) { + case 'B': case 'b': berkeley_format = 1; break; + case 'S': case 's': berkeley_format = 0; break; + default: printf("Unknown option to +format: %s\n", optarg); + usage(); + } + break; + } + + if (!strcmp("target",(long_options[option_index]).name)) { + target = optarg; + break; + } + + if (!strcmp("radix",(long_options[option_index]).name)) { +#ifdef ANSI_LIBRARIES + temp = strtol(optarg, NULL, 10); +#else + temp = atol(optarg); +#endif + switch(temp) { + case 10: radix = decimal; break; + case 8: radix = octal; break; + case 16: radix = hex; break; + default: printf("Unknown radix: %s\n", optarg); + usage(); + } + } + break; + case 'A': berkeley_format = 0; break; + case 'B': berkeley_format = 1; break; + case 'V': show_version = 1; break; + case 'd': radix = decimal; break; + case 'x': radix = hex; break; + case 'o': radix = octal; break; + case '?': usage(); + } + + if (show_version) printf("%s version %s\n", program_name, program_version); + if (show_help) usage(); + + if (berkeley_format) +#if 0 /* intel doesn't like bss/stk b/c they don't gave core files */ + puts((radix == octal) ? "text\tdata\tbss/stk\toct\thex\tfilename" : + "text\tdata\tbss/stk\tdec\thex\tfilename"); +#else + puts((radix == octal) ? "text\tdata\tbss\toct\thex\tfilename" : + "text\tdata\tbss\tdec\thex\tfilename"); +#endif + if (optind == argc) + display_file ("a.out"); + else + for (; optind < argc;) + display_file (argv[optind++]); + + return 0; +} + +/** Display a file's stats */ + +void +display_bfd (abfd) + bfd *abfd; +{ + char *core_cmd; + + if (bfd_check_format(abfd, bfd_archive)) return; + + if (bfd_check_format(abfd, bfd_object)) { + print_sizes(abfd); + goto done; + } + + if (bfd_check_format(abfd, bfd_core)) { + print_sizes(abfd); + fputs(" (core file", stdout); + + core_cmd = bfd_core_file_failing_command(abfd); + if (core_cmd) printf(" invoked as %s", core_cmd); + + puts(")"); + goto done; + } + + printf("Unknown file format: %s.", bfd_get_filename(abfd)); + + done: + + + printf("\n"); + return; +} + +void +display_file(filename) + char *filename; +{ + bfd *file, *arfile = (bfd *) NULL; + + file = bfd_openr (filename, target); + if (file == NULL) { + bfd_perror (filename); + return; + } + + if (bfd_check_format(file, bfd_archive) == true) { + for(;;) { + + bfd_error = no_error; + + arfile = bfd_openr_next_archived_file (file, arfile); + if (arfile == NULL) { + if (bfd_error != no_more_archived_files) + bfd_perror (bfd_get_filename (file)); + return; + } + + display_bfd (arfile); + /* Don't close the archive elements; we need them for next_archive */ + } + } + else + display_bfd (file); + + bfd_close (file); +} + +/* This is what lexical functions are for */ +void +lprint_number (width, num) + int width, num; +{ + printf ((radix == decimal ? "%-*d\t" : + ((radix == octal) ? "%-*o\t" : "%-*x\t")), width, num); +} + +void +rprint_number(width, num) + int width, num; +{ + printf ((radix == decimal ? "%*d\t" : + ((radix == octal) ? "%*o\t" : "%*x\t")), width, num); +} + +static char *bss_section_name = ".bss"; +static char *data_section_name = ".data"; +static char *stack_section_name = ".stack"; +static char *text_section_name = ".text"; + +void print_berkeley_format(abfd) +bfd *abfd; +{ + sec_ptr bsssection = NULL; + sec_ptr datasection = NULL; + sec_ptr textsection = NULL; + unsigned long bsssize = 0; + unsigned long datasize = 0; + unsigned long textsize = 0; + unsigned long total = 0; + + + if ((textsection = bfd_get_section_by_name (abfd, text_section_name)) + != NULL) { + textsize = bfd_section_size (abfd, textsection); + } + + if ((datasection = bfd_get_section_by_name (abfd, data_section_name)) + != NULL) { + datasize = bfd_section_size(abfd, datasection); + } + + if (bfd_get_format (abfd) == bfd_object) { + if ((bsssection = bfd_get_section_by_name (abfd, bss_section_name)) + != NULL) { + bsssize = bfd_section_size(abfd, bsssection); + } + } else { + if ((bsssection = bfd_get_section_by_name (abfd, stack_section_name)) + != NULL) { + bsssize = bfd_section_size(abfd, bsssection); + } + } + + total = textsize + datasize + bsssize; + + lprint_number (7, textsize); + lprint_number (7, datasize); + lprint_number (7, bsssize); + printf (((radix == octal) ? "%-7o\t%-7x\t" : "%-7d\t%-7x\t"), total, total); + + fputs(bfd_get_filename(abfd), stdout); + if (abfd->my_archive) printf (" (ex %s)", abfd->my_archive->filename); +} + +/* I REALLY miss lexical functions! */ +int svi_total = 0; + +void +sysv_internal_printer(file, sec) + bfd *file; + sec_ptr sec; +{ + int size = bfd_section_size (file, sec); + + svi_total += size; + + printf ("%-12s", bfd_section_name(file, sec)); + rprint_number (8, size); + printf(" "); + rprint_number (8, bfd_section_vma(file, sec)); + printf ("\n"); +} + +void +print_sysv_format(file) + bfd *file; +{ + svi_total = 0; + + printf ("%s ", bfd_get_filename (file)); + if (file->my_archive) printf (" (ex %s)", file->my_archive->filename); + + puts(":\nsection\t\tsize\t addr"); + bfd_map_over_sections (file, sysv_internal_printer, NULL); + + printf("Total "); + rprint_number(8, svi_total); + printf("\n"); printf("\n"); +} + +void +print_sizes(file) + bfd *file; +{ + if (berkeley_format) + print_berkeley_format(file); + else print_sysv_format(file); +} diff --git a/binutils/sparc-pinsn.c b/binutils/sparc-pinsn.c new file mode 100644 index 00000000000..550722b8a79 --- /dev/null +++ b/binutils/sparc-pinsn.c @@ -0,0 +1,490 @@ +/* disassemble sparc instructions for objdump + Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc. + + +This file is part of the binutils. + +The binutils are 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. + +The binutils are distributed in the hope that they 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 the binutils; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ + $Log$ + Revision 1.1 1991/03/21 21:26:55 gumby + Initial revision + + * Revision 1.1 1991/03/13 00:34:40 chrisb + * Initial revision + * + * Revision 1.3 1991/03/09 04:36:31 rich + * Modified Files: + * sparc-pinsn.c ostrip.c objdump.c m68k-pinsn.c i960-pinsn.c + * binutils.h + * + * Pulled sysdep.h out of bfd.h. + * + * Revision 1.2 1991/03/08 21:54:53 rich + * Modified Files: + * Makefile ar.c binutils.h bucomm.c copy.c cplus-dem.c getopt.c + * i960-pinsn.c m68k-pinsn.c nm.c objdump.c sparc-opcode.h + * sparc-pinsn.c strip.c + * + * Verifying Portland tree with steve's last changes. Also, some partial + * porting. + * + * Revision 1.1 1991/02/22 16:48:04 sac + * Initial revision + * +*/ + +#include +#include "sysdep.h" +#include "bfd.h" +#include "sparc-opcode.h" + +extern int fputs(); +extern int print_address(); + +static char *reg_names[] = + { "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", + "o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7", + "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", + "i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", + "y", "psr", "wim", "tbr", "pc", "npc", "fpsr", "cpsr" }; + +#define freg_names (®_names[4 * 8]) + +union sparc_insn + { + unsigned long int code; + struct + { + unsigned int OP:2; +#define op ldst.OP + unsigned int RD:5; +#define rd ldst.RD + unsigned int op3:6; + unsigned int RS1:5; +#define rs1 ldst.RS1 + unsigned int i:1; + unsigned int ASI:8; +#define asi ldst.ASI + unsigned int RS2:5; +#define rs2 ldst.RS2 +#define shcnt rs2 + } ldst; + struct + { + unsigned int OP:2, RD:5, op3:6, RS1:5, i:1; + unsigned int IMM13:13; +#define imm13 IMM13.IMM13 + } IMM13; + struct + { + unsigned int OP:2; + unsigned int a:1; + unsigned int cond:4; + unsigned int op2:3; + unsigned int DISP22:22; +#define disp22 branch.DISP22 + } branch; +#define imm22 disp22 + struct + { + unsigned int OP:2; + unsigned int DISP30:30; +#define disp30 call.DISP30 + } call; + }; + +/* Nonzero if INSN is the opcode for a delayed branch. */ +static int +is_delayed_branch (insn) + union sparc_insn insn; +{ + unsigned int i; + + for (i = 0; i < NUMOPCODES; ++i) + { + const struct sparc_opcode *opcode = &sparc_opcodes[i]; + if ((opcode->match & insn.code) == opcode->match + && (opcode->lose & insn.code) == 0 + && (opcode->delayed)) + return 1; + } + return 0; +} + +static int opcodes_sorted = 0; + +/* Print one instruction from MEMADDR on STREAM. */ +int +print_insn_sparc (memaddr, buffer, stream) + bfd_vma memaddr; + bfd_byte *buffer; + FILE *stream; + +{ + union sparc_insn insn; + + register unsigned int i; + + if (!opcodes_sorted) + { + static int compare_opcodes (); + qsort ((char *) sparc_opcodes, NUMOPCODES, + sizeof (sparc_opcodes[0]), compare_opcodes); + opcodes_sorted = 1; + } + +memcpy(&insn,buffer, sizeof (insn)); + + for (i = 0; i < NUMOPCODES; ++i) + { + const struct sparc_opcode *opcode = &sparc_opcodes[i]; + if ((opcode->match & insn.code) == opcode->match + && (opcode->lose & insn.code) == 0) + { + /* Nonzero means that we have found an instruction which has + the effect of adding or or'ing the imm13 field to rs1. */ + int imm_added_to_rs1 = 0; + + /* Nonzero means that we have found a plus sign in the args + field of the opcode table. */ + int found_plus = 0; + + /* Do we have an 'or' instruction where rs1 is the same + as rsd, and which has the i bit set? */ + if (opcode->match == 0x80102000 + && insn.rs1 == insn.rd) + imm_added_to_rs1 = 1; + + if (index (opcode->args, 'S') != 0) + /* Reject the special case for `set'. + The real `sethi' will match. */ + continue; + if (insn.rs1 != insn.rd + && index (opcode->args, 'r') != 0) + /* Can't do simple format if source and dest are different. */ + continue; + + fputs (opcode->name, stream); + + { + register const char *s; + + if (opcode->args[0] != ',') + fputs (" ", stream); + for (s = opcode->args; *s != '\0'; ++s) + { + if (*s == ',') + { + fputs (",", stream); + ++s; + if (*s == 'a') + { + fputs ("a", stream); + ++s; + } + fputs (" ", stream); + } + + switch (*s) + { + case '+': + found_plus = 1; + + /* note fall-through */ + default: + fprintf (stream, "%c", *s); + break; + + case '#': + fputs ("0", stream); + break; + +#define reg(n) fprintf (stream, "%%%s", reg_names[n]) + case '1': + case 'r': + reg (insn.rs1); + break; + + case '2': + reg (insn.rs2); + break; + + case 'd': + reg (insn.rd); + break; +#undef reg + +#define freg(n) fprintf (stream, "%%%s", freg_names[n]) + case 'e': + freg (insn.rs1); + break; + + case 'f': + freg (insn.rs2); + break; + + case 'g': + freg (insn.rd); + break; +#undef freg + +#define creg(n) fprintf (stream, "%%c%u", (unsigned int) (n)) + case 'b': + creg (insn.rs1); + break; + + case 'c': + creg (insn.rs2); + break; + + case 'D': + creg (insn.rd); + break; +#undef creg + + case 'h': + fprintf (stream, "%%hi(%#x)", + (unsigned int) insn.imm22 << 10); + break; + + case 'i': + { + /* We cannot trust the compiler to sign-extend + when extracting the bitfield, hence the shifts. */ + int imm = ((int) insn.imm13 << 19) >> 19; + + /* Check to see whether we have a 1+i, and take + note of that fact. + + Note: because of the way we sort the table, + we will be matching 1+i rather than i+1, + so it is OK to assume that i is after +, + not before it. */ + if (found_plus) + imm_added_to_rs1 = 1; + + if (imm <= 9) + fprintf (stream, "%d", imm); + else + fprintf (stream, "%#x", (unsigned) imm); + } + break; + + case 'L': + print_address ((bfd_vma) memaddr + insn.disp30 * 4, + stream); + break; + + case 'l': + if ((insn.code >> 22) == 0) + /* Special case for `unimp'. Don't try to turn + it's operand into a function offset. */ + fprintf (stream, "%#x", + (unsigned) (((int) insn.disp22 << 10) >> 10)); + else + /* We cannot trust the compiler to sign-extend + when extracting the bitfield, hence the shifts. */ + print_address ((bfd_vma) + (memaddr + + (((int) insn.disp22 << 10) >> 10) * 4), + stream); + break; + + case 'A': + fprintf (stream, "(%d)", (int) insn.asi); + break; + + case 'C': + fputs ("%csr", stream); + break; + + case 'F': + fputs ("%fsr", stream); + break; + + case 'p': + fputs ("%psr", stream); + break; + + case 'q': + fputs ("%fq", stream); + break; + + case 'Q': + fputs ("%cq", stream); + break; + + case 't': + fputs ("%tbr", stream); + break; + + case 'w': + fputs ("%wim", stream); + break; + + case 'y': + fputs ("%y", stream); + break; + } + } + } + + /* If we are adding or or'ing something to rs1, then + check to see whether the previous instruction was + a sethi to the same register as in the sethi. + If so, attempt to print the result of the add or + or (in this context add and or do the same thing) + and its symbolic value. */ + if (imm_added_to_rs1) + { + union sparc_insn prev_insn; + int errcode; + + memcpy(&prev_insn, buffer -4, sizeof (prev_insn)); + + if (errcode == 0) + { + /* If it is a delayed branch, we need to look at the + instruction before the delayed branch. This handles + sequences such as + + sethi %o1, %hi(_foo), %o1 + call _printf + or %o1, %lo(_foo), %o1 + */ + + if (is_delayed_branch (prev_insn)) + memcpy(&prev_insn, buffer - 8, sizeof(prev_insn)); + + } + + /* If there was a problem reading memory, then assume + the previous instruction was not sethi. */ + if (errcode == 0) + { + /* Is it sethi to the same register? */ + if ((prev_insn.code & 0xc1c00000) == 0x01000000 + && prev_insn.rd == insn.rs1) + { + fprintf (stream, "\t! "); + /* We cannot trust the compiler to sign-extend + when extracting the bitfield, hence the shifts. */ + print_address (((int) prev_insn.imm22 << 10) + | (insn.imm13 << 19) >> 19, stream); + } + } + } + + return sizeof (insn); + } + } + + fprintf ("%#8x", insn.code); + return sizeof (insn); +} + + +/* Compare opcodes A and B. */ + +static int +compare_opcodes (a, b) + char *a, *b; +{ + struct sparc_opcode *op0 = (struct sparc_opcode *) a; + struct sparc_opcode *op1 = (struct sparc_opcode *) b; + unsigned long int match0 = op0->match, match1 = op1->match; + unsigned long int lose0 = op0->lose, lose1 = op1->lose; + register unsigned int i; + + /* If a bit is set in both match and lose, there is something + wrong with the opcode table. */ + if (match0 & lose0) + { + fprintf (stderr, "Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n", + op0->name, match0, lose0); + op0->lose &= ~op0->match; + lose0 = op0->lose; + } + + if (match1 & lose1) + { + fprintf (stderr, "Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n", + op1->name, match1, lose1); + op1->lose &= ~op1->match; + lose1 = op1->lose; + } + + /* Because the bits that are variable in one opcode are constant in + another, it is important to order the opcodes in the right order. */ + for (i = 0; i < 32; ++i) + { + unsigned long int x = 1 << i; + int x0 = (match0 & x) != 0; + int x1 = (match1 & x) != 0; + + if (x0 != x1) + return x1 - x0; + } + + for (i = 0; i < 32; ++i) + { + unsigned long int x = 1 << i; + int x0 = (lose0 & x) != 0; + int x1 = (lose1 & x) != 0; + + if (x0 != x1) + return x1 - x0; + } + + /* They are functionally equal. So as long as the opcode table is + valid, we can put whichever one first we want, on aesthetic grounds. */ + { + int length_diff = strlen (op0->args) - strlen (op1->args); + if (length_diff != 0) + /* Put the one with fewer arguments first. */ + return length_diff; + } + + /* Put 1+i before i+1. */ + { + char *p0 = (char *) index(op0->args, '+'); + char *p1 = (char *) index(op1->args, '+'); + + if (p0 && p1) + { + /* There is a plus in both operands. Note that a plus + sign cannot be the first character in args, + so the following [-1]'s are valid. */ + if (p0[-1] == 'i' && p1[1] == 'i') + /* op0 is i+1 and op1 is 1+i, so op1 goes first. */ + return 1; + if (p0[1] == 'i' && p1[-1] == 'i') + /* op0 is 1+i and op1 is i+1, so op0 goes first. */ + return -1; + } + } + + /* They are, as far as we can tell, identical. + Since qsort may have rearranged the table partially, there is + no way to tell which one was first in the opcode table as + written, so just say there are equal. */ + return 0; +} diff --git a/binutils/version.c b/binutils/version.c new file mode 100644 index 00000000000..d3e5a132f05 --- /dev/null +++ b/binutils/version.c @@ -0,0 +1,5 @@ +/*** version.c -- version number for binutils. + They all change in lockstep -- it's easier that way +*/ + +char *program_version = "1.10 (Cygnus BFD)"; diff --git a/ld/Makefile b/ld/Makefile new file mode 100755 index 00000000000..014b17fc4fd --- /dev/null +++ b/ld/Makefile @@ -0,0 +1,182 @@ +# +# Makefile for ld version 2 +# +# $Id$ +# +srcdir = . +BASEDIR = ../.. + +INCLUDE = $(srcdir)/$(BASEDIR)/include-cygnus +INCLUDES = -I$(srcdir) -I$(INCLUDE) +DEBUG = -g +CFLAGS = $(INCLUDES) $(DEBUG) + +# go directly to ld.new in case this ld isn't capable of +# linking native object on this host. It can be renamed on +# install. +PROGS = ld.new + +# for self hosting +GNUTARGET=a.out-generic-big +LDEMULATION=gld +bfdlib=$(srcdir)/$(BASEDIR)/bfd/$(HOST)/libbfd.a + +OBJS= ldgram.o ldlex.o ldlang.o ldmain.o ldwrite.o ldexp.o ld-lnk960.o \ + ld-gld.o ld-gld960.o ld-emul.o ldversion.o ldmisc.o ldsym.o ldfile.o + +HEADERS=config.h ldmain.h ldmain.h ldmisc.h ldsym.h ldlang.h ldexp.h \ + ldlex.h ldwrite.h ldversion.h ld-emul.h ldfile.h ldgram.h ld.h + +MANSOURCES=ld.tex + +LDCSOURCES=ldlang.c ldmain.c ldwrite.c ld-lnk960.c ld-gld.c \ + ld-gld960.c ld-emul.c ldversion.c ldmisc.c ldexp.c ldsym.c ldfile.c + +GENERATED_SOURCES=ldgram.tab.c ldlex.c +GENERATED_HEADERS=ldgram.tab.h + +LDSOURCES=$(LDCSOURCES) ldgram.y ldlex.l + +#BFDSOURCES=../bfd/libbfd.c ../bfd/bfd.c ../bfd/sunos.c ../bfd/icoff.c ../bfd/b.out.c ../bfd/archive.c ../bfd/srec.c + +SOURCES= $(LDSOURCES) $(BFDSOURCES) +LINTSOURCES= $(LDCSOURCES) $(BFDSOURCES) $(GENERATED_SOURCES) + +all: $(PROGS) + +$(PROGS): $(OBJS) +# (cd ../bfd; make) +# LDEMULATION=gld; export LDEMULATION; GNUTARGET=a.out-generic-big;./ldok -format a.out-generic-big -o ld /lib/crt0.o $(OBJS) $(bfdlib) -lc /usr/local/lib/gcc/sparc/1.91/gnulib +# gld -o ld /lib/crt0.o $(OBJS) $(bfdlib) -lc /usr/local/lib/gcc/sparc/1.91/gnulib + $(CC) -Bstatic -o ld.new $(OBJS) $(bfdlib) + + +ld1: ld + gcc -v -B./ -o ld1 $(OBJS) $(bfdlib) + +ld2: ld1 + mv ld1 ld + gcc -v -B./ -o ld2 $(OBJS) $(bfdlib) + +ld3: ld2 + mv ld2 ld + gcc -v -B./ -o ld3 $(OBJS) $(bfdlib) + +ld.dvi:ld.tex + tex ld.tex + +ldgram.o:ldgram.y + yacc -d ldgram.y + mv y.tab.c ldgram.tab.c + $(CC) -c $(CFLAGS) ldgram.tab.c + mv ldgram.tab.o ldgram.o + +ldgram.tab.h:ldgram.o + cp y.tab.h ldgram.tab.h + +ldlex.c: ldlex.l ldgram.tab.h +ldlex.o: ldlex.c ldgram.tab.h + +ldmain.o: ldmain.c +ldversion.o: ldversion.c +ldfile.o: ldfile.c +ldwrite.o: ldwrite.c +ldlang.o: ldlang.c ldgram.tab.h +ld-gld.o: ld-gld.c +ld-gld960.o: ld-gld960.c +ld-emul.o:ld-emul.c +ld-lnk960.o:ld-lnk960.c +ldexp.o:ldexp.c ldgram.tab.h +ldmisc.o:ldmisc.c +ldsym.o:ldsym.c + +clean: + - rm -f $(OBJS) $(GENERATED_SOURCES) $(GENERATED_HEADERS) + - rm -f ld ld1 ld2 + +lintlog:$(SOURCES) Makefile + $(LINT) -abhxzn $(CFLAGS) $(LINTSOURCES) \ +| grep -v "pointer casts may be troublesome" \ +| grep -v "possible pointer alignment problem" \ +| grep -v "ignore" \ +| grep -v "conversion from long may lose accuracy" \ +| grep -v "warning: constant argument to NOT" \ +| grep -v "enumeration type clash, operator CAST" \ +| grep -v "warning: constant in conditional context"\ +| grep -v "archive\.c" + + +tags TAGS:$(SOURCES) $(HEADERS) + etags -t $(SOURCES) $(HEADERS) + +release: + (cd /4/steve/ld; tar cf - $(LDSOURCES) $(HEADERS) $(MANSOURCES)) | tar xf - + +objdump:objdump.c + +install: $(PROGS) + rm -f $G960BASE/bin/$(PROGS) + cp $(PROGS) $$G960BASE/bin/gld960c + +#----------------------------------------------------------------------------- +# 'STANDARD' GNU/960 TARGETS BELOW THIS POINT +# +# 'VERSION' file must be present and contain a string of the form "x.y" +#----------------------------------------------------------------------------- + +ver960.c: FORCE + rm -f ver960.c + echo "char ${TARG}_ver[]= \"${TARG} `cat VERSION`, `date`\";" > ver960.c + + +# This target should be invoked before building a new release. +# 'VERSION' file must be present and contain a string of the form "x.y" +# +roll: + @V=`cat VERSION` ; \ + MAJ=`sed 's/\..*//' VERSION` ; \ + MIN=`sed 's/.*\.//' VERSION` ; \ + V=$$MAJ.`expr $$MIN + 1` ; \ + rm -f VERSION ; \ + echo $$V >VERSION ; \ + echo Version $$V + +# Dummy target to force execution of dependent targets. +# +.force: +FORCE: + +# Target to uncomment host-specific lines in this makefile. Such lines must +# have the following string beginning in column 1: #____# +# Original Makefile is backed up as 'Makefile.old'. +# +# Invoke with: make make HOST=xxx +# +make: + -@if test $(HOST)x = x ; then \ + echo 'Specify "make make HOST=???"'; \ + exit 1; \ + fi ; \ + grep -s "^#The next line was generated by 'make make'" Makefile; \ + if test $$? = 0 ; then \ + echo "Makefile has already been processed with 'make make'";\ + exit 1; \ + fi ; \ + mv -f Makefile Makefile.old; \ + echo "#The next line was generated by 'make make'" >Makefile ; \ + echo "HOST=$(HOST)" >>Makefile ; \ + echo >>Makefile ; \ + sed "s/^#__$(HOST)__#//" < Makefile.old >>Makefile + +# + +Makefile: ../common/Makefile + mv Makefile Makefile.backup + cp ../common/Makefile . + $(MAKE) "HOST=$(HOST)" make + +### Local Variables: *** +### mode:fundamental *** +### page-delimiter: "^# " *** +### End: *** +### end of file diff --git a/ld/ld-emul.c b/ld/ld-emul.c new file mode 100755 index 00000000000..7eb23d4f009 --- /dev/null +++ b/ld/ld-emul.c @@ -0,0 +1,144 @@ + + +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + $Id$ + + $Log$ + Revision 1.1 1991/03/21 21:28:19 gumby + Initial revision + + * Revision 1.1 1991/03/13 00:48:09 chrisb + * Initial revision + * + * Revision 1.4 1991/03/10 09:31:16 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.3 1991/02/22 17:14:55 sac + * Added RCS keywords and copyrights + * +*/ +/* + * clearing house for ld emulation states + */ + +#include "sysdep.h" +#include "bfd.h" + +#include "config.h" +#include "ld.h" +#include "ld-emul.h" +#include "ldmisc.h" + +extern ld_emulation_xfer_type ld_lnk960_emulation; +extern ld_emulation_xfer_type ld_gld_emulation; +extern ld_emulation_xfer_type ld_gld960_emulation; + + + +ld_emulation_xfer_type *ld_emulation; + +void +ldemul_hll(name) +char *name; +{ + ld_emulation->hll(name); +} + + +void ldemul_syslib(name) +char *name; +{ + ld_emulation->syslib(name); +} + +void +ldemul_after_parse() +{ + ld_emulation->after_parse(); +} + +void +ldemul_before_parse() +{ + ld_emulation->before_parse(); +} + +void +ldemul_after_allocation() +{ + ld_emulation->after_allocation(); +} + +void +ldemul_before_allocation() +{ + if (ld_emulation->before_allocation) { + ld_emulation->before_allocation(); + } +} + + +void +ldemul_set_output_arch() +{ + ld_emulation->set_output_arch(); +} + +char * +ldemul_choose_target() +{ + return ld_emulation->choose_target(); +} + +char * +ldemul_get_script() +{ + return ld_emulation->get_script(); +} + +void +ldemul_choose_mode(target) +char *target; +{ + if (strcmp(target,LNK960_EMULATION_NAME)==0) { + ld_emulation = &ld_lnk960_emulation; + } + else if (strcmp(target,GLD_EMULATION_NAME)==0) { + ld_emulation = &ld_gld_emulation; + } + else if (strcmp(target,GLD960_EMULATION_NAME)==0) { + ld_emulation = &ld_gld960_emulation; + } + else { + info("%P%F unrecognised emulation mode: %s",target); + } +} + + + diff --git a/ld/ld-gld.c b/ld/ld-gld.c new file mode 100755 index 00000000000..4c3df1ac6e3 --- /dev/null +++ b/ld/ld-gld.c @@ -0,0 +1,258 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:24 gumby + * Initial revision + * + * Revision 1.2 1991/03/15 18:45:55 rich + * foo + * + * Revision 1.1 1991/03/13 00:48:11 chrisb + * Initial revision + * + * Revision 1.7 1991/03/10 09:31:18 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.6 1991/03/09 03:23:12 sac + * Added -Ur loader script. + * + * Revision 1.5 1991/03/06 21:59:29 sac + * Completed G++ support + * + * Revision 1.4 1991/03/06 02:23:34 sac + * Added support for partial linking. + * + * Revision 1.3 1991/02/22 17:14:56 sac + * Added RCS keywords and copyrights + * +*/ + +/* + * emulate the original gld + * + * Written by Steve Chamberlain steve@cygnus.com + */ + + +#include "sysdep.h" +#include "bfd.h" + + +#include "ld.h" +#include "config.h" +#include "ld-emul.h" +#include "ldfile.h" +#include "ldmisc.h" + +extern boolean lang_float_flag; + + +extern enum bfd_architecture ldfile_output_architecture; +extern unsigned long ldfile_output_machine; +extern char *ldfile_output_machine_name; + +extern bfd *output_bfd; + + + +static void gld_before_parse() +{ + ldfile_add_library_path("/lib"); + ldfile_add_library_path("/usr/lib"); + ldfile_add_library_path("/usr/local/lib/lib"); + ldfile_output_architecture = bfd_arch_sparc; +} + + +static void +gld_after_parse() +{ + +} + +static void +gld_after_allocation() +{ + +} + +static void +gld_before_allocation() +{ + +} + + +static void +gld_set_output_arch() +{ + /* Set the output architecture and machine if possible */ + unsigned long machine = 0; + bfd_set_arch_mach(output_bfd, ldfile_output_architecture, machine); +} + +static char * +gld_choose_target() +{ + char *from_outside = getenv(TARGET_ENVIRON); + if (from_outside != (char *)NULL) + return from_outside; + return GLD_TARGET; +} + +static void +gld_syslib() +{ + info("%S SYSLIB ignored\n"); +} + +static void +gld_hll(ignore) +char *ignore; +{ + info("%S HLL ignored\n"); +} + +static char *gld_script = " \ +SEARCH_DIR(/lib) \ +SEARCH_DIR(/usr/lib) \ +SEARCH_DIR(/usr/local/lib) \ +__DYNAMIC = 0; \ +SECTIONS \ +{ \ + .text 0x2020 BLOCK(0x2000): \ + { \ + CREATE_OBJECT_SYMBOLS \ + *(.text) \ + _etext = ALIGN( 0x2000); \ + } \ + .data ALIGN(0x2000) : \ + { \ + *(.data) \ + ___DTOR_LIST__=. ; \ + LONG((___CTOR_LIST__ - .)/4 -2) \ + *(___DTOR_LIST__) \ + LONG(0) \ + ___CTOR_LIST__=. ; \ + LONG((_edata - .)/4 -2) \ + *(___CTOR_LIST__) \ + LONG(0) \ + _edata = .; \ + } \ + .bss SIZEOF(.data) + ADDR(.data) : \ + { \ + *(.bss) \ + [COMMON] \ + _end=.; \ + } \ +}"; + + +static char *gld_script_option_Ur = "\ +SEARCH_DIR(/lib) \ +SEARCH_DIR(/usr/lib) \ +SEARCH_DIR(/usr/local/lib) \ +SECTIONS \ +{ \ + .text 0: \ + { \ + CREATE_OBJECT_SYMBOLS \ + *(.text) \ + } \ + .data SIZEOF(.text) + ADDR(.text) : \ + { \ + *(.data) \ + ___DTOR_LIST__=. ; \ + LONG((___CTOR_LIST__ - .)/4 -2) \ + *(___DTOR_LIST__) \ + LONG(0) \ + ___CTOR_LIST__=. ; \ + LONG((___end_list__ - .)/4 -2) \ + *(___CTOR_LIST__) \ + LONG(0) \ + ___end_list__ = . ; \ + } \ + .bss SIZEOF(.data) + ADDR(.data) : \ + { \ + *(.bss) \ + [COMMON] \ + } \ +} \ +"; + +static char *gld_script_option_r = "\ +SEARCH_DIR(/lib) \ +SEARCH_DIR(/usr/lib) \ +SEARCH_DIR(/usr/local/lib) \ +SECTIONS \ +{ \ + .text 0: \ + { \ + CREATE_OBJECT_SYMBOLS \ + *(.text) \ + } \ + .data SIZEOF(.text) + ADDR(.text) : \ + { \ + *(.data) \ + } \ + .bss SIZEOF(.data) + ADDR(.data) : \ + { \ + *(.bss) \ + [COMMON] \ + } \ +} \ +"; + +static char *gld_get_script() +{ + extern ld_config_type config; + if (config.relocateable_output == true && + config.build_constructors == true) { + return gld_script_option_Ur; + } + if (config.relocateable_output) { + return gld_script_option_r; + } + + return gld_script; +} +struct ld_emulation_xfer_struct ld_gld_emulation = +{ + gld_before_parse, + gld_syslib, + gld_hll, + gld_after_parse, + gld_after_allocation, + gld_set_output_arch, + gld_choose_target, + gld_before_allocation, + gld_get_script, +}; + diff --git a/ld/ld-gld960.c b/ld/ld-gld960.c new file mode 100755 index 00000000000..5e0c1a28f99 --- /dev/null +++ b/ld/ld-gld960.c @@ -0,0 +1,189 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + $Id$ + + $Log$ + Revision 1.1 1991/03/21 21:28:26 gumby + Initial revision + + * Revision 1.3 1991/03/16 22:27:24 rich + * fish + * + * Revision 1.2 1991/03/15 18:45:55 rich + * foo + * + * Revision 1.1 1991/03/13 00:48:12 chrisb + * Initial revision + * + * Revision 1.4 1991/03/10 09:31:19 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.3 1991/02/22 17:14:57 sac + * Added RCS keywords and copyrights + * +*/ + +/* + * emulate the Intels port of gld + */ + + +#include "sysdep.h" +#include "bfd.h" + + +#include "ld.h" +#include "config.h" +#include "ld-emul.h" +#include "ldfile.h" +#include "ldmisc.h" + + +/* IMPORTS */ +extern char *output_filename; +extern boolean lang_float_flag; + + +extern enum bfd_architecture ldfile_output_architecture; +extern unsigned long ldfile_output_machine; +extern char *ldfile_output_machine_name; + +extern bfd *output_bfd; + + + +static void gld960_before_parse() +{ + char *env ; + env = getenv("G960LIB"); + if (env) { + ldfile_add_library_path(env); + } + env = getenv("G960BASE"); + if (env) { + ldfile_add_library_path(concat(env,"/lib","")); + } + ldfile_output_architecture = bfd_arch_i960; +} + + +static void +gld960_after_parse() +{ + +} + +static void +gld960_after_allocation() +{ + +} + +static void +gld960_before_allocation() +{ + +} + + +static void +gld960_set_output_arch() +{ + /* Set the output architecture and machine if possible */ + unsigned long machine = 0; + bfd_set_arch_mach(output_bfd, ldfile_output_architecture, machine); +} + +static char * +gld960_choose_target() +{ + char *from_outside = getenv(TARGET_ENVIRON); + output_filename = "b.out"; + + if (from_outside != (char *)NULL) + return from_outside; + return GLD960_TARGET; +} + +static void +gld960_syslib() +{ + info("%S SYSLIB ignored\n"); +} + +static void +gld960_hll() +{ + info("%S HLL ignored\n"); +} + + +static char *script = "\ + \ +SECTIONS \ +{ \ + .text : \ + { \ + CREATE_OBJECT_SYMBOLS \ + *(.text) \ + _etext =.;\ + } \ + \ + .data SIZEOF(.text) + ADDR(.text):\ + { \ + \ + *(.data) \ + _edata = .; \ + } \ + .bss SIZEOF(.data) + ADDR(.data) : \ + { _bss_start = .;\ + *(.bss) \ + [COMMON] \ + _end = . ; \ + } \ +} \ +"; + +static char * +gld960_get_script() +{ +return script; +} + +struct ld_emulation_xfer_struct ld_gld960_emulation = +{ + gld960_before_parse, + gld960_syslib, + gld960_hll, + gld960_after_parse, + gld960_after_allocation, + gld960_set_output_arch, + gld960_choose_target, + gld960_before_allocation, + gld960_get_script, +}; diff --git a/ld/ld-lnk960.c b/ld/ld-lnk960.c new file mode 100755 index 00000000000..0f82ebe22c0 --- /dev/null +++ b/ld/ld-lnk960.c @@ -0,0 +1,321 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + $Id$ + + $Log$ + Revision 1.1 1991/03/21 21:28:28 gumby + Initial revision + + * Revision 1.2 1991/03/15 18:45:55 rich + * foo + * + * Revision 1.1 1991/03/13 00:48:13 chrisb + * Initial revision + * + * Revision 1.6 1991/03/10 09:31:20 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.5 1991/03/09 03:23:47 sac + * Now looks in G960BASE if I960BASE isn't defined. + * + * Revision 1.4 1991/03/06 02:23:35 sac + * Added support for partial linking. + * + * Revision 1.3 1991/02/22 17:14:58 sac + * Added RCS keywords and copyrights + * +*/ + +/* + + Written by Steve Chamberlain steve@cygnus.com + + * intel coff loader emulation specific stuff + */ + +#include "sysdep.h" +#include "bfd.h" + +/*#include "archures.h"*/ +#include "ld.h" +#include "config.h" +#include "ld-emul.h" +#include "ldmisc.h" +#include "ldlang.h" +#include "ldfile.h" + +extern boolean lang_float_flag; +extern bfd *output_bfd; + + + +extern enum bfd_architecture ldfile_output_architecture; +extern unsigned long ldfile_output_machine; +extern char *ldfile_output_machine_name; + + +typedef struct lib_list { + char *name; + struct lib_list *next; +} lib_list_type; + +static lib_list_type *hll_list; +static lib_list_type **hll_list_tail = &hll_list; + +static lib_list_type *syslib_list; +static lib_list_type **syslib_list_tail = &syslib_list; + + +static void +append(list, name) +lib_list_type ***list; +char *name; +{ + lib_list_type *element = + (lib_list_type *)(ldmalloc(sizeof(lib_list_type))); + + element->name = name; + element->next = (lib_list_type *)NULL; + **list = element; + *list = &element->next; + +} + +static boolean had_hll = false; +static boolean had_hll_name = false; +static void +lnk960_hll(name) +char *name; +{ + had_hll = true; + if (name != (char *)NULL) { + had_hll_name = true; + append(&hll_list_tail, name); + } +} + +static void +lnk960_syslib(name) +char *name; +{ + append(&syslib_list_tail,name); +} + + + +static void +lnk960_before_parse() +{ + char *name = getenv("I960BASE"); + + if (name == (char *)NULL) { + name = getenv("G960BASE"); + if (name == (char *)NULL) { + info("%P%F I960BASE and G960BASE not set\n"); + } + } + + + ldfile_add_library_path(concat(name,"/lib","")); + ldfile_output_architecture = bfd_arch_i960; + ldfile_output_machine = bfd_mach_i960_core; +} + +static void +add_on(list, search) +lib_list_type *list; +lang_input_file_enum_type search; +{ + while (list) { + lang_add_input_file(list->name, + search, + (char *)NULL); + list = list->next; + } +} +static void lnk960_after_parse() +{ + + /* If there has been no arch, default to -KB */ + if (ldfile_output_machine_name[0] ==0) { + ldfile_add_arch("kb"); + } + + /* if there has been no hll list then add our own */ + + if(had_hll && !had_hll_name) { + append(&hll_list_tail,"c"); + if (lang_float_flag == true) { + append(&hll_list_tail,"m"); + } + else { + append(&hll_list_tail,"mstub"); + } + if (ldfile_output_machine == bfd_mach_i960_ka_sa || + ldfile_output_machine == bfd_mach_i960_ca) { + { + append(&hll_list_tail,"f"); + } + } + } + + + + add_on(hll_list, lang_input_file_is_l_enum); + add_on(syslib_list, lang_input_file_is_search_file_enum); + + +} + +static void +lnk960_before_allocation() +{ +} +static void +lnk960_after_allocation() +{ + lang_abs_symbol_at_end_of(".text","_etext"); + lang_abs_symbol_at_end_of(".data","_edata"); + lang_abs_symbol_at_end_of(".bss","_end"); +} + +static struct + { + unsigned long number; + char *name; + } +machine_table[] = { + bfd_mach_i960_core ,"CORE", + bfd_mach_i960_kb_sb ,"KB", + bfd_mach_i960_kb_sb ,"SB", + bfd_mach_i960_mc ,"MC", + bfd_mach_i960_xa ,"XA", + bfd_mach_i960_ca ,"CA", + bfd_mach_i960_ka_sa ,"KA", + bfd_mach_i960_ka_sa ,"SA", + + bfd_mach_i960_core ,"core", + bfd_mach_i960_kb_sb ,"kb", + bfd_mach_i960_kb_sb ,"sb", + bfd_mach_i960_mc ,"mc", + bfd_mach_i960_xa ,"xa", + bfd_mach_i960_ca ,"ca", + bfd_mach_i960_ka_sa ,"ka", + bfd_mach_i960_ka_sa ,"sa", + 0,(char *)NULL +}; + +static void +lnk960_set_output_arch() +{ + /* Set the output architecture and machine if possible */ + unsigned int i; + ldfile_output_machine = bfd_mach_i960_core; + for (i= 0; machine_table[i].name != (char*)NULL; i++) { + if (strcmp(ldfile_output_machine_name,machine_table[i].name)==0) { + ldfile_output_machine = machine_table[i].number; + break; + } + } + bfd_set_arch_mach(output_bfd, ldfile_output_architecture, ldfile_output_machine); +} + +static char * +lnk960_choose_target() +{ + char *from_outside = getenv(TARGET_ENVIRON); + if (from_outside != (char *)NULL) + return from_outside; + return LNK960_TARGET; +} + +/* The default script if none is offered */ +static char *lnk960_script = "\ +SECTIONS \ +{ \ + .text : \ + { \ + *(.text) \ + } \ +_etext = .;\ + .data SIZEOF(.text) + ADDR(.text):\ + { \ + *(.data) \ + } \ +_edata = .; \ + .bss SIZEOF(.data) + ADDR(.data) : \ + { \ + _bss_start = . ;\ + *(.bss) \ + [COMMON] \ + } \ +_end = . ; \ +} \ +"; + +static char *lnk960_script_relocateable = "\ +SECTIONS \ +{ \ + .text 0x40000000: \ + { \ + *(.text) \ + } \ + .data 0:\ + { \ + *(.data) \ + } \ + .bss SIZEOF(.data) + ADDR(.data) : \ + { \ + *(.bss) \ + [COMMON] \ + } \ +} \ +"; + +static char *lnk960_get_script() +{ +extern ld_config_type config; +if (config.relocateable_output) { + return lnk960_script_relocateable; +} +return lnk960_script; + + +} +struct ld_emulation_xfer_struct ld_lnk960_emulation = +{ + lnk960_before_parse, + lnk960_syslib, + lnk960_hll, + lnk960_after_parse, + lnk960_after_allocation, + lnk960_set_output_arch, + lnk960_choose_target, + lnk960_before_allocation, + lnk960_get_script, +}; diff --git a/ld/ld.h b/ld/ld.h new file mode 100644 index 00000000000..fb2b62a908b --- /dev/null +++ b/ld/ld.h @@ -0,0 +1,132 @@ +/* ld.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD 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. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#define flag_is_not_at_end(x) ((x) & BSF_NOT_AT_END) +#define flag_is_ordinary_local(x) (((x) & (BSF_LOCAL))&!((x) & (BSF_DEBUGGING))) +#define flag_is_debugger(x) ((x) & BSF_DEBUGGING) +#define flag_is_undefined_or_global(x) ((x) & (BSF_UNDEFINED | BSF_GLOBAL)) +#define flag_is_defined(x) (!((x) & (BSF_UNDEFINED))) +#define flag_is_global_or_common(x) ((x) & (BSF_GLOBAL | BSF_FORT_COMM)) +#define flag_is_undefined_or_global_or_common(x) ((x) & (BSF_UNDEFINED | BSF_GLOBAL | BSF_FORT_COMM)) +#define flag_is_common(x) ((x) & BSF_FORT_COMM) +#define flag_is_global(x) ((x) & (BSF_GLOBAL)) +#define flag_is_undefined(x) ((x) & BSF_UNDEFINED) +#define flag_set(x,y) (x = y) +#define flag_is_fort_comm(x) ((x) & BSF_FORT_COMM) +#define flag_is_absolute(x) ((x) & BSF_ABSOLUTE) +/* Extra information we hold on sections */ +typedef struct user_section_struct { + /* Pointer to the section where this data will go */ + struct lang_input_statement_struct *file; +} section_userdata_type; + + +#define get_userdata(x) ((x)->userdata) +#define as_output_section_statement(x) ((x)->otheruserdata) + +#if 0 +/* + * Structure for communication between do_file_warnings and it's + * helper routines. Will in practice be an array of three of these: + * 0) Current line, 1) Next line, 2) Source file info. + */ +struct line_debug_entry +{ + int line; + char *filename; + struct nlist *sym; +}; + +#endif + + +/* Which symbols should be stripped (omitted from the output): + none, all, or debugger symbols. */ +enum { STRIP_NONE, STRIP_ALL, STRIP_DEBUGGER } strip_symbols; + + + + +/* Which local symbols should be omitted: + none, all, or those starting with L. + This is irrelevant if STRIP_NONE. */ +enum { DISCARD_NONE, DISCARD_ALL, DISCARD_L } discard_locals; + + + + + + +#define ALIGN(this, boundary) ((( (this) + ((boundary) -1)) & (~((boundary)-1)))) +#if 0 +#define FOREACHGLOBALSYMBOL(x) ldsym_type *x; for (x = symbol_head; x; x=x->next) + + + + +#define SECTIONLOOP(abfd, ptr) \ + asection *ptr; for(ptr = abfd->sections; ptr;ptr=ptr->next) + + +#endif +typedef struct { + + /* 1 => assign space to common symbols even if `relocatable_output'. */ + boolean force_common_definition; + +} args_type; + +typedef int token_code_type; + +typedef struct +{ + unsigned int specified_data_size; + boolean magic_demand_paged; + boolean make_executable; + /* 1 => write relocation into output file so can re-input it later. */ + boolean relocateable_output; + + /* Will we build contstructors, or leave alone ? */ + boolean build_constructors; + /* 1 => write relocation such that a UNIX linker can understand it. + This is used mainly to finish of sets that were built. */ + boolean unix_relocate; + + +} ld_config_type; +#define set_asymbol_chain(x,y) ((x)->udata = (void *)y) +#define get_asymbol_chain(x) ((asymbol **)((x)->udata)) +#define get_loader_symbol(x) ((loader_global_asymbol *)((x)->udata)) +#define set_loader_symbol(x,y) ((x)->udata = (void *)y) + + + + + + +typedef enum { + lang_first_phase_enum, + lang_allocating_phase_enum, + lang_final_phase_enum } lang_phase_type; + + + + diff --git a/ld/ldexp.c b/ld/ldexp.c new file mode 100644 index 00000000000..5b8581f0aef --- /dev/null +++ b/ld/ldexp.c @@ -0,0 +1,770 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + $Id$ + + $Log$ + Revision 1.1 1991/03/21 21:28:34 gumby + Initial revision + + * Revision 1.1 1991/03/13 00:48:16 chrisb + * Initial revision + * + * Revision 1.6 1991/03/10 09:31:22 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.5 1991/03/09 03:25:04 sac + * Added support for LONG, SHORT and BYTE keywords in scripts + * + * Revision 1.4 1991/03/06 02:27:15 sac + * Added LONG, SHORT and BYTE keywords + * + * Revision 1.3 1991/02/22 17:14:59 sac + * Added RCS keywords and copyrights + * +*/ + +/* + * Written by Steve Chamberlain + * steve@cygnus.com + * + * This module handles expression trees. + */ + + +#include "sysdep.h" +#include "bfd.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldmisc.h" +#include "ldexp.h" +#include "ldgram.tab.h" +#include "ldsym.h" +#include "ldlang.h" + +extern char *output_filename; +extern unsigned int undefined_global_sym_count; +extern unsigned int defined_global_sym_count; +extern bfd *output_bfd; +extern size_t largest_section; +extern lang_statement_list_type file_chain; +extern args_type command_line; +extern ld_config_type config; + +extern lang_input_statement_type *script_file; +extern unsigned int defined_global_sym_count; + +extern bfd_vma print_dot; + + +static void +exp_print_token(outfile, code) +FILE *outfile; +token_code_type code; +{ + static struct { + token_code_type code; + char *name; + } table[] = + { + INT, "int", + CHAR,"char", + NAME,"NAME", + PLUSEQ,"+=", + MINUSEQ,"-=", + MULTEQ,"*=", + DIVEQ,"/=", + LSHIFTEQ,"<<=", + RSHIFTEQ,">>=", + ANDEQ,"&=", + OREQ,"|=", + OROR,"||", + ANDAND,"&&", + EQ,"==", + NE,"!=", + LE,"<=", + GE,">=", + LSHIFT,"<<", + RSHIFT,">>=", + ALIGN_K,"ALIGN", + BLOCK,"BLOCK", + SECTIONS,"SECTIONS", + ALIGNMENT,"ALIGNMENT", + SIZEOF_HEADERS,"SIZEOF_HEADERS", + NEXT,"NEXT", + SIZEOF,"SIZEOF", + ADDR,"ADDR", + MEMORY,"MEMORY", + DSECT,"DSECT", + NOLOAD,"NOLOAD", + COPY,"COPY", + INFO,"INFO", + OVERLAY,"OVERLAY", + DEFINED,"DEFINED", + TARGET_K,"TARGET", + SEARCH_DIR,"SEARCH_DIR", + MAP,"MAP", + LONG,"LONG", + SHORT,"SHORT", + BYTE,"BYTE", + ENTRY,"ENTRY", + 0,(char *)NULL} ; + + + + unsigned int idx; + for (idx = 0; table[idx].name != (char*)NULL; idx++) { + if (table[idx].code == code) { + fprintf(outfile, "%s", table[idx].name); + return; + } + } + /* Not in table, just print it alone */ + fprintf(outfile, "%c",code); +} + +static void +make_abs(ptr) +etree_value_type *ptr; +{ + if (ptr->section != (lang_output_section_statement_type *)NULL) { + asection *s = ptr->section->bfd_section; + ptr->value += s->vma; + ptr->section = (lang_output_section_statement_type *)NULL; + } + +} +static +etree_value_type new_abs(value) +bfd_vma value; +{ + etree_value_type new; + new.valid = true; + new.section = (lang_output_section_statement_type *)NULL; + new.value = value; + return new; +} + +static void check(os) +lang_output_section_statement_type *os; +{ + if (os == (lang_output_section_statement_type *)NULL) { + info("%F%P undefined section"); + } + if (os->processed == false) { + info("%F%P forward reference of section"); + } +} + +etree_type *exp_intop(value) +bfd_vma value; +{ + etree_type *new = (etree_type *)ldmalloc(sizeof(new->value)); + new->type.node_code = INT; + new->value.value = value; + new->type.node_class = etree_value; + return new; + +} + + +static +etree_value_type new_rel(value, section) +bfd_vma value; +lang_output_section_statement_type *section; +{ + etree_value_type new; + new.valid = true; + new.value = value; + new.section = section; + return new; +} + +static +etree_value_type new_rel_from_section(value, section) +bfd_vma value; +lang_output_section_statement_type *section; +{ + etree_value_type new; + new.valid = true; + new.value = value; + new.section = section; + if (new.section != (lang_output_section_statement_type *)NULL) { + new.value -= section->bfd_section->vma; + } + return new; +} + +static etree_value_type +fold_binary(tree, current_section, allocation_done, dot, dotp) +etree_type *tree; +lang_output_section_statement_type *current_section; +lang_phase_type allocation_done; +bfd_vma dot; +bfd_vma *dotp; +{ + etree_value_type result; + + result = exp_fold_tree(tree->binary.lhs, current_section, + allocation_done, dot, dotp); + if (result.valid) { + etree_value_type other; + other = exp_fold_tree(tree->binary.rhs, + current_section, + allocation_done, dot,dotp) ; + if (other.valid) { + /* If values are from different sections, or this is an */ + /* absolute expression, make both source args absolute */ + if (result.section != other.section || + current_section == (lang_output_section_statement_type *)NULL) { + + make_abs(&result); + make_abs(&other); + } + + switch (tree->type.node_code) + { + case '%': + /* Mod, both absolule*/ + + if (other.value == 0) { + info("%F%S % by zero\n"); + } + result.value %= other.value; + break; + case '/': + if (other.value == 0) { + info("%F%S / by zero\n"); + } + result.value /= other.value; + break; +#define BOP(x,y) case x : result.value = result.value y other.value;break; + BOP('+',+); + BOP('*',*); + BOP('-',-); + BOP(LSHIFT,<<); + BOP(RSHIFT,>>); + BOP(EQ,==); + BOP(NE,!=); + BOP('<',<); + BOP('>',>); + BOP(LE,<=); + BOP(GE,>=); + BOP('&',&); + BOP('^',^); + BOP('|',|); + BOP(ANDAND,&&); + BOP(OROR,||); + default: + FAIL(); + } + } + } + return result; +} +etree_value_type invalid() +{ + etree_value_type new; + new.valid = false; + return new; +} + +etree_value_type fold_name(tree, current_section, allocation_done, dot) +etree_type *tree; +lang_output_section_statement_type *current_section; +lang_phase_type allocation_done; +bfd_vma dot; + +{ + etree_value_type result; + switch (tree->type.node_code) + { + case DEFINED: + result.value = + ldsym_get_soft(tree->name.name) != (ldsym_type *)NULL; + result.section = 0; + result.valid = true; + break; + case NAME: + result.valid = false; + if (tree->name.name[0] == '.' && tree->name.name[1] == 0) { + + if (allocation_done != lang_first_phase_enum) { + result = new_rel_from_section(dot, current_section); + } + else { + result = invalid(); + } + } + else { + if (allocation_done == lang_final_phase_enum) { + ldsym_type *sy = ldsym_get_soft(tree->name.name); + + if (sy) { + asymbol **sdefp = sy->sdefs_chain; + + if (sdefp) { + asymbol *sdef = *sdefp; + if (sdef->section == (asection *)NULL) { + /* This is an absolute symbol */ + result = new_abs(sdef->value); + } + else { + lang_output_section_statement_type *os = + lang_output_section_statement_lookup( sdef->section->output_section->name); + result = new_rel(sdef->value, os); + } + } + } + if (result.valid == false) { + info("%F%S: undefined symbol `%s' referenced in expression.\n", + tree->name.name); + } + + } + } + + break; + + case ADDR: + + if (allocation_done != lang_first_phase_enum) { + lang_output_section_statement_type *os = + lang_output_section_find(tree->name.name); + check(os); + result = new_rel((bfd_vma)0, os); + } + else { + result = invalid(); + } + break; + case SIZEOF: + if(allocation_done != lang_first_phase_enum) { + lang_output_section_statement_type *os = + lang_output_section_find(tree->name.name); + check(os); + result = new_abs((bfd_vma)(os->bfd_section->size)); + } + else { + result = invalid(); + } + break; + + default: + FAIL(); + break; + } + + return result; +} +etree_value_type exp_fold_tree(tree, current_section, allocation_done, + dot, dotp) +etree_type *tree; +lang_output_section_statement_type *current_section; +lang_phase_type allocation_done; +bfd_vma dot; +bfd_vma *dotp; +{ + etree_value_type result; + + if (tree == (etree_type *)NULL) { + result.valid = false; + } + else { + switch (tree->type.node_class) + { + case etree_value: + result = new_rel(tree->value.value, current_section); + break; + case etree_unary: + result = exp_fold_tree(tree->unary.child, + current_section, + allocation_done, dot, dotp); + if (result.valid == true) + { + switch(tree->type.node_code) + { + case ALIGN_K: + if (allocation_done != lang_first_phase_enum) { + result = new_rel_from_section(ALIGN(dot, + result.value) , + current_section); + + } + else { + result.valid = false; + } + break; + case '-': + result.value = -result.value; + break; + case NEXT: + result.valid = false; + break; + default: + FAIL(); + } + } + + break; + case etree_trinary: + + result = exp_fold_tree(tree->trinary.cond, + current_section, + allocation_done, dot, dotp); + if (result.valid) { + result = exp_fold_tree(result.value ? + tree->trinary.lhs:tree->trinary.rhs, + current_section, + allocation_done, dot, dotp); + } + + break; + case etree_binary: + result = fold_binary(tree, current_section, allocation_done, + dot, dotp); + break; + case etree_assign: + if (tree->assign.dst[0] == '.' && tree->assign.dst[1] == 0) { + /* Assignment to dot can only be done during allocation */ + if (allocation_done == lang_allocating_phase_enum) { + result = exp_fold_tree(tree->assign.src, + current_section, + lang_allocating_phase_enum, dot, dotp); + if (result.valid == false) { + info("%F%S invalid assignment to location counter\n"); + } + else { + if (current_section == + (lang_output_section_statement_type *)NULL) { + info("%F%S assignment to location counter invalid outside of SECTION\n"); + } + else { + unsigned long nextdot =result.value + + current_section->bfd_section->vma; + if (nextdot < dot) { + info("%F%S cannot move location counter backwards"); + } + else { + *dotp = nextdot; + } + } + } + } + } + else { + ldsym_type *sy = ldsym_get(tree->assign.dst); + + /* If this symbol has just been created then we'll place it into + * a section of our choice + */ + result = exp_fold_tree(tree->assign.src, + current_section, allocation_done, + dot, dotp); + if (result.valid) + { + asymbol *def; + asymbol **def_ptr = (asymbol **)ldmalloc(sizeof(asymbol **)); + /* Add this definition to script file */ + def = (asymbol *)bfd_make_empty_symbol(script_file->the_bfd); + *def_ptr = def; + + + def->value = result.value; + if (result.section != + (lang_output_section_statement_type *)NULL) { + if (current_section != + (lang_output_section_statement_type *)NULL) { + + def->section = result.section->bfd_section; + def->flags = BSF_GLOBAL | BSF_EXPORT; + } + else { + /* Force to absolute */ + def->value += result.section->bfd_section->vma; + def->section = (asection *)NULL; + def->flags = BSF_GLOBAL | BSF_EXPORT | BSF_ABSOLUTE; + } + + + } + else { + def->section = (asection *)NULL; + def->flags = BSF_GLOBAL | BSF_EXPORT | BSF_ABSOLUTE; + } + + + def->udata = (void *)NULL; + def->name = sy->name; + Q_enter_global_ref(def_ptr); + } + + } + + + break; + case etree_name: + result = fold_name(tree, current_section, allocation_done, dot); + break; + default: + info("%F%S Need more of these %d",tree->type.node_class ); + + } + } + + return result; +} + + +etree_value_type exp_fold_tree_no_dot(tree, current_section, allocation_done) +etree_type *tree; +lang_output_section_statement_type *current_section; +lang_phase_type allocation_done; +{ +return exp_fold_tree(tree, current_section, allocation_done, (bfd_vma) + 0, (bfd_vma *)NULL); +} + +etree_type * +exp_binop(code, lhs, rhs) +int code; +etree_type *lhs; +etree_type *rhs; +{ + etree_type value, *new; + etree_value_type r; + + value.type.node_code = code; + value.binary.lhs = lhs; + value.binary.rhs = rhs; + value.type.node_class = etree_binary; + r = exp_fold_tree_no_dot(&value, (lang_output_section_statement_type *)NULL, + lang_first_phase_enum ); + if (r.valid) + { + return exp_intop(r.value); + } + new = (etree_type *)ldmalloc(sizeof(new->binary)); + memcpy((char *)new, (char *)&value, sizeof(new->binary)); + return new; +} + +etree_type * +exp_trinop(code, cond, lhs, rhs) +int code; +etree_type *cond; +etree_type *lhs; +etree_type *rhs; +{ + etree_type value, *new; + etree_value_type r; + value.type.node_code = code; + value.trinary.lhs = lhs; + value.trinary.cond = cond; + value.trinary.rhs = rhs; + value.type.node_class = etree_trinary; + r= exp_fold_tree_no_dot(&value, (lang_output_section_statement_type + *)NULL,lang_first_phase_enum); + if (r.valid) { + return exp_intop(r.value); + } + new = (etree_type *)ldmalloc(sizeof(new->trinary)); + memcpy((char *)new,(char *) &value, sizeof(new->trinary)); + return new; +} + + +etree_type * +exp_unop(code, child) +int code; +etree_type *child; +{ + etree_type value, *new; + + etree_value_type r; + value.unary.type.node_code = code; + value.unary.child = child; + value.unary.type.node_class = etree_unary; +r = exp_fold_tree_no_dot(&value,(lang_output_section_statement_type *)NULL, + lang_first_phase_enum); +if (r.valid) { + return exp_intop(r.value); + } + new = (etree_type *)ldmalloc(sizeof(new->unary)); + memcpy((char *)new, (char *)&value, sizeof(new->unary)); + return new; +} + + +etree_type * +exp_nameop(code, name) +int code; +char *name; +{ + + etree_type value, *new; + + etree_value_type r; + value.name.type.node_code = code; + value.name.name = name; + value.name.type.node_class = etree_name; + + + r = exp_fold_tree_no_dot(&value,(lang_output_section_statement_type *)NULL, + lang_first_phase_enum); + if (r.valid) { + return exp_intop(r.value); + } + new = (etree_type *)ldmalloc(sizeof(new->name)); + memcpy((char *)new, (char *)&value, sizeof(new->name)); + return new; + +} + + + + +etree_type * +exp_assop(code, dst, src) +int code; +char *dst; +etree_type *src; +{ + etree_type value, *new; + + value.assign.type.node_code = code; + + + value.assign.src = src; + value.assign.dst = dst; + value.assign.type.node_class = etree_assign; + +#if 0 + if (exp_fold_tree_no_dot(&value, &result)) { + return exp_intop(result); + } +#endif + new = (etree_type*)ldmalloc(sizeof(new->assign)); + memcpy((char *)new, (char *)&value, sizeof(new->assign)); + return new; +} + +void +exp_print_tree(outfile, tree) +FILE *outfile; +etree_type *tree; +{ + switch (tree->type.node_class) { + case etree_value: + fprintf(outfile,"0x%08lx",(bfd_vma)(tree->value.value)); + return; + case etree_assign: +#if 0 + if (tree->assign.dst->sdefs != (asymbol *)NULL){ + fprintf(outfile,"%s (%x) ",tree->assign.dst->name, + tree->assign.dst->sdefs->value); + } + else { + fprintf(outfile,"%s (UNDEFINED)",tree->assign.dst->name); + } +#endif + fprintf(outfile,"%s ",tree->assign.dst); + exp_print_token(outfile,tree->type.node_code); + exp_print_tree(outfile,tree->assign.src); + break; + case etree_binary: + exp_print_tree(outfile,tree->binary.lhs); + exp_print_token(outfile,tree->type.node_code); + exp_print_tree(outfile,tree->binary.rhs); + break; + case etree_trinary: + exp_print_tree(outfile,tree->trinary.cond); + fprintf(outfile,"?"); + exp_print_tree(outfile,tree->trinary.lhs); + fprintf(outfile,":"); + exp_print_tree(outfile,tree->trinary.rhs); + break; + case etree_unary: + exp_print_token(outfile,tree->unary.type.node_code); + fprintf(outfile,"("); + exp_print_tree(outfile,tree->unary.child); + fprintf(outfile,")"); + break; + case etree_undef: + fprintf(outfile,"????????"); + break; + case etree_name: + if (tree->type.node_code == NAME) { + fprintf(outfile,"%s", tree->name.name); + } + else { + exp_print_token(outfile,tree->type.node_code); + fprintf(outfile,"(%s)", tree->name.name); + } + break; + default: + FAIL(); + break; + } +} + + + + +bfd_vma +exp_get_vma(tree, def, name, allocation_done) +etree_type *tree; +bfd_vma def; +char *name; +lang_phase_type allocation_done; +{ + etree_value_type r; + + if (tree != (etree_type *)NULL) { + r = exp_fold_tree_no_dot(tree, + (lang_output_section_statement_type *)NULL, + allocation_done); + if (r.valid == false && name) { + info("%F%S Nonconstant expression for %s\n",name); + } + return r.value; + } + else { + return def; + } +} + +int +exp_get_value_int(tree,def,name, allocation_done) +etree_type *tree; +int def; +char *name; +lang_phase_type allocation_done; +{ + return (int)exp_get_vma(tree,(bfd_vma)def,name, allocation_done); +} diff --git a/ld/ldexp.h b/ld/ldexp.h new file mode 100644 index 00000000000..f7e132124c8 --- /dev/null +++ b/ld/ldexp.h @@ -0,0 +1,99 @@ +/* ldexp.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD 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. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + + +/* The result of an expression tree */ +typedef struct +{ + bfd_vma value; + struct lang_output_section_statement_struct *section; + boolean valid; +} etree_value_type; + + + +typedef struct +{ + int node_code; + enum { etree_binary, + etree_trinary, + etree_unary, + etree_name, + etree_assign, + etree_undef, + etree_unspec, + etree_value } node_class; +} node_type; + + + +typedef union etree_union +{ + node_type type; + struct { + node_type type; + union etree_union *lhs; + union etree_union *rhs; + } binary; + struct { + node_type type; + union etree_union *cond; + union etree_union *lhs; + union etree_union *rhs; + } trinary; + struct { + node_type type; + char *dst; + union etree_union *src; + } assign; + + struct { + node_type type; + union etree_union *child; + } unary; + struct { + node_type type; + char *name; + } name; + struct { + node_type type; + bfd_vma value; + } value; + +} etree_type; + + +PROTO(etree_type *,exp_intop,(bfd_vma)); + +PROTO(etree_value_type, invalid,(void)); +PROTO(etree_value_type, exp_fold_tree,(etree_type *, struct + lang_output_section_statement_struct *, lang_phase_type, + bfd_vma, bfd_vma *)); + +PROTO(etree_type *, exp_binop,(int, etree_type *, etree_type *)); +PROTO(etree_type *,exp_trinop,(int,etree_type *, etree_type *, etree_type *)); +PROTO(etree_type *,exp_unop,(int, etree_type *)); +PROTO(etree_type *,exp_nameop,(int, char *)); +PROTO(etree_type *,exp_assop,(int, char *, etree_type *)); +PROTO(void, exp_print_tree,(struct _iobuf *, etree_type *)); +PROTO(bfd_vma, exp_get_vma,(etree_type *, bfd_vma, char *, enum boolean)); +PROTO(int, exp_get_value_int,(etree_type *, int, char *, enum boolean)); diff --git a/ld/ldfile.c b/ld/ldfile.c new file mode 100644 index 00000000000..bc55f04b835 --- /dev/null +++ b/ld/ldfile.c @@ -0,0 +1,284 @@ + +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + $Id$ + + $Log$ + Revision 1.1 1991/03/21 21:28:37 gumby + Initial revision + + * Revision 1.2 1991/03/15 18:45:55 rich + * foo + * + * Revision 1.1 1991/03/13 00:48:18 chrisb + * Initial revision + * + * Revision 1.4 1991/03/10 09:31:24 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.3 1991/02/22 17:15:00 sac + * Added RCS keywords and copyrights + * +*/ + +/* + ldfile.c + + look after all the file stuff + + */ + +#include "sysdep.h" +#include "bfd.h" + +#include "ldmisc.h" +#include "ldlang.h" +#include "ldfile.h" + +#include + +/* EXPORT */ +char *ldfile_input_filename; +char *ldfile_output_machine_name; +unsigned long ldfile_output_machine; +enum bfd_architecture ldfile_output_architecture; +boolean had_script; + +/* IMPORT */ + +extern boolean option_v; + + + + + +/* LOACL */ +typedef struct search_dirs_struct +{ + char *name; + struct search_dirs_struct *next; +} search_dirs_type; + +static search_dirs_type *search_head; +static search_dirs_type **search_tail_ptr = &search_head; + +typedef struct search_arch_struct +{ + char *name; + struct search_arch_struct *next; +} search_arch_type; + +static search_arch_type *search_arch_head; +static search_arch_type **search_arch_tail_ptr = &search_arch_head; + +void +ldfile_add_library_path(name) +char *name; +{ + search_dirs_type *new = + (search_dirs_type *)ldmalloc(sizeof(search_dirs_type)); + new->name = name; + new->next = (search_dirs_type*)NULL; + *search_tail_ptr = new; + search_tail_ptr = &new->next; +} + + +static bfd* +cached_bfd_openr(attempt,entry) +char *attempt; +lang_input_statement_type *entry; +{ + entry->the_bfd = bfd_openr(attempt, entry->target); + + + return entry->the_bfd; +} + +static bfd * +open_a(arch, entry, lib, suffix) +char *arch; +lang_input_statement_type *entry; +char *lib; +char *suffix; +{ + bfd*desc; + search_dirs_type *search ; + for (search = search_head; + search != (search_dirs_type *)NULL; + search = search->next) + { + char buffer[1000]; + char *string; + if (entry->is_archive == true) { + sprintf(buffer, + "%s/%s%s%s%s", + search->name, + lib, + entry->filename, arch, suffix); + } + else { + if (entry->filename[0] == '/') { + strcpy(buffer, entry->filename); + } else { + sprintf(buffer,"%s/%s",search->name, entry->filename); + } /* */ + } + string = buystring(buffer); + desc = cached_bfd_openr (string, entry); + if (desc) + { + entry->filename = string; + entry->search_dirs_flag = false; + entry->the_bfd = desc; + return desc; + } + free(string); + } + return (bfd *)NULL; +} + +/* Open the input file specified by 'entry', and return a descriptor. + The open file is remembered; if the same file is opened twice in a row, + a new open is not actually done. */ + +void +ldfile_open_file (entry) +lang_input_statement_type *entry; +{ + + if (entry->superfile) + ldfile_open_file (entry->superfile); + + if (entry->search_dirs_flag) + { + search_arch_type *arch; + for (arch = search_arch_head; + arch != (search_arch_type *)NULL; + arch = arch->next) { + if (open_a(arch->name,entry,"","") != (bfd *)NULL) { + return; + } + if (open_a(arch->name,entry,"lib",".a") != (bfd *)NULL) { + return; + } + + } + + + } + else { + entry->the_bfd = cached_bfd_openr (entry->filename, entry); + + } + if (!entry->the_bfd) info("%F%P: %E %I\n", entry); + +} + + + + + + +static FILE * +try_open(name, exten) +char *name; +char *exten; +{ + FILE *result; + char buff[1000]; + result = fopen(name, "r"); + if (result && option_v == true) { + info("%s\n",name); + return result; + } + sprintf(buff, "%s%s", name, exten); + result = fopen(buff, "r"); + + if (result && option_v == true) { + info("%s\n", buff); + } + return result; +} +static FILE * +find_a_name(name, extend) +char *name; +char *extend; +{ + search_dirs_type *search; + FILE *result; + char buffer[1000]; + /* First try raw name */ + result = try_open(name,""); + if (result == (FILE *)NULL) { + /* Try now prefixes */ + for (search = search_head; + search != (search_dirs_type *)NULL; + search = search->next) { + sprintf(buffer,"%s/%s", search->name, name); + result = try_open(buffer, extend); + if (result)break; + } + } + return result; +} + +void ldfile_open_command_file(name) +char *name; +{ + extern FILE *ldlex_input_stack; + ldlex_input_stack = find_a_name(name, ".ld"); + + if (ldlex_input_stack == (FILE *)NULL) { + info("%P%F cannot open load script file %s\n",name); + } + ldfile_input_filename = name; + had_script = true; +} + + + + +void +ldfile_add_arch(name) +char *name; +{ + search_arch_type *new = + (search_arch_type *)ldmalloc(sizeof(search_arch_type)); + ldfile_output_machine_name = name; + + new->name = name; + new->next = (search_arch_type*)NULL; + while (*name) { + if (isupper(*name)) *name = tolower(*name); + name++; + } + *search_arch_tail_ptr = new; + search_arch_tail_ptr = &new->next; + +} diff --git a/ld/ldfile.h b/ld/ldfile.h new file mode 100644 index 00000000000..876d47f6db9 --- /dev/null +++ b/ld/ldfile.h @@ -0,0 +1,27 @@ +/* ldfile.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD 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. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +PROTO(void,ldfile_add_arch,(char *)); +PROTO(void,ldfile_add_library_path,(char *)); +PROTO(void,ldfile_open_command_file,(char *name)); +PROTO(void,ldfile_open_file,(struct lang_input_statement_struct *)); + diff --git a/ld/ldgram.y b/ld/ldgram.y new file mode 100644 index 00000000000..aa0f325df73 --- /dev/null +++ b/ld/ldgram.y @@ -0,0 +1,693 @@ +%{ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:41 gumby + * Initial revision + * + * Revision 1.2 1991/03/16 22:27:24 rich + * fish + * + * Revision 1.1 1991/03/13 00:48:21 chrisb + * Initial revision + * + * Revision 1.6 1991/03/10 09:31:26 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.5 1991/03/09 03:25:48 sac + * Can now parse the -Ur flag + * + * Revision 1.4 1991/03/06 02:26:01 sac + * Added support for constructor sections. + * Remove parsing ambiguity. + * Lint + * + * Revision 1.3 1991/02/22 17:15:13 sac + * Added RCS keywords and copyrights + * +*/ + +/* + This is a YACC grammer intended to parse a superset of the AT&T + linker scripting languaue. + + + Written by Steve Chamberlain steve@cygnus.com +*/ + + +/*SUPPRESS 166*/ +/*SUPPRESS 112*/ + +#include "sysdep.h" +#include "bfd.h" + +#include "ld.h" +#include "ldexp.h" +#include "ldversion.h" +#include "ldlang.h" +#include "ld-emul.h" +#include "ldfile.h" +#include "ldmisc.h" +#define YYDEBUG 1 + +boolean option_v; + + +extern unsigned int lineno; +extern boolean trace_files; +extern boolean write_map; + +boolean hex_mode; + + + + +lang_memory_region_type *region; + + +lang_memory_region_type *lang_memory_region_lookup(); +lang_output_section_statement_type *lang_output_section_statement_lookup(); + +#ifdef __STDC__ + +void lang_add_data(int type, union etree_union *exp); +void lang_enter_output_section_statement(char *output_section_statement_name, etree_type *address_exp, bfd_vma block_value); + +#else + +void lang_add_data(); +void lang_enter_output_section_statement(); + +#endif /* __STDC__ */ + +extern args_type command_line; +char *current_file; +boolean ldgram_want_filename = true; +boolean had_script = false; +boolean force_make_executable = false; +boolean ldgram_mustbe_filename = false; +boolean ldgram_mustbe_symbolname = false; +boolean ldgram_has_inputfile = false; + +/* LOCALS */ + + + + +%} +%union { + bfd_vma integer; + int voidval; + char *name; + int token; + union etree_union *etree; + asection *section; + struct lang_output_section_statement_struct *output_section_statement; + union lang_statement_union **statement_ptr; + int lineno; + struct { + FILE *file; + char *name; + unsigned int lineno; + } state; + + +} + +%type exp opt_exp exp_head +%type fill_opt opt_block +%type memspec_opt +%token INT CHAR +%token NAME +%type length + +%right PLUSEQ MINUSEQ MULTEQ DIVEQ '=' LSHIFTEQ RSHIFTEQ ANDEQ OREQ +%right '?' ':' +%left OROR +%left ANDAND +%left '|' +%left '^' +%left '&' +%left EQ NE +%left '<' '>' LE GE +%left LSHIFT RSHIFT +%left '+' '-' +%left '*' '/' '%' +%right UNARY +%left '(' +%token ALIGN_K BLOCK LONG SHORT BYTE +%token SECTIONS +%token '{' '}' +%token ALIGNMENT SIZEOF_HEADERS +%token NEXT SIZEOF ADDR +%token MEMORY +%token DSECT NOLOAD COPY INFO OVERLAY +%token NAME DEFINED TARGET_K SEARCH_DIR MAP ENTRY +%token OPTION_e OPTION_c OPTION_noinhibit_exec OPTION_s OPTION_S OPTION_format +%token OPTION_d OPTION_dc OPTION_dp OPTION_x OPTION_X +%token OPTION_v OPTION_M OPTION_t STARTUP HLL SYSLIB FLOAT NOFLOAT OPTION_defsym +%token OPTION_n OPTION_r OPTION_o OPTION_b OPTION_A +%token OPTION_l OPTION_L OPTION_T OPTION_Aarch OPTION_Tfile OPTION_Texp +%token OPTION_Ur +%token ORIGIN FILL OPTION_g +%token LENGTH BIND SUBSECTION_ALIGN CREATE_OBJECT_SYMBOLS INPUT OUTPUT +%type assign_op SIZEOF NEXT ADDR +%type assignment +%type filename + +%{ +ld_config_type config; +%} + +%% + + + +file: command_line { lang_final(); }; + + +filename: + { + ldgram_mustbe_filename =true; + } + NAME + { + ldgram_mustbe_filename = false; + $$ = $2; + } + +command_line: + command_line command_line_option + | + ; + +command_line_option: + OPTION_v + { + ldversion(); + option_v = true; + } + | OPTION_t { + trace_files = true; + } + | OPTION_M { + write_map = true; + } + | OPTION_n { + config.magic_demand_paged = false; + config.make_executable = false; + } + | OPTION_s { + strip_symbols = STRIP_ALL; + } + | OPTION_S { + strip_symbols = STRIP_DEBUGGER; + } + + | OPTION_r { + config.relocateable_output = true; + config.build_constructors = false; + config.magic_demand_paged = false; + } + | OPTION_Ur { + config.relocateable_output = true; + config.build_constructors = true; + config.magic_demand_paged = false; + } + | OPTION_o filename + { + lang_add_output($2); + } + | OPTION_e NAME + { lang_add_entry($2); + } + | OPTION_X { + discard_locals = DISCARD_L; + } + | OPTION_x { + discard_locals = DISCARD_ALL; + } + + | OPTION_noinhibit_exec + { + force_make_executable = true; + } + | OPTION_d { + command_line.force_common_definition = true; + } + | OPTION_dc + { + command_line.force_common_definition = true; + } + | OPTION_g + { + /* Ignored */ + } + | OPTION_dp + { + command_line.force_common_definition = true; + } + | OPTION_format NAME + { + lang_add_target($2); + } + + | OPTION_Texp { hex_mode =true; } + exp_head + { lang_section_start($1, $3); + hex_mode = false; } + + | OPTION_Aarch + { ldfile_add_arch($1); } + | OPTION_b NAME + { + lang_add_target($2); + } + | OPTION_L + { + ldfile_add_library_path($1); + } + | ifile_p1 + | input_list + | OPTION_c filename + { ldfile_open_command_file($2); } + | OPTION_Tfile + { ldfile_open_command_file($1); } + + | OPTION_T filename + { ldfile_open_command_file($2); } + + | OPTION_l + { + lang_add_input_file($1, + lang_input_file_is_l_enum, + (char *)NULL); + } + | OPTION_A filename + { + lang_add_input_file($2, + lang_input_file_is_symbols_only_enum, + (char *)NULL); + } + | OPTION_defsym assignment_with_nospaces + ; + + +input_section_spec: + NAME + { + lang_add_wild((char *)NULL, $1); + } + | '[' + { + current_file = (char *)NULL; + } + file_NAME_list + ']' + | NAME + { + current_file =$1; + } + '(' file_NAME_list ')' + | '*' + { + current_file = (char *)NULL; + } + '(' file_NAME_list ')' + ; + + + +file_NAME_list: + NAME + { lang_add_wild($1, current_file); } + | file_NAME_list opt_comma NAME + { lang_add_wild($3, current_file); } + ; + + + +ifile_p1: + memory + | sections + | startup + | high_level_library + | low_level_library + | floating_point_support + | assignment end + | TARGET_K '(' NAME ')' + { lang_add_target($3); } + | SEARCH_DIR '(' filename ')' + { ldfile_add_library_path($3); } + | OUTPUT '(' filename ')' + { lang_add_output($3); } + | INPUT '(' input_list ')' + | MAP '(' filename ')' + { lang_add_map($3); } + ; + +input_list: + NAME + { lang_add_input_file($1,lang_input_file_is_file_enum, + (char *)NULL); } + | input_list ',' NAME + { lang_add_input_file($3,lang_input_file_is_file_enum, + (char *)NULL); } + | input_list NAME + { lang_add_input_file($2, lang_input_file_is_file_enum, + (char *)NULL); } + ; + +sections: + SECTIONS '{'sec_or_group_p1 '}' + ; + +sec_or_group_p1: + sec_or_group_p1 section + | sec_or_group_p1 statement_anywhere + | + ; + +statement_anywhere: + ENTRY '(' NAME ')' + { lang_add_entry($3); } + | assignment end + ; + +statement: + statement assignment end + | statement CREATE_OBJECT_SYMBOLS + { lang_add_attribute(lang_object_symbols_statement_enum); } + | statement input_section_spec + | statement length '(' exp_head ')' + { + lang_add_data($2,$4); + } + + | statement FILL '(' exp_head ')' + { + lang_add_fill + (exp_get_value_int($4, + 0, + "fill value", + lang_first_phase_enum)); + } + | + ; + +length: + LONG + { $$ = $1; } + | SHORT + { $$ = $1; } + | BYTE + { $$ = $1; } + ; + +fill_opt: + '=' exp_head + { + $$ = exp_get_value_int($2, + 0, + "fill value", + lang_first_phase_enum); + } + | { $$ = 0; } + ; + + + +assign_op: + PLUSEQ + { $$ = '+'; } + | MINUSEQ + { $$ = '-'; } + | MULTEQ + { $$ = '*'; } + | DIVEQ + { $$ = '/'; } + | LSHIFTEQ + { $$ = LSHIFT; } + | RSHIFTEQ + { $$ = RSHIFT; } + | ANDEQ + { $$ = '&'; } + | OREQ + { $$ = '|'; } + + ; + +end: ';' | ',' + ; + +assignment_with_nospaces: + { ldgram_want_filename = false; } + assignment + { ldgram_want_filename = true; } + ; + +assignment: + + NAME '=' exp_head + { + lang_add_assignment(exp_assop($2,$1,$3)); + } + | NAME assign_op exp_head + { + lang_add_assignment(exp_assop('=',$1,exp_binop($2,exp_nameop(NAME,$1),$3))); + } + + ; + + +opt_comma: + ',' | ; + + +memory: + MEMORY '{' memory_spec memory_spec_list '}' + ; + +memory_spec_list: + memory_spec_list memory_spec + | memory_spec_list ',' memory_spec + | + ; + + +memory_spec: + NAME + { region = lang_memory_region_lookup($1); } + attributes_opt ':' origin_spec opt_comma length_spec + + { + + + } + ; +origin_spec: + ORIGIN '=' exp + { region->current = + region->origin = + exp_get_vma($3, 0L,"origin", lang_first_phase_enum); } + ; +length_spec: + LENGTH '=' exp + { region->length = exp_get_vma($3, + ~((bfd_vma)0), + "length", + lang_first_phase_enum); + } + + +attributes_opt: + '(' NAME ')' + { + lang_set_flags(®ion->flags, $2); + } + | + + ; + +startup: + STARTUP '(' filename ')' + { lang_startup($3); } + ; + +high_level_library: + HLL '(' high_level_library_NAME_list ')' + | HLL '(' ')' + { ldemul_hll((char *)NULL); } + ; + +high_level_library_NAME_list: + high_level_library_NAME_list opt_comma filename + { ldemul_hll($3); } + | filename + { ldemul_hll($1); } + + ; + +low_level_library: + SYSLIB '(' low_level_library_NAME_list ')' + ; +low_level_library_NAME_list: + low_level_library_NAME_list opt_comma filename + { ldemul_syslib($3); } + | + ; + +floating_point_support: + FLOAT + { lang_float(true); } + | NOFLOAT + { lang_float(false); } + ; + + + + +exp : + '-' exp %prec UNARY + { $$ = exp_unop('-', $2); } + | '(' exp ')' + { $$ = $2; } + | NEXT '(' exp ')' %prec UNARY + { $$ = exp_unop($1,$3); } + | '!' exp %prec UNARY + { $$ = exp_unop('!', $2); } + | '+' exp %prec UNARY + { $$ = $2; } + | '~' exp %prec UNARY + { $$ = exp_unop('~', $2);} + + | exp '*' exp + { $$ = exp_binop('*', $1, $3); } + | exp '/' exp + { $$ = exp_binop('/', $1, $3); } + | exp '%' exp + { $$ = exp_binop('%', $1, $3); } + | exp '+' exp + { $$ = exp_binop('+', $1, $3); } + | exp '-' exp + { $$ = exp_binop('-' , $1, $3); } + | exp LSHIFT exp + { $$ = exp_binop(LSHIFT , $1, $3); } + | exp RSHIFT exp + { $$ = exp_binop(RSHIFT , $1, $3); } + | exp EQ exp + { $$ = exp_binop(EQ , $1, $3); } + | exp NE exp + { $$ = exp_binop(NE , $1, $3); } + | exp LE exp + { $$ = exp_binop(LE , $1, $3); } + | exp GE exp + { $$ = exp_binop(GE , $1, $3); } + | exp '<' exp + { $$ = exp_binop('<' , $1, $3); } + | exp '>' exp + { $$ = exp_binop('>' , $1, $3); } + | exp '&' exp + { $$ = exp_binop('&' , $1, $3); } + | exp '^' exp + { $$ = exp_binop('^' , $1, $3); } + | exp '|' exp + { $$ = exp_binop('|' , $1, $3); } + | exp '?' exp ':' exp + { $$ = exp_trinop('?' , $1, $3, $5); } + | exp ANDAND exp + { $$ = exp_binop(ANDAND , $1, $3); } + | exp OROR exp + { $$ = exp_binop(OROR , $1, $3); } + | DEFINED '(' NAME ')' + { $$ = exp_nameop(DEFINED, $3); } + | INT + { $$ = exp_intop($1); } + + | SIZEOF '(' NAME ')' + { $$ = exp_nameop($1,$3); } + | ADDR '(' NAME ')' + { $$ = exp_nameop($1,$3); } + | ALIGN_K '(' exp ')' + { $$ = exp_unop($1,$3); } + | NAME + { $$ = exp_nameop(NAME,$1); } + ; + + + + +section: NAME opt_exp opt_block ':' opt_things'{' + { + lang_enter_output_section_statement($1,$2,$3); + } + statement '}' fill_opt memspec_opt + { + lang_leave_output_section_statement($10, $11); + } + + ; + +opt_things: + { + + } + ; + +exp_head: + { ldgram_mustbe_symbolname = true; } + exp + { ldgram_mustbe_symbolname = false; + $$ = $2; + } + +opt_exp: + exp + { $$ = $1; } + | { $$= (etree_type *)NULL; } + ; + +opt_block: + BLOCK '(' exp_head ')' + { $$ = exp_get_value_int($3, + 1L, + "block", + lang_first_phase_enum); + } + | { $$ = 1; } + ; + +memspec_opt: + '>' NAME + { $$ = $2; } + | { $$ = "*default*"; } + ; + diff --git a/ld/ldlang.c b/ld/ldlang.c new file mode 100644 index 00000000000..1fccbc29f0e --- /dev/null +++ b/ld/ldlang.c @@ -0,0 +1,2231 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:45 gumby + * Initial revision + * + * Revision 1.3 1991/03/16 22:19:21 rich + * pop + * + * Revision 1.2 1991/03/15 18:52:42 rich + * pop + * + * Revision 1.1 1991/03/13 00:48:23 chrisb + * Initial revision + * + * Revision 1.8 1991/03/10 09:31:28 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.7 1991/03/09 03:31:03 sac + * After a fatal info message, the output file is deleted. + * + * Revision 1.6 1991/03/09 03:25:06 sac + * Added support for LONG, SHORT and BYTE keywords in scripts + * + * Revision 1.5 1991/03/06 21:59:31 sac + * Completed G++ support + * + * Revision 1.4 1991/03/06 02:26:02 sac + * Added support for constructor sections. + * Remove parsing ambiguity. + * Lint + * + * Revision 1.3 1991/02/22 17:15:01 sac + * Added RCS keywords and copyrights + * +*/ + + + +#include "sysdep.h" +#include "bfd.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldsym.h" +#include "ldgram.tab.h" +#include "ldmisc.h" +#include "ldlang.h" +#include "ldexp.h" +#include "ld-emul.h" +#include "ldlex.h" + +/* EXPORTS */ + + + +extern unsigned int undefined_global_sym_count; + +static char *startup_file; +static lang_input_statement_type *first_file; +lang_statement_list_type statement_list; +lang_statement_list_type *stat_ptr = &statement_list; +lang_statement_list_type lang_output_section_statement; +lang_statement_list_type input_file_chain; +lang_statement_list_type file_chain; +extern char *current_file; +static boolean placed_commons = false; + +boolean lang_float_flag; + +static lang_output_section_statement_type *default_common_section; + + +/* FORWARDS */ +PROTO(static void, print_statements,(void)); +PROTO(static void, print_statement,(lang_statement_union_type *, + lang_output_section_statement_type *)); + + + +/* EXPORTS */ +boolean lang_has_input_file = false; + + +extern bfd *output_bfd; +size_t largest_section; + + +extern enum bfd_architecture ldfile_output_architecture; +extern unsigned long ldfile_output_machine; +extern char *ldfile_output_machine_name; + + +extern ldsym_type *symbol_head; + +bfd_vma print_dot; +unsigned int commons_pending; + + + + +extern args_type command_line; +extern ld_config_type config; + +char *entry_symbol; + + + +lang_output_section_statement_type *create_object_symbols; + +extern boolean had_script; +static boolean map_option_f; + + +boolean had_output_filename = false; +extern boolean write_map; + + + + + + +size_t longest_section_name = 8; + + +lang_input_statement_type *script_file; + +section_userdata_type common_section_userdata; +asection common_section; + +#ifdef __STDC__ +#define cat(a,b) a##b +#else +#define cat(a,b) a/**/b +#endif + +#define new_stat(x,y) (cat(x,_type)*) new_statement(cat(x,_enum), sizeof(cat(x,_type)),y) + +#define outside_section_address(q) ( (q)->output_offset + (q)->output_section->vma) + +#define outside_symbol_address(q) ((q)->value + outside_section_address(q->section)) + +boolean option_longmap = false; + +static void lang_list_init(list) +lang_statement_list_type *list; +{ +list->head = (lang_statement_union_type *)NULL; +list->tail = &list->head; +} + +static void +print_section(name) +char *name; +{ + printf("%*s", -longest_section_name, name); +} +static void +print_space() +{ + printf(" "); +} +static void +print_nl() +{ + printf("\n"); +} +static void +print_address(value) +bfd_vma value; +{ + printf("%8lx", value); +} +static void +print_size(value) +size_t value; +{ + printf("%5x", (unsigned)value); +} +static void +print_alignment(value) +unsigned int value; +{ + printf("2**%2u",value); +} +static void +print_fill(value) +fill_type value; +{ + printf("%04x",(unsigned)value); +} + + +static +lang_statement_union_type *new_statement(type, size, list) +enum statement_enum type; +size_t size; +lang_statement_list_type *list; +{ + lang_statement_union_type *new = (lang_statement_union_type *) + ldmalloc(size); + new->header.type = type; + new->header.next = (lang_statement_union_type *)NULL; + lang_statement_append(list, new, &new->header.next); + return new; +} + +static lang_input_statement_type * +new_afile(name, file_type, target) +char *name; +lang_input_file_enum_type file_type; +char *target; +{ + lang_input_statement_type *p = new_stat(lang_input_statement, + stat_ptr); + lang_has_input_file = true; + p->target = target; + switch (file_type) { + case lang_input_file_is_symbols_only_enum: + p->filename = name; + p->is_archive =false; + p->real = true; + p->local_sym_name= name; + p->just_syms_flag = true; + p->search_dirs_flag = false; + break; + case lang_input_file_is_fake_enum: + p->filename = name; + p->is_archive =false; + p->real = false; + p->local_sym_name= name; + p->just_syms_flag = false; + p->search_dirs_flag =false; + + break; + case lang_input_file_is_l_enum: + p->is_archive = true; + p->filename = name; + p->real = true; + p->local_sym_name = concat("-l",name,""); + p->just_syms_flag = false; + p->search_dirs_flag = true; + break; + + case lang_input_file_is_search_file_enum: + case lang_input_file_is_marker_enum: + p->filename = name; + p->is_archive =false; + p->real = true; + p->local_sym_name= name; + p->just_syms_flag = false; + p->search_dirs_flag =true; + break; + + + + case lang_input_file_is_file_enum: + p->filename = name; + p->is_archive =false; + p->real = true; + p->local_sym_name= name; + p->just_syms_flag = false; + p->search_dirs_flag =false; + break; + + + default: + FAIL(); + } + p->asymbols = (asymbol **)NULL; + p->superfile = (lang_input_statement_type *)NULL; + + p->next_real_file = (lang_statement_union_type*)NULL; + p->next = (lang_statement_union_type*)NULL; + p->symbol_count = 0; + p->common_output_section = (asection *)NULL; + + lang_statement_append(&input_file_chain, + (lang_statement_union_type *)p, + &p->next_real_file); + return p; +} + +lang_input_statement_type * +lang_add_input_file(name, + file_type, + target) +char *name; +lang_input_file_enum_type file_type; +char *target; +{ + /* Look it up or build a new one */ + + lang_input_statement_type *p; + + for (p = (lang_input_statement_type *)input_file_chain.head; + p != (lang_input_statement_type *)NULL; + p = (lang_input_statement_type *)(p->next_real_file)) + { + /* Sometimes we have incomplete entries in here */ + if (p->filename != (char *)NULL) { + if(strcmp(name,p->filename) == 0) return p; + } + } + + return new_afile(name, file_type, target); +} + + + +void +lang_init() +{ + + stat_ptr= &statement_list; + lang_list_init(stat_ptr); + + lang_list_init(&input_file_chain); + lang_list_init(&lang_output_section_statement); + lang_list_init(&file_chain); + first_file = lang_add_input_file((char *)NULL, + lang_input_file_is_marker_enum, + (char *)NULL); + +} + +static void +lang_init2() +{ + script_file = lang_add_input_file("script file", + lang_input_file_is_fake_enum, + (char *)NULL); + script_file->the_bfd = bfd_create("script file", output_bfd); + script_file->symbol_count = 0; + + common_section.userdata = &common_section_userdata; + +} + + + +/* this function mainains a dictionary of regions. If the *default* + region is asked for then a pointer to the first region is + returned. If there is no first pointer then one is created +*/ + +static lang_memory_region_type *lang_memory_region_list; +static lang_memory_region_type **lang_memory_region_list_tail = &lang_memory_region_list; + +lang_memory_region_type * +lang_memory_region_lookup(name) +char *name; +{ + + lang_memory_region_type *p = lang_memory_region_list; + for (p = lang_memory_region_list; + p != ( lang_memory_region_type *)NULL; + p = p->next) { + if (strcmp(p->name, name) == 0) { + return p; + } + } + if (strcmp(name,"*default*")==0) { + /* This is the default region, dig out first one on the list */ + if (lang_memory_region_list != (lang_memory_region_type*)NULL){ + return lang_memory_region_list; + } + } + { + lang_memory_region_type *new = + (lang_memory_region_type *)ldmalloc(sizeof(lang_memory_region_type)); + new->name = name; + new->next = (lang_memory_region_type *)NULL; + + *lang_memory_region_list_tail = new; + lang_memory_region_list_tail = &new->next; + new->origin = 0; + new->length = ~0; + new->current = 0; + return new; + } +} + + + +lang_output_section_statement_type * +lang_output_section_find(name) +char *name; +{ + lang_statement_union_type *u; + lang_output_section_statement_type *lookup; + + for (u = lang_output_section_statement.head; + u != (lang_statement_union_type *)NULL; + u = lookup->next) + { + lookup = &u->output_section_statement; + if (strcmp(name, lookup->name)==0) { + return lookup; + } + } + return (lang_output_section_statement_type *)NULL; +} + +lang_output_section_statement_type * +lang_output_section_statement_lookup(name) +char *name; + +{ + lang_output_section_statement_type *lookup; + lookup =lang_output_section_find(name); + if (lookup == (lang_output_section_statement_type *)NULL) { + + lookup =(lang_output_section_statement_type *) + new_stat(lang_output_section_statement, stat_ptr); + lookup->region = (lang_memory_region_type *)NULL; + lookup->fill = 0; + lookup->block_value = 1; + lookup->name = name; + + lookup->next = (lang_statement_union_type*)NULL; + lookup->bfd_section = (asection *)NULL; + lookup->processed = false; + lookup->addr_tree = (etree_type *)NULL; + lang_list_init(&lookup->children); + + lang_statement_append(&lang_output_section_statement, + (lang_statement_union_type *)lookup, + &lookup->next); + } + return lookup; +} + + + + + +static void + print_flags(outfile, ignore_flags) +FILE *outfile; +lang_section_flags_type *ignore_flags; +{ + fprintf(outfile,"("); +#if 0 + if (flags->flag_read) fprintf(outfile,"R"); + if (flags->flag_write) fprintf(outfile,"W"); + if (flags->flag_executable) fprintf(outfile,"X"); + if (flags->flag_loadable) fprintf(outfile,"L"); +#endif + fprintf(outfile,")"); +} + +void +lang_map(outfile) + FILE *outfile; +{ + lang_memory_region_type *m; + fprintf(outfile,"**MEMORY CONFIGURATION**\n\n"); + + fprintf(outfile,"name\t\torigin\t\tlength\t\tattributes\n"); + for (m = lang_memory_region_list; + m != (lang_memory_region_type *)NULL; + m = m->next) + { + fprintf(outfile,"%-16s", m->name); + + fprintf(outfile,"%08lx\t%08lx\t", m->origin, m->length); + print_flags(outfile, &m->flags); + fprintf(outfile,"\n"); + } + fprintf(outfile,"\n\n**LINK EDITOR MEMORY MAP**\n\n"); + fprintf(outfile,"output\t\tinput\t\tvirtual\n"); + fprintf(outfile,"section\t\tsection\t\taddress\tsize\n\n"); + + print_statements(); + +} + +/* + * + */ +static void init_os(s) +lang_output_section_statement_type *s; +{ + section_userdata_type *new = + (section_userdata_type *) + ldmalloc(sizeof(section_userdata_type)); + + s->bfd_section = bfd_make_section(output_bfd, s->name); + s->bfd_section->output_section = s->bfd_section; + s->bfd_section->flags = SEC_NO_FLAGS; + /* We initialize an output sections output offset to minus its own */ + /* vma to allow us to output a section through itself */ + s->bfd_section->output_offset = 0; + get_userdata( s->bfd_section) = new; +} + +static void +wild_doit(ptr, section,output, file) +lang_statement_list_type *ptr; +asection *section; +lang_output_section_statement_type *output; +lang_input_statement_type *file; +{ + if(output->bfd_section == (asection *)NULL) + { + init_os(output); + } + + if (section != (asection *)NULL + && section->output_section == (asection *)NULL) { + /* Add a section reference to the list */ + lang_input_section_type *new = new_stat(lang_input_section, ptr); + + new->section = section; + new->ifile = file; + section->output_section = output->bfd_section; + section->output_section->flags |= section->flags; + if (section->alignment_power > output->bfd_section->alignment_power) { + output->bfd_section->alignment_power = section->alignment_power; + } + + } +} + +static asection * +our_bfd_get_section_by_name(abfd, section) +bfd *abfd; +char *section; +{ + return bfd_get_section_by_name(abfd, section); + +} +static void +wild_section(ptr, section, file , output) +lang_wild_statement_type *ptr; +char *section; +lang_input_statement_type *file; +lang_output_section_statement_type *output; +{ + asection *s; + if (section == (char *)NULL) { + /* Do the creation to all sections in the file */ + for (s = file->the_bfd->sections; s != (asection *)NULL; s=s->next) { + wild_doit(&ptr->children, s, output, file); + } + } + else { + /* Do the creation to the named section only */ + wild_doit(&ptr->children, + our_bfd_get_section_by_name(file->the_bfd, section), + output, file); + } + + + +} + + + +static +lang_input_statement_type *lookup_name(name, target) +char *name; +char *target; +{ + lang_input_statement_type *search; + for(search = (lang_input_statement_type *)input_file_chain.head; + search != (lang_input_statement_type *)NULL; + search = (lang_input_statement_type *)search->next_real_file) + { + if (search->filename == (char *)NULL && name == (char *)NULL) { + return search; + } + if (search->filename != (char *)NULL && name != (char *)NULL) { + if (strcmp(search->filename, name) == 0) { + Q_read_file_symbols(search); + return search; + } + } + } + + /* There isn't an afile entry for this file yet, this must be */ + /* because the name has only appeared inside a load script and not */ + /* on the command line */ + search = new_afile(name, lang_input_file_is_file_enum, target); + Q_read_file_symbols(search); + return search; +} + +static void + +wild(s, section, file, target, output) +lang_wild_statement_type *s; +char *section; +char *file; +char *target; +lang_output_section_statement_type *output; +{ + lang_input_statement_type *f; + if (file == (char *)NULL) { + /* Perform the iteration over all files in the list */ + for (f = (lang_input_statement_type *)file_chain.head; + f != (lang_input_statement_type *)NULL; + f = (lang_input_statement_type *)f->next) { + wild_section(s, section, f, output); + } + } + else { + /* Perform the iteration over a single file */ + wild_section( s, section, lookup_name(file, target), output); + } +} + +/* + read in all the files + */ +static bfd * +open_output(name, target) +char *name; +char *target; +{ + extern char *output_filename; + bfd * output = bfd_openw(name, target); + output_filename = name; + if (output == (bfd *)NULL) + { + if (bfd_error == invalid_target) { + info("%P%F target %s not found\n", target); + } + info("%P%F problem opening output file %s, %E", name); + } + + output->flags |= D_PAGED; + bfd_set_format(output, bfd_object); + return output; +} +extern char *default_target; +static void +lang_phase_0(sh,target) +lang_statement_union_type *sh; +char *target; +{ + lang_statement_union_type *s = (lang_statement_union_type *)sh; + for (; s != (lang_statement_union_type *)NULL ; s = s->next) + { + switch (s->header.type) { + case lang_output_section_statement_enum: + lang_phase_0(s->output_section_statement.children.head, + target); + break; + case lang_output_statement_enum: +#if 1 + output_bfd = open_output(s->output_statement.name, + target == (char *)NULL ? + default_target : target); + ldemul_set_output_arch(); +#endif + break; + case lang_target_statement_enum: + target = s->target_statement.target; + break; + case lang_wild_statement_enum: + /* Maybe we should load the file's symbols */ + if (s->wild_statement.filename) { + (void) lookup_name(s->wild_statement.filename, target); + } + break; + /* Attatch this to the current output section */ + case lang_common_statement_enum: + case lang_fill_statement_enum: + case lang_input_section_enum: + case lang_object_symbols_statement_enum: + case lang_address_statement_enum: + case lang_data_statement_enum: + break; + case lang_afile_asection_pair_statement_enum: + + FAIL(); + break; + + case lang_input_statement_enum: + if (s->input_statement.real == true) { + s->input_statement.target = target; + lookup_name(s->input_statement.filename, target); + } + break; + case lang_assignment_statement_enum: +#if 0 + (void) exp_fold_tree(s->assignment_statement.exp, + output_section, + false); +#endif + break; + + case lang_padding_statement_enum: + + break; + } + } + +} + +/* If there are [COMMONS] statements, put a wild one into the bss section */ + +static void +lang_reasonable_defaults() +{ + default_common_section = + lang_output_section_statement_lookup(".bss"); + if (placed_commons == false) { + lang_wild_statement_type *new = + new_stat(lang_wild_statement, + &default_common_section->children); + new->section_name = "COMMON"; + new->filename = (char *)NULL; + lang_list_init(&new->children); + } +} + +static void lang() +{ + if (had_script == false) { + parse_line(ldemul_get_script()); + } + + lang_reasonable_defaults(); + lang_phase_0(statement_list.head,default_target); +} + + +/* Open input files and attatch to output sections */ +static void +lang_open_input(s, target, output_section_statement) +lang_statement_union_type *s; +char *target; +lang_output_section_statement_type *output_section_statement; +{ + for (; s != (lang_statement_union_type *)NULL ; s = s->next) + { + switch (s->header.type) { + case lang_wild_statement_enum: + wild(&s->wild_statement, s->wild_statement.section_name, + s->wild_statement.filename, target, + output_section_statement); + + break; + + case lang_output_section_statement_enum: + lang_open_input(s->output_section_statement.children.head, + target, + &s->output_section_statement); + break; + case lang_output_statement_enum: + break; + case lang_target_statement_enum: + target = s->target_statement.target; + break; + case lang_common_statement_enum: + case lang_fill_statement_enum: + case lang_input_section_enum: + case lang_object_symbols_statement_enum: + case lang_data_statement_enum: + break; + case lang_afile_asection_pair_statement_enum: + FAIL(); + break; + + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + + break; + case lang_address_statement_enum: + /* Mark the specified section with the supplied address */ + { + lang_output_section_statement_type *os = + lang_output_section_statement_lookup + (s->address_statement.section_name); + os->addr_tree = s->address_statement.address; + } + break; + case lang_input_statement_enum: + /* A standard input statement, has no wildcards */ + /* Q_read_file_symbols(&s->input_statement);*/ + break; + } + } +} + + + + + +static void +print_output_section_statement(output_section_statement) +lang_output_section_statement_type *output_section_statement; +{ + asection *section = output_section_statement->bfd_section; + print_nl(); + print_section(output_section_statement->name); + + if (section) { + print_dot = section->vma; + print_space(); + print_section(""); + print_space(); + print_address(section->vma); + print_space(); + print_size(section->size); + print_space(); + print_alignment(section->alignment_power); + print_space(); +#if 0 + printf("%s flags", output_section_statement->region->name); + print_flags(stdout, &output_section_statement->flags); +#endif + + } + else { + printf("No attached output section"); + } + print_nl(); + print_statement(output_section_statement->children.head, + output_section_statement); + +} + +static void +print_assignment(assignment, output_section) +lang_assignment_statement_type *assignment; +lang_output_section_statement_type *output_section; +{ + etree_value_type result; + print_section(""); + print_space(); + print_section(""); + print_space(); + print_address(print_dot); + print_space(); + result = exp_fold_tree(assignment->exp->assign.src, + output_section, + lang_final_phase_enum, + print_dot, + &print_dot); + + if (result.valid) { + print_address(result.value); + } + else + { + printf("*undefined*"); + } + print_space(); + exp_print_tree(stdout, assignment->exp); + printf("\n"); +} + +static void +print_input_statement(statm) +lang_input_statement_type *statm; +{ + printf("LOAD %s\n",statm->filename); +} + +static void print_symbol(q) +asymbol *q; +{ + print_section(""); + printf(" "); + print_section(""); + printf(" "); + print_address(outside_symbol_address(q)); + printf(" %s", q->name ? q->name : " "); + print_nl(); +} +static void +print_input_section(in) +lang_input_section_type *in; +{ + asection *i = in->section; + + if(i->size != 0) { + print_section(""); + printf(" "); + print_section(i->name); + printf(" "); + if (i->output_section) { + print_address(i->output_section->vma + i->output_offset); + printf(" "); + print_size(i->size); + printf(" "); + print_alignment(i->alignment_power); + printf(" "); + if (in->ifile) { + bfd *abfd = in->ifile->the_bfd; + printf(" %s ",abfd->xvec->name); + if(abfd->my_archive != (bfd *)NULL) { + printf("[%s]%s", abfd->my_archive->filename, + abfd->filename); + } + else { + printf("%s", abfd->filename); + } + print_nl(); + + /* Find all the symbols in this file defined in this section */ + { + asymbol **p; + for (p = in->ifile->asymbols; *p; p++) { + asymbol *q = *p; + + if (bfd_get_section(q) == i && q->flags & BSF_GLOBAL) { + print_symbol(q); + } + } + } + } + else { + print_nl(); + } + + + print_dot = outside_section_address(i) + i->size; + } + else { + printf("No output section allocated\n"); + } + } +} +static void +print_common_statement() +{ + ldsym_type *lgs; + print_section(""); + print_space(); + print_section(common_section.output_section->name); + print_space(); + print_address(common_section.output_offset + + common_section.output_section->vma); + print_space(); + print_size(common_section.size); + print_space(); + printf("(common)"); + print_nl(); + /* Print out all the global symbols */ + + + for (lgs = symbol_head; lgs != (ldsym_type *)NULL; lgs = + lgs->next) { + if (lgs->sdefs_chain) { + asymbol *def = *(lgs->sdefs_chain); + if (def->section == &common_section) { + print_symbol(def); + } + } + + } + print_dot = common_section.output_offset + + common_section.output_section->vma + common_section.size; + + +} +static void +print_fill_statement(fill) +lang_fill_statement_type *fill; +{ + printf("FILL mask "); + print_fill( fill->fill); +} + +static void +print_data_statement(data) +lang_data_statement_type *data; +{ +/* bfd_vma value; */ + print_section(""); + print_space(); + print_section(""); + print_space(); + ASSERT(print_dot == data->output_vma); + + print_address(data->output_vma); + print_space(); + print_address(data->value); + print_space(); + switch (data->type) { + case BYTE : + printf("BYTE "); + print_dot += BYTE_SIZE; + break; + case SHORT: + printf("SHORT "); + print_dot += SHORT_SIZE; + break; + case LONG: + printf("LONG "); + print_dot += LONG_SIZE; + break; + } + + exp_print_tree(stdout, data->exp); + + printf("\n"); +} + + +static void +print_padding_statement(s) +lang_padding_statement_type *s; +{ + print_section(""); + print_space(); + print_section("*fill*"); + print_space(); + print_address(s->output_offset + s->output_section->vma); + print_space(); + print_size(s->size); + print_space(); + print_fill(s->fill); + print_nl(); +} + +static void print_wild_statement(w,os) +lang_wild_statement_type *w; +lang_output_section_statement_type *os; +{ + if (w->filename != (char *)NULL) { + printf("%s",w->filename); + } + else { + printf("*"); + } + if (w->section_name != (char *)NULL) { + printf("(%s)",w->section_name); + } + else { + printf("(*)"); + } + print_nl(); + print_statement(w->children.head, os); + +} +static void +print_statement(s, os) +lang_statement_union_type *s; +lang_output_section_statement_type *os; +{ + while (s) { + switch (s->header.type) { + case lang_wild_statement_enum: + print_wild_statement(&s->wild_statement, os); + break; + default: + printf("Fail with %d\n",s->header.type); + FAIL(); + break; + case lang_address_statement_enum: + printf("address\n"); + break; + case lang_common_statement_enum: + print_common_statement(); + break; + case lang_object_symbols_statement_enum: + printf("object symbols\n"); + break; + case lang_fill_statement_enum: + print_fill_statement(&s->fill_statement); + break; + case lang_data_statement_enum: + print_data_statement(&s->data_statement); + break; + + + case lang_input_section_enum: + print_input_section(&s->input_section); + break; + case lang_padding_statement_enum: + print_padding_statement(&s->padding_statement); + break; + case lang_output_section_statement_enum: + print_output_section_statement(&s->output_section_statement); + break; + case lang_assignment_statement_enum: + print_assignment(&s->assignment_statement, + os); + break; + + + case lang_target_statement_enum: + printf("TARGET(%s)\n", s->target_statement.target); + break; + case lang_output_statement_enum: + printf("OUTPUT(%s)\n", s->output_statement.name); + break; + case lang_input_statement_enum: + print_input_statement(&s->input_statement); + break; + case lang_afile_asection_pair_statement_enum: + FAIL(); + break; + } + s = s->next; + } +} + + +static void +print_statements() +{ + print_statement(statement_list.head, + (lang_output_section_statement_type *)NULL); +} + +static bfd_vma +insert_pad(this_ptr, fill, power, output_section_statement, dot) +lang_statement_union_type **this_ptr; +fill_type fill; +unsigned int power; +asection * output_section_statement; +bfd_vma dot; +{ + /* Align this section first to the + input sections requirement, then + to the output section's requirement. + If this alignment is > than any seen before, + then record it too. Perform the alignment by + inserting a magic 'padding' statement. + */ + + unsigned int alignment_needed = align_power(dot, power) - dot; + + if (alignment_needed != 0) + { + lang_statement_union_type *new = + (lang_statement_union_type *) + ldmalloc(sizeof(lang_padding_statement_type)); + /* Link into existing chain */ + new->header.next = *this_ptr; + *this_ptr = new; + new->header.type = lang_padding_statement_enum; + new->padding_statement.output_section = output_section_statement; + new->padding_statement.output_offset = + dot - output_section_statement->vma; + new->padding_statement.fill = fill; + new->padding_statement.size = alignment_needed; + } + + + /* Remember the most restrictive alignment */ + if (power > output_section_statement->alignment_power) { + output_section_statement->alignment_power = power; + } + output_section_statement->size += alignment_needed; + return alignment_needed + dot; + +} + +/* + size_common runs run though each global symboxl, and works + out how big the common section will be. + */ + +static bfd_vma +size_common(output_section_statement, this_ptr, dot) +lang_output_section_statement_type *output_section_statement; +lang_statement_union_type **this_ptr; +bfd_vma dot; +{ + extern ldsym_type *symbol_head; + ldsym_type *sp; + /* Make sure that each symbol is only defined once. + Allocate common symbols + Make the ref chain point to the defining asymbol. + */ + /* Now, for each symbol, verify that it is defined globally at most once. + Put the global value into the symbol entry. + Common symbols are allocated here, in the BSS section. + Each defined symbol is given a '->defined' field + which is the correct N_ code for its definition, + except in the case of common symbols with -r. + Then make all the references point at the symbol entry + instead of being chained together. */ + + + common_section.name = output_section_statement->bfd_section->name; + common_section.output_section = output_section_statement->bfd_section; + common_section.output_offset = + dot - output_section_statement->bfd_section->vma; + if (config.relocateable_output == false || + command_line.force_common_definition== true) { + dot = insert_pad(this_ptr, + 0x0, 4, output_section_statement->bfd_section, dot); + + for (sp = symbol_head; sp != (ldsym_type *)NULL; sp = sp->next) + { + /* Attatch this symbol to the correct output section*/ + + /* Allocate as common if wanted */ + + if (sp->scoms_chain ) + + { + unsigned long com = (*(sp->scoms_chain))->value; + /* Work out what alignment this common item s + hould be put on. Anything < int is int aligned, + anything bigger is self aligned, + up to the restriction of the machine */ + + unsigned int align = sizeof(int); + + /* Round up size of object to nearest int */ + com = ALIGN(com, sizeof(int)); + /* See what alignment is necessary -*/ + if (com) { + while ((com & align)==0) align <<=1; + /* FIXME */ + if (align > 8) { + align = 8; + } + } + dot = ALIGN(dot, align); + + + /* Transmogrify this from a common symbol + into a definition of a symbol in common + */ + sp->sdefs_chain = sp->scoms_chain; + + { + asymbol *com_ptr = *(sp->sdefs_chain); + + sp->scoms_chain = (asymbol **)NULL; + commons_pending--; + /* Assign address, but keep section relative */ + + /* Force the symbol to belong in the bss section */ + com_ptr->flags = BSF_EXPORT | BSF_GLOBAL ; + com_ptr->section = &common_section; + common_section.size += com; + if (write_map) + { + printf ("Allocating common %s: %lx at %lx\n", + sp->name, + com, + com_ptr->value); + } + com_ptr->value = common_section.size; + } + } + } + } + if (dot > + (common_section.output_section->vma + + common_section.output_section->size)) { + common_section.output_section->size = + dot - common_section.output_section->vma; + } + return dot + common_section.size; +} + +static bfd_vma +size_input_section( this_ptr, output_section_statement, fill, dot) +lang_statement_union_type **this_ptr; +lang_output_section_statement_type*output_section_statement; +unsigned short fill; +bfd_vma dot; +{ + lang_input_section_type *is = &((*this_ptr)->input_section); + asection *i = is->section; + + dot = insert_pad(this_ptr, fill, i->alignment_power, + output_section_statement->bfd_section, dot); + + /* remember the largest size so we can malloc the largest area */ + /* needed for the output stage */ + if (i->size > largest_section) { + largest_section = i->size; + } + + /* Remember where in the output section this input section goes */ + i->output_offset = dot - output_section_statement->bfd_section->vma; + + /* Mark how big the output section must be to contain this now */ + dot += i->size; + output_section_statement->bfd_section->size = + dot - output_section_statement->bfd_section->vma; + + + return dot ; +} + + +/* Work out the size of the output sections + from the sizes of the input sections */ +static bfd_vma +lang_size_sections(s, output_section_statement, prev, fill, dot) +lang_statement_union_type *s; +lang_output_section_statement_type * output_section_statement; +lang_statement_union_type **prev; +unsigned short fill; +bfd_vma dot; +{ + /* Size up the sections from their constituent parts */ + for (; s != (lang_statement_union_type *)NULL ; s = s->next) + { + switch (s->header.type) { + case lang_output_section_statement_enum: + { + bfd_vma after; + lang_output_section_statement_type *os = + &(s->output_section_statement); + /* The start of a section */ + + if (os->addr_tree == (etree_type *)NULL) { + /* No address specified for this section, get one + from the region specification + */ + if (os->region == (lang_memory_region_type *)NULL) { + os->region = lang_memory_region_lookup("*default*"); + } + dot = os->region->current; + } + else { + etree_value_type r ; + r = exp_fold_tree(os->addr_tree, + (lang_output_section_statement_type *)NULL, + lang_allocating_phase_enum, + dot, &dot); + if (r.valid == false) { + info("%F%S: non constant address expression for section %s\n", + os->name); + } + dot = r.value; + } + /* The section starts here */ + /* First, align to what the section needs */ + + dot = align_power(dot, os->bfd_section->alignment_power); + os->bfd_section->vma = dot; + os->bfd_section->output_offset = 0; + + (void) lang_size_sections(os->children.head, os, &os->children.head, + os->fill, dot); + /* Ignore the size of the input sections, use the vma and size to */ + /* align against */ + + + after = ALIGN(os->bfd_section->vma + + os->bfd_section->size, + os->block_value) ; + + + os->bfd_section->size = after - os->bfd_section->vma; + dot = os->bfd_section->vma + os->bfd_section->size; + os->processed = true; + + /* Replace into region ? */ + if (os->addr_tree == (etree_type *)NULL + && os->region !=(lang_memory_region_type*)NULL ) { + os->region->current = dot; + } + } + + break; + + case lang_data_statement_enum: + { + unsigned int size; + s->data_statement.output_vma = dot; + s->data_statement.output_section = + output_section_statement->bfd_section; + + switch (s->data_statement.type) { + case LONG: + size = LONG_SIZE; + break; + case SHORT: + size = SHORT_SIZE; + break; + case BYTE: + size = BYTE_SIZE; + break; + + } + dot += size; + output_section_statement->bfd_section->size += size; + } + break; + + case lang_wild_statement_enum: + + dot = lang_size_sections(s->wild_statement.children.head, + output_section_statement, + &s->wild_statement.children.head, + + fill, dot); + + break; + + case lang_object_symbols_statement_enum: + create_object_symbols = output_section_statement; + break; + case lang_output_statement_enum: + + case lang_target_statement_enum: + break; + case lang_common_statement_enum: + dot = size_common(output_section_statement, prev, dot); + + break; + + case lang_input_section_enum: + dot = size_input_section(prev, + output_section_statement, + output_section_statement->fill, dot); + break; + case lang_input_statement_enum: + break; + case lang_fill_statement_enum: + fill = s->fill_statement.fill; + break; + case lang_assignment_statement_enum: + { + bfd_vma newdot = dot; + exp_fold_tree(s->assignment_statement.exp, + output_section_statement, + lang_allocating_phase_enum, + dot, + &newdot); + + if (newdot != dot) + /* We've been moved ! so insert a pad */ + { + lang_statement_union_type *new = + (lang_statement_union_type *) + ldmalloc(sizeof(lang_padding_statement_type)); + /* Link into existing chain */ + new->header.next = *prev; + *prev = new; + new->header.type = lang_padding_statement_enum; + new->padding_statement.output_section = + output_section_statement->bfd_section; + new->padding_statement.output_offset = + dot - output_section_statement->bfd_section->vma; + new->padding_statement.fill = fill; + new->padding_statement.size = newdot - dot; + output_section_statement->bfd_section->size += + new->padding_statement.size; + dot = newdot; + } + } + + break; + case lang_padding_statement_enum: + FAIL(); + break; + default: + FAIL(); + break; + case lang_address_statement_enum: + break; + } + prev = &s->header.next; + } + return dot; +} + + +static bfd_vma +lang_do_assignments(s, output_section_statement, fill, dot) +lang_statement_union_type *s; +lang_output_section_statement_type * output_section_statement; +unsigned short fill; +bfd_vma dot; +{ + + for (; s != (lang_statement_union_type *)NULL ; s = s->next) + { + switch (s->header.type) { + case lang_output_section_statement_enum: + { + lang_output_section_statement_type *os = + &(s->output_section_statement); + dot = os->bfd_section->vma; + (void) lang_do_assignments(os->children.head, os, os->fill, dot); + dot = os->bfd_section->vma + os->bfd_section->size; + } + break; + case lang_wild_statement_enum: + + dot = lang_do_assignments(s->wild_statement.children.head, + output_section_statement, + fill, dot); + + break; + + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_common_statement_enum: + break; + case lang_data_statement_enum: + { + etree_value_type value ; + value = exp_fold_tree(s->data_statement.exp, + 0, lang_final_phase_enum, dot, &dot); + s->data_statement.value = value.value; + if (value.valid == false) info("%F%P: Invalid data statement\n"); + } + switch (s->data_statement.type) { + case LONG: + dot += LONG_SIZE; + break; + case SHORT: + dot += SHORT_SIZE; + break; + case BYTE: + dot += BYTE_SIZE; + break; + } + break; + case lang_input_section_enum: + { + asection *in = s->input_section.section; + dot += in->size; + } + break; + + case lang_input_statement_enum: + break; + case lang_fill_statement_enum: + fill = s->fill_statement.fill; + break; + case lang_assignment_statement_enum: + { + exp_fold_tree(s->assignment_statement.exp, + output_section_statement, + lang_final_phase_enum, + dot, + &dot); + } + + break; + case lang_padding_statement_enum: + dot += s->padding_statement.size; + break; + default: + FAIL(); + break; + case lang_address_statement_enum: + break; + } + + } + return dot; +} + + + +static void lang_relocate_globals() +{ + + /* + Each ldsym_type maintains a chain of pointers to asymbols which + references the definition. Replace each pointer to the referenence + with a pointer to only one place, preferably the definition. If + the defintion isn't available then the common symbol, and if + there isn't one of them then choose one reference. + */ + + FOR_EACH_LDSYM(lgs) { + asymbol *it; + if (lgs->sdefs_chain) { + it = *(lgs->sdefs_chain); + } + else if (lgs->scoms_chain != (asymbol **)NULL) { + it = *(lgs->scoms_chain); + } + else if (lgs->srefs_chain != (asymbol **)NULL) { + it = *(lgs->srefs_chain); + } + else { + FAIL(); + } + if (it != (asymbol *)NULL) + { + asymbol **ptr= lgs->srefs_chain; + + while (ptr != (asymbol **)NULL) { + asymbol *ref = *ptr; + *ptr = it; + ptr = (asymbol **)(ref->udata); + } + } + } +} + + + +/* now that all the jiggery pokery is finished, copy important data from + * out internal form to the bfd way. Also create a section + * for each dummy file + */ + +static void +lang_create_output_section_statements() +{ + lang_statement_union_type*os; + for (os = lang_output_section_statement.head; + os != (lang_statement_union_type*)NULL; + os = os->output_section_statement.next) { + lang_output_section_statement_type *s = + &os->output_section_statement; + init_os(s); + } + script_file->the_bfd->sections = output_bfd->sections; +} + +static void +lang_finish() +{ + ldsym_type *lgs; + + if (entry_symbol == (char *)NULL) { + /* No entry has been specified, look for start */ + entry_symbol = "start"; + } + lgs = ldsym_get_soft(entry_symbol); + if (lgs && lgs->sdefs_chain) { + asymbol *sy = *(lgs->sdefs_chain); + /* We can set the entry address*/ + bfd_set_start_address(output_bfd, + outside_symbol_address(sy)); + + } + else { + /* Can't find anything reasonable, + use the first address in the text section + */ + asection *ts = bfd_get_section_by_name(output_bfd, ".text"); + if (ts) { + bfd_set_start_address(output_bfd, ts->vma); + } + } +} + +/* By now we know the target architecture, and we may have an */ +/* ldfile_output_machine_name */ +static void +lang_check() +{ + lang_statement_union_type *file; + + + for (file = file_chain.head; + file != (lang_statement_union_type *)NULL; + file=file->input_statement.next) + { + /* Inspect the architecture and ensure we're linking like + with like + */ + + if (bfd_arch_compatible( file->input_statement.the_bfd, + output_bfd, + &ldfile_output_architecture, + &ldfile_output_machine)) { + bfd_set_arch_mach(output_bfd, + ldfile_output_architecture, ldfile_output_machine); + } + else { + enum bfd_architecture this_architecture = + bfd_get_architecture(file->input_statement.the_bfd); + unsigned long this_machine = + bfd_get_machine(file->input_statement.the_bfd); + + info("%I: architecture %s", + file, + bfd_printable_arch_mach(this_architecture, this_machine)); + info(" incompatible with output %s\n", + bfd_printable_arch_mach(ldfile_output_architecture, + ldfile_output_machine)); + ldfile_output_architecture = this_architecture; + ldfile_output_machine = this_machine; + bfd_set_arch_mach(output_bfd, + ldfile_output_architecture, + ldfile_output_machine); + + + } + } +} + + +/* + * run through all the global common symbols and tie them + * to the output section requested. + */ + +static void +lang_common() +{ + ldsym_type *lgs; + if (config.relocateable_output == false || + command_line.force_common_definition== true) { + for (lgs = symbol_head; + lgs != (ldsym_type *)NULL; + lgs=lgs->next) + { + asymbol *com ; + size_t size; + size_t align; + if (lgs->scoms_chain != (asymbol **)NULL) { + + com = *(lgs->scoms_chain); + size = com->value; + align = sizeof(int); + /* Round up size of object to nearest int */ + size = ALIGN(size, sizeof(int)); + /* Force alignment */ + if (size) { + while ((size & align)==0) align<<=1; + if (align > 8) { + align = 8; + } + } + /* Change from a common symbol into a definition of + a symbol */ + lgs->sdefs_chain = lgs->scoms_chain; + lgs->scoms_chain = (asymbol **)NULL; + commons_pending--; + /* Point to the correct common section */ + com->section = + ((lang_input_statement_type *) + (com->the_bfd->usrdata))->common_section; + /* Fix the size of the common section */ + + + com->flags = BSF_EXPORT | BSF_GLOBAL; + + if (write_map) + { + printf ("Allocating common %s: %x at %x\n", + lgs->name, + (unsigned) size, + (unsigned) com->section->size); + } + com->value = com->section->size; + com->section->size += size; + } + } + } +} + +/* +run through the input files and ensure that every input +section has somewhere to go. If one is found without +a destination then create an input request and place it +into the statement tree. +*/ + +static void lang_place_orphans() +{ + lang_input_statement_type *file; + for (file = (lang_input_statement_type*)file_chain.head; + file != (lang_input_statement_type*)NULL; + file = (lang_input_statement_type*)file->next) { + asection *s; + for (s = file->the_bfd->sections; + s != (asection *)NULL; + s = s->next) { + if ( s->output_section == (asection *)NULL) { + /* This section of the file is not attatched, root + around for a sensible place for it to go */ + + if (file->common_section == s) { + /* This is a lonely common section which must + have come from an archive. We attatch to the + section with the wildcard */ + wild_doit(&default_common_section->children, s, + default_common_section, file); + } + else { + lang_output_section_statement_type *os = + lang_output_section_statement_lookup(s->name); + + wild_doit(&os->children, s, os, file); + } + } + } + + } +} + + +/* + * phase_2 + * + * peformed after every file has been opened and symbols read + */ +static void +lang_phase_2() +{ + lang_init2(); + + lang_create_output_section_statements(); + lang_open_input(statement_list.head, (char *)NULL, + ( lang_output_section_statement_type *)NULL); + lang_place_orphans(); + lang_common(); + + ldemul_before_allocation(); + + lang_size_sections(statement_list.head, + (lang_output_section_statement_type *)NULL, + &(statement_list.head), 0, (bfd_vma)0); + ldemul_after_allocation(); + /* Do it once again now that we know the sizes of everything */ + + lang_do_assignments(statement_list.head, + (lang_output_section_statement_type *)NULL, + 0, (bfd_vma)0); + + + + lang_check(); + + lang_relocate_globals(); + + + lang_finish(); +} + + + + +void +lang_set_flags(ptr, flags) +lang_section_flags_type *ptr; +char *flags; +{ + boolean state = true; + ptr->flag_read = false; + ptr->flag_write = false; + ptr->flag_executable = false; + ptr->flag_loadable= false; + while (*flags) + { + if (*flags == '!') { + state = false; + flags++; + } + else state = true; + switch (*flags) { + case 'R': + ptr->flag_read = state; + break; + case 'W': + ptr->flag_write = state; + break; + case 'X': + ptr->flag_executable= state; + break; + case 'L': + ptr->flag_loadable= state; + break; + default: + info("%P%F illegal syntax in flags\n"); + break; + } + flags++; + } +} + + + +void +lang_for_each_file(func) +void (*func)(); +{ + lang_input_statement_type *f; + for (f = (lang_input_statement_type *)file_chain.head; + f != (lang_input_statement_type *)NULL; + f = (lang_input_statement_type *)f->next) + { + func(f); + } +} + + +void +lang_for_each_input_section(func) +void (*func)(); +{ + lang_input_statement_type *f; + for (f = (lang_input_statement_type *)file_chain.head; + f != (lang_input_statement_type *)NULL; + f = (lang_input_statement_type *)f->next) + { + asection *s; + for (s = f->the_bfd->sections; + s != (asection *)NULL; + s = s->next) { + func(f->the_bfd, s); + } + } +} + + + +void +ldlang_add_file(entry) +lang_input_statement_type *entry; +{ + lang_has_input_file = true; + lang_statement_append(&file_chain, + (lang_statement_union_type *)entry, + &entry->next); +} + + + +void +lang_add_output(name) +char *name; +{ + lang_output_statement_type *new = new_stat(lang_output_statement, + stat_ptr); + new->name = name; + had_output_filename = true; +} + + +static lang_output_section_statement_type *current_section; + +void +lang_enter_output_section_statement(output_section_statement_name, +address_exp, +block_value) +char *output_section_statement_name; +etree_type *address_exp; +bfd_vma block_value; +{ + lang_output_section_statement_type *os; + current_section = + os = + lang_output_section_statement_lookup(output_section_statement_name); + + + /* Add this statement to tree */ + /* add_statement(lang_output_section_statement_enum, + output_section_statement);*/ + /* Make next things chain into subchain of this */ + + if (os->addr_tree == + (etree_type *)NULL) { + os->addr_tree = + address_exp; + } + os->block_value = block_value; + stat_ptr = & os->children; + +} + + +void +lang_final() +{ + if (had_output_filename == false) { + lang_add_output("a.out"); + } + + +} + + + + + +asymbol *create_symbol(name, flags, section) +char *name; +flagword flags; +asection *section; +{ + extern lang_input_statement_type *script_file; + asymbol **def_ptr = (asymbol **)ldmalloc(sizeof(asymbol **)); + /* Add this definition to script file */ + asymbol *def = (asymbol *)bfd_make_empty_symbol(script_file->the_bfd); + def->name = name; + def->udata = 0; + def->flags = flags; + def->section = section; + + *def_ptr = def; + Q_enter_global_ref(def_ptr); + return def; +} + + +void +lang_process() +{ + lang(); + lang_phase_2(); +} + + +/* EXPORTED TO YACC */ +void +lang_section_start(name, address) +char *name; +etree_type *address; +{ + lang_address_statement_type *ad =new_stat(lang_address_statement, stat_ptr); + ad->section_name = name; + ad->address = address; +} +void lang_add_entry(name) +char *name; +{ + entry_symbol = name; +} + +void +lang_add_target(name) +char *name; +{ + lang_target_statement_type *new = new_stat(lang_target_statement, + stat_ptr); + new->target = name; + +} +void +lang_add_wild(section_name, filename) +char *section_name; +char *filename; +{ + lang_wild_statement_type *new = new_stat(lang_wild_statement, + stat_ptr); + + if (section_name != (char *)NULL && strcmp(section_name,"COMMON") == 0) + { + placed_commons = true; + } + new->section_name = section_name; + new->filename = filename; + lang_list_init(&new->children); +} + +void +lang_add_map(name) +char *name; +{ + while (*name) { + switch (*name) { + case 'F': + map_option_f = true; + break; + } + name++; + } +} + +void lang_add_fill(exp) +int exp; +{ + lang_fill_statement_type *new = new_stat(lang_fill_statement, + stat_ptr); + new->fill = exp; +} + +void lang_add_data(type, exp) +int type; +union etree_union *exp; +{ + + lang_data_statement_type *new = new_stat(lang_data_statement, + stat_ptr); + new->exp = exp; + new->type = type; + +} +void +lang_add_assignment(exp) +etree_type *exp; +{ + lang_assignment_statement_type *new = new_stat(lang_assignment_statement, + stat_ptr); + new->exp = exp; +} + +void +lang_add_attribute(attribute) +enum statement_enum attribute; +{ + new_statement(attribute, sizeof(lang_statement_union_type),stat_ptr); +} + + + +void +lang_startup(name) +char *name; +{ + if (startup_file != (char *)NULL) { + info("%P%FMultiple STARTUP files\n"); + } + first_file->filename = name; + first_file->local_sym_name = name; + + startup_file= name; +} +void +lang_float(maybe) +boolean maybe; +{ + lang_float_flag = maybe; +} + +void +lang_leave_output_section_statement(fill, memspec) +bfd_vma fill; +char *memspec; +{ + current_section->fill = fill; + current_section->region = lang_memory_region_lookup(memspec); + stat_ptr = &statement_list; +} + +void +lang_abs_symbol_at_end_of(section, name) +char *section; +char *name; +{ + extern bfd *output_bfd; + extern asymbol *create_symbol(); + asection *s = bfd_get_section_by_name(output_bfd, section); + /* Add a symbol called _end */ + asymbol *def = create_symbol(name, + BSF_GLOBAL | BSF_EXPORT | + BSF_ABSOLUTE, + (asection *)NULL); + if (s != (asection *)NULL) { + def->value = s->vma + s->size; + } + else { + def->value = 0; + } +} + +void +lang_statement_append(list, element, field) +lang_statement_list_type *list; +lang_statement_union_type *element; +lang_statement_union_type **field; +{ + *(list->tail) = element; + list->tail = field; +} + + +static void +lang_for_each_statement_worker(func, s) +void (*func)(); +lang_statement_union_type *s; +{ + for (; s != (lang_statement_union_type *)NULL ; s = s->next) + { + func(s); + + switch (s->header.type) { + case lang_output_section_statement_enum: + lang_for_each_statement_worker + (func, + s->output_section_statement.children.head); + break; + case lang_wild_statement_enum: + lang_for_each_statement_worker + (func, + s->wild_statement.children.head); + break; + case lang_data_statement_enum: + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_common_statement_enum: + case lang_input_section_enum: + case lang_input_statement_enum: + case lang_fill_statement_enum: + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + case lang_address_statement_enum: + break; + default: + FAIL(); + break; + } + } +} + +void lang_for_each_statement(func) +void (*func)(); +{ + lang_for_each_statement_worker(func, + statement_list.head); + +} diff --git a/ld/ldlang.h b/ld/ldlang.h new file mode 100644 index 00000000000..ab102a50df6 --- /dev/null +++ b/ld/ldlang.h @@ -0,0 +1,347 @@ +/* ldlang.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD 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. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +typedef enum { + lang_input_file_is_l_enum, + lang_input_file_is_symbols_only_enum, + lang_input_file_is_marker_enum, + lang_input_file_is_fake_enum, +lang_input_file_is_search_file_enum, + lang_input_file_is_file_enum } lang_input_file_enum_type; + +typedef unsigned short fill_type; +typedef struct statement_list { + union lang_statement_union *head; + union lang_statement_union **tail; +} lang_statement_list_type; + + +typedef struct { + boolean flag_read; + boolean flag_write; + boolean flag_executable; + boolean flag_loadable; +} lang_section_flags_type; + +typedef struct memory_region_struct { + char *name; + struct memory_region_struct *next; + bfd_vma origin; + bfd_offset length; + bfd_vma current; + lang_section_flags_type flags; +} lang_memory_region_type ; + +typedef struct lang_statement_header_struct +{ +union lang_statement_union *next; + enum statement_enum { + lang_output_section_statement_enum, + lang_assignment_statement_enum, + lang_input_statement_enum, + lang_address_statement_enum, + lang_wild_statement_enum, + lang_input_section_enum, + lang_common_statement_enum, + lang_object_symbols_statement_enum, + lang_fill_statement_enum, + lang_data_statement_enum, + lang_target_statement_enum, + lang_output_statement_enum, + lang_padding_statement_enum, + + lang_afile_asection_pair_statement_enum + } type; + +} lang_statement_header_type; + + +typedef struct +{ + lang_statement_header_type header; + union etree_union *exp; +} lang_assignment_statement_type; + + +typedef struct lang_target_statement_struct { + lang_statement_header_type header; + char *target; +} lang_target_statement_type; + + +typedef struct lang_output_statement_struct { + lang_statement_header_type header; + char *name; +} lang_output_statement_type; + + +typedef struct lang_output_section_statement_struct +{ + lang_statement_header_type header; + union etree_union *addr_tree; + lang_statement_list_type children; + char *memspec; + union lang_statement_union *next; + char *name; + unsigned long subsection_alignment; + boolean processed; + + asection *bfd_section; + lang_section_flags_type flags; + struct memory_region_struct *region; + size_t block_value; + fill_type fill; +} lang_output_section_statement_type; + + +typedef struct { + lang_statement_header_type header; +} lang_common_statement_type; + +typedef struct { + lang_statement_header_type header; +} lang_object_symbols_statement_type; + +typedef struct { + lang_statement_header_type header; +fill_type fill; +} lang_fill_statement_type; + +typedef struct { + lang_statement_header_type header; + unsigned int type; + union etree_union *exp; + bfd_vma value; + asection *output_section; + bfd_vma output_vma; +} lang_data_statement_type; + + + + +typedef struct lang_input_statement_struct + { + lang_statement_header_type header; + /* Name of this file. */ + char *filename; + /* Name to use for the symbol giving address of text start */ + /* Usually the same as filename, but for a file spec'd with -l + this is the -l switch itself rather than the filename. */ + char *local_sym_name; + + /* Describe the layout of the contents of the file */ + + /* The file's a.out header. */ + /* struct exec header;*/ + /* Offset in file of GDB symbol segment, or 0 if there is none. */ + int symseg_offset; + + /* Describe data from the file loaded into core */ + + bfd *the_bfd; + + boolean closed; + file_ptr passive_position; + + /* Symbol table of the file. */ + asymbol **asymbols; + unsigned int symbol_count; + + /* Next two used only if `relocatable_output' or if needed for */ + /* output of undefined reference line numbers. */ + /* Text reloc info saved by `write_text' for `coptxtrel'. */ + + + /* Offset in bytes in the output file symbol table + of the first local symbol for this file. Set by `write_file_symbols'. */ + int local_syms_offset; + + /* For library members only */ + + /* For a library, points to chain of entries for the library members. */ + struct lang_input_statement_struct *subfiles; + /* For a library member, offset of the member within the archive. + Zero for files that are not library members. */ + /* int starting_offset;*/ + /* Size of contents of this file, if library member. */ + int total_size; + /* For library member, points to the library's own entry. */ + struct lang_input_statement_struct *superfile; + /* For library member, points to next entry for next member. */ + struct lang_input_statement_struct *chain; + /* Point to the next file - whatever it is, wanders up and down archives */ + union lang_statement_union *next; + /* Point to the next file, but skips archive contents */ + union lang_statement_union *next_real_file; + + boolean is_archive; + + /* 1 if file's header has been read into this structure. */ + boolean header_read_flag; + + /* 1 means search a set of directories for this file. */ + boolean search_dirs_flag; + + /* 1 means this is base file of incremental load. + Do not load this file's text or data. + Also default text_start to after this file's bss. */ + boolean just_syms_flag; + + boolean loaded; + + + /* unsigned int globals_in_this_file;*/ + char *target; + boolean real; + + asection *common_section; + asection *common_output_section; + } lang_input_statement_type; + +typedef struct { + lang_statement_header_type header; + asection *section; + lang_input_statement_type *ifile; +} lang_input_section_type; + + +typedef struct { + lang_statement_header_type header; + asection *section; + union lang_statement_union *file; +} lang_afile_asection_pair_statement_type; + +typedef struct lang_wild_statement_struct { + lang_statement_header_type header; + char *section_name; + char *filename; + lang_statement_list_type children; +} lang_wild_statement_type; + +typedef struct lang_address_statement_struct { + lang_statement_header_type header; + char *section_name; + union etree_union *address; +} lang_address_statement_type; + +typedef struct { + lang_statement_header_type header; + bfd_vma output_offset; + size_t size; + asection *output_section; + fill_type fill; +} lang_padding_statement_type; + +typedef union lang_statement_union +{ + lang_statement_header_type header; + union lang_statement_union *next; + lang_wild_statement_type wild_statement; + lang_data_statement_type data_statement; + lang_address_statement_type address_statement; + lang_output_section_statement_type output_section_statement; + lang_afile_asection_pair_statement_type afile_asection_pair_statement; + lang_assignment_statement_type assignment_statement; + lang_input_statement_type input_statement; + lang_target_statement_type target_statement; + lang_output_statement_type output_statement; + lang_input_section_type input_section; + lang_common_statement_type common_statement; + lang_object_symbols_statement_type object_symbols_statement; + lang_fill_statement_type fill_statement; + lang_padding_statement_type padding_statement; +} lang_statement_union_type; + + + +PROTO(void,lang_init,(void)); +PROTO(struct memory_region_struct ,*lang_memory_region_lookup,(char *)); +PROTO(struct lang_output_section_statement_struct *,lang_output_section_find,(char *)); + +PROTO(void ,lang_map,(struct _iobuf *)); +PROTO(void,lang_set_flags,(lang_section_flags_type *, char *)); +PROTO(void,lang_add_output,(char *)); + +PROTO(void,lang_final,(void)); +PROTO(struct symbol_cache_entry *,create_symbol,(char *, unsigned int, struct sec_struct *)); +PROTO(void ,lang_process,(void)); +PROTO(void ,lang_section_start,(char *, union etree_union *)); +PROTO(void,lang_add_entry,(char *)); +PROTO(void,lang_add_target,(char *)); +PROTO(void,lang_add_wild,(char *, char *)); +PROTO(void,lang_add_map,(char *)); +PROTO(void,lang_add_fill,(int)); +PROTO(void,lang_add_assignment,(union etree_union *)); +PROTO(void,lang_add_attribute,(enum statement_enum)); +PROTO(void,lang_startup,(char *)); +PROTO(void,lang_float,(enum boolean)); +PROTO(void,lang_leave_output_section_statement,(bfd_vma, char *)); +PROTO(void,lang_abs_symbol_at_end_of,(char *, char *)); +PROTO(void,lang_statement_append,(struct statement_list *, union lang_statement_union *, union lang_statement_union **)); +PROTO(void, lang_for_each_file,(void (*dothis)(lang_input_statement_type *))); + +#define LANG_FOR_EACH_ASYMBOL(asymbol) \ + +#define LANG_FOR_EACH_INPUT_STATEMENT(statement) \ + extern lang_statement_list_type file_chain; \ + lang_input_statement_type *statement; \ + for (statement = (lang_input_statement_type *)file_chain.head;\ + statement != (lang_input_statement_type *)NULL; \ + statement = (lang_input_statement_type *)statement->next)\ + +#define LANG_FOR_EACH_INPUT_SECTION(statement, abfd, section, x) \ +{ extern lang_statement_list_type file_chain; \ + lang_input_statement_type *statement; \ + for (statement = (lang_input_statement_type *)file_chain.head;\ + statement != (lang_input_statement_type *)NULL; \ + statement = (lang_input_statement_type *)statement->next)\ + { \ + asection *section; \ + bfd *abfd = statement->the_bfd; \ + for (section = abfd->sections; \ + section != (asection *)NULL; \ + section = section->next) { \ + x; \ + } \ + } \ + } + +#define LANG_FOR_EACH_OUTPUT_SECTION(section, x) \ + { extern bfd *output_bfd; \ + asection *section; \ + for (section = output_bfd->sections; \ + section != (asection *)NULL; \ + section = section->next) \ + { x; } \ + } + + +PROTO(void, lang_process,(void)); +PROTO(void, ldlang_add_file,(lang_input_statement_type *)); + +PROTO(lang_output_section_statement_type *,lang_output_section_find,()); + +PROTO(lang_input_statement_type *, + lang_add_input_file,(char *name, + lang_input_file_enum_type file_type, + char *target)); +PROTO(lang_output_section_statement_type *, +lang_output_section_statement_lookup,(char *name)); diff --git a/ld/ldlex.h b/ld/ldlex.h new file mode 100644 index 00000000000..fe4e017f759 --- /dev/null +++ b/ld/ldlex.h @@ -0,0 +1,26 @@ +/* ldlex.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD 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. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +PROTO(int, lex_input, (void)); +PROTO(void, lex_unput, (int)); +PROTO(int ,yywrap,(void)); +PROTO(void, parse_args,(int, char **)); +PROTO(void, parse_line,(char*)); + diff --git a/ld/ldlex.l b/ld/ldlex.l new file mode 100644 index 00000000000..67af46bc6b2 --- /dev/null +++ b/ld/ldlex.l @@ -0,0 +1,490 @@ +%{ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:50 gumby + * Initial revision + * + * Revision 1.3 1991/03/16 22:27:24 rich + * fish + * + * Revision 1.2 1991/03/15 18:45:55 rich + * foo + * + * Revision 1.1 1991/03/13 00:48:27 chrisb + * Initial revision + * + * Revision 1.6 1991/03/10 09:31:32 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.5 1991/03/09 03:25:49 sac + * Can now parse the -Ur flag + * + * Revision 1.4 1991/03/06 02:26:04 sac + * Added support for constructor sections. + * Remove parsing ambiguity. + * Lint + * + * Revision 1.3 1991/02/22 17:15:14 sac + * Added RCS keywords and copyrights + * +*/ + + + +/*SUPPRESS 529*/ +/*SUPPRESS 26*/ +/*SUPPRESS 29*/ +#define LEXDEBUG +#include "sysdep.h" +#include "bfd.h" + +#include +#include "ldlex.h" + +#include "ld.h" +#include "ldexp.h" +#include "ldgram.tab.h" +#include "ldmisc.h" + +#undef input +#undef unput +#define input lex_input +#define unput lex_unput +int debug; +extern boolean ldgram_want_filename; +extern boolean ldgram_mustbe_filename; +extern boolean ldgram_mustbe_symbolname; +static char *command_line; + +extern int fgetc(); +extern int yyparse(); + +typedef struct { + char *name; +int value; +} keyword_type; +#define RTOKEN(x) { yylval.token = x; return x; } +keyword_type keywords[] = +{ +"MEMORY",MEMORY, +"ORIGIN",ORIGIN, +"BLOCK",BLOCK, +"LENGTH",LENGTH, +"ALIGN",ALIGN_K, +"SUBSECTION_ALIGN",SUBSECTION_ALIGN, +"ADDR",ADDR, +"ENTRY",ENTRY, +"NEXT",NEXT, +"MAP",MAP, +"SIZEOF",SIZEOF, +"TARGET",TARGET_K, +"SEARCH_DIR",SEARCH_DIR, +"OUTPUT",OUTPUT, +"INPUT",INPUT, +"DEFINED",DEFINED, +"CREATE_OBJECT_SYMBOLS",CREATE_OBJECT_SYMBOLS, +"SECTIONS",SECTIONS, +"FILL",FILL, +"STARTUP",STARTUP, +"HLL",HLL, +"SYSLIB",SYSLIB, +"FLOAT",FLOAT, +"LONG", LONG, +"SHORT", SHORT, +"BYTE", BYTE, +"NOFLOAT",NOFLOAT, +"o",ORIGIN, +"org",ORIGIN, +"l", LENGTH, +"len", LENGTH, +0,0}; +unsigned int lineno; +extern boolean hex_mode; +FILE *ldlex_input_stack; +static unsigned int have_pushback; +#define NPUSHBACK 10 +int pushback[NPUSHBACK]; +int thischar; +extern char *ldfile_input_filename; + +int +lex_input() +{ + /* + When we know that the next token must be a filename we force the + input routine to return a '#' character, which will cause the special + filname regexp to match the following chars even if they don't look + much like a filename to any sane person. + */ + if (ldgram_mustbe_filename) { + ldgram_mustbe_filename = false; + return '#'; + } + + if (have_pushback > 0) + { + have_pushback --; + return thischar = pushback[have_pushback]; + } + if (ldlex_input_stack) { + thischar = fgetc(ldlex_input_stack); + + if (thischar == EOF) { + fclose(ldlex_input_stack); + ldlex_input_stack = (FILE *)NULL; + ldfile_input_filename = (char *)NULL; + thischar = lex_input(); + + } + } + else if (command_line && *command_line) { + thischar = *(command_line++); + } + else thischar = 0; + if(thischar == '\t') thischar = ' '; + return thischar ; +} + +void +lex_unput(c) +int c; +{ + if (have_pushback > NPUSHBACK) { + info("%F%P Too many pushbacks\n"); + } + + pushback[have_pushback] = c; + have_pushback ++; +} + + + int +yywrap() + { return 1; } +/*VARARGS*/ + +void +allprint(x) +int x; +{ +fprintf(yyout,"%d",x); +} + +void +sprint(x) +char *x; +{ +fprintf(yyout,"%s",x); +} + +int thischar; + +void parse_line(arg) +char *arg; +{ + command_line = arg; + have_pushback = 0; + yyparse(); +} + + + +void +parse_args(ac, av) +int ac; +char **av; +{ + char *p; + int i; + size_t size = 0; + char *dst; + debug = 1; + for (i= 1; i < ac; i++) { + size += strlen(av[i]) + 2; + } + dst = p = (char *)ldmalloc(size + 2); +/* Put a space arount each option */ + + + for (i =1; i < ac; i++) { + + unsigned int s = strlen(av[i]); + *dst++ = ' '; + memcpy(dst, av[i], s); + dst[s] = ' '; + dst += s + 1; + } + *dst = 0; + parse_line(p); + + free(p); + + +} + +long number(text, base) +char *text; +int base; +{ +unsigned long l = 0; + char *p; + for (p = text; *p != 0; p++) { + if (*p == 'K') { + l =l * 1024; + } + else if(*p== 'M') { + l =l * 1024 * 1024; + } + else { + l =l * base; + if (isdigit(*p)) { + l += *p - '0'; + } + else if (islower(*p)) { + l += *p - 'a' + 10; + } + else { + l += *p - 'A' + 10; + } + } + } + return l; +} +%} + +%a 4000 +%o 5000 +FILENAMECHAR [a-zA-Z0-9\/\.\-\_\+] +FILENAME {FILENAMECHAR}+ + + +WHITE [ \t]+ + +%% +"\n" { lineno++; } + + +"\ -defsym" { return OPTION_defsym; } +"\ -noinhibit_exec" { return OPTION_noinhibit_exec; } +"\ -format" { return OPTION_format; } +"\ -n" { return OPTION_n; } +"\ -r" { return OPTION_r; } +"\ -Ur" { return OPTION_Ur; } +"\ -o" { return OPTION_o; } +"\ -g" { return OPTION_g; } +"\ -e" { return OPTION_e; } +"\ -b" { return OPTION_b; } +"\ -dc" { return OPTION_dc; } +"\ -dp" { return OPTION_dp; } +"\ -d" { return OPTION_d; } +"\ -v" { return OPTION_v; } +"\ -M" { return OPTION_M; } +"\ -t" { return OPTION_t; } +"\ -X" { return OPTION_X; } +"\ -x" { return OPTION_x; } +"\ -c" { return OPTION_c; } +"\ -s" { return OPTION_s; } +"\ -S" { return OPTION_S; } +"\ -l"{FILENAME} { + yylval.name = buystring(yytext+3); + return OPTION_l; + } + +"\ -L"{FILENAME} { + yylval.name = buystring(yytext+3); + return OPTION_L; + } +"\ -Ttext" { + yylval.name = ".text"; + return OPTION_Texp; + } +"\ -Tdata" { + yylval.name = ".data"; + return OPTION_Texp; + } +"\ -Tbss" { + yylval.name = ".bss"; + return OPTION_Texp; + } + +"\ -T"{FILENAME} { + yylval.name = buystring(yytext+3); + return OPTION_Tfile; + } +"\ -T" { + return OPTION_T; + } + +"\ -A"{FILENAME} { + yylval.name = buystring(yytext+3); + return OPTION_Aarch; + } +" " { } +"<<=" { RTOKEN(LSHIFTEQ);} +">>=" { RTOKEN(RSHIFTEQ);} +"||" { RTOKEN(OROR);} +"==" { RTOKEN(EQ);} +"!=" { RTOKEN(NE);} +">=" { RTOKEN(GE);} +"<=" { RTOKEN(LE);} +"<<" { RTOKEN(LSHIFT);} +">>" { RTOKEN(RSHIFT);} +"+=" { RTOKEN(PLUSEQ);} +"-=" { RTOKEN(MINUSEQ);} +"*=" { RTOKEN(MULTEQ);} +"/=" { RTOKEN(DIVEQ);} +"&=" { RTOKEN(ANDEQ);} +"|=" { RTOKEN(OREQ);} + +"&&" { RTOKEN(ANDAND);} +">" { RTOKEN('>');} +"," { RTOKEN(',');} +"&" { RTOKEN('&');} +"|" { RTOKEN('|');} +"~" { RTOKEN('~');} +"!" { RTOKEN('!');} +"?" { RTOKEN('?');} +"*" { RTOKEN('*');} +"%" { RTOKEN('%');} +"<" { RTOKEN('<');} +"+" { RTOKEN('+');} +">" { RTOKEN('>');} +"}" { RTOKEN('}') ; } +"{" { RTOKEN('{'); } +")" { RTOKEN(')');} +"(" { RTOKEN('(');} +"]" { RTOKEN(']');} +"[" { RTOKEN('[');} +":" { RTOKEN(':'); } +";" { RTOKEN(';');} +"-" { RTOKEN('-');} +"=" { RTOKEN('=');} + + +"/*" { + while (1) { + int ch; + ch = input(); + while (ch != '*') { + if (ch == '\n') {lineno++; } + ch = input(); + } + + + + if (input() == '/') { + break; + } + unput(yytext[yyleng-1]); + } +} + +"\""[^\"]*"\"" { + + yylval.name = buystring(yytext+1); + yylval.name[yyleng-2] = 0; /* Fry final quote */ + return NAME; +} +[0][0-7KM]* { + + yylval.integer = number(yytext+1, 8); + return INT; +} + +[0-9]+[KM]? { + if (hex_mode == true) { + yylval.integer = number(yytext, 16); + } + else { + yylval.integer = number(yytext, 10); + } + return INT; +} + +0[Xx][0-9a-fA-FKM]+ { + + yylval.integer = number(yytext+2,16); + return INT; +} + +"\#"{WHITE}*{FILENAMECHAR}+ { + char *p = yytext+1; + while(*p ==' ' || *p == '\t') p++; + yylval.name = buystring(p); + return NAME; +} + + +{FILENAMECHAR} { + +int ch; + keyword_type *k; + if (yytext[0] == '/' && ldgram_mustbe_symbolname) + { RTOKEN('/');} + ch = input(); + while (true) { + if (isalpha(ch) || isdigit(ch) || ch == '.' || ch == '_') { + yytext[yyleng++] = ch; + } + else if (ch == '-' && ldgram_want_filename == true) { + yytext[yyleng++] = ch; + } + else if (ch == '+' && ldgram_want_filename == true) { + yytext[yyleng++] = ch; + } + + else if (ch == '/' && ldgram_want_filename == true) { + yytext[yyleng++] = ch; + } + + else break; + ch = input(); + } + + yytext[yyleng] = 0; + unput(ch); + + for(k = keywords; k ->name != (char *)NULL; k++) { + + if (strcmp(k->name, yytext)==0) { + yylval.token = k->value; + return k->value; + } + } + yylval.name = buystring(yytext); + return NAME; +} + + + + + +%% diff --git a/ld/ldmain.c b/ld/ldmain.c new file mode 100644 index 00000000000..3f9db087df9 --- /dev/null +++ b/ld/ldmain.c @@ -0,0 +1,806 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Written by Steve Chamberlain steve@cygnus.com + * + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:52 gumby + * Initial revision + * + * Revision 1.1 1991/03/13 00:48:27 chrisb + * Initial revision + * + * Revision 1.7 1991/03/10 19:15:45 sac + * Fixed a prototype problem + * + * Revision 1.6 1991/03/10 09:31:32 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.5 1991/03/09 03:31:02 sac + * After a fatal info message, the output file is deleted. + * + * Revision 1.4 1991/03/06 02:28:31 sac + * Fixed partial linking and error messages. + * + * Revision 1.3 1991/02/22 17:15:02 sac + * Added RCS keywords and copyrights + * + * + */ + + +#include "sysdep.h" +#include "bfd.h" + +#include "config.h" +#include "ld.h" +#include "ldmain.h" +#include "ldmisc.h" +#include "ldwrite.h" +#include "ldgram.h" +#include "ldsym.h" +#include "ldlang.h" +#include "ld-emul.h" +#include "ldlex.h" +#include "ldfile.h" + +/* IMPORTS */ +extern boolean lang_has_input_file; + +/* EXPORTS */ + +char *default_target; +char *output_filename = "a.out"; +/* Name this program was invoked by. */ +char *program_name; + +/* The file that we're creating */ +bfd *output_bfd; + +extern boolean option_v; + +/* The local symbol prefix */ +char lprefix = 'L'; + +/* Count the number of global symbols multiply defined. */ +int multiple_def_count; + + +/* Count the number of symbols defined through common declarations. + This count is referenced in symdef_library, linear_library, and + modified by enter_global_ref. + + It is incremented when a symbol is created as a common, and + decremented when the common declaration is overridden + + Another way of thinking of it is that this is a count of + all ldsym_types with a ->scoms field +*/ +unsigned int commons_pending; + + +/* Count the number of global symbols referenced and not defined. + common symbols are not included in this count. + */ + +unsigned int undefined_global_sym_count; + + + +/* Count the number of warning symbols encountered. */ +int warning_count; + +/* have we had a load script ? */ +extern boolean had_script; + + + +/* Nonzero means print names of input files as processed. */ +boolean trace_files; + + + +/* 1 => write load map. */ +boolean write_map; + + +int unix_relocate; + + + + + + + + +/* Force the make_executable to be output, even if there are non-fatal + errors */ +boolean force_make_executable; + + +/* A count of the total number of local symbols ever seen - by adding + the symbol_count field of each newly read afile.*/ + + +unsigned int total_symbols_seen; + +/* A count of the number of read files - the same as the number of elements + in file_chain + */ +unsigned int total_files_seen; + + +/* IMPORTS */ +args_type command_line; +ld_config_type config; +int +main (argc, argv) + char **argv; + int argc; +{ + char *emulation; + program_name = argv[0]; + output_filename = "a.out"; + + emulation = getenv(EMULATION_ENVIRON); + + /* Initialize the data about options. */ + strip_symbols = STRIP_NONE; + trace_files = false; + discard_locals = DISCARD_NONE; + + write_map = false; + config.relocateable_output = false; + unix_relocate = 0; + command_line.force_common_definition = false; + + ldfile_add_arch(""); + + config.make_executable = true; + force_make_executable = false; + + + /* Initialize the cumulative counts of symbols. */ + undefined_global_sym_count = 0; + warning_count = 0; + multiple_def_count = 0; + commons_pending = 0; + + config.magic_demand_paged = true ; + config.make_executable = true; + + if (emulation == (char *)NULL) { + emulation= DEFAULT_EMULATION; + } + ldemul_choose_mode(emulation); + + default_target = ldemul_choose_target(); + + lang_init(); + ldemul_before_parse(); + + lang_has_input_file = false; + parse_args(argc, argv); + + if (lang_has_input_file == false) { + info("%P%F: No input files\n"); + } + + ldemul_after_parse(); + + lang_process(); + + + + + /* Print error messages for any missing symbols, for any warning + symbols, and possibly multiple definitions */ + + /* Print a map, if requested. */ + + if (write_map) { + ldsym_print_symbol_table (); + lang_map(stdout); + } + + + if (config.relocateable_output) { + output_bfd->flags &= ~( D_PAGED); + output_bfd->flags |= EXEC_P; + ldwrite(); + bfd_close(output_bfd); + } + else { + output_bfd->flags |= EXEC_P; + + ldwrite(); + bfd_close(output_bfd); + if (config.make_executable == false && force_make_executable == false) { + unlink(output_filename); + } + return (!config.make_executable); + } + + return(0); +} /* main() */ + + +void +Q_read_entry_symbols (desc, entry) + bfd *desc; + struct lang_input_statement_struct *entry; +{ + if (entry->asymbols == (asymbol **)NULL) { + size_t table_size = get_symtab_upper_bound(desc); + entry->asymbols = (asymbol **)ldmalloc(table_size); + + entry->symbol_count = bfd_canonicalize_symtab(desc, entry->asymbols) ; + } +} + + +/* + * turn this item into a reference + */ +static void +refize(sp, nlist_p) +ldsym_type *sp; +asymbol **nlist_p; +{ + asymbol *sym = *nlist_p; + sym->value = 0; + sym->flags = BSF_UNDEFINED; + sym->section = (asection *)NULL; + sym->udata =(void *)( sp->srefs_chain); + sp->srefs_chain = nlist_p; +} +/* +This function is called for each name which is seen which has a global +scope. It enters the name into the global symbol table in the correct +symbol on the correct chain. Remember that each ldsym_type has three +chains attatched, one of all definitions of a symbol, one of all +references of a symbol and one of all common definitions of a symbol. + +When the function is over, the supplied is left connected to the bfd +to which is was born, with its udata field pointing to the next member +on the chain in which it has been inserted. + +A certain amount of jigery pokery is necessary since commons come +along and upset things, we only keep one item in the common chain; the +one with the biggest size seen sofar. When another common comes along +it either bumps the previous definition into the ref chain, since it +is bigger, or gets turned into a ref on the spot since the one on the +common chain is already bigger. If a real definition comes along then +the common gets bumped off anyway. + +Whilst all this is going on we keep a count of the number of multiple +definitions seen, undefined global symbols and pending commons. +*/ + + +void +Q_enter_global_ref (nlist_p) + asymbol **nlist_p; + +{ + asymbol *sym = *nlist_p; + char *name = sym->name; + ldsym_type *sp = ldsym_get (name); + + flagword this_symbol_flags = sym->flags; + + + ASSERT(sym->udata == 0); + + /* Just place onto correct chain */ + if (flag_is_common(this_symbol_flags)) { + /* If we have a definition of this symbol already then + * this common turns into a reference. Also we only + * ever point to the largest common, so if we + * have a common, but it's bigger that the new symbol + * the turn this into a reference too. + */ + if (sp->sdefs_chain) + { + /* This is a common symbol, but we already have a definition + for it, so just link it into the ref chain as if + it were a reference + */ + refize(sp, nlist_p); + } + else if (sp->scoms_chain) { + /* If we have a previous common, keep only the biggest */ + if ( (*(sp->scoms_chain))->value > sym->value) { + /* other common is bigger, throw this one away */ + refize(sp, nlist_p); + } + else if (sp->scoms_chain != nlist_p) { + /* other common is smaller, throw that away */ + refize(sp, sp->scoms_chain); + sp->scoms_chain = nlist_p; + } + } + else { + /* This is the first time we've seen a common, so + * remember it - if it was undefined before, we know it's defined now + */ + if (sp->srefs_chain) + undefined_global_sym_count--; + + commons_pending++; + sp->scoms_chain = nlist_p; + } + } + + else if (flag_is_defined(this_symbol_flags)) { + /* This is the definition of a symbol, add to def chain */ + if (sp->sdefs_chain && (*(sp->sdefs_chain))->section != sym->section) { + /* Multiple definition */ + asymbol *sy = *(sp->sdefs_chain); + lang_input_statement_type *stat = (lang_input_statement_type *) sy->the_bfd->usrdata; + lang_input_statement_type *stat1 = (lang_input_statement_type *) sym->the_bfd->usrdata; + asymbol ** stat1_symbols = stat1 ? stat1->asymbols: 0; + asymbol ** stat_symbols = stat ? stat->asymbols:0; + + multiple_def_count++; + info("%C: multiple definition of `%T'\n", + sym->the_bfd, + sym->section, + stat1_symbols, + sym->value, + sym); + + info("%C: first seen here\n", + sy->the_bfd, + sy->section, + stat_symbols, + sy->value); + } + else { + sym->udata =(void *)( sp->sdefs_chain); + sp->sdefs_chain = nlist_p; + } + /* A definition overrides a common symbol */ + if (sp->scoms_chain) { + refize(sp, sp->scoms_chain); + sp->scoms_chain = 0; + commons_pending--; + } + else if (sp->srefs_chain) { + /* If previously was undefined, then remember as defined */ + undefined_global_sym_count--; + } + } + else { + if (sp->scoms_chain == (asymbol **)NULL + && sp->srefs_chain == (asymbol **)NULL + && sp->sdefs_chain == (asymbol **)NULL) { + /* And it's the first time we've seen it */ + undefined_global_sym_count++; + + } + + refize(sp, nlist_p); + } + + ASSERT(sp->sdefs_chain == 0 || sp->scoms_chain == 0); + ASSERT(sp->scoms_chain ==0 || (*(sp->scoms_chain))->udata == 0); + + +} + +static void +Q_enter_file_symbols (entry) +lang_input_statement_type *entry; +{ + asymbol **q ; + entry->common_section = + bfd_make_section(entry->the_bfd, "COMMON"); + + ldlang_add_file(entry); + + + if (trace_files || option_v) { + info("%I\n", entry); + } + + total_symbols_seen += entry->symbol_count; + total_files_seen ++; + for (q = entry->asymbols; *q; q++) + { + asymbol *p = *q; + + if (flag_is_undefined_or_global_or_common(p->flags)) + { + + Q_enter_global_ref(q); + } + ASSERT(p->flags != 0); + } +} + + + +/* Searching libraries */ + +struct lang_input_statement_struct *decode_library_subfile (); +void linear_library (), symdef_library (); + +/* Search the library ENTRY, already open on descriptor DESC. + This means deciding which library members to load, + making a chain of `struct lang_input_statement_struct' for those members, + and entering their global symbols in the hash table. */ + +void +search_library (entry) + struct lang_input_statement_struct *entry; +{ + + /* No need to load a library if no undefined symbols */ + if (!undefined_global_sym_count) return; + + if (bfd_has_map(entry->the_bfd)) + symdef_library (entry); + else + linear_library (entry); + +} + + +void +Q_read_file_symbols (entry) +struct lang_input_statement_struct *entry; +{ + if (entry->asymbols == (asymbol **)NULL + &&entry->real == true + && entry->filename != (char *)NULL) + { + ldfile_open_file (entry); + + if (bfd_check_format(entry->the_bfd, bfd_object)) + { + entry->the_bfd->usrdata = (void*)entry; + + + Q_read_entry_symbols (entry->the_bfd, entry); + Q_enter_file_symbols (entry); + } + else if (bfd_check_format(entry->the_bfd, bfd_archive)) + { + entry->the_bfd->usrdata = (void *)entry; + + entry->subfiles = (lang_input_statement_type *)NULL; + search_library (entry); + } + else + { + info("%F%I: malformed input file (not rel or archive) \n", entry); + } + } + +} + + +/* Construct and return a lang_input_statement_struct for a library member. + The library's lang_input_statement_struct is library_entry, + and the library is open on DESC. + SUBFILE_OFFSET is the byte index in the library of this member's header. + We store the length of the member into *LENGTH_LOC. */ + +lang_input_statement_type * +decode_library_subfile (library_entry, subfile_offset) + struct lang_input_statement_struct *library_entry; + bfd *subfile_offset; +{ + register struct lang_input_statement_struct *subentry; + subentry = (struct lang_input_statement_struct *) ldmalloc (sizeof (struct lang_input_statement_struct)); + subentry->filename = subfile_offset -> filename; + subentry->local_sym_name = subfile_offset->filename; + subentry->asymbols = 0; + subentry->the_bfd = subfile_offset; + subentry->subfiles = 0; + subentry->next = 0; + subentry->superfile = library_entry; + subentry->is_archive = false; + subentry->header_read_flag = false; + subentry->just_syms_flag = false; + subentry->loaded = false; + subentry->chain = 0; + + return subentry; +} + +boolean subfile_wanted_p (); +void +clear_syms(entry, offset) +struct lang_input_statement_struct *entry; +file_ptr offset; +{ + carsym *car; + unsigned long indx = bfd_get_next_mapent(entry->the_bfd, + BFD_NO_MORE_SYMBOLS, + &car); + while (indx != BFD_NO_MORE_SYMBOLS) { + if (car->file_offset == offset) { + car->name = 0; + } + indx = bfd_get_next_mapent(entry->the_bfd, indx, &car); + } + +} + +/* Search a library that has a map + */ +void +symdef_library (entry) + struct lang_input_statement_struct *entry; + +{ + register struct lang_input_statement_struct *prev = 0; + + boolean not_finished = true; + + + while (not_finished == true) + { + carsym *exported_library_name; + bfd *prev_archive_member_bfd = 0; + + int idx = bfd_get_next_mapent(entry->the_bfd, + BFD_NO_MORE_SYMBOLS, + &exported_library_name); + + not_finished = false; + + while (idx != BFD_NO_MORE_SYMBOLS && undefined_global_sym_count) + { + + if (exported_library_name->name) + { + + ldsym_type *sp = ldsym_get_soft (exported_library_name->name); + + /* If we find a symbol that appears to be needed, think carefully + about the archive member that the symbol is in. */ + /* So - if it exists, and is referenced somewhere and is + undefined or */ + if (sp && sp->srefs_chain && !sp->sdefs_chain) + { + bfd *archive_member_bfd = bfd_get_elt_at_index(entry->the_bfd, idx); + struct lang_input_statement_struct *archive_member_lang_input_statement_struct; + + if (archive_member_bfd && bfd_check_format(archive_member_bfd, bfd_object)) + { + + /* Don't think carefully about any archive member + more than once in a given pass. */ + if (prev_archive_member_bfd != archive_member_bfd) + { + + prev_archive_member_bfd = archive_member_bfd; + + /* Read the symbol table of the archive member. */ + + if (archive_member_bfd->usrdata != (void *)NULL) { + + archive_member_lang_input_statement_struct =(lang_input_statement_type *) archive_member_bfd->usrdata; + } + else { + + archive_member_lang_input_statement_struct = + decode_library_subfile (entry, archive_member_bfd); + archive_member_bfd->usrdata = (void *) archive_member_lang_input_statement_struct; + + } + + if (archive_member_lang_input_statement_struct == 0) { + info ("%F%I contains invalid archive member %s\n", + entry, + sp->name); + } + + if (archive_member_lang_input_statement_struct->loaded == false) + { + + Q_read_entry_symbols (archive_member_bfd, archive_member_lang_input_statement_struct); + /* Now scan the symbol table and decide whether to load. */ + + + if (subfile_wanted_p (archive_member_lang_input_statement_struct) == true) + + { + /* This member is needed; load it. + Since we are loading something on this pass, + we must make another pass through the symdef data. */ + + not_finished = true; + + Q_enter_file_symbols (archive_member_lang_input_statement_struct); + + if (prev) + prev->chain = archive_member_lang_input_statement_struct; + else + entry->subfiles = archive_member_lang_input_statement_struct; + + + prev = archive_member_lang_input_statement_struct; + + + /* Clear out this member's symbols from the symdef data + so that following passes won't waste time on them. */ + clear_syms(entry, exported_library_name->file_offset); + archive_member_lang_input_statement_struct->loaded = true; + } + } + } + } + } + } + idx = bfd_get_next_mapent(entry->the_bfd, idx, &exported_library_name); + } + } +} + +void +linear_library (entry) +struct lang_input_statement_struct *entry; +{ + boolean more_to_do = true; + register struct lang_input_statement_struct *prev = 0; + + while (more_to_do) { + + bfd * archive = bfd_openr_next_archived_file(entry->the_bfd,0); + + more_to_do = false; + while (archive) { + if (bfd_check_format(archive, bfd_object)) + { + register struct lang_input_statement_struct *subentry; + + subentry = decode_library_subfile (entry, + archive); + + archive->usrdata = (void *) subentry; + if (!subentry) return; + if (subentry->loaded == false) { + Q_read_entry_symbols (archive, subentry); + + if (subfile_wanted_p (subentry) == true) + { + Q_enter_file_symbols (subentry); + + if (prev) + prev->chain = subentry; + else + entry->subfiles = subentry; + prev = subentry; + + more_to_do = true; + subentry->loaded = true; + } + } + } + archive = bfd_openr_next_archived_file(entry->the_bfd,archive); + + } + + } +} + + /* ENTRY is an entry for a library member. + Its symbols have been read into core, but not entered. + Return nonzero if we ought to load this member. */ + +boolean +subfile_wanted_p (entry) +struct lang_input_statement_struct *entry; +{ + asymbol **q; + + for (q = entry->asymbols; *q; q++) + { + asymbol *p = *q; + + /* If the symbol has an interesting definition, we could + potentially want it. */ + + if (p->flags & BSF_FORT_COMM + || p->flags & BSF_GLOBAL) + { + register ldsym_type *sp = ldsym_get_soft (p->name); + + + /* If this symbol has not been hashed, + we can't be looking for it. */ + if (sp != (ldsym_type *)NULL + && sp->sdefs_chain == (asymbol **)NULL) { + if (sp->srefs_chain != (asymbol **)NULL + || sp->scoms_chain != (asymbol **)NULL) + { + /* This is a symbol we are looking for. It is either + not yet defined or common. */ + + if (flag_is_common(p->flags)) + { + /* This libary member has something to + say about this element. We should + remember if its a new size */ + /* Move something from the ref list to the com list */ + if(sp->scoms_chain) { + /* Already a common symbol, maybe update it */ + if (p->value > (*(sp->scoms_chain))->value) { + (*(sp->scoms_chain))->value = p->value; + } + } + else { + /* Take a value from the ref chain + Here we are moving a symbol from the owning bfd + to another bfd. We must set up the + common_section portion of the bfd thing */ + + + + sp->scoms_chain = sp->srefs_chain; + sp->srefs_chain = + (asymbol **)((*(sp->srefs_chain))->udata); + (*(sp->scoms_chain))->udata = (void*)NULL; + + (*( sp->scoms_chain))->flags = BSF_FORT_COMM; + commons_pending++; + undefined_global_sym_count--; + } { + asymbol *com = *(sp->scoms_chain); + if (((lang_input_statement_type *) + (com->the_bfd->usrdata))->common_section == + (asection *)NULL) { + ((lang_input_statement_type *) + (com->the_bfd->usrdata))->common_section = + bfd_make_section(com->the_bfd, "COMMON"); + } + } + ASSERT(p->udata == 0); + } + + else { + if (write_map) + { + info("%I needed due to %s\n",entry, sp->name); + } + return true; + } + } + } + } + } + + return false; +} + + diff --git a/ld/ldmain.h b/ld/ldmain.h new file mode 100644 index 00000000000..9f3fa1a63ae --- /dev/null +++ b/ld/ldmain.h @@ -0,0 +1,23 @@ +/* ldmain.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD 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. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +PROTO(void, Q_enter_global_ref,(asymbol **)); +PROTO(void, Q_read_file_symbols,(struct lang_input_statement_struct *)); + diff --git a/ld/ldmisc.c b/ld/ldmisc.c new file mode 100644 index 00000000000..2f73066f4dd --- /dev/null +++ b/ld/ldmisc.c @@ -0,0 +1,303 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:55 gumby + * Initial revision + * + * Revision 1.2 1991/03/15 18:45:55 rich + * foo + * + * Revision 1.1 1991/03/13 00:48:30 chrisb + * Initial revision + * + * Revision 1.7 1991/03/10 09:31:34 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.6 1991/03/09 03:31:01 sac + * After a fatal info message, the output file is deleted. + * + * Revision 1.5 1991/03/06 21:59:54 sac + * Made %C print function name if available + * + * Revision 1.4 1991/03/06 02:27:45 sac + * Added support for linenumber printing via %C + * + * Revision 1.3 1991/02/22 17:15:03 sac + * Added RCS keywords and copyrights + * + */ + +/* + ldmisc.c + +*/ + +#include "sysdep.h" +#include +#include "bfd.h" + +#include "ld.h" +#include "ldmisc.h" +#include "ldlang.h" + +/* IMPORTS */ + +extern char *program_name; + +extern FILE *ldlex_input_stack; +extern char *ldfile_input_filename; +extern ld_config_type config; + +void +yyerror(arg) +char *arg; +{ + info("%P%F: %S %s\n",arg); +} + +extern int errno; +extern int sys_nerr; +extern char *sys_errlist[]; + +/* + %F error is fatal + %P print progam name + %S print script file and linenumber + %E current bfd error or errno + %I filename from a lang_input_statement_type + %B filename from a bfd + %T symbol table entry + %X no object output, fail return + %V hex bfd_vma + %C Clever filename:linenumber + % +*/ +void info(va_alist) +va_dcl +{ + char *fmt; + boolean fatal = false; + va_list arg; + va_start(arg); + fmt = va_arg(arg, char *); + while (*fmt) { + while (*fmt != '%' && *fmt != '\0') { + fputc(*fmt, stderr); + fmt++; + } + if (*fmt == '%') { + fmt ++; + switch (*fmt++) { + case 'X': + config.make_executable = false; + break; + case 'V': + fprintf(stderr,"%08lx", va_arg(arg, bfd_vma)); + break; + case 'T': + { + asymbol *symbol = va_arg(arg, asymbol *); + if (symbol) { + asection *section = symbol->section; + if ((symbol->flags & BSF_UNDEFINED) == 0) { + char *section_name = section == (asection *)NULL ? + "absolute" : section->name; + fprintf(stderr,"%s (%s)", symbol->name, section_name); + } + else { + fprintf(stderr,"%s", symbol->name); + } + } + else { + fprintf(stderr,"no symbol"); + } + } + break; + case 'B': + { + bfd *abfd = va_arg(arg, bfd *); + if (abfd->my_archive) { + fprintf(stderr,"%s(%s)", abfd->my_archive->filename, + abfd->filename); + } + else { + fprintf(stderr,"%s", abfd->filename); + + } + } + break; + case 'F': + fatal = true; + break; + case 'P': + fprintf(stderr,"%s", program_name); + break; + case 'E': + /* Replace with the most recent errno explanation */ + + + fprintf(stderr, bfd_errmsg(bfd_error)); + + + break; + case 'I': + { + lang_input_statement_type *i = + va_arg(arg,lang_input_statement_type *); + + fprintf(stderr,"%s", i->local_sym_name); + } + break; + case 'S': + /* Print source script file and line number */ + + if (ldlex_input_stack) { + extern unsigned int lineno; + if (ldfile_input_filename == (char *)NULL) { + fprintf(stderr,"command line"); + } + else { + fprintf(stderr,"%s:%u", ldfile_input_filename, lineno + 1); + } + } + else { + fprintf(stderr,"command line "); + } + break; + case 'C': + { + char *filename; + char *functionname; + unsigned int linenumber; + bfd *abfd = va_arg(arg, bfd *); + asection *section = va_arg(arg, asection *); + asymbol **symbols = va_arg(arg, asymbol **); + bfd_vma offset = va_arg(arg, bfd_vma); + + if (bfd_find_nearest_line(abfd, + section, + symbols, + offset, + &filename, + &functionname, + &linenumber)) + { + if (filename == (char *)NULL) + filename = abfd->filename; + if (functionname != (char *)NULL) + fprintf(stderr,"%s:%u: (%s)", filename, linenumber, functionname); + else if (linenumber != 0) + fprintf(stderr,"%s:%u", filename, linenumber); + else + fprintf(stderr,"%s", filename); + + } + else { + fprintf(stderr,"%s", abfd->filename); + } + } + break; + + case 's': + fprintf(stderr,"%s", va_arg(arg, char *)); + break; + case 'd': + fprintf(stderr,"%d", va_arg(arg, int)); + break; + default: + fprintf(stderr,"%s", va_arg(arg, char *)); + break; + } + } + } + if (fatal == true) { + extern char *output_filename; + if (output_filename) + unlink(output_filename); + exit(1); + } + va_end(arg); +} + + +void +info_assert(file, line) +char *file; +unsigned int line; +{ + info("%F%P internal error %s %d\n", file,line); +} + +/* Return a newly-allocated string + whose contents concatenate those of S1, S2, S3. */ + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + size_t len1 = strlen (s1); + size_t len2 = strlen (s2); + size_t len3 = strlen (s3); + char *result = ldmalloc (len1 + len2 + len3 + 1); + + if (len1 != 0) + memcpy(result, s1, len1); + if (len2 != 0) + memcpy(result+len1, s2, len2); + if (len3 != 0) + memcpy(result+len1+len2, s2, len3); + *(result + len1 + len2 + len3) = 0; + + return result; +} + + + +char *ldmalloc (size) +size_t size; +{ + char * result = malloc (size); + + if (result == (char *)NULL && size != 0) + info("%F%P virtual memory exhausted\n"); + + return result; +} + + + +char *buystring(x) +char *x; +{ + size_t l = strlen(x)+1; + char *r = ldmalloc(l); + memcpy(r, x,l); + return r; +} diff --git a/ld/ldmisc.h b/ld/ldmisc.h new file mode 100644 index 00000000000..e3463d1763c --- /dev/null +++ b/ld/ldmisc.h @@ -0,0 +1,34 @@ +/* ldmisc.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD 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. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +/* VARARGS*/ +PROTO(void,info,()); +PROTO(void,info_assert,(char *, unsigned int)); +PROTO(void,yyerror,(char *)); +PROTO(char *,concat,(char *, char *, char *)); +PROTO(char *, ldmalloc,(size_t)); +PROTO(char *,buystring,(char *)); +#define ASSERT(x) \ +{ if (!(x)) info_assert(__FILE__,__LINE__); } + +#define FAIL() \ +{ info_assert(__FILE__,__LINE__); } diff --git a/ld/ldsym.c b/ld/ldsym.c new file mode 100644 index 00000000000..796060ca7ad --- /dev/null +++ b/ld/ldsym.c @@ -0,0 +1,452 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:58 gumby + * Initial revision + * + * Revision 1.1 1991/03/13 00:48:32 chrisb + * Initial revision + * + * Revision 1.4 1991/03/10 09:31:36 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.3 1991/03/06 02:28:56 sac + * Cleaned up + * + * Revision 1.2 1991/02/22 17:15:06 sac + * Added RCS keywords and copyrights + * +*/ + +/* + Written by Steve Chamberlain steve@cygnus.com + + All symbol handling for the linker + */ + + +#include "sysdep.h" +#include "bfd.h" + +#include "ld.h" +#include "ldsym.h" +#include "ldmisc.h" +#include "ldlang.h" +/* IMPORT */ + +extern bfd *output_bfd; +/* Head and tail of global symbol table chronological list */ + +ldsym_type *symbol_head = (ldsym_type *)NULL; +ldsym_type **symbol_tail_ptr = &symbol_head; + +/* + incremented for each symbol in the ldsym_type table + no matter what flavour it is +*/ +unsigned int global_symbol_count; + +/* IMPORTS */ + +extern boolean option_longmap ; + +/* LOCALS */ +#define TABSIZE 1009 +static ldsym_type *global_symbol_hash_table[TABSIZE]; + +/* Compute the hash code for symbol name KEY. */ + +int +hash_string (key) + char *key; +{ + register char *cp; + register int k; + + cp = key; + k = 0; + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + return k; +} + +/* Get the symbol table entry for the global symbol named KEY. + Create one if there is none. */ +ldsym_type * +ldsym_get (key) + char *key; +{ + register int hashval; + register ldsym_type *bp; + + /* Determine the proper bucket. */ + + hashval = hash_string (key) % TABSIZE; + + /* Search the bucket. */ + + for (bp = global_symbol_hash_table[hashval]; bp; bp = bp->link) + if (! strcmp (key, bp->name)) + return bp; + + /* Nothing was found; create a new symbol table entry. */ + + bp = (ldsym_type *) ldmalloc (sizeof (ldsym_type)); + bp->srefs_chain = (asymbol **)NULL; + bp->sdefs_chain = (asymbol **)NULL; + bp->scoms_chain = (asymbol **)NULL; + bp->name = (char *) ldmalloc (strlen (key) + 1); + strcpy (bp->name, key); + + + + + /* Add the entry to the bucket. */ + + bp->link = global_symbol_hash_table[hashval]; + global_symbol_hash_table[hashval] = bp; + + /* Keep the chronological list up to date too */ + *symbol_tail_ptr = bp; + symbol_tail_ptr = &bp->next; + bp->next = 0; + global_symbol_count++; + + return bp; +} + +/* Like `ldsym_get' but return 0 if the symbol is not already known. */ + +ldsym_type * +ldsym_get_soft (key) + char *key; +{ + register int hashval; + register ldsym_type *bp; + + /* Determine which bucket. */ + + hashval = hash_string (key) % TABSIZE; + + /* Search the bucket. */ + + for (bp = global_symbol_hash_table[hashval]; bp; bp = bp->link) + if (! strcmp (key, bp->name)) + return bp; + + return 0; +} + + + + + +static void +list_file_locals (entry) +lang_input_statement_type *entry; +{ + asymbol **q; + fprintf (stderr, "\nLocal symbols of "); + info("%I", entry); + fprintf (stderr, ":\n\n"); + if (entry->asymbols) { + for (q = entry->asymbols; *q; q++) + { + asymbol *p = *q; + /* If this is a definition, + update it if necessary by this file's start address. */ + if (p->flags & BSF_LOCAL) + info(" %V %s\n",p->value, p->name); + } + } +} + + +static void +print_file_stuff(f) +lang_input_statement_type *f; +{ + fprintf (stderr, " %s", f->filename); + fprintf (stderr, " "); + if (f->just_syms_flag) + { + fprintf (stderr, " symbols only\n"); + } + else + { + asection *s; + if (option_longmap) { + for (s = f->the_bfd->sections; + s != (asection *)NULL; + s = s->next) { + fprintf (stderr, "%08lx %08x 2**%2ud %s\n", + s->output_offset, + (unsigned)s->size, s->alignment_power, s->name); + } + } + else { + for (s = f->the_bfd->sections; + s != (asection *)NULL; + s = s->next) { + fprintf (stderr, "%s %lx(%x) ", + s->name, + s->output_offset, + (unsigned) s->size); + } + fprintf (stderr, "hex \n"); + } + } +} + +void +ldsym_print_symbol_table () +{ + fprintf (stderr, "\nFiles:\n\n"); + + lang_for_each_file(print_file_stuff); + + fprintf (stderr, "\nGlobal symbols:\n\n"); + { + register ldsym_type *sp; + + for (sp = symbol_head; sp; sp = sp->next) + { + if (sp->sdefs_chain) + { + asymbol *defsym = *(sp->sdefs_chain); + asection *defsec = bfd_get_section(defsym); + fprintf(stderr,"%08lx ",defsym->value); + if (defsec) + { + fprintf(stderr,"%08lx ",defsym->value+defsec->vma); + fprintf(stderr, + "%7s", + bfd_section_name(output_bfd, + defsec)); + + } + else + { + fprintf(stderr," ......."); + } + + } + else { + fprintf(stderr,"undefined"); + } + + + if (sp->scoms_chain) { + fprintf(stderr, " common size %5lu %s", + (*(sp->scoms_chain))->value, sp->name); + } + if (sp->sdefs_chain) { + fprintf(stderr, " symbol def %08lx %s", + (*(sp->sdefs_chain))->value, + sp->name); + } + else { + fprintf(stderr, " undefined %s", + sp->name); + } + fprintf(stderr, "\n"); + + } + } + lang_for_each_file(list_file_locals); +} + +extern lang_output_section_statement_type *create_object_symbols; +extern char lprefix; +static asymbol ** +write_file_locals(output_buffer) +asymbol **output_buffer; +{ +LANG_FOR_EACH_INPUT_STATEMENT(entry) + { + /* Run trough the symbols and work out what to do with them */ + unsigned int i; + + /* Add one for the filename symbol if needed */ + if (create_object_symbols + != (lang_output_section_statement_type *)NULL) { + asection *s; + for (s = entry->the_bfd->sections; + s != (asection *)NULL; + s = s->next) { + if (s->output_section == create_object_symbols->bfd_section) { + /* Add symbol to this section */ + asymbol * newsym = + (asymbol *)bfd_make_empty_symbol(entry->the_bfd); + newsym->name = entry->local_sym_name; + /* The symbol belongs to the output file's text section */ + + /* The value is the start of this section in the output file*/ + newsym->value = 0; + newsym->flags = BSF_LOCAL; + newsym->section = s; + *output_buffer++ = newsym; + break; + } + } + } + for (i = 0; i < entry->symbol_count; i++) + { + asymbol *p = entry->asymbols[i]; + + if (flag_is_global(p->flags) || flag_is_absolute(p->flags)) + { + /* We are only interested in outputting + globals at this stage in special circumstances */ + if (p->the_bfd == entry->the_bfd + && flag_is_not_at_end(p->flags)) { + /* And this is one of them */ + *(output_buffer++) = p; + p->flags |= BSF_KEEP; + } + } + else { + if (flag_is_ordinary_local(p->flags)) + { + if (discard_locals == DISCARD_ALL) + { } + else if (discard_locals == DISCARD_L && + (p->name[0] == lprefix)) + { } + else if (p->flags == BSF_WARNING) + { } + else + { *output_buffer++ = p; } + } + else if (flag_is_debugger(p->flags)) + { + /* Only keep the debugger symbols if no stripping required */ + if (strip_symbols == STRIP_NONE) { + *output_buffer++ = p; + } + } + else if (flag_is_undefined(p->flags)) + { /* This must be global */ + } + else if (flag_is_common(p->flags)) { + /* And so must this */ + } + else if (p->flags & BSF_CTOR) { + /* Throw it away */ + } +else + { + FAIL(); + } + } + } + + + } + return output_buffer; +} + + +static asymbol ** +write_file_globals(symbol_table) +asymbol **symbol_table; +{ + FOR_EACH_LDSYM(sp) + { + if (sp->sdefs_chain != (asymbol **)NULL) { + asymbol *bufp = (*(sp->sdefs_chain)); + + if ((bufp->flags & BSF_KEEP) ==0) { + ASSERT(bufp != (asymbol *)NULL); + + bufp->name = sp->name; + + if (sp->scoms_chain != (asymbol **)NULL) + + { + /* + defined as common but not allocated, this happens + only with -r and not -d, write out a common + definition + */ + bufp = *(sp->scoms_chain); + } + *symbol_table++ = bufp; + } + } + else if (sp->scoms_chain != (asymbol **)NULL) { + /* This symbol is a common - just output */ + asymbol *bufp = (*(sp->scoms_chain)); + *symbol_table++ = bufp; + } + else if (sp->srefs_chain != (asymbol **)NULL) { + /* This symbol is undefined but has a reference */ + asymbol *bufp = (*(sp->srefs_chain)); + *symbol_table++ = bufp; + } + else { + /* + This symbol has neither defs nor refs, it must have come + from the command line, since noone has used it it has no + data attatched, so we'll ignore it + */ + } + } + return symbol_table; +} + + + +void +ldsym_write() +{ + if (strip_symbols != STRIP_ALL) { + /* We know the maximum size of the symbol table - + it's the size of all the global symbols ever seen + + the size of all the symbols from all the files + + the number of files (for the per file symbols) + +1 (for the null at the end) + */ + extern unsigned int total_files_seen; + extern unsigned int total_symbols_seen; + + asymbol ** symbol_table = (asymbol **) + ldmalloc ((size_t)(global_symbol_count + + total_files_seen + + total_symbols_seen + 1) * sizeof (asymbol *)); + asymbol ** tablep = write_file_locals(symbol_table); + + tablep = write_file_globals(tablep); + + *tablep = (asymbol *)NULL; + bfd_set_symtab(output_bfd, symbol_table, (unsigned)( tablep - symbol_table)); + } +} diff --git a/ld/ldwrite.c b/ld/ldwrite.c new file mode 100644 index 00000000000..a4282b31b5b --- /dev/null +++ b/ld/ldwrite.c @@ -0,0 +1,441 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD 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. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:29:04 gumby + * Initial revision + * + * Revision 1.2 1991/03/15 18:45:55 rich + * foo + * + * Revision 1.1 1991/03/13 00:48:37 chrisb + * Initial revision + * + * Revision 1.7 1991/03/10 19:15:03 sac + * Took out the abort() which had been put in the wrong place + * Updated the version #. + * + * Revision 1.6 1991/03/10 09:31:41 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.5 1991/03/09 03:25:08 sac + * Added support for LONG, SHORT and BYTE keywords in scripts + * + * Revision 1.4 1991/03/06 21:59:34 sac + * Completed G++ support + * + * Revision 1.3 1991/03/06 02:29:52 sac + * Added support for partial linking. + * + * Revision 1.2 1991/02/22 17:15:11 sac + * Added RCS keywords and copyrights + * +*/ + +/* + This module writes out the final image by reading sections from the + input files, relocating them and writing them out + + There are two main paths through this module, one for normal + operation and one for partial linking. + + During normal operation, raw section data is read along with the + associated relocation information, the relocation info applied and + the section data written out on a section by section basis. + + When partially linking, all the relocation records are read to work + out how big the output relocation vector will be. Then raw data is + read, relocated and written section by section. + + Written by Steve Chamberlain steve@cygnus.com + +*/ + + +#include "sysdep.h" +#include "bfd.h" + +#include "ldlang.h" +#include "ld.h" +#include "ldwrite.h" +#include "ldmisc.h" +#include "ldsym.h" +#include "ldgram.tab.h" + + + +char *ldmalloc(); +/* Static vars for do_warnings and subroutines of it */ +int list_unresolved_refs; /* List unresolved refs */ +int list_warning_symbols; /* List warning syms */ +int list_multiple_defs; /* List multiple definitions */ +extern int errno; +extern char *sys_errlist[]; + +extern unsigned int undefined_global_sym_count; + +extern bfd *output_bfd; + +extern struct lang_output_section_statement_struct * create_object_symbols; + +extern char lprefix; + +#ifdef __STDC__ +void lang_for_each_statement(void (*func)()); +#else /* __STDC__ */ +void lang_for_each_statement(); +#endif /* __STDC__ */ + +extern size_t largest_section; +ld_config_type config; + +extern unsigned int global_symbol_count; + +boolean trace_files; + +static void perform_relocation(input_bfd, + input_section, + data, + symbols) +bfd *input_bfd; +asection *input_section; +void *data; +asymbol **symbols; +{ + static asymbol *error_symbol = (asymbol *)NULL; + static unsigned int error_count = 0; +#define MAX_ERRORS_IN_A_ROW 5 + size_t reloc_size = get_reloc_upper_bound(input_bfd, input_section); + + arelent **reloc_vector = (arelent **)ldmalloc(reloc_size); + arelent **parent; + bfd *ob = output_bfd; + asection *os = input_section->output_section; + if (config.relocateable_output == false) ob = (bfd *)NULL; + + if (bfd_canonicalize_reloc(input_bfd, + input_section, + reloc_vector, + symbols) ) + { + for (parent = reloc_vector; *parent; parent++) + { + + bfd_reloc_status_enum_type r= + bfd_perform_relocation(input_bfd, + *parent, + data, + input_section, + ob); + + if (r == bfd_reloc_ok) { + if (ob != (bfd *)NULL) { + /* A parital link, so keep the relocs */ + os->orelocation[os->reloc_count] = *parent; + os->reloc_count++; + } + } + else + { + asymbol *s; + arelent *p = *parent; + + if (ob != (bfd *)NULL) { + /* A parital link, so keep the relocs */ + os->orelocation[os->reloc_count] = *parent; + os->reloc_count++; + } + + if (p->sym_ptr_ptr != (asymbol **)NULL) { + s = *(p->sym_ptr_ptr); + } + else { + s = (asymbol *)NULL; + } + switch (r) + { + case bfd_reloc_undefined: + /* We remember the symbol, and never print more than + a reasonable number of them in a row */ + if (s == error_symbol) { + error_count++; + } + else { + error_count = 0; + error_symbol = s; + } + if (error_count < MAX_ERRORS_IN_A_ROW) { + info("%C: undefined reference to `%T'\n", + input_bfd, + input_section, + symbols, + (*parent)->address, + s); + config.make_executable = false; + } + else if (error_count == MAX_ERRORS_IN_A_ROW) { + info("%C: more undefined references to `%T' follow\n", + input_bfd, + input_section, + symbols, + (*parent)->address, + s); + } + else { + /* Don't print any more */ + } + break; + case bfd_reloc_dangerous: + info("%B: relocation may be wrong `%T'\n", + input_bfd, + s); + break; + case bfd_reloc_outofrange: + info("%B:%s relocation address out of range %T (%x)\n", + input_bfd, + input_section->name, + s, + p->address); + break; + case bfd_reloc_overflow: + info("%B:%s relocation overflow in %T reloc type %d\n", + input_bfd, + input_section->name, + s, + p->howto->type); + break; + default: + info("%F%B: relocation error, symbol `%T'\n", + input_bfd, + s); + break; + } + } + } + } + free((char *)reloc_vector); +} + + + + + + +void *data_area; + +static void +copy_and_relocate(statement) +lang_statement_union_type *statement; +{ + switch (statement->header.type) { + case lang_fill_statement_enum: + { +#if 0 + bfd_byte play_area[SHORT_SIZE]; + unsigned int i; + bfd_putshort(output_bfd, statement->fill_statement.fill, play_area); + /* Write out all entire shorts */ + for (i = 0; + i < statement->fill_statement.size - SHORT_SIZE + 1; + i+= SHORT_SIZE) + { + bfd_set_section_contents(output_bfd, + statement->fill_statement.output_section, + play_area, + statement->data_statement.output_offset +i, + SHORT_SIZE); + + } + + /* Now write any remaining byte */ + if (i < statement->fill_statement.size) + { + bfd_set_section_contents(output_bfd, + statement->fill_statement.output_section, + play_area, + statement->data_statement.output_offset +i, + 1); + + } +#endif + } + break; + case lang_data_statement_enum: + { + bfd_vma value = statement->data_statement.value; + bfd_byte play_area[LONG_SIZE]; + unsigned int size; + switch (statement->data_statement.type) { + case LONG: + bfd_putlong(output_bfd, value, play_area); + size = LONG_SIZE; + break; + case SHORT: + bfd_putshort(output_bfd, value, play_area); + size = SHORT_SIZE; + break; + case BYTE: + bfd_putchar(output_bfd, value, play_area); + size = BYTE_SIZE; + break; + } + + bfd_set_section_contents(output_bfd, + statement->data_statement.output_section, + play_area, + statement->data_statement.output_vma, + size); + + + + + } + break; + case lang_input_section_enum: + { + + asection *i = statement->input_section.section; + asection *output_section = i->output_section; + lang_input_statement_type *ifile = statement->input_section.ifile; + bfd *inbfd = ifile->the_bfd; + if (output_section->flags & SEC_LOAD && i->size != 0) + { + if(bfd_get_section_contents(inbfd, + i, + data_area, + 0L, + i->size) == false) + { + info("%F%B error reading section contents %E\n", + inbfd); + } + perform_relocation (inbfd, i, data_area, ifile->asymbols); + + + if(bfd_set_section_contents(output_bfd, + output_section, + data_area, + (file_ptr)i->output_offset, + i->size) == false) + { + info("%F%B error writing section contents of %E\n", + output_bfd); + } + + } + } + break; + + default: + /* All the other ones fall through */ + ; + + } +} + +void +write_norel() +{ + /* Output the text and data segments, relocating as we go. */ + lang_for_each_statement(copy_and_relocate); +} + + +static void read_relocs(abfd, section, symbols) +bfd *abfd; +asection *section; +asymbol **symbols; +{ + /* Work out the output section ascociated with this input section */ + asection *output_section = section->output_section; + + size_t reloc_size = get_reloc_upper_bound(abfd, section); + arelent **reloc_vector = (arelent **)ldmalloc(reloc_size); + + if (bfd_canonicalize_reloc(abfd, + section, + reloc_vector, + symbols)) { + output_section->reloc_count += section->reloc_count; + } +} + + +static void +write_rel() +{ + /* + Run through each section of each file and work work out the total + number of relocation records which will finally be in each output + section + */ + + LANG_FOR_EACH_INPUT_SECTION + (statement, abfd, section, + (read_relocs(abfd, section, statement->asymbols))); + + + + /* + Now run though all the output sections and allocate the space for + all the relocations + */ + LANG_FOR_EACH_OUTPUT_SECTION + (section, + (section->orelocation = + (arelent **)ldmalloc((size_t)(sizeof(arelent **)* + section->reloc_count)), + section->reloc_count = 0, + section->flags |= SEC_HAS_CONTENTS)); + + + /* + Copy the data, relocating as we go + */ + lang_for_each_statement(copy_and_relocate); +} + +void +ldwrite () +{ + data_area = (void*) ldmalloc(largest_section); + if (config.relocateable_output == true) + { + write_rel(); + } + else + { + write_norel(); + } + free(data_area); + /* Output the symbol table (both globals and locals). */ + ldsym_write (); + +} + diff --git a/ld/ldwrite.h b/ld/ldwrite.h new file mode 100644 index 00000000000..2658801887c --- /dev/null +++ b/ld/ldwrite.h @@ -0,0 +1,24 @@ +/* ldwrite.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD 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. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + + +PROTO(void, ldwrite, (void));