Mercurial > hg > index.cgi
view lwlink/link.c @ 463:42be9eec7935
Make pcr relative forward references better able to select 8 bit offsets
author | William Astle <lost@l-w.ca> |
---|---|
date | Wed, 28 Feb 2018 23:00:59 -0700 |
parents | 9c6204534ab8 |
children | 3425005b9980 |
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" #ifdef _MSC_VER #include <lw_win.h> // windows build #endif void check_os9(void); struct section_list *sectlist = NULL; int nsects = 0; static int nforced = 0; static int resolveonly = 0; int quietsym = 1; symlist_t *symlist = NULL; sectopt_t *section_opts = NULL; void check_section_name(char *name, int *base, fileinfo_t *fn, int down) { int sn; sectopt_t *so; // fprintf(stderr, "Considering sections in %s (%d) for %s\n", fn -> filename, fn -> forced, name); if (fn -> forced == 0) return; for (so = section_opts; so; so = so -> next) if (!strcmp(so -> name, name)) break; 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; if (down) { *base -= fn -> sections[sn].codesize; fn -> sections[sn].loadaddress = *base; } else { fn -> sections[sn].loadaddress = *base; *base += fn -> sections[sn].codesize; } if (down && so && so -> aftersize) { sectlist[nsects].ptr -> afterbytes = so -> afterbytes; sectlist[nsects].ptr -> aftersize = so -> aftersize; sectlist[nsects].ptr -> loadaddress -= so -> aftersize; *base -= so -> aftersize; so -> aftersize = 0; } 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], down); } } void add_matching_sections(char *name, int yesflags, int noflags, int *base, int down); void check_section_flags(int yesflags, int noflags, int *base, fileinfo_t *fn, int down) { int sn; sectopt_t *so; // 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, down); /* handle "after padding" */ for (so = section_opts; so; so = so -> next) if (!strcmp(so -> name, (char *)(fn -> sections[sn].name))) break; if (so) { if (so -> aftersize) { sectlist[nsects - 1].ptr -> afterbytes = so -> afterbytes; sectlist[nsects - 1].ptr -> aftersize = so -> aftersize; if (down) { sectlist[nsects - 1].ptr -> loadaddress -= so -> aftersize; *base -= so -> aftersize; } else { *base += so -> aftersize; } } } // and then continue looking for sections } for (sn = 0; sn < fn -> nsubs; sn++) { check_section_flags(yesflags, noflags, base, fn -> subs[sn], down); } } void add_matching_sections(char *name, int yesflags, int noflags, int *base, int down) { 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], down); } } 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], down); } } } // 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 growdown = 0; int ln, sn, fn; sectopt_t *so; for (ln = 0; ln < linkscript.nlines; ln++) { if (linkscript.lines[ln].loadat >= 0) { laddr = linkscript.lines[ln].loadat; growdown = linkscript.lines[ln].growsdown; } //fprintf(stderr, "Adding section %s\n", linkscript.lines[ln].sectname); add_matching_sections(linkscript.lines[ln].sectname, linkscript.lines[ln].yesflags, linkscript.lines[ln].noflags, &laddr, growdown); if (linkscript.lines[ln].sectname) { char *sname = linkscript.lines[ln].sectname; /* handle "after padding" */ for (so = section_opts; so; so = so -> next) if (!strcmp(so -> name, sname)) break; if (so) { if (so -> aftersize) { sectlist[nsects - 1].ptr -> afterbytes = so -> afterbytes; sectlist[nsects - 1].ptr -> aftersize = so -> aftersize; if (growdown) { sectlist[nsects-1].ptr -> loadaddress -= so -> aftersize; laddr -= so -> aftersize; } else { laddr += so -> aftersize; } } } } 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); fprintf(stderr, "Adding sectoin %s\n", sname); 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; growdown = linkscript.lines[ln].growsdown; } else { sectlist[nsects].forceaddr = 0; } if (growdown) { laddr -= inputfiles[fn] -> sections[sn].codesize; inputfiles[fn] -> sections[sn].loadaddress = laddr; } else { 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; int lowaddr; for (sn = 0; sn < nsects; sn++) { 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; } if (lastsect && linkscript.basesympat) { se = lw_alloc(sizeof(symlist_t)); se -> val = lowaddr; snprintf(sym, 255, linkscript.basesympat, lastsect); se -> sym = lw_strdup(sym); se -> next = symlist; symlist = se; } lastsect = (char *)(sectlist[sn].ptr -> name); len = 0; lowaddr = sectlist[sn].ptr -> loadaddress; } len += sectlist[sn].ptr -> codesize; if (sectlist[sn].ptr -> loadaddress < lowaddr) lowaddr = sectlist[sn].ptr -> loadaddress; } 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; } if (lastsect && linkscript.basesympat) { se = lw_alloc(sizeof(symlist_t)); se -> val = lowaddr; snprintf(sym, 255, linkscript.basesympat, 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 if (!quietsym) { 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 (!quietsym) { 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; quietsym = 0; // 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); lw_expr_reval(te, resolve_sym, &(fn -> sections[sn])); // is it constant? error out if not // incompletes will error out during resolve_references() //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: library -l%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 foreach_section_aux(char *name, fileinfo_t *fn, void (*fnp)(section_t *s, void *arg), void *arg) { int sn; if (fn -> forced == 0) return; for (sn = 0; sn < fn -> nsections; sn++) { if (!strcmp(name, (char *)(fn -> sections[sn].name))) { (*fnp)(&(fn -> sections[sn]), arg); } } for (sn = 0; sn < fn -> nsubs; sn++) { foreach_section_aux(name, fn -> subs[sn], fnp, arg); } } void foreach_section(char *name, void (*fnp)(section_t *s, void *arg), void *arg) { int fn; for (fn = 0; fn < ninputfiles; fn++) { foreach_section_aux(name, inputfiles[fn], fnp, arg); } } struct check_os9_aux_s { int typeseen; int attrseen; int langseen; int revseen; int nameseen; int edseen; }; void check_os9_aux(section_t *s, void *arg) { struct check_os9_aux_s *st = arg; symtab_t *sym; // this section is special // several symbols may be defined as LOCAL symbols: // type: module type // lang: module language // attr: module attributes // rev: module revision // ed: module edition // // 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); st -> nameseen++; } for (sym = s -> localsyms; sym; sym = sym -> next) { char *sm = (char *)(sym -> sym); if (!strcasecmp(sm, "type")) { linkscript.modtype = sym -> offset; st -> typeseen++; } else if (!strcasecmp(sm, "lang")) { linkscript.modlang = sym -> offset; st -> langseen++; } else if (!strcasecmp(sm, "attr")) { linkscript.modattr = sym -> offset; st -> attrseen++; } else if (!strcasecmp(sm, "rev")) { linkscript.modrev = sym -> offset; st -> revseen++; } else if (!strcasecmp(sm, "stack")) { linkscript.stacksize += sym -> offset; } else if (!strcasecmp(sm, "edition")) { linkscript.edition = sym -> offset; st -> edseen++; } } } void check_os9(void) { struct check_os9_aux_s st = { 0 }; linkscript.name = outfile; foreach_section("__os9", check_os9_aux, &st); 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; if (st.attrseen > 1 || st.typeseen > 1 || st.langseen > 1 || st.revseen > 1 || st.nameseen > 1 || st.edseen > 1 ) { fprintf(stderr, "Warning: multiple instances of __os9 found with duplicate settings of type, lang, attr, rev, edition, or module name.\n"); } } void resolve_padding(void) { int sn; for (sn = 0; sn < nsects; sn++) { if (sectlist[sn].ptr -> afterbytes) { unsigned char *t; t = lw_alloc(sectlist[sn].ptr -> codesize + sectlist[sn].ptr -> aftersize); memmove(t, sectlist[sn].ptr -> code, sectlist[sn].ptr -> codesize); sectlist[sn].ptr -> code = t; memmove(sectlist[sn].ptr -> code + sectlist[sn].ptr -> codesize, sectlist[sn].ptr -> afterbytes, sectlist[sn].ptr -> aftersize); sectlist[sn].ptr -> codesize += sectlist[sn].ptr -> aftersize; } } }