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_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; }
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; } node = rtems_chain_next (node); } return true; }
bool rtems_rtl_elf_relocate_rela (const rtems_rtl_obj_t* obj, const Elf_Rela* rela, const rtems_rtl_obj_sect_t* sect, const char* symname, const Elf_Byte syminfo, const Elf_Word symvalue) { rtems_rtl_set_error (EINVAL, "rela type record not supported"); 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_cache_open (rtems_rtl_obj_cache_t* cache, size_t size) { cache->fd = -1; cache->file_size = 0; cache->offset = 0; cache->size = size; cache->level = 0; cache->buffer = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, size, false); if (!cache->buffer) { rtems_rtl_set_error (ENOMEM, "no memory for cache buffer"); return false; } return true; }
static rtems_rtl_unresolv_block_t* rtems_rtl_unresolved_block_alloc (rtems_rtl_unresolved_t* unresolved) { /* * The block header contains a record. */ size_t size = (sizeof(rtems_rtl_unresolv_block_t) + (sizeof(rtems_rtl_unresolv_rec_t) * (unresolved->block_recs - 1))); rtems_rtl_unresolv_block_t* block = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_EXTERNAL, size, true); if (block) rtems_chain_append (&unresolved->blocks, &block->link); else rtems_rtl_set_error (ENOMEM, "no memory for unresolved block"); return block; }
bool rtems_rtl_symbol_table_open (rtems_rtl_symbols_t* symbols, size_t buckets) { symbols->buckets = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_SYMBOL, buckets * sizeof (rtems_chain_control), true); if (!symbols->buckets) { rtems_rtl_set_error (ENOMEM, "no memory for global symbol table"); return false; } symbols->nbuckets = buckets; for (buckets = 0; buckets < symbols->nbuckets; ++buckets) rtems_chain_initialize_empty (&symbols->buckets[buckets]); rtems_rtl_symbol_global_insert (symbols, &global_sym_add); return true; }
bool rtems_rtl_obj_comp_open (rtems_rtl_obj_comp_t* comp, size_t size) { comp->cache = NULL; comp->fd = -1; comp->compression = RTEMS_RTL_COMP_LZ77; comp->offset = 0; comp->size = size; comp->level = 0; comp->buffer = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, size, false); if (!comp->buffer) { rtems_rtl_set_error (ENOMEM, "no memory for compressor buffer"); return false; } comp->read = 0; return true; }
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_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_find_file (const char* name, const char* paths, const char** file_name, size_t* size) { struct stat sb; *file_name = NULL; *size = 0; if (rtems_filesystem_is_delimiter (name[0]) || (name[0] == '.')) { if (stat (name, &sb) == 0) *file_name = rtems_rtl_strdup (name); } else if (paths) { const char* start; const char* end; int len; char* fname; start = paths; end = start + strlen (paths); len = strlen (name); while (!*file_name && (start != end)) { const char* delimiter = strchr (start, ':'); if (delimiter == NULL) delimiter = end; /* * Allocate the path fragment, separator, name, terminating nul. Form the * path then see if the stat call works. */ fname = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, (delimiter - start) + 1 + len + 1, true); if (!fname) { rtems_rtl_set_error (ENOMEM, "no memory searching for file"); return false; } memcpy (fname, start, delimiter - start); fname[delimiter - start] = '/'; memcpy (fname + (delimiter - start) + 1, name, len); if (rtems_rtl_trace (RTEMS_RTL_TRACE_LOAD)) printf ("rtl: find-file: path: %s\n", fname); if (stat (fname, &sb) < 0) rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, fname); else *file_name = fname; start = delimiter; if (start != end) ++start; } } if (!*file_name) return false; *size = sb.st_size; return true; }
bool rtems_rtl_elf_relocate_rela (const rtems_rtl_obj_t* obj, const Elf_Rela* rela, const rtems_rtl_obj_sect_t* sect, const char* symname, const Elf_Byte syminfo, const Elf_Word symvalue) { Elf_Addr* where; Elf_Word tmp; uint32_t mask = 0; uint32_t bits = 0; where = (Elf_Addr *)(sect->base + rela->r_offset); switch (ELF_R_TYPE(rela->r_info)) { case R_TYPE(NONE): break; case R_TYPE(32): /* * value:1; Field: word32; Expression: S + A */ *where = symvalue + rela->r_addend; if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: ADDR32 %p @ %p in %s\n", (void *)*(where), where, rtems_rtl_obj_oname (obj)); break; case R_TYPE(14): /* * value:7; Field: low14*; Expression: (S + A) >> 2 */ case R_TYPE(24): /* * value:2; Field: low24*; Expression: (S + A) >> 2 */ if (ELF_R_TYPE(rela->r_info) == R_TYPE(14)) { bits = 14; mask = 0xfffc; } else { bits = 24; mask = 0x3fffffc; } tmp = (symvalue + rela->r_addend) >> 2; if (tmp > ((1<<bits) - 1 )) { printf("Overflow ADDR14/ADDR24\n"); return false; } tmp = *where; tmp &= ~mask; tmp |= (symvalue + rela->r_addend) & mask; *where = tmp; if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: ADDR14/ADDR24 %p @ %p in %s\n", (void *)*where, where, rtems_rtl_obj_oname (obj)); break; case R_TYPE(16_HA): /* * value:6; Field:half16; Expression: #ha(S+A) */ tmp = symvalue + rela->r_addend; *(uint16_t *)where = (((tmp >> 16) + ((tmp & 0x8000) ? 1: 0)) & 0xffff); if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: 16_HA %p @ %p in %s\n", (void *)*(where), where, rtems_rtl_obj_oname (obj)); break; case R_TYPE(16_HI): /* * value:5; Field:half16; Expression: #hi(S+A) */ *(uint16_t *)where = ((symvalue + rela->r_addend) >> 16) & 0xffff; if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: 16_HI %p @ %p in %s\n", (void *)*where, where, rtems_rtl_obj_oname (obj)); break; case R_TYPE(16_LO): /* * value:4; Field:half16; Expression: #lo(S+A) */ *(uint16_t *)where = (symvalue + (rela->r_addend)) & 0xffff; if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: 16_LO %p @ %p in %s\n", (void *)*where, where, rtems_rtl_obj_oname (obj)); break; case R_TYPE(REL14): /* * value:11; Field:low14*; Expression:(S+A-P)>>2 */ case R_TYPE(REL24): /* * value:10; Field:low24*; Expression:(S+A-P)>>2 */ if (ELF_R_TYPE(rela->r_info) == R_TYPE(REL24)) { mask = 0x3fffffc; bits = 24; } else if (ELF_R_TYPE(rela->r_info) == R_TYPE(REL14)) { mask = 0xfffc; bits = 14; } tmp =((int) (symvalue + rela->r_addend - (Elf_Addr)where)) >> 2; if (((Elf_Sword)tmp > ((1<<(bits-1)) - 1)) || ((Elf_Sword)tmp < -(1<<(bits-1)))) { printf("Overflow REL14/REL24\n"); return false; } tmp = *where; tmp &= ~mask; tmp |= (symvalue + rela->r_addend - (Elf_Addr)where) & mask; *where = tmp; if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: REL24/REL14 %p @ %p in %s\n", (void *)*where, where, rtems_rtl_obj_oname (obj)); break; case R_TYPE(REL32): /* * value:26; Field:word32*; Expression:S+A-P */ *where = symvalue + rela->r_addend - (Elf_Addr)where; if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: REL32 %p @ %p in %s\n", (void *)*where, where, rtems_rtl_obj_oname (obj)); break; default: printf ("rtl: reloc unknown: sym = %lu, type = %lu, offset = %p, " "contents = %p\n", ELF_R_SYM(rela->r_info), (uint32_t) ELF_R_TYPE(rela->r_info), (void *)rela->r_offset, (void *)*where); rtems_rtl_set_error (EINVAL, "%s: Unsupported relocation type %ld " "in non-PLT relocations", sect->name, (uint32_t) ELF_R_TYPE(rela->r_info)); return false; } return true; }
bool rtems_rtl_symbol_global_add (rtems_rtl_obj_t* obj, const unsigned char* esyms, unsigned int size) { rtems_rtl_symbols_t* symbols; rtems_rtl_obj_sym_t* sym; size_t count; size_t s; uint32_t marker; count = 0; s = 0; while ((s < size) && (esyms[s] != 0)) { int l = strlen ((char*) &esyms[s]); if ((esyms[s + l] != '\0') || ((s + l) > size)) { rtems_rtl_set_error (EINVAL, "invalid exported symbol table"); return false; } ++count; s += l + sizeof (unsigned long) + 1; } /* * Check this is the correct end of the table. */ marker = esyms[s + 1]; marker <<= 8; marker |= esyms[s + 2]; marker <<= 8; marker |= esyms[s + 3]; marker <<= 8; marker |= esyms[s + 4]; if (marker != 0xdeadbeefUL) { rtems_rtl_set_error (ENOMEM, "invalid export symbol table"); return false; } if (rtems_rtl_trace (RTEMS_RTL_TRACE_GLOBAL_SYM)) printf ("rtl: global symbol add: %zi\n", count); obj->global_size = count * sizeof (rtems_rtl_obj_sym_t); obj->global_table = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_SYMBOL, obj->global_size, true); if (!obj->global_table) { obj->global_size = 0; rtems_rtl_set_error (ENOMEM, "no memory for global symbols"); return false; } symbols = rtems_rtl_global_symbols (); s = 0; sym = obj->global_table; while ((s < size) && (esyms[s] != 0)) { /* * Copy the void* using a union and memcpy to avoid any strict aliasing or * alignment issues. The variable length of the label and the packed nature * of the table means casting is not suitable. */ union { uint8_t data[sizeof (void*)]; void* value; } copy_voidp; int b; sym->name = (const char*) &esyms[s]; s += strlen (sym->name) + 1; for (b = 0; b < sizeof (void*); ++b, ++s) copy_voidp.data[b] = esyms[s]; sym->value = copy_voidp.value; if (rtems_rtl_trace (RTEMS_RTL_TRACE_GLOBAL_SYM)) printf ("rtl: esyms: %s -> %8p\n", sym->name, sym->value); if (rtems_rtl_symbol_global_find (sym->name) == NULL) rtems_rtl_symbol_global_insert (symbols, sym); ++sym; } obj->global_syms = count; return true; }
bool rtems_rtl_obj_comp_read (rtems_rtl_obj_comp_t* comp, void* buffer, size_t length) { uint8_t* bin = buffer; if (!comp->cache) { rtems_rtl_set_error (EINVAL, "not open"); return false; } if (comp->fd != comp->cache->fd) { comp->level = 0; } while (length) { size_t buffer_level; buffer_level = length > comp->level ? comp->level : length; if (buffer_level) { memcpy (bin, comp->buffer, buffer_level); if ((comp->level - buffer_level) != 0) { memmove (comp->buffer, comp->buffer + buffer_level, comp->level - buffer_level); } bin += buffer_level; length -= buffer_level; comp->level -= buffer_level; comp->read += buffer_level; } if (length) { uint8_t* input = NULL; uint16_t block_size; size_t in_length = sizeof (block_size); int decompressed; if (!rtems_rtl_obj_cache_read (comp->cache, comp->fd, comp->offset, (void**) &input, &in_length)) return false; block_size = (input[0] << 8) | input[1]; comp->offset += sizeof (block_size); in_length = block_size; if (!rtems_rtl_obj_cache_read (comp->cache, comp->fd, comp->offset, (void**) &input, &in_length)) return false; if (in_length != block_size) { rtems_rtl_set_error (EIO, "compressed read failed: bs=%u in=%u", block_size, in_length); return false; } switch (comp->compression) { case RTEMS_RTL_COMP_NONE: memcpy (comp->buffer, input, in_length); decompressed = in_length; break; case RTEMS_RTL_COMP_LZ77: decompressed = fastlz_decompress (input, in_length, comp->buffer, comp->size); if (decompressed == 0) { rtems_rtl_set_error (EBADF, "decompression failed"); return false; } break; default: rtems_rtl_set_error (EINVAL, "bad compression type"); return false; } comp->offset += block_size; comp->level = decompressed; } } return true; }
bool rtems_rtl_elf_relocate_rel (const rtems_rtl_obj_t* obj, const Elf_Rel* rel, const rtems_rtl_obj_sect_t* sect, const char* symname, const Elf_Byte syminfo, const Elf_Word symvalue) { Elf_Addr target = 0; Elf_Addr* where; Elf_Addr tmp; where = (Elf_Addr *)(sect->base + rel->r_offset); switch (ELF_R_TYPE(rel->r_info)) { case R_TYPE(NONE): break; case R_TYPE(PC32): target = (Elf_Addr) symvalue; *where += target - (Elf_Addr)where; if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: reloc PC32 in %s --> %p (%p @ %p) in %s\n", sect->name, (void*) symvalue, (void *)*where, where, rtems_rtl_obj_oname (obj)); break; case R_TYPE(GOT32): case R_TYPE(32): case R_TYPE(GLOB_DAT): target = (Elf_Addr) symvalue; tmp = target + *where; if (*where != tmp) *where = tmp; if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: reloc 32/GLOB_DAT in %s --> %p @ %p in %s\n", sect->name, (void *)*where, where, rtems_rtl_obj_oname (obj)); break; case R_TYPE(RELATIVE): *where += (Elf_Addr)sect->base; if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: reloc RELATIVE in %s --> %p @ %p\n", rtems_rtl_obj_oname (obj), (void *)*where, where); break; case R_TYPE(COPY): printf ("rtl: reloc COPY (please report)\n"); break; default: printf ("rtl: reloc unknown: sym = %lu, type = %lu, offset = %p, " "contents = %p\n", ELF_R_SYM(rel->r_info), (uint32_t) ELF_R_TYPE(rel->r_info), (void *)rel->r_offset, (void *)*where); rtems_rtl_set_error (EINVAL, "%s: Unsupported relocation type %ld " "in non-PLT relocations", sect->name, (uint32_t) ELF_R_TYPE(rel->r_info)); return false; } return true; }
bool rtems_rtl_obj_cache_read (rtems_rtl_obj_cache_t* cache, int fd, off_t offset, void** buffer, size_t* length) { struct stat sb; if (*length > cache->size) { rtems_rtl_set_error (EINVAL, "read size larger than cache size"); return false; } if (cache->fd == fd) { if (offset > cache->file_size) { rtems_rtl_set_error (EINVAL, "offset past end of file: offset=%i size=%i", (int) offset, (int) cache->file_size); return false; } if ((offset + *length) > cache->file_size) *length = cache->file_size - offset; } while (true) { size_t buffer_offset = 0; size_t buffer_read = cache->size; /* * Is the data in the cache for this file ? */ if (fd == cache->fd) { /* * Is any part of the data in the cache ? */ if ((offset >= cache->offset) && (offset < (cache->offset + cache->level))) { buffer_offset = offset - cache->offset; /* * Return the location of the data in the cache. */ *buffer = cache->buffer + buffer_offset; /* * Is all the data in the cache or just a part ? */ if (*length <= (cache->level - buffer_offset)) { return true; } /* * Copy down the data in the buffer and then fill the remaining * space with as much data we are able to read. */ memmove (cache->buffer, cache->buffer + buffer_offset, cache->size - buffer_offset); buffer_read = buffer_offset; buffer_offset = cache->size - buffer_offset; } } if (lseek (fd, offset + buffer_offset, SEEK_SET) < 0) { rtems_rtl_set_error (errno, "file seek failed"); return false; } /* * Loop reading the data from the file until either an error or 0 is * returned and if data has been read check if the amount is what we * want. If not it is an error. A POSIX read can read data in fragments. */ cache->level = buffer_read; while (buffer_read) { int r = read (fd, cache->buffer + buffer_offset, buffer_read); if (r < 0) { rtems_rtl_set_error (errno, "file read failed"); return false; } if ((r == 0) && buffer_read) { cache->level = cache->level - buffer_read; buffer_read = 0; } else { buffer_read -= r; buffer_offset += r; } } cache->fd = fd; cache->offset = offset; if (fstat (cache->fd, &sb) < 0) { rtems_rtl_set_error (errno, "file stat failed"); return false; } cache->file_size = sb.st_size; } return false; }
/** * 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 "!<arch>\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_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; }
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; }