Mercurial > hg > index.cgi
view lwlink/link.c @ 234:d389adbcc4ab
Added section base and length symbols to lwlink
Added the ability for a link script to define section base and section
length symbols when linking. These symbols are searched for when an external
reference is resolved before looking up any symbols in the various objects
being linked. Also documented the new link script directives and added such
directives to all default link scripts.
author | William Astle <lost@l-w.ca> |
---|---|
date | Fri, 10 Aug 2012 23:47:56 -0600 |
parents | 4682460aed00 |
children | e3741cf53e00 |
line wrap: on
line source
/* link.c Copyright © 2009 William Astle This file is part of LWLINK. LWLINK 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. Resolve section and symbol addresses; handle incomplete references */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <lw_alloc.h> #include <lw_string.h> #include "expr.h" #include "lwlink.h" void check_os9(void); struct section_list *sectlist = NULL; int nsects = 0; static int nforced = 0; static int resolveonly = 0; symlist_t *symlist = NULL; void check_section_name(char *name, int *base, fileinfo_t *fn) { int sn; // fprintf(stderr, "Considering sections in %s (%d) for %s\n", fn -> filename, fn -> forced, name); if (fn -> forced == 0) return; for (sn = 0; sn < fn -> nsections; sn++) { // fprintf(stderr, " Considering section %s\n", fn -> sections[sn].name); if (!strcmp(name, (char *)(fn -> sections[sn].name))) { if (fn -> sections[sn].flags & SECTION_CONST) continue; // we have a match // fprintf(stderr, " Found\n"); sectlist = lw_realloc(sectlist, sizeof(struct section_list) * (nsects + 1)); sectlist[nsects].ptr = &(fn -> sections[sn]); fn -> sections[sn].processed = 1; fn -> sections[sn].loadaddress = *base; *base += fn -> sections[sn].codesize; nsects++; // fprintf(stderr, "Adding section %s (%s)\n",fn -> sections[sn].name, fn -> filename); } } for (sn = 0; sn < fn -> nsubs; sn++) { check_section_name(name, base, fn -> subs[sn]); } } void add_matching_sections(char *name, int yesflags, int noflags, int *base); void check_section_flags(int yesflags, int noflags, int *base, fileinfo_t *fn) { int sn; // fprintf(stderr, "Considering sections in %s (%d) for %x/%x\n", fn -> filename, fn -> forced, yesflags, noflags); if (fn -> forced == 0) return; for (sn = 0; sn < fn -> nsections; sn++) { // ignore "constant" sections - they're added during the file resolve stage if (fn -> sections[sn].flags & SECTION_CONST) continue; // ignore if the noflags tell us to if (noflags && (fn -> sections[sn].flags & noflags)) continue; // ignore unless the yesflags tell us not to if (yesflags && ((fn -> sections[sn].flags & yesflags) == 0)) continue; // ignore it if already processed if (fn -> sections[sn].processed) continue; // we have a match - now collect *all* sections of the same name! // fprintf(stderr, " Found\n"); add_matching_sections((char *)(fn -> sections[sn].name), 0, 0, base); // and then continue looking for sections } for (sn = 0; sn < fn -> nsubs; sn++) { check_section_flags(yesflags, noflags, base, fn -> subs[sn]); } } void add_matching_sections(char *name, int yesflags, int noflags, int *base) { int fn; if (name) { // named section // look for all instances of a section by the specified name // and resolve base addresses and add to the list for (fn = 0; fn < ninputfiles; fn++) { check_section_name(name, base, inputfiles[fn]); } } else { // wildcard section // named section // look for all instances of a section by the specified name // and resolve base addresses and add to the list for (fn = 0; fn < ninputfiles; fn++) { check_section_flags(yesflags, noflags, base, inputfiles[fn]); } } } // work out section load order and resolve base addresses for each section // make a list of sections to load in order void resolve_sections(void) { int laddr = 0; int ln, sn, fn; for (ln = 0; ln < linkscript.nlines; ln++) { if (linkscript.lines[ln].loadat >= 0) laddr = linkscript.lines[ln].loadat; add_matching_sections(linkscript.lines[ln].sectname, linkscript.lines[ln].yesflags, linkscript.lines[ln].noflags, &laddr); if (linkscript.lines[ln].sectname) { } else { // wildcard section // look for all sections not yet processed that match flags int f = 0; int fn0, sn0; char *sname; // named section // look for all instances of a section by the specified name // and resolve base addresses and add to the list for (fn0 = 0; fn0 < ninputfiles; fn0++) { for (sn0 = 0; sn0 < inputfiles[fn0] -> nsections; sn0++) { // ignore "constant" sections if (inputfiles[fn0] -> sections[sn0].flags & SECTION_CONST) continue; // ignore if the "no flags" bit says to if (linkscript.lines[ln].noflags && (inputfiles[fn0] -> sections[sn0].flags & linkscript.lines[ln].noflags)) continue; // ignore unless the yes flags tell us not to if (linkscript.lines[ln].yesflags && ((inputfiles[fn0] -> sections[sn0].flags & linkscript.lines[ln].yesflags) == 0)) continue; if (inputfiles[fn0] -> sections[sn0].processed == 0) { sname = (char *)(inputfiles[fn0] -> sections[sn0].name); for (fn = 0; fn < ninputfiles; fn++) { for (sn = 0; sn < inputfiles[fn] -> nsections; sn++) { if (!strcmp(sname, (char *)(inputfiles[fn] -> sections[sn].name))) { // we have a match sectlist = lw_realloc(sectlist, sizeof(struct section_list) * (nsects + 1)); sectlist[nsects].ptr = &(inputfiles[fn] -> sections[sn]); inputfiles[fn] -> sections[sn].processed = 1; if (!f && linkscript.lines[ln].loadat >= 0) { f = 1; sectlist[nsects].forceaddr = 1; laddr = linkscript.lines[ln].loadat; } else { sectlist[nsects].forceaddr = 0; } inputfiles[fn] -> sections[sn].loadaddress = laddr; laddr += inputfiles[fn] -> sections[sn].codesize; nsects++; } } } } } } } } // theoretically, all the base addresses are set now } /* run through all sections and generate any synthetic symbols */ void generate_symbols(void) { int sn; char *lastsect = NULL; char sym[256]; int len; symlist_t *se; fprintf(stderr, "Generating symbols\n"); for (sn = 0; sn < nsects; sn++) { fprintf(stderr, "Section %s (%s)\n", sectlist[sn].ptr -> name, lastsect); if (!lastsect || strcmp(lastsect, (char *)(sectlist[sn].ptr -> name))) { if (lastsect && linkscript.lensympat) { /* handle length symbol */ se = lw_alloc(sizeof(symlist_t)); se -> val = len; snprintf(sym, 255, linkscript.lensympat, lastsect); se -> sym = lw_strdup(sym); se -> next = symlist; symlist = se; } lastsect = (char *)(sectlist[sn].ptr -> name); len = 0; /* handle base symbol */ if (lastsect && linkscript.basesympat) { se = lw_alloc(sizeof(symlist_t)); se -> val = sectlist[sn].ptr -> loadaddress; snprintf(sym, 255, linkscript.basesympat, lastsect); se -> sym = lw_strdup(sym); se -> next = symlist; symlist = se; } } len += sectlist[sn].ptr -> codesize; } if (lastsect && linkscript.lensympat) { /* handle length symbol */ se = lw_alloc(sizeof(symlist_t)); se -> val = len; snprintf(sym, 255, linkscript.lensympat, lastsect); se -> sym = lw_strdup(sym); se -> next = symlist; symlist = se; } } lw_expr_stack_t *find_external_sym_recurse(char *sym, fileinfo_t *fn) { int sn; lw_expr_stack_t *r; lw_expr_term_t *term; symtab_t *se; int val; for (sn = 0; sn < fn -> nsections; sn++) { for (se = fn -> sections[sn].exportedsyms; se; se = se -> next) { // fprintf(stderr, " Considering %s\n", se -> sym); if (!strcmp(sym, (char *)(se -> sym))) { // fprintf(stderr, " Match (%d)\n", fn -> sections[sn].processed); // if the section was not previously processed and is CONSTANT, force it in // otherwise error out if it is not being processed if (fn -> sections[sn].processed == 0) { if (fn -> sections[sn].flags & SECTION_CONST) { // add to section list sectlist = lw_realloc(sectlist, sizeof(struct section_list) * (nsects + 1)); sectlist[nsects].ptr = &(fn -> sections[sn]); fn -> sections[sn].processed = 1; fn -> sections[sn].loadaddress = 0; nsects++; } else { if (resolveonly == 0) { fprintf(stderr, "Symbol %s found in section %s (%s) which is not going to be included\n", sym, fn -> sections[sn].name, fn -> filename); continue; } } } // fprintf(stderr, "Found symbol %s in %s\n", sym, fn -> filename); if (!(fn -> forced)) { // fprintf(stderr, " Forced\n"); fn -> forced = 1; nforced = 1; } if (fn -> sections[sn].flags & SECTION_CONST) val = se -> offset; else val = se -> offset + fn -> sections[sn].loadaddress; r = lw_expr_stack_create(); term = lw_expr_term_create_int(val & 0xffff); lw_expr_stack_push(r, term); lw_expr_term_free(term); return r; } } } for (sn = 0; sn < fn -> nsubs; sn++) { // fprintf(stderr, "Looking in %s\n", fn -> subs[sn] -> filename); r = find_external_sym_recurse(sym, fn -> subs[sn]); if (r) { // fprintf(stderr, "Found symbol %s in %s\n", sym, fn -> filename); if (!(fn -> forced)) { // fprintf(stderr, " Forced\n"); nforced = 1; fn -> forced = 1; } return r; } } return NULL; } // resolve all incomplete references now // anything that is unresolvable at this stage will throw an error // because we know the load address of every section now lw_expr_stack_t *resolve_sym(char *sym, int symtype, void *state) { section_t *sect = state; lw_expr_term_t *term; int val = 0, i, fn; lw_expr_stack_t *s; symtab_t *se; fileinfo_t *fp; // fprintf(stderr, "Looking up %s\n", sym); if (symtype == 1) { // local symbol if (!sym) { val = sect -> loadaddress; goto out; } // start with this section for (se = sect -> localsyms; se; se = se -> next) { if (!strcmp((char *)(se -> sym), sym)) { if (sect -> flags & SECTION_CONST) val = se -> offset; else val = se -> offset + sect -> loadaddress; goto out; } } // not in this section - check all sections in this file for (i = 0; i < sect -> file -> nsections; i++) { for (se = sect -> file -> sections[i].localsyms; se; se = se -> next) { if (!strcmp((char *)(se -> sym), sym)) { if (sect -> file -> sections[i].flags & SECTION_CONST) val = se -> offset; else val = se -> offset + sect -> file -> sections[i].loadaddress; goto out; } } } // not found symerr = 1; fprintf(stderr, "Local symbol %s not found in %s:%s\n", sanitize_symbol(sym), sect -> file -> filename, sect -> name); goto outerr; } else { symlist_t *se; // external symbol // first look up synthesized symbols for (se = symlist; se; se = se -> next) { if (!strcmp(se -> sym, sym)) { val = se -> val; goto out; } } // read all files in order until found (or not found) if (sect) { for (fp = sect -> file; fp; fp = fp -> parent) { // fprintf(stderr, "Looking in %s\n", fp -> filename); s = find_external_sym_recurse(sym, fp); if (s) return s; } } for (fn = 0; fn < ninputfiles; fn++) { // fprintf(stderr, "Looking in %s\n", inputfiles[fn] -> filename); s = find_external_sym_recurse(sym, inputfiles[fn]); if (s) return s; } if (sect) { fprintf(stderr, "External symbol %s not found in %s:%s\n", sanitize_symbol(sym), sect -> file -> filename, sect -> name); } else { fprintf(stderr, "External symbol %s not found\n", sym); } symerr = 1; goto outerr; } fprintf(stderr, "Shouldn't ever get here!!!\n"); exit(88); out: s = lw_expr_stack_create(); term = lw_expr_term_create_int(val & 0xffff); lw_expr_stack_push(s, term); lw_expr_term_free(term); return s; outerr: return NULL; } void resolve_references(void) { int sn; reloc_t *rl; int rval; // resolve entry point if required // this must resolve to an *exported* symbol and will resolve to the // first instance of that symbol if (linkscript.execsym) { lw_expr_stack_t *s; s = resolve_sym(linkscript.execsym, 0, NULL); if (!s) { fprintf(stderr, "Cannot resolve exec address '%s'\n", linkscript.execsym); symerr = 1; } else { linkscript.execaddr = lw_expr_get_value(s); lw_expr_stack_free(s); } } for (sn = 0; sn < nsects; sn++) { for (rl = sectlist[sn].ptr -> incompletes; rl; rl = rl -> next) { // do a "simplify" on the expression rval = lw_expr_reval(rl -> expr, resolve_sym, sectlist[sn].ptr); // is it constant? error out if not if (rval != 0 || !lw_expr_is_constant(rl -> expr)) { fprintf(stderr, "Incomplete reference at %s:%s+%02X\n", sectlist[sn].ptr -> file -> filename, sectlist[sn].ptr -> name, rl -> offset); symerr = 1; } else { // put the value into the relocation address rval = lw_expr_get_value(rl -> expr); if (rl -> flags & RELOC_8BIT) { sectlist[sn].ptr -> code[rl -> offset] = rval & 0xff; } else { sectlist[sn].ptr -> code[rl -> offset] = (rval >> 8) & 0xff; sectlist[sn].ptr -> code[rl -> offset + 1] = rval & 0xff; } } } } if (symerr) exit(1); if (outformat == OUTPUT_OS9) check_os9(); } void resolve_files_aux(fileinfo_t *fn) { int sn; reloc_t *rl; lw_expr_stack_t *te; int rval; if (fn -> forced == 0) return; for (sn = 0; sn < fn -> nsections; sn++) { for (rl = fn -> sections[sn].incompletes; rl; rl = rl -> next) { // do a "simplify" on the expression te = lw_expr_stack_dup(rl -> expr); rval = lw_expr_reval(te, resolve_sym, &(fn -> sections[sn])); // is it constant? error out if not if (rval != 0 || !lw_expr_is_constant(te)) { fprintf(stderr, "Incomplete reference at %s:%s+%02X\n", fn -> filename, fn -> sections[sn].name, rl -> offset); symerr = 1; } lw_expr_stack_free(te); } } // handle any sub files for (sn = 0; sn < fn -> nsubs; sn++) resolve_files_aux(fn -> subs[sn]); } /* This is just a pared down version of the algo for resolving references. */ void resolve_files(void) { int fn; resolveonly = 1; // resolve entry point if required // this must resolve to an *exported* symbol and will resolve to the // first instance of that symbol // if (linkscript.execsym) // { // lw_expr_stack_t *s; // // s = resolve_sym(linkscript.execsym, 0, NULL); // if (!s) // { // fprintf(stderr, "Cannot resolve exec address '%s'\n", sanitize_symbol(linkscript.execsym)); // symerr = 1; // } // } do { nforced = 0; for (fn = 0; fn < ninputfiles; fn++) { resolve_files_aux(inputfiles[fn]); } } while (nforced == 1); resolveonly = 0; // if (symerr) // exit(1); symerr = 0; // theoretically, all files referenced by other files now have "forced" set to 1 for (fn = 0; fn < ninputfiles; fn++) { if (inputfiles[fn] -> forced == 1) continue; fprintf(stderr, "Warning: %s (%d) does not resolve any symbols\n", inputfiles[fn] -> filename, fn); } } void find_section_by_name_once_aux(char *name, fileinfo_t *fn, section_t **rval, int *found); void find_section_by_name_once_aux(char *name, fileinfo_t *fn, section_t **rval, int *found) { int sn; if (fn -> forced == 0) return; for (sn = 0; sn < fn -> nsections; sn++) { if (!strcmp(name, (char *)(fn -> sections[sn].name))) { (*found)++; *rval = &(fn -> sections[sn]); } } for (sn = 0; sn < fn -> nsubs; sn++) { find_section_by_name_once_aux(name, fn -> subs[sn], rval, found); } } section_t *find_section_by_name_once(char *name) { int fn; int found = 0; section_t *rval = NULL; for (fn = 0; fn < ninputfiles; fn++) { find_section_by_name_once_aux(name, inputfiles[fn], &rval, &found); } if (found != 1) { rval = NULL; if (found > 1) fprintf(stderr, "Warning: multiple instances of section %s found; ignoring all of them which is probably not what you want\n", name); } return rval; } void check_os9(void) { section_t *s; symtab_t *sym; s = find_section_by_name_once("__os9"); linkscript.name = outfile; if (!s) { // use defaults if not found return; } // this section is special // several symbols may be defined as LOCAL symbols: // type: module type // lang: module language // attr: module attributes // rev: module revision // // the symbols are not case sensitive // // any unrecognized symbols are ignored // the contents of the actual data to the first NUL // is assumed to be the module name unless it is an empty string if (s -> codesize > 0 && (s -> flags & SECTION_BSS) == 0 && s -> code[0] != 0) { linkscript.name = (char *)(s -> code); } for (sym = s -> localsyms; sym; sym = sym -> next) { char *sm = (char *)(sym -> sym); if (!strcasecmp(sm, "type")) { linkscript.modtype = sym -> offset; } else if (!strcasecmp(sm, "lang")) { linkscript.modlang = sym -> offset; } else if (!strcasecmp(sm, "attr")) { linkscript.modlang = sym -> offset; } else if (!strcasecmp(sm, "rev")) { linkscript.modrev = sym -> offset; } } if (linkscript.modtype > 15) linkscript.modtype = linkscript.modtype >> 4; if (linkscript.modattr > 15) linkscript.modattr = linkscript.modattr >> 4; linkscript.modlang &= 15; linkscript.modtype &= 15; linkscript.modrev &= 15; linkscript.modattr &= 15; }