/* coff.c -- rutines for manipulating coff executable files. */ #include <coff.h> #include "tboot.h" /* Convert COFF to load table. * Used to generate loading instructions for use by tboot main(). * Returns true on successful translation. */ int coff2load(ip, table, data_seg) struct inode *ip; /* input: File to read. */ struct load_segment table[]; /* output: How to read it. */ uint16 *data_seg; /* output: Where to point es. */ { FILHDR fh; /* COFF file header. */ AOUTHDR oh; /* COFF optional header. */ SCNHDR sh; /* COFF section header. */ int i, j; /* Loop counters. */ fsize_t section_seek; /* Seek counter for section headers. */ /* Read the file header. */ iread(ip, &fh, (fsize_t) 0, (uint16) sizeof (fh)); /* Check for the 5000 possible failures... */ /* Is this really a i386 COFF file? */ if (I386MAGIC != fh.f_magic) { puts("COFF COFF! File header bad magic.\r\n"); puts("This is not an i386 COFF file.\r\n"); return 0; } /* Is this an executable COFF file? */ if (!(fh.f_flags & F_EXEC)) { puts("Non-executable COFF file.\r\n"); return 0; } /* Does it have the information we need to execute it? */ if (sizeof(oh) != fh.f_opthdr) { puts("COFF optional header is wrong size.\r\n"); return 0; } /* * Pointless witticism: * (Actually, this helped us find a bug in ld.) */ if (fh.f_timdat < 0x1000) { puts( "Wow! An executable from the first few thousand seconds of time!\r\n" ); } /* ASSERTION: We know this to be an executable i386 COFF file. */ /* Read the optional header. */ /* This is the one with the data we really want. */ iread(ip, &oh, (fsize_t) sizeof(fh), (uint16) sizeof (oh)); /* Validate the optional header. */ if (NORMAL_MAGIC != oh.magic) { puts("COFF COFF! Optional header bad magic.\r\n"); puts("This isn't a normal executable file.\r\n"); return 0; } /* Read through the section headers, * looking for text and data segments, and * turning them into little toads. Or something like that. */ #define TEXT table[0] #define DATA table[1] /* Loop until we have both the sections we want, at most twice. */ for (TEXT.valid = 0, DATA.valid = 0, j = 0; !(TEXT.valid && DATA.valid) && j < 2; ++j) { for (section_seek = sizeof(FILHDR)+sizeof(AOUTHDR), i = 0; i < fh.f_nscns; /* i < number of sections */ section_seek += SCNHSZ, ++i) { iread(ip, &sh, section_seek, SCNHSZ); switch ((int) sh.s_flags) { case STYP_TEXT: TEXT.valid = 1; TEXT.message = "\r\nLoading COHERENT.\r\n"; TEXT.load_toseg = sys_base; TEXT.load_tooffset = 0; TEXT.load_offset = sh.s_scnptr; TEXT.load_length = sh.s_size; break; case STYP_DATA: /* The text header must already have been read to * put meaningful numbers here. */ if (TEXT.valid) { DATA.valid = 1; DATA.message = "\r\nLoading COHERENT data.\r\n"; /* Round up to next paragraph beyond end * of text. */ DATA.load_toseg = (sys_base + (TEXT.load_length + 15) / 16); DATA.load_tooffset = 0; DATA.load_offset = sh.s_scnptr; DATA.load_length = sh.s_size; *data_seg = (sys_base + (TEXT.load_length + 15) / 16); } break; case STYP_BSS: break; default: puts("Warning. Unrecognized COFF section.\r\n"); break; } /* switch (sh.s_flags) */ } /* for walk through headers */ } /* while haven't got both, at most twice */ if (!TEXT.valid) { puts("Failed to find COFF text section.\r\n"); return 0; } if (!DATA.valid) { puts("Failed to find COFF data section.\r\n"); return 0; } table[2].valid = 0; /* Terminate the list. */ return 1; } /* * Symbol name. */ static char * symName(sym, str_tab, work) SYMENT *sym; char *str_tab, *work; { if (!sym->_n._n_n._n_zeroes) return (str_tab + sym->_n._n_n._n_offset - 4); memcpy(work, sym->_n._n_name, SYMNMLEN); work[SYMNMLEN] = '\0'; return (work); } /* * Look up the value of a single data symbol in a coff file, * relative to the start of the data segment. * * We use the symbol "sdata" to find the start of the data segment-- * this works for 386 COHERENT kernels but will not work in general. * It should really fetch the address of the start of the data segment * from the data section header. */ uint32 wrap_coffnlist(fn, symbol) char *fn; /* file name */ char *symbol; /* symbol to look up */ { /* Something goes wrong with looking up symbol if * nlp is automatic rather than static, even with a huge stack. */ static SYMENT nlp[2]; uint32 retval; /* Start of the data segment. */ strcpy(nlp[0]._n._n_name, "sdata"); nlp[0].n_type = -1; nlp[1]._n._n_n._n_zeroes = 0; nlp[1]._n._n_n._n_offset = sizeof(int32); nlp[1].n_type = -1; coffnlist(fn, nlp, symbol, 2); retval = ((uint32)nlp[1].n_value) - ((uint32)nlp[0].n_value); if (verbose_flag) printf("sdata=%lx %s=%lx retval=%lx\r\n", nlp[0].n_value, symbol, nlp[1].n_value, retval); if (0L == nlp[1].n_value) { return(0L); } else { return retval; } puts("Unreachable code in wrap_coffnlist().\r\n"); return(0L); } /* wrap_coffnlist() */ int coffnlist(fn, nlp, names, count) char *fn; /* file name */ SYMENT *nlp; /* names to look up */ char *names; /* long names */ int count; /* size of passed table */ { FILHDR head; int fp; /* str_tab should be malloc'd. Blows up if file's sym table too big. */ #define STR_TAB_SIZE 5000 char str_tab[STR_TAB_SIZE]; long str_length; int aux, i; if (-1 == (fp = open(fn, 0))) { puts("coffnlist open failed\r\n"); return 0; } if (FILHSZ != read(fp, &head, FILHSZ) || head.f_magic != I386MAGIC) { close (fp); printf("coffnlist header read (%d) failed\r\n", FILHSZ); return 0; } lseek(fp, head.f_symptr + (SYMESZ * head.f_nsyms), 0); if (sizeof(str_length) != read(fp, &str_length, sizeof(str_length))) str_length = 0; if (str_length) { uint16 len; len = str_length -= 4; if (len != str_length || len > STR_TAB_SIZE) { close (fp); printf("coffnlist str table overflow, len = %d\r\n", len); return 0; } if (len != read(fp, str_tab, len)) { close (fp); puts("coffnlist str read failed\r\n"); return 0; } } lseek(fp, head.f_symptr, 0); for (i = aux = 0; i < head.f_nsyms; i++) { SYMENT sym; /* symbol read in */ int taux, j; if (SYMESZ != read(fp, &sym, SYMESZ)) { close (fp); puts("coffnlist sym read failed\r\n"); return 0; } if (aux) { aux--; continue; } aux = sym.n_numaux; for (j = taux = 0; j < count; j++) { static char n1[SYMNMLEN + 1], n2[SYMNMLEN + 1]; register SYMENT *np; if (taux) { taux--; continue; } np = nlp + j; taux = np->n_numaux; if (np->n_type != -1 || strcmp(symName(np, names, n1), symName(&sym, str_tab, n2))) continue; np->n_value = sym.n_value; np->n_scnum = sym.n_scnum; np->n_type = sym.n_type; np->n_sclass = sym.n_sclass; break; } } close (fp); return 1; } #ifdef TEST #include <stdio.h> main() { int i; static SYMENT sym[3]; static char ptr[]="rootdev"; strcpy(sym[0]._n._n_name, "NCLIST"); sym[0].n_type = -1; strcpy(sym[1]._n._n_name, "sdata"); sym[1].n_type = -1; sym[2]._n._n_n._n_zeroes = 0; sym[2]._n._n_n._n_offset = sizeof(int32); sym[2].n_type = -1; coffnlist("/at386", sym, ptr, 3); for (i = 0; i < 3; ++i) { printf("sym[%d]._n._n_name: %s\n", i, sym[i]._n._n_name); printf("sym[%d].n_value: %lx\n\n", i, sym[i].n_value); } printf("\nrootdev as offset: %lx\n", wrap_coffnlist("/at386", "rootdev")); } #endif