| /* |
| * cload.c |
| * |
| * DSP-BIOS Bridge driver support functions for TI OMAP processors. |
| * |
| * Copyright (C) 2005-2006 Texas Instruments, Inc. |
| * |
| * This package is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
| * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| */ |
| |
| #include "header.h" |
| |
| #include "module_list.h" |
| #define LINKER_MODULES_HEADER ("_" MODULES_HEADER) |
| |
| /* |
| * forward references |
| */ |
| static void dload_symbols(struct dload_state *dlthis); |
| static void dload_data(struct dload_state *dlthis); |
| static void allocate_sections(struct dload_state *dlthis); |
| static void string_table_free(struct dload_state *dlthis); |
| static void symbol_table_free(struct dload_state *dlthis); |
| static void section_table_free(struct dload_state *dlthis); |
| static void init_module_handle(struct dload_state *dlthis); |
| #if BITS_PER_AU > BITS_PER_BYTE |
| static char *unpack_name(struct dload_state *dlthis, u32 soffset); |
| #endif |
| |
| static const char cinitname[] = { ".cinit" }; |
| static const char loader_dllview_root[] = { "?DLModules?" }; |
| |
| /* |
| * Error strings |
| */ |
| static const char readstrm[] = { "Error reading %s from input stream" }; |
| static const char err_alloc[] = { "Syms->dload_allocate( %d ) failed" }; |
| static const char tgtalloc[] = { |
| "Target memory allocate failed, section %s size " FMT_UI32 }; |
| static const char initfail[] = { "%s to target address " FMT_UI32 " failed" }; |
| static const char dlvwrite[] = { "Write to DLLview list failed" }; |
| static const char iconnect[] = { "Connect call to init interface failed" }; |
| static const char err_checksum[] = { "Checksum failed on %s" }; |
| |
| /************************************************************************* |
| * Procedure dload_error |
| * |
| * Parameters: |
| * errtxt description of the error, printf style |
| * ... additional information |
| * |
| * Effect: |
| * Reports or records the error as appropriate. |
| *********************************************************************** */ |
| void dload_error(struct dload_state *dlthis, const char *errtxt, ...) |
| { |
| va_list args; |
| |
| va_start(args, errtxt); |
| dlthis->mysym->error_report(dlthis->mysym, errtxt, args); |
| va_end(args); |
| dlthis->dload_errcount += 1; |
| |
| } /* dload_error */ |
| |
| #define DL_ERROR(zza, zzb) dload_error(dlthis, zza, zzb) |
| |
| /************************************************************************* |
| * Procedure dload_syms_error |
| * |
| * Parameters: |
| * errtxt description of the error, printf style |
| * ... additional information |
| * |
| * Effect: |
| * Reports or records the error as appropriate. |
| *********************************************************************** */ |
| void dload_syms_error(struct dynamic_loader_sym *syms, const char *errtxt, ...) |
| { |
| va_list args; |
| |
| va_start(args, errtxt); |
| syms->error_report(syms, errtxt, args); |
| va_end(args); |
| } |
| |
| /************************************************************************* |
| * Procedure dynamic_load_module |
| * |
| * Parameters: |
| * module The input stream that supplies the module image |
| * syms Host-side symbol table and malloc/free functions |
| * alloc Target-side memory allocation |
| * init Target-side memory initialization |
| * options Option flags DLOAD_* |
| * mhandle A module handle for use with Dynamic_Unload |
| * |
| * Effect: |
| * The module image is read using *module. Target storage for the new |
| * image is |
| * obtained from *alloc. Symbols defined and referenced by the module are |
| * managed using *syms. The image is then relocated and references |
| * resolved as necessary, and the resulting executable bits are placed |
| * into target memory using *init. |
| * |
| * Returns: |
| * On a successful load, a module handle is placed in *mhandle, |
| * and zero is returned. On error, the number of errors detected is |
| * returned. Individual errors are reported during the load process |
| * using syms->error_report(). |
| ********************************************************************** */ |
| int dynamic_load_module(struct dynamic_loader_stream *module, |
| struct dynamic_loader_sym *syms, |
| struct dynamic_loader_allocate *alloc, |
| struct dynamic_loader_initialize *init, |
| unsigned options, void **mhandle) |
| { |
| register unsigned *dp, sz; |
| struct dload_state dl_state; /* internal state for this call */ |
| |
| /* blast our internal state */ |
| dp = (unsigned *)&dl_state; |
| for (sz = sizeof(dl_state) / sizeof(unsigned); sz > 0; sz -= 1) |
| *dp++ = 0; |
| |
| /* Enable _only_ BSS initialization if enabled by user */ |
| if ((options & DLOAD_INITBSS) == DLOAD_INITBSS) |
| dl_state.myoptions = DLOAD_INITBSS; |
| |
| /* Check that mandatory arguments are present */ |
| if (!module || !syms) { |
| dload_error(&dl_state, "Required parameter is NULL"); |
| } else { |
| dl_state.strm = module; |
| dl_state.mysym = syms; |
| dload_headers(&dl_state); |
| if (!dl_state.dload_errcount) |
| dload_strings(&dl_state, false); |
| if (!dl_state.dload_errcount) |
| dload_sections(&dl_state); |
| |
| if (init && !dl_state.dload_errcount) { |
| if (init->connect(init)) { |
| dl_state.myio = init; |
| dl_state.myalloc = alloc; |
| /* do now, before reducing symbols */ |
| allocate_sections(&dl_state); |
| } else |
| dload_error(&dl_state, iconnect); |
| } |
| |
| if (!dl_state.dload_errcount) { |
| /* fix up entry point address */ |
| unsigned sref = dl_state.dfile_hdr.df_entry_secn - 1; |
| if (sref < dl_state.allocated_secn_count) |
| dl_state.dfile_hdr.df_entrypt += |
| dl_state.ldr_sections[sref].run_addr; |
| |
| dload_symbols(&dl_state); |
| } |
| |
| if (init && !dl_state.dload_errcount) |
| dload_data(&dl_state); |
| |
| init_module_handle(&dl_state); |
| |
| /* dl_state.myio is init or 0 at this point. */ |
| if (dl_state.myio) { |
| if ((!dl_state.dload_errcount) && |
| (dl_state.dfile_hdr.df_entry_secn != DN_UNDEF) && |
| (!init->execute(init, |
| dl_state.dfile_hdr.df_entrypt))) |
| dload_error(&dl_state, "Init->Execute Failed"); |
| init->release(init); |
| } |
| |
| symbol_table_free(&dl_state); |
| section_table_free(&dl_state); |
| string_table_free(&dl_state); |
| dload_tramp_cleanup(&dl_state); |
| |
| if (dl_state.dload_errcount) { |
| dynamic_unload_module(dl_state.myhandle, syms, alloc, |
| init); |
| dl_state.myhandle = NULL; |
| } |
| } |
| |
| if (mhandle) |
| *mhandle = dl_state.myhandle; /* give back the handle */ |
| |
| return dl_state.dload_errcount; |
| } /* DLOAD_File */ |
| |
| /************************************************************************* |
| * Procedure dynamic_open_module |
| * |
| * Parameters: |
| * module The input stream that supplies the module image |
| * syms Host-side symbol table and malloc/free functions |
| * alloc Target-side memory allocation |
| * init Target-side memory initialization |
| * options Option flags DLOAD_* |
| * mhandle A module handle for use with Dynamic_Unload |
| * |
| * Effect: |
| * The module image is read using *module. Target storage for the new |
| * image is |
| * obtained from *alloc. Symbols defined and referenced by the module are |
| * managed using *syms. The image is then relocated and references |
| * resolved as necessary, and the resulting executable bits are placed |
| * into target memory using *init. |
| * |
| * Returns: |
| * On a successful load, a module handle is placed in *mhandle, |
| * and zero is returned. On error, the number of errors detected is |
| * returned. Individual errors are reported during the load process |
| * using syms->error_report(). |
| ********************************************************************** */ |
| int |
| dynamic_open_module(struct dynamic_loader_stream *module, |
| struct dynamic_loader_sym *syms, |
| struct dynamic_loader_allocate *alloc, |
| struct dynamic_loader_initialize *init, |
| unsigned options, void **mhandle) |
| { |
| register unsigned *dp, sz; |
| struct dload_state dl_state; /* internal state for this call */ |
| |
| /* blast our internal state */ |
| dp = (unsigned *)&dl_state; |
| for (sz = sizeof(dl_state) / sizeof(unsigned); sz > 0; sz -= 1) |
| *dp++ = 0; |
| |
| /* Enable _only_ BSS initialization if enabled by user */ |
| if ((options & DLOAD_INITBSS) == DLOAD_INITBSS) |
| dl_state.myoptions = DLOAD_INITBSS; |
| |
| /* Check that mandatory arguments are present */ |
| if (!module || !syms) { |
| dload_error(&dl_state, "Required parameter is NULL"); |
| } else { |
| dl_state.strm = module; |
| dl_state.mysym = syms; |
| dload_headers(&dl_state); |
| if (!dl_state.dload_errcount) |
| dload_strings(&dl_state, false); |
| if (!dl_state.dload_errcount) |
| dload_sections(&dl_state); |
| |
| if (init && !dl_state.dload_errcount) { |
| if (init->connect(init)) { |
| dl_state.myio = init; |
| dl_state.myalloc = alloc; |
| /* do now, before reducing symbols */ |
| allocate_sections(&dl_state); |
| } else |
| dload_error(&dl_state, iconnect); |
| } |
| |
| if (!dl_state.dload_errcount) { |
| /* fix up entry point address */ |
| unsigned sref = dl_state.dfile_hdr.df_entry_secn - 1; |
| if (sref < dl_state.allocated_secn_count) |
| dl_state.dfile_hdr.df_entrypt += |
| dl_state.ldr_sections[sref].run_addr; |
| |
| dload_symbols(&dl_state); |
| } |
| |
| init_module_handle(&dl_state); |
| |
| /* dl_state.myio is either 0 or init at this point. */ |
| if (dl_state.myio) { |
| if ((!dl_state.dload_errcount) && |
| (dl_state.dfile_hdr.df_entry_secn != DN_UNDEF) && |
| (!init->execute(init, |
| dl_state.dfile_hdr.df_entrypt))) |
| dload_error(&dl_state, "Init->Execute Failed"); |
| init->release(init); |
| } |
| |
| symbol_table_free(&dl_state); |
| section_table_free(&dl_state); |
| string_table_free(&dl_state); |
| |
| if (dl_state.dload_errcount) { |
| dynamic_unload_module(dl_state.myhandle, syms, alloc, |
| init); |
| dl_state.myhandle = NULL; |
| } |
| } |
| |
| if (mhandle) |
| *mhandle = dl_state.myhandle; /* give back the handle */ |
| |
| return dl_state.dload_errcount; |
| } /* DLOAD_File */ |
| |
| /************************************************************************* |
| * Procedure dload_headers |
| * |
| * Parameters: |
| * none |
| * |
| * Effect: |
| * Loads the DOFF header and verify record. Deals with any byte-order |
| * issues and checks them for validity. |
| *********************************************************************** */ |
| #define COMBINED_HEADER_SIZE (sizeof(struct doff_filehdr_t)+ \ |
| sizeof(struct doff_verify_rec_t)) |
| |
| void dload_headers(struct dload_state *dlthis) |
| { |
| u32 map; |
| |
| /* Read the header and the verify record as one. If we don't get it |
| all, we're done */ |
| if (dlthis->strm->read_buffer(dlthis->strm, &dlthis->dfile_hdr, |
| COMBINED_HEADER_SIZE) != |
| COMBINED_HEADER_SIZE) { |
| DL_ERROR(readstrm, "File Headers"); |
| return; |
| } |
| /* |
| * Verify that we have the byte order of the file correct. |
| * If not, must fix it before we can continue |
| */ |
| map = REORDER_MAP(dlthis->dfile_hdr.df_byte_reshuffle); |
| if (map != REORDER_MAP(BYTE_RESHUFFLE_VALUE)) { |
| /* input is either byte-shuffled or bad */ |
| if ((map & 0xFCFCFCFC) == 0) { /* no obviously bogus bits */ |
| dload_reorder(&dlthis->dfile_hdr, COMBINED_HEADER_SIZE, |
| map); |
| } |
| if (dlthis->dfile_hdr.df_byte_reshuffle != |
| BYTE_RESHUFFLE_VALUE) { |
| /* didn't fix the problem, the byte swap map is bad */ |
| dload_error(dlthis, |
| "Bad byte swap map " FMT_UI32 " in header", |
| dlthis->dfile_hdr.df_byte_reshuffle); |
| return; |
| } |
| dlthis->reorder_map = map; /* keep map for future use */ |
| } |
| |
| /* |
| * Verify checksum of header and verify record |
| */ |
| if (~dload_checksum(&dlthis->dfile_hdr, |
| sizeof(struct doff_filehdr_t)) || |
| ~dload_checksum(&dlthis->verify, |
| sizeof(struct doff_verify_rec_t))) { |
| DL_ERROR(err_checksum, "header or verify record"); |
| return; |
| } |
| #if HOST_ENDIANNESS |
| dlthis->dfile_hdr.df_byte_reshuffle = map; /* put back for later */ |
| #endif |
| |
| /* Check for valid target ID */ |
| if ((dlthis->dfile_hdr.df_target_id != TARGET_ID) && |
| -(dlthis->dfile_hdr.df_target_id != TMS470_ID)) { |
| dload_error(dlthis, "Bad target ID 0x%x and TARGET_ID 0x%x", |
| dlthis->dfile_hdr.df_target_id, TARGET_ID); |
| return; |
| } |
| /* Check for valid file format */ |
| if ((dlthis->dfile_hdr.df_doff_version != DOFF0)) { |
| dload_error(dlthis, "Bad DOFF version 0x%x", |
| dlthis->dfile_hdr.df_doff_version); |
| return; |
| } |
| |
| /* |
| * Apply reasonableness checks to count fields |
| */ |
| if (dlthis->dfile_hdr.df_strtab_size > MAX_REASONABLE_STRINGTAB) { |
| dload_error(dlthis, "Excessive string table size " FMT_UI32, |
| dlthis->dfile_hdr.df_strtab_size); |
| return; |
| } |
| if (dlthis->dfile_hdr.df_no_scns > MAX_REASONABLE_SECTIONS) { |
| dload_error(dlthis, "Excessive section count 0x%x", |
| dlthis->dfile_hdr.df_no_scns); |
| return; |
| } |
| #ifndef TARGET_ENDIANNESS |
| /* |
| * Check that endianness does not disagree with explicit specification |
| */ |
| if ((dlthis->dfile_hdr.df_flags >> ALIGN_COFF_ENDIANNESS) & |
| dlthis->myoptions & ENDIANNESS_MASK) { |
| dload_error(dlthis, |
| "Input endianness disagrees with specified option"); |
| return; |
| } |
| dlthis->big_e_target = dlthis->dfile_hdr.df_flags & DF_BIG; |
| #endif |
| |
| } /* dload_headers */ |
| |
| /* COFF Section Processing |
| * |
| * COFF sections are read in and retained intact. Each record is embedded |
| * in a new structure that records the updated load and |
| * run addresses of the section */ |
| |
| static const char secn_errid[] = { "section" }; |
| |
| /************************************************************************* |
| * Procedure dload_sections |
| * |
| * Parameters: |
| * none |
| * |
| * Effect: |
| * Loads the section records into an internal table. |
| *********************************************************************** */ |
| void dload_sections(struct dload_state *dlthis) |
| { |
| s16 siz; |
| struct doff_scnhdr_t *shp; |
| unsigned nsecs = dlthis->dfile_hdr.df_no_scns; |
| |
| /* allocate space for the DOFF section records */ |
| siz = nsecs * sizeof(struct doff_scnhdr_t); |
| shp = |
| (struct doff_scnhdr_t *)dlthis->mysym->dload_allocate(dlthis->mysym, |
| siz); |
| if (!shp) { /* not enough storage */ |
| DL_ERROR(err_alloc, siz); |
| return; |
| } |
| dlthis->sect_hdrs = shp; |
| |
| /* read in the section records */ |
| if (dlthis->strm->read_buffer(dlthis->strm, shp, siz) != siz) { |
| DL_ERROR(readstrm, secn_errid); |
| return; |
| } |
| |
| /* if we need to fix up byte order, do it now */ |
| if (dlthis->reorder_map) |
| dload_reorder(shp, siz, dlthis->reorder_map); |
| |
| /* check for validity */ |
| if (~dload_checksum(dlthis->sect_hdrs, siz) != |
| dlthis->verify.dv_scn_rec_checksum) { |
| DL_ERROR(err_checksum, secn_errid); |
| return; |
| } |
| |
| } /* dload_sections */ |
| |
| /***************************************************************************** |
| * Procedure allocate_sections |
| * |
| * Parameters: |
| * alloc target memory allocator class |
| * |
| * Effect: |
| * Assigns new (target) addresses for sections |
| **************************************************************************** */ |
| static void allocate_sections(struct dload_state *dlthis) |
| { |
| u16 curr_sect, nsecs, siz; |
| struct doff_scnhdr_t *shp; |
| struct ldr_section_info *asecs; |
| struct my_handle *hndl; |
| nsecs = dlthis->dfile_hdr.df_no_scns; |
| if (!nsecs) |
| return; |
| if ((dlthis->myalloc == NULL) && |
| (dlthis->dfile_hdr.df_target_scns > 0)) { |
| DL_ERROR("Arg 3 (alloc) required but NULL", 0); |
| return; |
| } |
| /* |
| * allocate space for the module handle, which we will keep for unload |
| * purposes include an additional section store for an auto-generated |
| * trampoline section in case we need it. |
| */ |
| siz = (dlthis->dfile_hdr.df_target_scns + 1) * |
| sizeof(struct ldr_section_info) + MY_HANDLE_SIZE; |
| |
| hndl = |
| (struct my_handle *)dlthis->mysym->dload_allocate(dlthis->mysym, |
| siz); |
| if (!hndl) { /* not enough storage */ |
| DL_ERROR(err_alloc, siz); |
| return; |
| } |
| /* initialize the handle header */ |
| hndl->dm.hnext = hndl->dm.hprev = hndl; /* circular list */ |
| hndl->dm.hroot = NULL; |
| hndl->dm.dbthis = 0; |
| dlthis->myhandle = hndl; /* save away for return */ |
| /* pointer to the section list of allocated sections */ |
| dlthis->ldr_sections = asecs = hndl->secns; |
| /* * Insert names into all sections, make copies of |
| the sections we allocate */ |
| shp = dlthis->sect_hdrs; |
| for (curr_sect = 0; curr_sect < nsecs; curr_sect++) { |
| u32 soffset = shp->ds_offset; |
| #if BITS_PER_AU <= BITS_PER_BYTE |
| /* attempt to insert the name of this section */ |
| if (soffset < dlthis->dfile_hdr.df_strtab_size) |
| ((struct ldr_section_info *)shp)->name = |
| dlthis->str_head + soffset; |
| else { |
| dload_error(dlthis, "Bad name offset in section %d", |
| curr_sect); |
| ((struct ldr_section_info *)shp)->name = NULL; |
| } |
| #endif |
| /* allocate target storage for sections that require it */ |
| if (ds_needs_allocation(shp)) { |
| *asecs = *(struct ldr_section_info *)shp; |
| asecs->context = 0; /* zero the context field */ |
| #if BITS_PER_AU > BITS_PER_BYTE |
| asecs->name = unpack_name(dlthis, soffset); |
| dlthis->debug_string_size = soffset + dlthis->temp_len; |
| #else |
| dlthis->debug_string_size = soffset; |
| #endif |
| if (dlthis->myalloc != NULL) { |
| if (!dlthis->myalloc-> |
| dload_allocate(dlthis->myalloc, asecs, |
| ds_alignment(asecs->type))) { |
| dload_error(dlthis, tgtalloc, |
| asecs->name, asecs->size); |
| return; |
| } |
| } |
| /* keep address deltas in original section table */ |
| shp->ds_vaddr = asecs->load_addr - shp->ds_vaddr; |
| shp->ds_paddr = asecs->run_addr - shp->ds_paddr; |
| dlthis->allocated_secn_count += 1; |
| } /* allocate target storage */ |
| shp += 1; |
| asecs += 1; |
| } |
| #if BITS_PER_AU <= BITS_PER_BYTE |
| dlthis->debug_string_size += |
| strlen(dlthis->str_head + dlthis->debug_string_size) + 1; |
| #endif |
| } /* allocate sections */ |
| |
| /************************************************************************* |
| * Procedure section_table_free |
| * |
| * Parameters: |
| * none |
| * |
| * Effect: |
| * Frees any state used by the symbol table. |
| * |
| * WARNING: |
| * This routine is not allowed to declare errors! |
| *********************************************************************** */ |
| static void section_table_free(struct dload_state *dlthis) |
| { |
| struct doff_scnhdr_t *shp; |
| |
| shp = dlthis->sect_hdrs; |
| if (shp) |
| dlthis->mysym->dload_deallocate(dlthis->mysym, shp); |
| |
| } /* section_table_free */ |
| |
| /************************************************************************* |
| * Procedure dload_strings |
| * |
| * Parameters: |
| * sec_names_only If true only read in the "section names" |
| * portion of the string table |
| * |
| * Effect: |
| * Loads the DOFF string table into memory. DOFF keeps all strings in a |
| * big unsorted array. We just read that array into memory in bulk. |
| *********************************************************************** */ |
| static const char stringtbl[] = { "string table" }; |
| |
| void dload_strings(struct dload_state *dlthis, bool sec_names_only) |
| { |
| u32 ssiz; |
| char *strbuf; |
| |
| if (sec_names_only) { |
| ssiz = BYTE_TO_HOST(DOFF_ALIGN |
| (dlthis->dfile_hdr.df_scn_name_size)); |
| } else { |
| ssiz = BYTE_TO_HOST(DOFF_ALIGN |
| (dlthis->dfile_hdr.df_strtab_size)); |
| } |
| if (ssiz == 0) |
| return; |
| |
| /* get some memory for the string table */ |
| #if BITS_PER_AU > BITS_PER_BYTE |
| strbuf = (char *)dlthis->mysym->dload_allocate(dlthis->mysym, ssiz + |
| dlthis->dfile_hdr. |
| df_max_str_len); |
| #else |
| strbuf = (char *)dlthis->mysym->dload_allocate(dlthis->mysym, ssiz); |
| #endif |
| if (strbuf == NULL) { |
| DL_ERROR(err_alloc, ssiz); |
| return; |
| } |
| dlthis->str_head = strbuf; |
| #if BITS_PER_AU > BITS_PER_BYTE |
| dlthis->str_temp = strbuf + ssiz; |
| #endif |
| /* read in the strings and verify them */ |
| if ((unsigned)(dlthis->strm->read_buffer(dlthis->strm, strbuf, |
| ssiz)) != ssiz) { |
| DL_ERROR(readstrm, stringtbl); |
| } |
| /* if we need to fix up byte order, do it now */ |
| #ifndef _BIG_ENDIAN |
| if (dlthis->reorder_map) |
| dload_reorder(strbuf, ssiz, dlthis->reorder_map); |
| |
| if ((!sec_names_only) && (~dload_checksum(strbuf, ssiz) != |
| dlthis->verify.dv_str_tab_checksum)) { |
| DL_ERROR(err_checksum, stringtbl); |
| } |
| #else |
| if (dlthis->dfile_hdr.df_byte_reshuffle != |
| HOST_BYTE_ORDER(REORDER_MAP(BYTE_RESHUFFLE_VALUE))) { |
| /* put strings in big-endian order, not in PC order */ |
| dload_reorder(strbuf, ssiz, |
| HOST_BYTE_ORDER(dlthis-> |
| dfile_hdr.df_byte_reshuffle)); |
| } |
| if ((!sec_names_only) && (~dload_reverse_checksum(strbuf, ssiz) != |
| dlthis->verify.dv_str_tab_checksum)) { |
| DL_ERROR(err_checksum, stringtbl); |
| } |
| #endif |
| } /* dload_strings */ |
| |
| /************************************************************************* |
| * Procedure string_table_free |
| * |
| * Parameters: |
| * none |
| * |
| * Effect: |
| * Frees any state used by the string table. |
| * |
| * WARNING: |
| * This routine is not allowed to declare errors! |
| ************************************************************************ */ |
| static void string_table_free(struct dload_state *dlthis) |
| { |
| if (dlthis->str_head) |
| dlthis->mysym->dload_deallocate(dlthis->mysym, |
| dlthis->str_head); |
| |
| } /* string_table_free */ |
| |
| /* |
| * Symbol Table Maintenance Functions |
| * |
| * COFF symbols are read by dload_symbols(), which is called after |
| * sections have been allocated. Symbols which might be used in |
| * relocation (ie, not debug info) are retained in an internal temporary |
| * compressed table (type local_symbol). A particular symbol is recovered |
| * by index by calling dload_find_symbol(). dload_find_symbol |
| * reconstructs a more explicit representation (type SLOTVEC) which is |
| * used by reloc.c |
| */ |
| /* real size of debug header */ |
| #define DBG_HDR_SIZE (sizeof(struct dll_module) - sizeof(struct dll_sect)) |
| |
| static const char sym_errid[] = { "symbol" }; |
| |
| /************************************************************************** |
| * Procedure dload_symbols |
| * |
| * Parameters: |
| * none |
| * |
| * Effect: |
| * Reads in symbols and retains ones that might be needed for relocation |
| * purposes. |
| *********************************************************************** */ |
| /* size of symbol buffer no bigger than target data buffer, to limit stack |
| * usage */ |
| #define MY_SYM_BUF_SIZ (BYTE_TO_HOST(IMAGE_PACKET_SIZE)/\ |
| sizeof(struct doff_syment_t)) |
| |
| static void dload_symbols(struct dload_state *dlthis) |
| { |
| u32 sym_count, siz, dsiz, symbols_left; |
| u32 checks; |
| struct local_symbol *sp; |
| struct dynload_symbol *symp; |
| struct dynload_symbol *newsym; |
| |
| sym_count = dlthis->dfile_hdr.df_no_syms; |
| if (sym_count == 0) |
| return; |
| |
| /* |
| * We keep a local symbol table for all of the symbols in the input. |
| * This table contains only section & value info, as we do not have |
| * to do any name processing for locals. We reuse this storage |
| * as a temporary for .dllview record construction. |
| * Allocate storage for the whole table. Add 1 to the section count |
| * in case a trampoline section is auto-generated as well as the |
| * size of the trampoline section name so DLLView doens't get lost. |
| */ |
| |
| siz = sym_count * sizeof(struct local_symbol); |
| dsiz = DBG_HDR_SIZE + |
| (sizeof(struct dll_sect) * dlthis->allocated_secn_count) + |
| BYTE_TO_HOST_ROUND(dlthis->debug_string_size + 1); |
| if (dsiz > siz) |
| siz = dsiz; /* larger of symbols and .dllview temp */ |
| sp = (struct local_symbol *)dlthis->mysym->dload_allocate(dlthis->mysym, |
| siz); |
| if (!sp) { |
| DL_ERROR(err_alloc, siz); |
| return; |
| } |
| dlthis->local_symtab = sp; |
| /* Read the symbols in the input, store them in the table, and post any |
| * globals to the global symbol table. In the process, externals |
| become defined from the global symbol table */ |
| checks = dlthis->verify.dv_sym_tab_checksum; |
| symbols_left = sym_count; |
| do { /* read all symbols */ |
| char *sname; |
| u32 val; |
| s32 delta; |
| struct doff_syment_t *input_sym; |
| unsigned syms_in_buf; |
| struct doff_syment_t my_sym_buf[MY_SYM_BUF_SIZ]; |
| input_sym = my_sym_buf; |
| syms_in_buf = symbols_left > MY_SYM_BUF_SIZ ? |
| MY_SYM_BUF_SIZ : symbols_left; |
| siz = syms_in_buf * sizeof(struct doff_syment_t); |
| if (dlthis->strm->read_buffer(dlthis->strm, input_sym, siz) != |
| siz) { |
| DL_ERROR(readstrm, sym_errid); |
| return; |
| } |
| if (dlthis->reorder_map) |
| dload_reorder(input_sym, siz, dlthis->reorder_map); |
| |
| checks += dload_checksum(input_sym, siz); |
| do { /* process symbols in buffer */ |
| symbols_left -= 1; |
| /* attempt to derive the name of this symbol */ |
| sname = NULL; |
| if (input_sym->dn_offset > 0) { |
| #if BITS_PER_AU <= BITS_PER_BYTE |
| if ((u32) input_sym->dn_offset < |
| dlthis->dfile_hdr.df_strtab_size) |
| sname = dlthis->str_head + |
| BYTE_TO_HOST(input_sym->dn_offset); |
| else |
| dload_error(dlthis, |
| "Bad name offset in symbol " |
| " %d", symbols_left); |
| #else |
| sname = unpack_name(dlthis, |
| input_sym->dn_offset); |
| #endif |
| } |
| val = input_sym->dn_value; |
| delta = 0; |
| sp->sclass = input_sym->dn_sclass; |
| sp->secnn = input_sym->dn_scnum; |
| /* if this is an undefined symbol, |
| * define it (or fail) now */ |
| if (sp->secnn == DN_UNDEF) { |
| /* pointless for static undefined */ |
| if (input_sym->dn_sclass != DN_EXT) |
| goto loop_cont; |
| |
| /* try to define symbol from previously |
| * loaded images */ |
| symp = dlthis->mysym->find_matching_symbol |
| (dlthis->mysym, sname); |
| if (!symp) { |
| DL_ERROR |
| ("Undefined external symbol %s", |
| sname); |
| goto loop_cont; |
| } |
| val = delta = symp->value; |
| #ifdef ENABLE_TRAMP_DEBUG |
| dload_syms_error(dlthis->mysym, |
| "===> ext sym [%s] at %x", |
| sname, val); |
| #endif |
| |
| goto loop_cont; |
| } |
| /* symbol defined by this module */ |
| if (sp->secnn > 0) { |
| /* symbol references a section */ |
| if ((unsigned)sp->secnn <= |
| dlthis->allocated_secn_count) { |
| /* section was allocated */ |
| struct doff_scnhdr_t *srefp = |
| &dlthis->sect_hdrs[sp->secnn - 1]; |
| |
| if (input_sym->dn_sclass == |
| DN_STATLAB || |
| input_sym->dn_sclass == DN_EXTLAB) { |
| /* load */ |
| delta = srefp->ds_vaddr; |
| } else { |
| /* run */ |
| delta = srefp->ds_paddr; |
| } |
| val += delta; |
| } |
| goto loop_itr; |
| } |
| /* This symbol is an absolute symbol */ |
| if (sp->secnn == DN_ABS && ((sp->sclass == DN_EXT) || |
| (sp->sclass == |
| DN_EXTLAB))) { |
| symp = |
| dlthis->mysym->find_matching_symbol(dlthis-> |
| mysym, |
| sname); |
| if (!symp) |
| goto loop_itr; |
| /* This absolute symbol is already defined. */ |
| if (symp->value == input_sym->dn_value) { |
| /* If symbol values are equal, continue |
| * but don't add to the global symbol |
| * table */ |
| sp->value = val; |
| sp->delta = delta; |
| sp += 1; |
| input_sym += 1; |
| continue; |
| } else { |
| /* If symbol values are not equal, |
| * return with redefinition error */ |
| DL_ERROR("Absolute symbol %s is " |
| "defined multiple times with " |
| "different values", sname); |
| return; |
| } |
| } |
| loop_itr: |
| /* if this is a global symbol, post it to the |
| * global table */ |
| if (input_sym->dn_sclass == DN_EXT || |
| input_sym->dn_sclass == DN_EXTLAB) { |
| /* Keep this global symbol for subsequent |
| * modules. Don't complain on error, to allow |
| * symbol API to suppress global symbols */ |
| if (!sname) |
| goto loop_cont; |
| |
| newsym = dlthis->mysym->add_to_symbol_table |
| (dlthis->mysym, sname, |
| (unsigned)dlthis->myhandle); |
| if (newsym) |
| newsym->value = val; |
| |
| } /* global */ |
| loop_cont: |
| sp->value = val; |
| sp->delta = delta; |
| sp += 1; |
| input_sym += 1; |
| } while ((syms_in_buf -= 1) > 0); /* process sym in buf */ |
| } while (symbols_left > 0); /* read all symbols */ |
| if (~checks) |
| dload_error(dlthis, "Checksum of symbols failed"); |
| |
| } /* dload_symbols */ |
| |
| /***************************************************************************** |
| * Procedure symbol_table_free |
| * |
| * Parameters: |
| * none |
| * |
| * Effect: |
| * Frees any state used by the symbol table. |
| * |
| * WARNING: |
| * This routine is not allowed to declare errors! |
| **************************************************************************** */ |
| static void symbol_table_free(struct dload_state *dlthis) |
| { |
| if (dlthis->local_symtab) { |
| if (dlthis->dload_errcount) { /* blow off our symbols */ |
| dlthis->mysym->purge_symbol_table(dlthis->mysym, |
| (unsigned) |
| dlthis->myhandle); |
| } |
| dlthis->mysym->dload_deallocate(dlthis->mysym, |
| dlthis->local_symtab); |
| } |
| } /* symbol_table_free */ |
| |
| /* .cinit Processing |
| * |
| * The dynamic loader does .cinit interpretation. cload_cinit() |
| * acts as a special write-to-target function, in that it takes relocated |
| * data from the normal data flow, and interprets it as .cinit actions. |
| * Because the normal data flow does not necessarily process the whole |
| * .cinit section in one buffer, cload_cinit() must be prepared to |
| * interpret the data piecemeal. A state machine is used for this |
| * purpose. |
| */ |
| |
| /* The following are only for use by reloc.c and things it calls */ |
| static const struct ldr_section_info cinit_info_init = { cinitname, 0, 0, |
| (ldr_addr)-1, 0, DLOAD_BSS, 0 |
| }; |
| |
| /************************************************************************* |
| * Procedure cload_cinit |
| * |
| * Parameters: |
| * ipacket Pointer to data packet to be loaded |
| * |
| * Effect: |
| * Interprets the data in the buffer as .cinit data, and performs the |
| * appropriate initializations. |
| *********************************************************************** */ |
| static void cload_cinit(struct dload_state *dlthis, |
| struct image_packet_t *ipacket) |
| { |
| #if TDATA_TO_HOST(CINIT_COUNT)*BITS_PER_AU > 16 |
| s32 init_count, left; |
| #else |
| s16 init_count, left; |
| #endif |
| unsigned char *pktp = ipacket->img_data; |
| unsigned char *pktend = pktp + BYTE_TO_HOST_ROUND(ipacket->packet_size); |
| int temp; |
| ldr_addr atmp; |
| struct ldr_section_info cinit_info; |
| |
| /* PROCESS ALL THE INITIALIZATION RECORDS THE BUFFER. */ |
| while (true) { |
| left = pktend - pktp; |
| switch (dlthis->cinit_state) { |
| case CI_COUNT: /* count field */ |
| if (left < TDATA_TO_HOST(CINIT_COUNT)) |
| goto loopexit; |
| temp = dload_unpack(dlthis, (tgt_au_t *) pktp, |
| CINIT_COUNT * TDATA_AU_BITS, 0, |
| ROP_SGN); |
| pktp += TDATA_TO_HOST(CINIT_COUNT); |
| /* negative signifies BSS table, zero means done */ |
| if (temp <= 0) { |
| dlthis->cinit_state = CI_DONE; |
| break; |
| } |
| dlthis->cinit_count = temp; |
| dlthis->cinit_state = CI_ADDRESS; |
| break; |
| #if CINIT_ALIGN < CINIT_ADDRESS |
| case CI_PARTADDRESS: |
| pktp -= TDATA_TO_HOST(CINIT_ALIGN); |
| /* back up pointer into space courtesy of caller */ |
| *(uint16_t *) pktp = dlthis->cinit_addr; |
| /* stuff in saved bits !! FALL THRU !! */ |
| #endif |
| case CI_ADDRESS: /* Address field for a copy packet */ |
| if (left < TDATA_TO_HOST(CINIT_ADDRESS)) { |
| #if CINIT_ALIGN < CINIT_ADDRESS |
| if (left == TDATA_TO_HOST(CINIT_ALIGN)) { |
| /* address broken into halves */ |
| dlthis->cinit_addr = *(uint16_t *) pktp; |
| /* remember 1st half */ |
| dlthis->cinit_state = CI_PARTADDRESS; |
| left = 0; |
| } |
| #endif |
| goto loopexit; |
| } |
| atmp = dload_unpack(dlthis, (tgt_au_t *) pktp, |
| CINIT_ADDRESS * TDATA_AU_BITS, 0, |
| ROP_UNS); |
| pktp += TDATA_TO_HOST(CINIT_ADDRESS); |
| #if CINIT_PAGE_BITS > 0 |
| dlthis->cinit_page = atmp & |
| ((1 << CINIT_PAGE_BITS) - 1); |
| atmp >>= CINIT_PAGE_BITS; |
| #else |
| dlthis->cinit_page = CINIT_DEFAULT_PAGE; |
| #endif |
| dlthis->cinit_addr = atmp; |
| dlthis->cinit_state = CI_COPY; |
| break; |
| case CI_COPY: /* copy bits to the target */ |
| init_count = HOST_TO_TDATA(left); |
| if (init_count > dlthis->cinit_count) |
| init_count = dlthis->cinit_count; |
| if (init_count == 0) |
| goto loopexit; /* get more bits */ |
| cinit_info = cinit_info_init; |
| cinit_info.page = dlthis->cinit_page; |
| if (!dlthis->myio->writemem(dlthis->myio, pktp, |
| TDATA_TO_TADDR |
| (dlthis->cinit_addr), |
| &cinit_info, |
| TDATA_TO_HOST(init_count))) { |
| dload_error(dlthis, initfail, "write", |
| dlthis->cinit_addr); |
| } |
| dlthis->cinit_count -= init_count; |
| if (dlthis->cinit_count <= 0) { |
| dlthis->cinit_state = CI_COUNT; |
| init_count = (init_count + CINIT_ALIGN - 1) & |
| -CINIT_ALIGN; |
| /* align to next init */ |
| } |
| pktp += TDATA_TO_HOST(init_count); |
| dlthis->cinit_addr += init_count; |
| break; |
| case CI_DONE: /* no more .cinit to do */ |
| return; |
| } /* switch (cinit_state) */ |
| } /* while */ |
| |
| loopexit: |
| if (left > 0) { |
| dload_error(dlthis, "%d bytes left over in cinit packet", left); |
| dlthis->cinit_state = CI_DONE; /* left over bytes are bad */ |
| } |
| } /* cload_cinit */ |
| |
| /* Functions to interface to reloc.c |
| * |
| * reloc.c is the relocation module borrowed from the linker, with |
| * minimal (we hope) changes for our purposes. cload_sect_data() invokes |
| * this module on a section to relocate and load the image data for that |
| * section. The actual read and write actions are supplied by the global |
| * routines below. |
| */ |
| |
| /************************************************************************ |
| * Procedure relocate_packet |
| * |
| * Parameters: |
| * ipacket Pointer to an image packet to relocate |
| * |
| * Effect: |
| * Performs the required relocations on the packet. Returns a checksum |
| * of the relocation operations. |
| *********************************************************************** */ |
| #define MY_RELOC_BUF_SIZ 8 |
| /* careful! exists at the same time as the image buffer */ |
| static int relocate_packet(struct dload_state *dlthis, |
| struct image_packet_t *ipacket, |
| u32 *checks, bool *tramps_generated) |
| { |
| u32 rnum; |
| *tramps_generated = false; |
| |
| rnum = ipacket->num_relocs; |
| do { /* all relocs */ |
| unsigned rinbuf; |
| int siz; |
| struct reloc_record_t *rp, rrec[MY_RELOC_BUF_SIZ]; |
| rp = rrec; |
| rinbuf = rnum > MY_RELOC_BUF_SIZ ? MY_RELOC_BUF_SIZ : rnum; |
| siz = rinbuf * sizeof(struct reloc_record_t); |
| if (dlthis->strm->read_buffer(dlthis->strm, rp, siz) != siz) { |
| DL_ERROR(readstrm, "relocation"); |
| return 0; |
| } |
| /* reorder the bytes if need be */ |
| if (dlthis->reorder_map) |
| dload_reorder(rp, siz, dlthis->reorder_map); |
| |
| *checks += dload_checksum(rp, siz); |
| do { |
| /* perform the relocation operation */ |
| dload_relocate(dlthis, (tgt_au_t *) ipacket->img_data, |
| rp, tramps_generated, false); |
| rp += 1; |
| rnum -= 1; |
| } while ((rinbuf -= 1) > 0); |
| } while (rnum > 0); /* all relocs */ |
| /* If trampoline(s) were generated, we need to do an update of the |
| * trampoline copy of the packet since a 2nd phase relo will be done |
| * later. */ |
| if (*tramps_generated == true) { |
| dload_tramp_pkt_udpate(dlthis, |
| (dlthis->image_secn - |
| dlthis->ldr_sections), |
| dlthis->image_offset, ipacket); |
| } |
| |
| return 1; |
| } /* dload_read_reloc */ |
| |
| #define IPH_SIZE (sizeof(struct image_packet_t) - sizeof(u32)) |
| |
| /* VERY dangerous */ |
| static const char imagepak[] = { "image packet" }; |
| |
| /************************************************************************* |
| * Procedure dload_data |
| * |
| * Parameters: |
| * none |
| * |
| * Effect: |
| * Read image data from input file, relocate it, and download it to the |
| * target. |
| *********************************************************************** */ |
| static void dload_data(struct dload_state *dlthis) |
| { |
| u16 curr_sect; |
| struct doff_scnhdr_t *sptr = dlthis->sect_hdrs; |
| struct ldr_section_info *lptr = dlthis->ldr_sections; |
| #ifdef OPT_ZERO_COPY_LOADER |
| bool zero_copy = false; |
| #endif |
| u8 *dest; |
| |
| struct { |
| struct image_packet_t ipacket; |
| u8 bufr[BYTE_TO_HOST(IMAGE_PACKET_SIZE)]; |
| } ibuf; |
| |
| /* Indicates whether CINIT processing has occurred */ |
| bool cinit_processed = false; |
| |
| /* Loop through the sections and load them one at a time. |
| */ |
| for (curr_sect = 0; curr_sect < dlthis->dfile_hdr.df_no_scns; |
| curr_sect += 1) { |
| if (ds_needs_download(sptr)) { |
| s32 nip; |
| ldr_addr image_offset = 0; |
| /* set relocation info for this section */ |
| if (curr_sect < dlthis->allocated_secn_count) |
| dlthis->delta_runaddr = sptr->ds_paddr; |
| else { |
| lptr = (struct ldr_section_info *)sptr; |
| dlthis->delta_runaddr = 0; |
| } |
| dlthis->image_secn = lptr; |
| #if BITS_PER_AU > BITS_PER_BYTE |
| lptr->name = unpack_name(dlthis, sptr->ds_offset); |
| #endif |
| nip = sptr->ds_nipacks; |
| while ((nip -= 1) >= 0) { /* process packets */ |
| |
| s32 ipsize; |
| u32 checks; |
| bool tramp_generated = false; |
| |
| /* get the fixed header bits */ |
| if (dlthis->strm->read_buffer(dlthis->strm, |
| &ibuf.ipacket, |
| IPH_SIZE) != |
| IPH_SIZE) { |
| DL_ERROR(readstrm, imagepak); |
| return; |
| } |
| /* reorder the header if need be */ |
| if (dlthis->reorder_map) { |
| dload_reorder(&ibuf.ipacket, IPH_SIZE, |
| dlthis->reorder_map); |
| } |
| /* now read the rest of the packet */ |
| ipsize = |
| BYTE_TO_HOST(DOFF_ALIGN |
| (ibuf.ipacket.packet_size)); |
| if (ipsize > BYTE_TO_HOST(IMAGE_PACKET_SIZE)) { |
| DL_ERROR("Bad image packet size %d", |
| ipsize); |
| return; |
| } |
| dest = ibuf.bufr; |
| #ifdef OPT_ZERO_COPY_LOADER |
| zero_copy = false; |
| if (!dload_check_type(sptr, DLOAD_CINIT) { |
| dlthis->myio->writemem(dlthis->myio, |
| &dest, |
| lptr->load_addr + |
| image_offset, |
| lptr, 0); |
| zero_copy = (dest != ibuf.bufr); |
| } |
| #endif |
| /* End of determination */ |
| |
| if (dlthis->strm->read_buffer(dlthis->strm, |
| ibuf.bufr, |
| ipsize) != |
| ipsize) { |
| DL_ERROR(readstrm, imagepak); |
| return; |
| } |
| ibuf.ipacket.img_data = dest; |
| |
| /* reorder the bytes if need be */ |
| #if !defined(_BIG_ENDIAN) || (TARGET_AU_BITS > 16) |
| if (dlthis->reorder_map) { |
| dload_reorder(dest, ipsize, |
| dlthis->reorder_map); |
| } |
| checks = dload_checksum(dest, ipsize); |
| #else |
| if (dlthis->dfile_hdr.df_byte_reshuffle != |
| TARGET_ORDER(REORDER_MAP |
| (BYTE_RESHUFFLE_VALUE))) { |
| /* put image bytes in big-endian order, |
| * not PC order */ |
| dload_reorder(dest, ipsize, |
| TARGET_ORDER |
| (dlthis->dfile_hdr. |
| df_byte_reshuffle)); |
| } |
| #if TARGET_AU_BITS > 8 |
| checks = dload_reverse_checksum16(dest, ipsize); |
| #else |
| checks = dload_reverse_checksum(dest, ipsize); |
| #endif |
| #endif |
| |
| checks += dload_checksum(&ibuf.ipacket, |
| IPH_SIZE); |
| /* relocate the image bits as needed */ |
| if (ibuf.ipacket.num_relocs) { |
| dlthis->image_offset = image_offset; |
| if (!relocate_packet(dlthis, |
| &ibuf.ipacket, |
| &checks, |
| &tramp_generated)) |
| return; /* serious error */ |
| } |
| if (~checks) |
| DL_ERROR(err_checksum, imagepak); |
| /* Only write the result to the target if no |
| * trampoline was generated. Otherwise it |
| *will be done during trampoline finalize. */ |
| |
| if (tramp_generated == false) { |
| |
| /* stuff the result into target |
| * memory */ |
| if (dload_check_type(sptr, |
| DLOAD_CINIT)) { |
| cload_cinit(dlthis, |
| &ibuf.ipacket); |
| cinit_processed = true; |
| } else { |
| #ifdef OPT_ZERO_COPY_LOADER |
| if (!zero_copy) { |
| #endif |
| /* FIXME */ |
| if (!dlthis->myio-> |
| writemem(dlthis-> |
| myio, |
| ibuf.bufr, |
| lptr-> |
| load_addr + |
| image_offset, |
| lptr, |
| BYTE_TO_HOST |
| (ibuf. |
| ipacket. |
| packet_size))) { |
| DL_ERROR |
| ("Write to " |
| FMT_UI32 |
| " failed", |
| lptr-> |
| load_addr + |
| image_offset); |
| } |
| #ifdef OPT_ZERO_COPY_LOADER |
| } |
| #endif |
| } |
| } |
| image_offset += |
| BYTE_TO_TADDR(ibuf.ipacket.packet_size); |
| } /* process packets */ |
| /* if this is a BSS section, we may want to fill it */ |
| if (!dload_check_type(sptr, DLOAD_BSS)) |
| goto loop_cont; |
| |
| if (!(dlthis->myoptions & DLOAD_INITBSS)) |
| goto loop_cont; |
| |
| if (cinit_processed) { |
| /* Don't clear BSS after load-time |
| * initialization */ |
| DL_ERROR |
| ("Zero-initialization at " FMT_UI32 |
| " after " "load-time initialization!", |
| lptr->load_addr); |
| goto loop_cont; |
| } |
| /* fill the .bss area */ |
| dlthis->myio->fillmem(dlthis->myio, |
| TADDR_TO_HOST(lptr->load_addr), |
| lptr, TADDR_TO_HOST(lptr->size), |
| DLOAD_FILL_BSS); |
| goto loop_cont; |
| } |
| /* if DS_DOWNLOAD_MASK */ |
| /* If not loading, but BSS, zero initialize */ |
| if (!dload_check_type(sptr, DLOAD_BSS)) |
| goto loop_cont; |
| |
| if (!(dlthis->myoptions & DLOAD_INITBSS)) |
| goto loop_cont; |
| |
| if (curr_sect >= dlthis->allocated_secn_count) |
| lptr = (struct ldr_section_info *)sptr; |
| |
| if (cinit_processed) { |
| /*Don't clear BSS after load-time initialization */ |
| DL_ERROR("Zero-initialization at " FMT_UI32 |
| " attempted after " |
| "load-time initialization!", lptr->load_addr); |
| goto loop_cont; |
| } |
| /* fill the .bss area */ |
| dlthis->myio->fillmem(dlthis->myio, |
| TADDR_TO_HOST(lptr->load_addr), lptr, |
| TADDR_TO_HOST(lptr->size), |
| DLOAD_FILL_BSS); |
| loop_cont: |
| sptr += 1; |
| lptr += 1; |
| } /* load sections */ |
| |
| /* Finalize any trampolines that were created during the load */ |
| if (dload_tramp_finalize(dlthis) == 0) { |
| DL_ERROR("Finalization of auto-trampolines (size = " FMT_UI32 |
| ") failed", dlthis->tramp.tramp_sect_next_addr); |
| } |
| } /* dload_data */ |
| |
| /************************************************************************* |
| * Procedure dload_reorder |
| * |
| * Parameters: |
| * data 32-bit aligned pointer to data to be byte-swapped |
| * dsiz size of the data to be reordered in sizeof() units. |
| * map 32-bit map defining how to reorder the data. Value |
| * must be REORDER_MAP() of some permutation |
| * of 0x00 01 02 03 |
| * |
| * Effect: |
| * Re-arranges the bytes in each word according to the map specified. |
| * |
| *********************************************************************** */ |
| /* mask for byte shift count */ |
| #define SHIFT_COUNT_MASK (3 << LOG_BITS_PER_BYTE) |
| |
| void dload_reorder(void *data, int dsiz, unsigned int map) |
| { |
| register u32 tmp, tmap, datv; |
| u32 *dp = (u32 *) data; |
| |
| map <<= LOG_BITS_PER_BYTE; /* align map with SHIFT_COUNT_MASK */ |
| do { |
| tmp = 0; |
| datv = *dp; |
| tmap = map; |
| do { |
| tmp |= (datv & BYTE_MASK) << (tmap & SHIFT_COUNT_MASK); |
| tmap >>= BITS_PER_BYTE; |
| } while (datv >>= BITS_PER_BYTE); |
| *dp++ = tmp; |
| } while ((dsiz -= sizeof(u32)) > 0); |
| } /* dload_reorder */ |
| |
| /************************************************************************* |
| * Procedure dload_checksum |
| * |
| * Parameters: |
| * data 32-bit aligned pointer to data to be checksummed |
| * siz size of the data to be checksummed in sizeof() units. |
| * |
| * Effect: |
| * Returns a checksum of the specified block |
| * |
| *********************************************************************** */ |
| u32 dload_checksum(void *data, unsigned siz) |
| { |
| u32 sum; |
| u32 *dp; |
| int left; |
| |
| sum = 0; |
| dp = (u32 *) data; |
| for (left = siz; left > 0; left -= sizeof(u32)) |
| sum += *dp++; |
| return sum; |
| } /* dload_checksum */ |
| |
| #if HOST_ENDIANNESS |
| /************************************************************************* |
| * Procedure dload_reverse_checksum |
| * |
| * Parameters: |
| * data 32-bit aligned pointer to data to be checksummed |
| * siz size of the data to be checksummed in sizeof() units. |
| * |
| * Effect: |
| * Returns a checksum of the specified block, which is assumed to be bytes |
| * in big-endian order. |
| * |
| * Notes: |
| * In a big-endian host, things like the string table are stored as bytes |
| * in host order. But dllcreate always checksums in little-endian order. |
| * It is most efficient to just handle the difference a word at a time. |
| * |
| ********************************************************************** */ |
| u32 dload_reverse_checksum(void *data, unsigned siz) |
| { |
| u32 sum, temp; |
| u32 *dp; |
| int left; |
| |
| sum = 0; |
| dp = (u32 *) data; |
| |
| for (left = siz; left > 0; left -= sizeof(u32)) { |
| temp = *dp++; |
| sum += temp << BITS_PER_BYTE * 3; |
| sum += temp >> BITS_PER_BYTE * 3; |
| sum += (temp >> BITS_PER_BYTE) & (BYTE_MASK << BITS_PER_BYTE); |
| sum += (temp & (BYTE_MASK << BITS_PER_BYTE)) << BITS_PER_BYTE; |
| } |
| |
| return sum; |
| } /* dload_reverse_checksum */ |
| |
| #if (TARGET_AU_BITS > 8) && (TARGET_AU_BITS < 32) |
| u32 dload_reverse_checksum16(void *data, unsigned siz) |
| { |
| uint_fast32_t sum, temp; |
| u32 *dp; |
| int left; |
| |
| sum = 0; |
| dp = (u32 *) data; |
| |
| for (left = siz; left > 0; left -= sizeof(u32)) { |
| temp = *dp++; |
| sum += temp << BITS_PER_BYTE * 2; |
| sum += temp >> BITS_PER_BYTE * 2; |
| } |
| |
| return sum; |
| } /* dload_reverse_checksum16 */ |
| #endif |
| #endif |
| |
| /************************************************************************* |
| * Procedure swap_words |
| * |
| * Parameters: |
| * data 32-bit aligned pointer to data to be swapped |
| * siz size of the data to be swapped. |
| * bitmap Bit map of how to swap each 32-bit word; 1 => 2 shorts, |
| * 0 => 1 long |
| * |
| * Effect: |
| * Swaps the specified data according to the specified map |
| * |
| *********************************************************************** */ |
| static void swap_words(void *data, unsigned siz, unsigned bitmap) |
| { |
| register int i; |
| #if TARGET_AU_BITS < 16 |
| register u16 *sp; |
| #endif |
| register u32 *lp; |
| |
| siz /= sizeof(u16); |
| |
| #if TARGET_AU_BITS < 16 |
| /* pass 1: do all the bytes */ |
| i = siz; |
| sp = (u16 *) data; |
| do { |
| register u16 tmp; |
| tmp = *sp; |
| *sp++ = SWAP16BY8(tmp); |
| } while ((i -= 1) > 0); |
| #endif |
| |
| #if TARGET_AU_BITS < 32 |
| /* pass 2: fixup the 32-bit words */ |
| i = siz >> 1; |
| lp = (u32 *) data; |
| do { |
| if ((bitmap & 1) == 0) { |
| register u32 tmp; |
| tmp = *lp; |
| *lp = SWAP32BY16(tmp); |
| } |
| lp += 1; |
| bitmap >>= 1; |
| } while ((i -= 1) > 0); |
| #endif |
| } /* swap_words */ |
| |
| /************************************************************************* |
| * Procedure copy_tgt_strings |
| * |
| * Parameters: |
| * dstp Destination address. Assumed to be 32-bit aligned |
| * srcp Source address. Assumed to be 32-bit aligned |
| * charcount Number of characters to copy. |
| * |
| * Effect: |
| * Copies strings from the source (which is in usual .dof file order on |
| * the loading processor) to the destination buffer (which should be in proper |
| * target addressable unit order). Makes sure the last string in the |
| * buffer is NULL terminated (for safety). |
| * Returns the first unused destination address. |
| *********************************************************************** */ |
| static char *copy_tgt_strings(void *dstp, void *srcp, unsigned charcount) |
| { |
| register tgt_au_t *src = (tgt_au_t *) srcp; |
| register tgt_au_t *dst = (tgt_au_t *) dstp; |
| register int cnt = charcount; |
| do { |
| #if TARGET_AU_BITS <= BITS_PER_AU |
| /* byte-swapping issues may exist for strings on target */ |
| *dst++ = *src++; |
| #else |
| *dst++ = *src++; |
| #endif |
| } while ((cnt -= (sizeof(tgt_au_t) * BITS_PER_AU / BITS_PER_BYTE)) > 0); |
| /*apply force to make sure that the string table has null terminator */ |
| #if (BITS_PER_AU == BITS_PER_BYTE) && (TARGET_AU_BITS == BITS_PER_BYTE) |
| dst[-1] = 0; |
| #else |
| /* little endian */ |
| dst[-1] &= (1 << (BITS_PER_AU - BITS_PER_BYTE)) - 1; |
| #endif |
| return (char *)dst; |
| } /* copy_tgt_strings */ |
| |
| /************************************************************************* |
| * Procedure init_module_handle |
| * |
| * Parameters: |
| * none |
| * |
| * Effect: |
| * Initializes the module handle we use to enable unloading, and installs |
| * the debug information required by the target. |
| * |
| * Notes: |
| * The handle returned from dynamic_load_module needs to encapsulate all the |
| * allocations done for the module, and enable them plus the modules symbols to |
| * be deallocated. |
| * |
| *********************************************************************** */ |
| #ifndef _BIG_ENDIAN |
| static const struct ldr_section_info dllview_info_init = { ".dllview", 0, 0, |
| (ldr_addr)-1, DBG_LIST_PAGE, DLOAD_DATA, 0 |
| }; |
| #else |
| static const struct ldr_section_info dllview_info_init = { ".dllview", 0, 0, |
| (ldr_addr)-1, DLOAD_DATA, DBG_LIST_PAGE, 0 |
| }; |
| #endif |
| static void init_module_handle(struct dload_state *dlthis) |
| { |
| struct my_handle *hndl; |
| u16 curr_sect; |
| struct ldr_section_info *asecs; |
| struct dll_module *dbmod; |
| struct dll_sect *dbsec; |
| struct dbg_mirror_root *mlist; |
| register char *cp; |
| struct modules_header mhdr; |
| struct ldr_section_info dllview_info; |
| struct dynload_symbol *debug_mirror_sym; |
| hndl = dlthis->myhandle; |
| if (!hndl) |
| return; /* must be errors detected, so forget it */ |
| |
| /* Store the section count */ |
| hndl->secn_count = dlthis->allocated_secn_count; |
| |
| /* If a trampoline section was created, add it in */ |
| if (dlthis->tramp.tramp_sect_next_addr != 0) |
| hndl->secn_count += 1; |
| |
| hndl->secn_count = hndl->secn_count << 1; |
| |
| hndl->secn_count = dlthis->allocated_secn_count << 1; |
| #ifndef TARGET_ENDIANNESS |
| if (dlthis->big_e_target) |
| hndl->secn_count += 1; /* flag for big-endian */ |
| #endif |
| if (dlthis->dload_errcount) |
| return; /* abandon if errors detected */ |
| /* Locate the symbol that names the header for the CCS debug list |
| of modules. If not found, we just don't generate the debug record. |
| If found, we create our modules list. We make sure to create the |
| loader_dllview_root even if there is no relocation info to record, |
| just to try to put both symbols in the same symbol table and |
| module. */ |
| debug_mirror_sym = dlthis->mysym->find_matching_symbol(dlthis->mysym, |
| loader_dllview_root); |
| if (!debug_mirror_sym) { |
| struct dynload_symbol *dlmodsym; |
| struct dbg_mirror_root *mlst; |
| |
| /* our root symbol is not yet present; |
| check if we have DLModules defined */ |
| dlmodsym = dlthis->mysym->find_matching_symbol(dlthis->mysym, |
| LINKER_MODULES_HEADER); |
| if (!dlmodsym) |
| return; /* no DLModules list so no debug info */ |
| /* if we have DLModules defined, construct our header */ |
| mlst = (struct dbg_mirror_root *) |
| dlthis->mysym->dload_allocate(dlthis->mysym, |
| sizeof(struct |
| dbg_mirror_root)); |
| if (!mlst) { |
| DL_ERROR(err_alloc, sizeof(struct dbg_mirror_root)); |
| return; |
| } |
| mlst->hnext = NULL; |
| mlst->changes = 0; |
| mlst->refcount = 0; |
| mlst->dbthis = TDATA_TO_TADDR(dlmodsym->value); |
| /* add our root symbol */ |
| debug_mirror_sym = dlthis->mysym->add_to_symbol_table |
| (dlthis->mysym, loader_dllview_root, |
| (unsigned)dlthis->myhandle); |
| if (!debug_mirror_sym) { |
| /* failed, recover memory */ |
| dlthis->mysym->dload_deallocate(dlthis->mysym, mlst); |
| return; |
| } |
| debug_mirror_sym->value = (u32) mlst; |
| } |
| /* First create the DLLview record and stuff it into the buffer. |
| Then write it to the DSP. Record pertinent locations in our hndl, |
| and add it to the per-processor list of handles with debug info. */ |
| #ifndef DEBUG_HEADER_IN_LOADER |
| mlist = (struct dbg_mirror_root *)debug_mirror_sym->value; |
| if (!mlist) |
| return; |
| #else |
| mlist = (struct dbg_mirror_root *)&debug_list_header; |
| #endif |
| hndl->dm.hroot = mlist; /* set pointer to root into our handle */ |
| if (!dlthis->allocated_secn_count) |
| return; /* no load addresses to be recorded */ |
| /* reuse temporary symbol storage */ |
| dbmod = (struct dll_module *)dlthis->local_symtab; |
| /* Create the DLLview record in the memory we retain for our handle */ |
| dbmod->num_sects = dlthis->allocated_secn_count; |
| dbmod->timestamp = dlthis->verify.dv_timdat; |
| dbmod->version = INIT_VERSION; |
| dbmod->verification = VERIFICATION; |
| asecs = dlthis->ldr_sections; |
| dbsec = dbmod->sects; |
| for (curr_sect = dlthis->allocated_secn_count; |
| curr_sect > 0; curr_sect -= 1) { |
| dbsec->sect_load_adr = asecs->load_addr; |
| dbsec->sect_run_adr = asecs->run_addr; |
| dbsec += 1; |
| asecs += 1; |
| } |
| |
| /* If a trampoline section was created go ahead and add its info */ |
| if (dlthis->tramp.tramp_sect_next_addr != 0) { |
| dbmod->num_sects++; |
| dbsec->sect_load_adr = asecs->load_addr; |
| dbsec->sect_run_adr = asecs->run_addr; |
| dbsec++; |
| asecs++; |
| } |
| |
| /* now cram in the names */ |
| cp = copy_tgt_strings(dbsec, dlthis->str_head, |
| dlthis->debug_string_size); |
| |
| /* If a trampoline section was created, add its name so DLLView |
| * can show the user the section info. */ |
| if (dlthis->tramp.tramp_sect_next_addr != 0) { |
| cp = copy_tgt_strings(cp, |
| dlthis->tramp.final_string_table, |
| strlen(dlthis->tramp.final_string_table) + |
| 1); |
| } |
| |
| /* round off the size of the debug record, and remember same */ |
| hndl->dm.dbsiz = HOST_TO_TDATA_ROUND(cp - (char *)dbmod); |
| *cp = 0; /* strictly to make our test harness happy */ |
| dllview_info = dllview_info_init; |
| dllview_info.size = TDATA_TO_TADDR(hndl->dm.dbsiz); |
| /* Initialize memory context to default heap */ |
| dllview_info.context = 0; |
| hndl->dm.context = 0; |
| /* fill in next pointer and size */ |
| if (mlist->hnext) { |
| dbmod->next_module = TADDR_TO_TDATA(mlist->hnext->dm.dbthis); |
| dbmod->next_module_size = mlist->hnext->dm.dbsiz; |
| } else { |
| dbmod->next_module_size = 0; |
| dbmod->next_module = 0; |
| } |
| /* allocate memory for on-DSP DLLview debug record */ |
| if (!dlthis->myalloc) |
| return; |
| if (!dlthis->myalloc->dload_allocate(dlthis->myalloc, &dllview_info, |
| HOST_TO_TADDR(sizeof(u32)))) { |
| return; |
| } |
| /* Store load address of .dllview section */ |
| hndl->dm.dbthis = dllview_info.load_addr; |
| /* Store memory context (segid) in which .dllview section |
| * was allocated */ |
| hndl->dm.context = dllview_info.context; |
| mlist->refcount += 1; |
| /* swap bytes in the entire debug record, but not the string table */ |
| if (TARGET_ENDIANNESS_DIFFERS(TARGET_BIG_ENDIAN)) { |
| swap_words(dbmod, (char *)dbsec - (char *)dbmod, |
| DLL_MODULE_BITMAP); |
| } |
| /* Update the DLLview list on the DSP write new record */ |
| if (!dlthis->myio->writemem(dlthis->myio, dbmod, |
| dllview_info.load_addr, &dllview_info, |
| TADDR_TO_HOST(dllview_info.size))) { |
| return; |
| } |
| /* write new header */ |
| mhdr.first_module_size = hndl->dm.dbsiz; |
| mhdr.first_module = TADDR_TO_TDATA(dllview_info.load_addr); |
| /* swap bytes in the module header, if needed */ |
| if (TARGET_ENDIANNESS_DIFFERS(TARGET_BIG_ENDIAN)) { |
| swap_words(&mhdr, sizeof(struct modules_header) - sizeof(u16), |
| MODULES_HEADER_BITMAP); |
| } |
| dllview_info = dllview_info_init; |
| if (!dlthis->myio->writemem(dlthis->myio, &mhdr, mlist->dbthis, |
| &dllview_info, |
| sizeof(struct modules_header) - |
| sizeof(u16))) { |
| return; |
| } |
| /* Add the module handle to this processor's list |
| of handles with debug info */ |
| hndl->dm.hnext = mlist->hnext; |
| if (hndl->dm.hnext) |
| hndl->dm.hnext->dm.hprev = hndl; |
| hndl->dm.hprev = (struct my_handle *)mlist; |
| mlist->hnext = hndl; /* insert after root */ |
| } /* init_module_handle */ |
| |
| /************************************************************************* |
| * Procedure dynamic_unload_module |
| * |
| * Parameters: |
| * mhandle A module handle from dynamic_load_module |
| * syms Host-side symbol table and malloc/free functions |
| * alloc Target-side memory allocation |
| * |
| * Effect: |
| * The module specified by mhandle is unloaded. Unloading causes all |
| * target memory to be deallocated, all symbols defined by the module to |
| * be purged, and any host-side storage used by the dynamic loader for |
| * this module to be released. |
| * |
| * Returns: |
| * Zero for success. On error, the number of errors detected is returned. |
| * Individual errors are reported using syms->error_report(). |
| *********************************************************************** */ |
| int dynamic_unload_module(void *mhandle, |
| struct dynamic_loader_sym *syms, |
| struct dynamic_loader_allocate *alloc, |
| struct dynamic_loader_initialize *init) |
| { |
| s16 curr_sect; |
| struct ldr_section_info *asecs; |
| struct my_handle *hndl; |
| struct dbg_mirror_root *root; |
| unsigned errcount = 0; |
| struct ldr_section_info dllview_info = dllview_info_init; |
| struct modules_header mhdr; |
| |
| hndl = (struct my_handle *)mhandle; |
| if (!hndl) |
| return 0; /* if handle is null, nothing to do */ |
| /* Clear out the module symbols |
| * Note that if this is the module that defined MODULES_HEADER |
| (the head of the target debug list) |
| * then this operation will blow away that symbol. |
| It will therefore be impossible for subsequent |
| * operations to add entries to this un-referenceable list. */ |
| if (!syms) |
| return 1; |
| syms->purge_symbol_table(syms, (unsigned)hndl); |
| /* Deallocate target memory for sections |
| * NOTE: The trampoline section, if created, gets deleted here, too */ |
| |
| asecs = hndl->secns; |
| if (alloc) |
| for (curr_sect = (hndl->secn_count >> 1); curr_sect > 0; |
| curr_sect -= 1) { |
| asecs->name = NULL; |
| alloc->dload_deallocate(alloc, asecs++); |
| } |
| root = hndl->dm.hroot; |
| if (!root) { |
| /* there is a debug list containing this module */ |
| goto func_end; |
| } |
| if (!hndl->dm.dbthis) { /* target-side dllview record exists */ |
| goto loop_end; |
| } |
| /* Retrieve memory context in which .dllview was allocated */ |
| dllview_info.context = hndl->dm.context; |
| if (hndl->dm.hprev == hndl) |
| goto exitunltgt; |
| |
| /* target-side dllview record is in list */ |
| /* dequeue this record from our GPP-side mirror list */ |
| hndl->dm.hprev->dm.hnext = hndl->dm.hnext; |
| if (hndl->dm.hnext) |
| hndl->dm.hnext->dm.hprev = hndl->dm.hprev; |
| /* Update next_module of previous entry in target list |
| * We are using mhdr here as a surrogate for either a |
| struct modules_header or a dll_module */ |
| if (hndl->dm.hnext) { |
| mhdr.first_module = TADDR_TO_TDATA(hndl->dm.hnext->dm.dbthis); |
| mhdr.first_module_size = hndl->dm.hnext->dm.dbsiz; |
| } else { |
| mhdr.first_module = 0; |
| mhdr.first_module_size = 0; |
| } |
| if (!init) |
| goto exitunltgt; |
| |
| if (!init->connect(init)) { |
| dload_syms_error(syms, iconnect); |
| errcount += 1; |
| goto exitunltgt; |
| } |
| /* swap bytes in the module header, if needed */ |
| if (TARGET_ENDIANNESS_DIFFERS(hndl->secn_count & 0x1)) { |
| swap_words(&mhdr, sizeof(struct modules_header) - sizeof(u16), |
| MODULES_HEADER_BITMAP); |
| } |
| if (!init->writemem(init, &mhdr, hndl->dm.hprev->dm.dbthis, |
| &dllview_info, sizeof(struct modules_header) - |
| sizeof(mhdr.update_flag))) { |
| dload_syms_error(syms, dlvwrite); |
| errcount += 1; |
| } |
| /* update change counter */ |
| root->changes += 1; |
| if (!init->writemem(init, &(root->changes), |
| root->dbthis + HOST_TO_TADDR |
| (sizeof(mhdr.first_module) + |
| sizeof(mhdr.first_module_size)), |
| &dllview_info, sizeof(mhdr.update_flag))) { |
| dload_syms_error(syms, dlvwrite); |
| errcount += 1; |
| } |
| init->release(init); |
| exitunltgt: |
| /* release target storage */ |
| dllview_info.size = TDATA_TO_TADDR(hndl->dm.dbsiz); |
| dllview_info.load_addr = hndl->dm.dbthis; |
| if (alloc) |
| alloc->dload_deallocate(alloc, &dllview_info); |
| root->refcount -= 1; |
| /* target-side dllview record exists */ |
| loop_end: |
| #ifndef DEBUG_HEADER_IN_LOADER |
| if (root->refcount <= 0) { |
| /* if all references gone, blow off the header */ |
| /* our root symbol may be gone due to the Purge above, |
| but if not, do not destroy the root */ |
| if (syms->find_matching_symbol |
| (syms, loader_dllview_root) == NULL) |
| syms->dload_deallocate(syms, root); |
| } |
| #endif |
| func_end: |
| /* there is a debug list containing this module */ |
| syms->dload_deallocate(syms, mhandle); /* release our storage */ |
| return errcount; |
| } /* dynamic_unload_module */ |
| |
| #if BITS_PER_AU > BITS_PER_BYTE |
| /************************************************************************* |
| * Procedure unpack_name |
| * |
| * Parameters: |
| * soffset Byte offset into the string table |
| * |
| * Effect: |
| * Returns a pointer to the string specified by the offset supplied, or |
| * NULL for error. |
| * |
| *********************************************************************** */ |
| static char *unpack_name(struct dload_state *dlthis, u32 soffset) |
| { |
| u8 tmp, *src; |
| char *dst; |
| |
| if (soffset >= dlthis->dfile_hdr.df_strtab_size) { |
| dload_error(dlthis, "Bad string table offset " FMT_UI32, |
| soffset); |
| return NULL; |
| } |
| src = (uint_least8_t *) dlthis->str_head + |
| (soffset >> (LOG_BITS_PER_AU - LOG_BITS_PER_BYTE)); |
| dst = dlthis->str_temp; |
| if (soffset & 1) |
| *dst++ = *src++; /* only 1 character in first word */ |
| do { |
| tmp = *src++; |
| *dst = (tmp >> BITS_PER_BYTE); |
| if (!(*dst++)) |
| break; |
| } while ((*dst++ = tmp & BYTE_MASK)); |
| dlthis->temp_len = dst - dlthis->str_temp; |
| /* squirrel away length including terminating null */ |
| return dlthis->str_temp; |
| } /* unpack_name */ |
| #endif |