/* * COPYRIGHT (c) 2012 Chris Johns * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ /** * @file * * @ingroup rtl * * @brief RTEMS Run-Time Linker Error */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "rtl-chain-iterator.h" #include "rtl-obj.h" #include "rtl-error.h" #include "rtl-find-file.h" #include "rtl-string.h" #include "rtl-trace.h" #if RTEMS_RTL_RAP_LOADER #include "rtl-rap.h" #define RTEMS_RTL_RAP_LOADER_COUNT 1 #else #define RTEMS_RTL_RAP_LOADER_COUNT 0 #endif #if RTEMS_RTL_ELF_LOADER #include "rtl-elf.h" #define RTEMS_RTL_ELF_LOADER_COUNT 1 #else #define RTEMS_RTL_ELF_LOADER_COUNT 0 #endif /** * The table of supported loader formats. */ static rtems_rtl_loader_table_t loaders[RTEMS_RTL_ELF_LOADER_COUNT + RTEMS_RTL_RAP_LOADER_COUNT] = { #if RTEMS_RTL_RAP_LOADER { rtems_rtl_rap_file_check, rtems_rtl_rap_file_load, rtems_rtl_rap_file_sig }, #endif #if RTEMS_RTL_ELF_LOADER { rtems_rtl_elf_file_check, rtems_rtl_elf_file_load, rtems_rtl_elf_file_sig }, #endif }; rtems_rtl_obj_t* rtems_rtl_obj_alloc (void) { rtems_rtl_obj_t* obj = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, sizeof (rtems_rtl_obj_t), true); if (obj) { /* * Initialise the chains. */ rtems_chain_initialize_empty (&obj->sections); } return obj; } static void rtems_rtl_obj_free_names (rtems_rtl_obj_t* obj) { if (rtems_rtl_obj_oname_valid (obj)) rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) obj->oname); if (rtems_rtl_obj_aname_valid (obj)) rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) obj->aname); if (rtems_rtl_obj_fname_valid (obj)) rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) obj->fname); } bool rtems_rtl_obj_free (rtems_rtl_obj_t* obj) { if (obj->users || ((obj->flags & RTEMS_RTL_OBJ_LOCKED) != 0)) { rtems_rtl_set_error (EINVAL, "cannot free obj still in use"); return false; } if (!rtems_chain_is_node_off_chain (&obj->link)) rtems_chain_extract (&obj->link); rtems_rtl_alloc_module_del (&obj->text_base, &obj->const_base, &obj->data_base, &obj->bss_base); rtems_rtl_symbol_obj_erase (obj); rtems_rtl_obj_free_names (obj); if (obj->sec_num) free (obj->sec_num); if (obj->detail) rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*)obj->detail); rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, obj); return true; } bool rtems_rtl_obj_unresolved (rtems_rtl_obj_t* obj) { return (obj->flags & RTEMS_RTL_OBJ_UNRESOLVED) != 0 ? true : false; } bool rtems_rtl_parse_name (const char* name, const char** aname, const char** oname, off_t* ooffset) { const char* laname = NULL; const char* loname = NULL; const char* colon; const char* end; /* * Parse the name to determine if the object file is part of an archive or it * is an object file. If an archive check the name for a '@' to see if the * archive contains an offset. */ end = name + strlen (name); colon = strrchr (name, ':'); if (colon == NULL || colon < strrchr(name, '/')) colon = end; loname = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, colon - name + 1, true); if (!oname) { rtems_rtl_set_error (ENOMEM, "no memory for object file name"); return false; } memcpy ((void*) loname, name, colon - name); /* * If the pointers match there is no ':' delimiter. */ if (colon != end) { const char* at; /* * The file name is an archive and the object file name is next after the * delimiter. Move the pointer to the archive name. */ laname = loname; ++colon; /* * See if there is a '@' to delimit an archive offset for the object in the * archive. */ at = strchr (colon, '@'); if (at == NULL) at = end; loname = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, at - colon + 1, true); if (!loname) { rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) laname); rtems_rtl_set_error (ENOMEM, "no memory for object file name"); return false; } memcpy ((void*) loname, colon, at - colon); if (at != end) { /* * The object name has an archive offset. If the number * does not parse 0 will be returned and the archive will be * searched. */ *ooffset = strtoul (at + 1, 0, 0); } } *oname = loname; *aname = laname; return true; } static bool rtems_rtl_obj_parse_name (rtems_rtl_obj_t* obj, const char* name) { return rtems_rtl_parse_name (name, &(obj->aname), &(obj->oname), &(obj->ooffset)); } static bool rtems_rtl_seek_read (int fd, off_t off, size_t len, uint8_t* buffer) { if (lseek (fd, off, SEEK_SET) < 0) return false; if (read (fd, buffer, len) != len) return false; return true; } /** * Scan the decimal number returning the value found. */ static uint64_t rtems_rtl_scan_decimal (const uint8_t* string, size_t len) { uint64_t value = 0; while (len && (*string != ' ')) { value *= 10; value += *string - '0'; ++string; --len; } return value; } /** * Align the size to the next alignment point. Assume the alignment is a * positive integral power of 2 if not 0 or 1. If 0 or 1 then there is no * alignment. */ static size_t rtems_rtl_sect_align (size_t offset, uint32_t alignment) { if ((alignment > 1) && ((offset & ~alignment) != 0)) offset = (offset + alignment) & ~(alignment - 1); return offset; } /** * Section size summer iterator data. */ typedef struct { uint32_t mask; /**< The selection mask to sum. */ size_t size; /**< The size of all section fragments. */ } rtems_rtl_obj_sect_summer_t; static bool rtems_rtl_obj_sect_summer (rtems_chain_node* node, void* data) { rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node; rtems_rtl_obj_sect_summer_t* summer = data; if ((sect->flags & summer->mask) == summer->mask) summer->size = rtems_rtl_sect_align (summer->size, sect->alignment) + sect->size; return true; } static size_t rtems_rtl_obj_section_size (rtems_rtl_obj_t* obj, uint32_t mask) { rtems_rtl_obj_sect_summer_t summer; summer.mask = mask; summer.size = 0; rtems_rtl_chain_iterate (&obj->sections, rtems_rtl_obj_sect_summer, &summer); return summer.size; } /** * Section alignment iterator data. The first section's alignment sets the * alignment for that type of section. */ typedef struct { uint32_t mask; /**< The selection mask to look for alignment. */ uint32_t alignment; /**< The alignment of the section type. */ } rtems_rtl_obj_sect_aligner_t; /** * The section aligner iterator. */ static bool rtems_rtl_obj_sect_aligner (rtems_chain_node* node, void* data) { rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node; rtems_rtl_obj_sect_aligner_t* aligner = data; if ((sect->flags & aligner->mask) == aligner->mask) { aligner->alignment = sect->alignment; return false; } return true; } static size_t rtems_rtl_obj_section_alignment (rtems_rtl_obj_t* obj, uint32_t mask) { rtems_rtl_obj_sect_aligner_t aligner; aligner.mask = mask; aligner.alignment = 0; rtems_rtl_chain_iterate (&obj->sections, rtems_rtl_obj_sect_aligner, &aligner); return aligner.alignment; } static bool rtems_rtl_obj_section_handler (uint32_t mask, rtems_rtl_obj_t* obj, int fd, rtems_rtl_obj_sect_handler_t handler, void* data) { rtems_chain_node* node = rtems_chain_first (&obj->sections); while (!rtems_chain_is_tail (&obj->sections, node)) { rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node; if ((sect->flags & mask) != 0) { if (!handler (obj, fd, sect, data)) return false; } node = rtems_chain_next (node); } return true; } bool rtems_rtl_match_name (rtems_rtl_obj_t* obj, const char* name) { const char* n1 = obj->oname; while ((*n1 != '\0') && (*n1 != '\n') && (*n1 != '/') && (*name != '\0') && (*name != '/') && (*n1 == *name)) { ++n1; ++name; } if (((*n1 == '\0') || (*n1 == '\n') || (*n1 == '/')) && ((*name == '\0') || (*name == '/'))) return true; return false; } bool rtems_rtl_obj_find_file (rtems_rtl_obj_t* obj, const char* name) { const char* pname; rtems_rtl_data_t* rtl; /* * Parse the name. The object descriptor will have the archive name and/or * object name fields filled in. A find of the file will result in the file * name (fname) field pointing to the actual file if present on the file * system. */ if (!rtems_rtl_obj_parse_name (obj, name)) return false; /* * If the archive field (aname) is set we use that name else we use the * object field (oname). If selected name is absolute we just point the aname * field to the fname field to that name. If the field is relative we search * the paths set in the RTL for the file. */ if (rtems_rtl_obj_aname_valid (obj)) pname = rtems_rtl_obj_aname (obj); else pname = rtems_rtl_obj_oname (obj); rtl = rtems_rtl_lock (); if (!rtems_rtl_find_file (pname, rtl->paths, &obj->fname, &obj->fsize)) { rtems_rtl_set_error (ENOENT, "file not found"); rtems_rtl_unlock (); return false; } rtems_rtl_unlock (); return true; } bool rtems_rtl_obj_add_section (rtems_rtl_obj_t* obj, int section, const char* name, size_t size, off_t offset, uint32_t alignment, int link, int info, uint32_t flags) { rtems_rtl_obj_sect_t* sect = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, sizeof (rtems_rtl_obj_sect_t), true); if (!sect) { rtems_rtl_set_error (ENOMEM, "adding allocated section"); return false; } sect->section = section; sect->name = rtems_rtl_strdup (name); sect->size = size; sect->offset = offset; sect->alignment = alignment; sect->link = link; sect->info = info; sect->flags = flags; sect->base = NULL; rtems_chain_append (&obj->sections, §->node); if (rtems_rtl_trace (RTEMS_RTL_TRACE_SECTION)) printf ("rtl: sect: %-2d: %s\n", section, name); return true; } void rtems_rtl_obj_erase_sections (rtems_rtl_obj_t* obj) { rtems_chain_node* node = rtems_chain_first (&obj->sections); while (!rtems_chain_is_tail (&obj->sections, node)) { rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node; rtems_chain_node* next_node = rtems_chain_next (node); rtems_chain_extract (node); rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) sect->name); rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, sect); node = next_node; } } /** * Section finder iterator data. */ typedef struct { rtems_rtl_obj_sect_t* sect; /**< The matching section. */ const char* name; /**< The name to match. */ int index; /**< The index to match. */ } rtems_rtl_obj_sect_finder_t; static bool rtems_rtl_obj_sect_match_name (rtems_chain_node* node, void* data) { rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node; rtems_rtl_obj_sect_finder_t* match = data; if (strcmp (sect->name, match->name) == 0) { match->sect = sect; return false; } return true; } rtems_rtl_obj_sect_t* rtems_rtl_obj_find_section (rtems_rtl_obj_t* obj, const char* name) { rtems_rtl_obj_sect_finder_t match; match.sect = NULL; match.name = name; rtems_rtl_chain_iterate (&obj->sections, rtems_rtl_obj_sect_match_name, &match); return match.sect; } static bool rtems_rtl_obj_sect_match_index (rtems_chain_node* node, void* data) { rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node; rtems_rtl_obj_sect_finder_t* match = data; if (sect->section == match->index) { match->sect = sect; return false; } return true; } rtems_rtl_obj_sect_t* rtems_rtl_obj_find_section_by_index (rtems_rtl_obj_t* obj, int index) { rtems_rtl_obj_sect_finder_t match; match.sect = NULL; match.index = index; rtems_rtl_chain_iterate (&obj->sections, rtems_rtl_obj_sect_match_index, &match); return match.sect; } size_t rtems_rtl_obj_text_size (rtems_rtl_obj_t* obj) { return rtems_rtl_obj_section_size (obj, RTEMS_RTL_OBJ_SECT_TEXT); } uint32_t rtems_rtl_obj_text_alignment (rtems_rtl_obj_t* obj) { return rtems_rtl_obj_section_alignment (obj, RTEMS_RTL_OBJ_SECT_TEXT); } size_t rtems_rtl_obj_const_size (rtems_rtl_obj_t* obj) { return rtems_rtl_obj_section_size (obj, RTEMS_RTL_OBJ_SECT_CONST); } uint32_t rtems_rtl_obj_const_alignment (rtems_rtl_obj_t* obj) { return rtems_rtl_obj_section_alignment (obj, RTEMS_RTL_OBJ_SECT_CONST); } size_t rtems_rtl_obj_data_size (rtems_rtl_obj_t* obj) { return rtems_rtl_obj_section_size (obj, RTEMS_RTL_OBJ_SECT_DATA); } uint32_t rtems_rtl_obj_data_alignment (rtems_rtl_obj_t* obj) { return rtems_rtl_obj_section_alignment (obj, RTEMS_RTL_OBJ_SECT_DATA); } size_t rtems_rtl_obj_bss_size (rtems_rtl_obj_t* obj) { return rtems_rtl_obj_section_size (obj, RTEMS_RTL_OBJ_SECT_BSS); } uint32_t rtems_rtl_obj_bss_alignment (rtems_rtl_obj_t* obj) { return rtems_rtl_obj_section_alignment (obj, RTEMS_RTL_OBJ_SECT_BSS); } bool rtems_rtl_obj_relocate (rtems_rtl_obj_t* obj, int fd, rtems_rtl_obj_sect_handler_t handler, void* data) { uint32_t mask = RTEMS_RTL_OBJ_SECT_REL | RTEMS_RTL_OBJ_SECT_RELA; return rtems_rtl_obj_section_handler (mask, obj, fd, handler, data); } bool rtems_rtl_obj_load_symbols (rtems_rtl_obj_t* obj, int fd, rtems_rtl_obj_sect_handler_t handler, void* data) { uint32_t mask = RTEMS_RTL_OBJ_SECT_SYM; return rtems_rtl_obj_section_handler (mask, obj, fd, handler, data); } static size_t rtems_rtl_obj_sections_loader (uint32_t mask, rtems_rtl_obj_t* obj, int fd, uint8_t* base, rtems_rtl_obj_sect_handler_t handler, void* data) { rtems_chain_control* sections = &obj->sections; rtems_chain_node* node = rtems_chain_first (sections); size_t base_offset = 0; bool first = true; while (!rtems_chain_is_tail (sections, node)) { rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node; if ((sect->size != 0) && ((sect->flags & mask) != 0)) { if (!first) base_offset = rtems_rtl_sect_align (base_offset, sect->alignment); sect->base = base + base_offset; if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD_SECT)) printf ("rtl: loading: %s -> %8p (%zi)\n", sect->name, sect->base, sect->size); if ((sect->flags & RTEMS_RTL_OBJ_SECT_LOAD) == RTEMS_RTL_OBJ_SECT_LOAD) { if (!handler (obj, fd, sect, data)) { sect->base = 0; return false; } } else if ((sect->flags & RTEMS_RTL_OBJ_SECT_ZERO) == RTEMS_RTL_OBJ_SECT_ZERO) { memset (base + base_offset, 0, sect->size); } else { sect->base = 0; rtems_rtl_set_error (errno, "section has no load op"); return false; } base_offset += sect->size; first = false; } rtems_cache_instruction_sync_after_code_change(base, base_offset); node = rtems_chain_next (node); } return true; } bool rtems_rtl_obj_load_sections (rtems_rtl_obj_t* obj, int fd, rtems_rtl_obj_sect_handler_t handler, void* data) { size_t text_size; size_t const_size; size_t data_size; size_t bss_size; text_size = rtems_rtl_obj_text_size (obj) + rtems_rtl_obj_const_alignment (obj); const_size = rtems_rtl_obj_const_size (obj) + rtems_rtl_obj_data_alignment (obj); data_size = rtems_rtl_obj_data_size (obj) + rtems_rtl_obj_bss_alignment (obj); bss_size = rtems_rtl_obj_bss_size (obj); /* * Let the allocator manage the actual allocation. The user can use the * standard heap or provide a specific allocator with memory protection. */ if (!rtems_rtl_alloc_module_new (&obj->text_base, text_size, &obj->const_base, const_size, &obj->data_base, data_size, &obj->bss_base, bss_size)) { obj->exec_size = 0; rtems_rtl_set_error (ENOMEM, "no memory to load obj"); return false; } obj->exec_size = text_size + const_size + data_size + bss_size; if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD_SECT)) { printf ("rtl: load sect: text - b:%p s:%zi a:%" PRIu32 "\n", obj->text_base, text_size, rtems_rtl_obj_text_alignment (obj)); printf ("rtl: load sect: const - b:%p s:%zi a:%" PRIu32 "\n", obj->const_base, const_size, rtems_rtl_obj_const_alignment (obj)); printf ("rtl: load sect: data - b:%p s:%zi a:%" PRIu32 "\n", obj->data_base, data_size, rtems_rtl_obj_data_alignment (obj)); printf ("rtl: load sect: bss - b:%p s:%zi a:%" PRIu32 "\n", obj->bss_base, bss_size, rtems_rtl_obj_bss_alignment (obj)); } /* * Load all text then data then bss sections in seperate operations so each * type of section is grouped together. */ if (!rtems_rtl_obj_sections_loader (RTEMS_RTL_OBJ_SECT_TEXT, obj, fd, obj->text_base, handler, data) || !rtems_rtl_obj_sections_loader (RTEMS_RTL_OBJ_SECT_CONST, obj, fd, obj->const_base, handler, data) || !rtems_rtl_obj_sections_loader (RTEMS_RTL_OBJ_SECT_DATA, obj, fd, obj->data_base, handler, data) || !rtems_rtl_obj_sections_loader (RTEMS_RTL_OBJ_SECT_BSS, obj, fd, obj->bss_base, handler, data)) { rtems_rtl_alloc_module_del (&obj->text_base, &obj->const_base, &obj->data_base, &obj->bss_base); obj->exec_size = 0; return false; } return true; } static void rtems_rtl_obj_run_cdtors (rtems_rtl_obj_t* obj, uint32_t mask) { rtems_chain_node* node = rtems_chain_first (&obj->sections); while (!rtems_chain_is_tail (&obj->sections, node)) { rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node; if ((sect->flags & mask) == mask) { rtems_rtl_cdtor_t* handler; size_t handlers = sect->size / sizeof (rtems_rtl_cdtor_t); int c; for (c = 0, handler = sect->base; c < handlers; ++c) if (*handler) (*handler) (); } node = rtems_chain_next (node); } } void rtems_rtl_obj_run_ctors (rtems_rtl_obj_t* obj) { return rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_CTOR); } void rtems_rtl_obj_run_dtors (rtems_rtl_obj_t* obj) { return rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_DTOR); } /** * Find a module in an archive returning the offset in the archive in the * object descriptor. */ static bool rtems_rtl_obj_archive_find (rtems_rtl_obj_t* obj, int fd) { #define RTEMS_RTL_AR_IDENT "!\n" #define RTEMS_RTL_AR_IDENT_SIZE (sizeof (RTEMS_RTL_AR_IDENT) - 1) #define RTEMS_RTL_AR_FHDR_BASE RTEMS_RTL_AR_IDENT_SIZE #define RTEMS_RTL_AR_FNAME (0) #define RTEMS_RTL_AR_FNAME_SIZE (16) #define RTEMS_RTL_AR_SIZE (48) #define RTEMS_RTL_AR_SIZE_SIZE (10) #define RTEMS_RTL_AR_MAGIC (58) #define RTEMS_RTL_AR_MAGIC_SIZE (2) #define RTEMS_RTL_AR_FHDR_SIZE (60) size_t fsize = obj->fsize; off_t extended_file_names; uint8_t header[RTEMS_RTL_AR_FHDR_SIZE]; bool scanning; if (read (fd, &header[0], RTEMS_RTL_AR_IDENT_SIZE) != RTEMS_RTL_AR_IDENT_SIZE) { rtems_rtl_set_error (errno, "reading archive identifer"); return false; } if (memcmp (header, RTEMS_RTL_AR_IDENT, RTEMS_RTL_AR_IDENT_SIZE) != 0) { rtems_rtl_set_error (EINVAL, "invalid archive identifer"); return false; } /* * Seek to the current offset in the archive and if we have a valid archive * file header present check the file name for a match with the oname field * of the object descriptor. If the archive header is not valid and it is the * first pass reset the offset and start the search again in case the offset * provided is not valid any more. * * The archive can have a symbol table at the start. Ignore it. A symbol * table has the file name '/'. * * The archive can also have the GNU extended file name table. This * complicates the processing. If the object's file name starts with '/' the * remainder of the file name is an offset into the extended file name * table. To find the extended file name table we need to scan from start of * the archive for a file name of '//'. Once found we remeber the table's * start and can direct seek to file name location. In other words the scan * only happens once. * * If the name had the offset encoded we go straight to that location. */ if (obj->ooffset != 0) scanning = false; else { scanning = true; obj->ooffset = RTEMS_RTL_AR_FHDR_BASE; } extended_file_names = 0; while (obj->ooffset < fsize) { /* * Clean up any existing data. */ memset (header, 0, sizeof (header)); if (!rtems_rtl_seek_read (fd, obj->ooffset, RTEMS_RTL_AR_FHDR_SIZE, &header[0])) { rtems_rtl_set_error (errno, "seek/read archive file header"); obj->ooffset = 0; obj->fsize = 0; return false; } if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) || (header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a)) { if (scanning) { rtems_rtl_set_error (EINVAL, "invalid archive file header"); obj->ooffset = 0; obj->fsize = 0; return false; } scanning = true; obj->ooffset = RTEMS_RTL_AR_FHDR_BASE; continue; } /* * The archive header is always aligned to an even address. */ obj->fsize = (rtems_rtl_scan_decimal (&header[RTEMS_RTL_AR_SIZE], RTEMS_RTL_AR_SIZE_SIZE) + 1) & ~1; /* * Check for the GNU extensions. */ if (header[0] == '/') { off_t extended_off; switch (header[1]) { case ' ': /* * Symbols table. Ignore the table. */ break; case '/': /* * Extended file names table. Remember. */ extended_file_names = obj->ooffset + RTEMS_RTL_AR_FHDR_SIZE; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* * Offset into the extended file name table. If we do not have the * offset to the extended file name table find it. */ extended_off = rtems_rtl_scan_decimal (&header[1], RTEMS_RTL_AR_FNAME_SIZE); if (extended_file_names == 0) { off_t off = obj->ooffset; while (extended_file_names == 0) { off_t esize = (rtems_rtl_scan_decimal (&header[RTEMS_RTL_AR_SIZE], RTEMS_RTL_AR_SIZE_SIZE) + 1) & ~1; off += esize + RTEMS_RTL_AR_FHDR_SIZE; if (!rtems_rtl_seek_read (fd, off, RTEMS_RTL_AR_FHDR_SIZE, &header[0])) { rtems_rtl_set_error (errno, "seeking/reading archive ext file name header"); obj->ooffset = 0; obj->fsize = 0; return false; } if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) || (header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a)) { rtems_rtl_set_error (errno, "invalid archive file header"); obj->ooffset = 0; obj->fsize = 0; return false; } if ((header[0] == '/') && (header[1] == '/')) { extended_file_names = off + RTEMS_RTL_AR_FHDR_SIZE; break; } } } if (extended_file_names) { /* * We know the offset in the archive to the extended file. Read the * name from the table and compare with the name we are after. */ #define RTEMS_RTL_MAX_FILE_SIZE (256) char name[RTEMS_RTL_MAX_FILE_SIZE]; if (!rtems_rtl_seek_read (fd, extended_file_names + extended_off, RTEMS_RTL_MAX_FILE_SIZE, (uint8_t*) &name[0])) { rtems_rtl_set_error (errno, "invalid archive ext file seek/read"); obj->ooffset = 0; obj->fsize = 0; return false; } if (rtems_rtl_match_name (obj, name)) { obj->ooffset += RTEMS_RTL_AR_FHDR_SIZE; return true; } } break; default: /* * Ignore the file because we do not know what it it. */ break; } } else { if (rtems_rtl_match_name (obj, (const char*) &header[RTEMS_RTL_AR_FNAME])) { obj->ooffset += RTEMS_RTL_AR_FHDR_SIZE; return true; } } obj->ooffset += obj->fsize + RTEMS_RTL_AR_FHDR_SIZE; } rtems_rtl_set_error (ENOENT, "object file not found"); obj->ooffset = 0; obj->fsize = 0; return false; } bool rtems_rtl_obj_file_load (rtems_rtl_obj_t* obj, int fd) { int l; for (l = 0; l < (sizeof (loaders) / sizeof (rtems_rtl_loader_table_t)); ++l) { if (loaders[l].check (obj, fd)) return loaders[l].load (obj, fd); } rtems_rtl_set_error (ENOENT, "no format loader found"); return false; } bool rtems_rtl_obj_load (rtems_rtl_obj_t* obj) { int fd; if (!rtems_rtl_obj_fname_valid (obj)) { rtems_rtl_set_error (ENOMEM, "invalid object file name path"); return false; } fd = open (rtems_rtl_obj_fname (obj), O_RDONLY); if (fd < 0) { rtems_rtl_set_error (ENOMEM, "opening for object file"); return false; } /* * Find the object file in the archive if it is an archive that * has been opened. */ if (rtems_rtl_obj_aname_valid (obj)) { if (!rtems_rtl_obj_archive_find (obj, fd)) { rtems_rtl_obj_caches_flush (); close (fd); return false; } } /* * Call the format specific loader. Currently this is a call to the ELF * loader. This call could be changed to allow probes then calls if more than * one format is supported. */ if (!rtems_rtl_obj_file_load (obj, fd)) { rtems_rtl_obj_caches_flush (); close (fd); return false; } if (!_rtld_linkmap_add (obj)) /* For GDB */ { close (fd); return false; } rtems_rtl_obj_caches_flush (); close (fd); return true; } bool rtems_rtl_obj_unload (rtems_rtl_obj_t* obj) { _rtld_linkmap_delete(obj); rtems_rtl_symbol_obj_erase (obj); return rtems_rtl_obj_free (obj); }