diff options
author | midipix <writeonce@midipix.org> | 2025-05-30 08:51:56 +0000 |
---|---|---|
committer | midipix <writeonce@midipix.org> | 2025-05-30 08:51:56 +0000 |
commit | 906422a560cdbf078793143a30274a5faed37d6b (patch) | |
tree | ed36d22e031e0ab8b0f295c61e12a63845432e80 | |
parent | 2baf678ee420b74c3b08a225f111787645ac18a6 (diff) | |
download | perk-906422a560cdbf078793143a30274a5faed37d6b.tar.bz2 perk-906422a560cdbf078793143a30274a5faed37d6b.tar.xz |
arbits: added pe_ar_get_archive_meta(), the framework's archive parser.
-rw-r--r-- | include/perk/perk.h | 39 | ||||
-rw-r--r-- | project/common.mk | 5 | ||||
-rw-r--r-- | project/headers.mk | 1 | ||||
-rw-r--r-- | project/tree.mk | 1 | ||||
-rw-r--r-- | src/arbits/pe_archive_meta.c | 888 | ||||
-rw-r--r-- | src/arbits/pe_armap_bsd_32.c | 157 | ||||
-rw-r--r-- | src/arbits/pe_armap_bsd_64.c | 174 | ||||
-rw-r--r-- | src/arbits/pe_armap_sysv_32.c | 120 | ||||
-rw-r--r-- | src/arbits/pe_armap_sysv_64.c | 128 | ||||
-rw-r--r-- | src/internal/perk_ar_impl.h | 100 |
10 files changed, 1613 insertions, 0 deletions
diff --git a/include/perk/perk.h b/include/perk/perk.h index 1d081a8..78f1a0f 100644 --- a/include/perk/perk.h +++ b/include/perk/perk.h @@ -6,6 +6,7 @@ #include "perk_api.h" #include "perk_meta.h" +#include "perk_arbits.h" #ifdef __cplusplus extern "C" { @@ -61,6 +62,27 @@ enum pe_custom_error { PERK_ERR_BAD_COFF_HEADER, PERK_ERR_BAD_IMAGE_TYPE, PERK_ERR_UNSUPPORTED_ABI, + PERK_ERR_AR_FAIL, + PERK_ERR_AR_EMPTY_FILE, + PERK_ERR_AR_INVALID_SIGNATURE, + PERK_ERR_AR_INVALID_HEADER, + PERK_ERR_AR_TRUNCATED_DATA, + PERK_ERR_AR_DUPLICATE_LONG_NAMES, + PERK_ERR_AR_DUPLICATE_ARMAP_MEMBER, + PERK_ERR_AR_MISPLACED_ARMAP_MEMBER, + PERK_ERR_AR_NO_ACTION_SPECIFIED, + PERK_ERR_AR_NO_INPUT_SPECIFIED, + PERK_ERR_AR_DRIVER_MISMATCH, + PERK_ERR_AR_ARMAP_MISMATCH, + PERK_ERR_AR_INVALID_ARMAP_NUMBER_OF_SYMS, + PERK_ERR_AR_INVALID_ARMAP_SIZE_OF_REFS, + PERK_ERR_AR_INVALID_ARMAP_SIZE_OF_STRS, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE, + PERK_ERR_AR_INVALID_ARMAP_MEMBER_OFFSET, + PERK_ERR_AR_INVALID_ARMAP_NAME_OFFSET, + PERK_ERR_AR_DLUNIT_NOT_SPECIFIED, + PERK_ERR_AR_OUTPUT_NOT_SPECIFIED, + PERK_ERR_AR_OUTPUT_NOT_APPLICABLE, PERK_ERR_CAP, }; @@ -85,6 +107,11 @@ struct pe_raw_image { size_t map_size; }; +struct pe_raw_archive { + void * map_addr; + size_t map_size; +}; + struct pe_expsym { const char * s_name; void * s_eaddr; @@ -138,6 +165,18 @@ struct pe_image_meta { enum pe_framework m_framework; }; +struct pe_archive_meta { + struct pe_raw_archive r_archive; + struct ar_raw_signature * r_signature; + + struct ar_meta_signature * m_signature; + + struct ar_meta_member_info ** a_memberv; + struct ar_meta_member_info * a_arref_longnames; + struct ar_meta_armap_info a_armap_primary; + struct ar_meta_armap_info a_armap_pecoff; +}; + struct pe_source_version { int major; int minor; diff --git a/project/common.mk b/project/common.mk index 647d3c3..7d9b68f 100644 --- a/project/common.mk +++ b/project/common.mk @@ -1,4 +1,9 @@ API_SRCS = \ + src/arbits/pe_archive_meta.c \ + src/arbits/pe_armap_bsd_32.c \ + src/arbits/pe_armap_bsd_64.c \ + src/arbits/pe_armap_sysv_32.c \ + src/arbits/pe_armap_sysv_64.c \ src/cmds/pe_cmd_perk.c \ src/driver/pe_amain.c \ src/driver/pe_driver_ctx.c \ diff --git a/project/headers.mk b/project/headers.mk index 3085971..2c2a247 100644 --- a/project/headers.mk +++ b/project/headers.mk @@ -9,6 +9,7 @@ API_HEADERS = \ INTERNAL_HEADERS = \ $(SOURCE_DIR)/src/internal/argv/argv.h \ + $(SOURCE_DIR)/src/internal/$(PACKAGE)_ar_impl.h \ $(SOURCE_DIR)/src/internal/$(PACKAGE)_dprintf_impl.h \ $(SOURCE_DIR)/src/internal/$(PACKAGE)_driver_impl.h \ $(SOURCE_DIR)/src/internal/$(PACKAGE)_endian_impl.h \ diff --git a/project/tree.mk b/project/tree.mk index 2ac5649..6d78f1f 100644 --- a/project/tree.mk +++ b/project/tree.mk @@ -1,5 +1,6 @@ tree.tag: mkdir -p src + mkdir -p src/arbits mkdir -p src/cmds mkdir -p src/driver mkdir -p src/hdrdump diff --git a/src/arbits/pe_archive_meta.c b/src/arbits/pe_archive_meta.c new file mode 100644 index 0000000..f5fcf0d --- /dev/null +++ b/src/arbits/pe_archive_meta.c @@ -0,0 +1,888 @@ +/***************************************************************/ +/* perk: PE Resource Kit */ +/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.PERK. */ +/***************************************************************/ + +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +#include <perk/perk.h> +#include <perk/perk_arbits.h> +#include "perk_ar_impl.h" +#include "perk_driver_impl.h" +#include "perk_errinfo_impl.h" +#include "perk_visibility_impl.h" + +/* transient header info vector */ +struct ar_header_info { + struct ar_raw_file_header * phdr; + uint32_t attr; +}; + +static const char ar_signature[] = AR_SIGNATURE; + +static int pe_ar_free_archive_meta_impl(struct pe_archive_meta_impl * meta, int ret) +{ + if (meta) { + if (meta->armaps.armap_symrefs_32) + free(meta->armaps.armap_symrefs_32); + + if (meta->armaps.armap_symrefs_64) + free(meta->armaps.armap_symrefs_64); + + if (meta->hdrinfov) + free(meta->hdrinfov); + + if (meta->namestrs) + free(meta->namestrs); + + if (meta->syminfo) + free(meta->syminfo); + + if (meta->syminfv) + free(meta->syminfv); + + if (meta->memberv) + free(meta->memberv); + + if (meta->offsetv) + free(meta->offsetv); + + if (meta->members) + free(meta->members); + + if (meta->symstrv) + free(meta->symstrv); + + if (meta->mapstrv) + free(meta->mapstrv); + + if (meta->nminfo) + (void)0; + + free(meta); + } + + return ret; +} + + +static int pe_ar_read_octal(const char * mark, int len, uint32_t * dec) +{ + int i; + uint64_t res; + + for (; len && (mark[len-1]==AR_DEC_PADDING); ) + len--; + + for (i=0,res=0; i<len; i++) { + if ((mark[i] >= '0') && (mark[i] <= '7')) { + res *= 8; + res += (mark[i] - '0'); + } else { + return -1; + } + } + + *dec = res; + + return 0; +} + +static int pe_ar_read_decimal_64(const char * mark, int len, uint64_t * dec) +{ + int i; + uint64_t res; + + for (; len && (mark[len-1]==AR_DEC_PADDING); ) + len--; + + for (i=0,res=0; i<len; i++) { + if ((mark[i] >= '0') && (mark[i] <= '9')) { + res *= 10; + res += (mark[i] - '0'); + } else { + return -1; + } + } + + *dec = res; + + return 0; +} + +static int pe_ar_read_decimal_32(const char * mark, int len, uint32_t * dec) +{ + uint64_t res; + + if (pe_ar_read_decimal_64(mark,len,&res) < 0) + return -1; + + *dec = res; + + return 0; +} + +static uint32_t pe_ar_get_member_attr(struct ar_meta_member_info * m) +{ + const char * hdrname; + uint32_t hdrattr; + const char * data; + const char * data_cap; + const unsigned char * udata; + unsigned char uch; + const size_t siglen = sizeof(struct ar_raw_signature); + + hdrname = m->ar_file_header.ar_member_name; + hdrattr = m->ar_file_header.ar_header_attr; + + data = m->ar_object_data; + data_cap = &data[m->ar_file_header.ar_file_size]; + + if (hdrattr & AR_HEADER_ATTR_SYSV) { + /* long names member? */ + if ((hdrname[0] == '/') && (hdrname[1] == '/')) + return AR_MEMBER_ATTR_NAMESTRS; + + /* mips 64-bit armap member? */ + else if (!strncmp(hdrname,"/SYM64/",7)) + return AR_MEMBER_ATTR_ARMAP; + + /* armap member? */ + else if (hdrname[0] == '/' && (hdrname[1] == '\0')) + return AR_MEMBER_ATTR_ARMAP; + + /* nested archive? */ + else if (m->ar_file_header.ar_file_size >= siglen) + if (!strncmp(data,ar_signature,siglen)) + return AR_MEMBER_ATTR_ARCHIVE; + + } else if (hdrattr & AR_HEADER_ATTR_BSD) { + if (!strcmp(hdrname,"__.SYMDEF")) + return AR_MEMBER_ATTR_ARMAP; + + else if (!strcmp(hdrname,"__.SYMDEF SORTED")) + return AR_MEMBER_ATTR_ARMAP; + + else if (!strcmp(hdrname,"__.SYMDEF_64")) + return AR_MEMBER_ATTR_ARMAP; + + else if (!strcmp(hdrname,"__.SYMDEF_64 SORTED")) + return AR_MEMBER_ATTR_ARMAP; + } + + /* ascii only data? */ + for (; data<data_cap; ) { + if ((uch = *data) >= 0x80) + break; + + data++; + } + + if (data == data_cap) + return AR_MEMBER_ATTR_ASCII; + + data = m->ar_object_data; + udata = (unsigned char *)data; + + /* elf object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 5) + if ((udata[0] == 0x7f) + && (udata[1] == 'E') + && (udata[2] == 'L') + && (udata[3] == 'F')) + if ((m->ar_object_attr = AR_OBJECT_ATTR_ELF)) + return AR_MEMBER_ATTR_OBJECT; + + /* coff i386 object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 2) + if ((udata[0] == 0x4c) && (udata[1] == 0x01)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_COFF)) + return AR_MEMBER_ATTR_OBJECT; + + /* coff x86_64 object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 2) + if ((udata[0] == 0x64) && (udata[1] == 0x86)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_COFF)) + return AR_MEMBER_ATTR_OBJECT; + + /* big endian 32-bit macho object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 4) + if ((udata[0] == 0xfe) && (udata[1] == 0xed)) + if ((udata[2] == 0xfa) && (udata[3] == 0xce)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_MACHO)) + return AR_MEMBER_ATTR_OBJECT; + + /* big endian 64-bit macho object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 4) + if ((udata[0] == 0xfe) && (udata[1] == 0xed)) + if ((udata[2] == 0xfa) && (udata[3] == 0xcf)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_MACHO)) + return AR_MEMBER_ATTR_OBJECT; + + /* little endian 32-bit macho object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 4) + if ((udata[3] == 0xfe) && (udata[2] == 0xed)) + if ((udata[1] == 0xfa) && (udata[0] == 0xce)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_MACHO)) + return AR_MEMBER_ATTR_OBJECT; + + /* little endian 64-bit macho object? [quick and dirty] */ + if (m->ar_file_header.ar_file_size >= 4) + if ((udata[3] == 0xfe) && (udata[2] == 0xed)) + if ((udata[1] == 0xfa) && (udata[0] == 0xcf)) + if ((m->ar_object_attr = AR_OBJECT_ATTR_MACHO)) + return AR_MEMBER_ATTR_OBJECT; + + /* all other */ + return AR_MEMBER_ATTR_DEFAULT; +} + +static int pe_ar_parse_primary_armap( + const struct pe_driver_ctx * dctx, + struct pe_archive_meta_impl * m) + +{ + struct ar_meta_member_info * memberp; + const char * hdrname; + uint32_t hdrattr; + + memberp = m->memberv[0]; + hdrname = memberp->ar_file_header.ar_member_name; + hdrattr = memberp->ar_file_header.ar_header_attr; + + if (!(memberp->ar_member_attr & AR_MEMBER_ATTR_ARMAP)) + return 0; + + if (hdrattr & AR_HEADER_ATTR_SYSV) { + /* mips 64-bit armap member? */ + if (!strncmp(hdrname,"/SYM64/",7)) + return pe_ar_parse_primary_armap_sysv_64( + dctx,m); + + /* sysv 32-bit armap member */ + return pe_ar_parse_primary_armap_sysv_32( + dctx,m); + + } else if (hdrattr & AR_HEADER_ATTR_BSD) { + if (!strcmp(hdrname,"__.SYMDEF")) + return pe_ar_parse_primary_armap_bsd_32( + dctx,m); + + else if (!strcmp(hdrname,"__.SYMDEF SORTED")) + return pe_ar_parse_primary_armap_bsd_32( + dctx,m); + + else if (!strcmp(hdrname,"__.SYMDEF_64")) + return pe_ar_parse_primary_armap_bsd_64( + dctx,m); + + else if (!strcmp(hdrname,"__.SYMDEF_64 SORTED")) + return pe_ar_parse_primary_armap_bsd_64( + dctx,m); + } + + return PERK_CUSTOM_ERROR(dctx,PERK_ERR_FLOW_ERROR); +} + +perk_hidden struct ar_meta_member_info * pe_archive_member_from_offset( + struct pe_archive_meta_impl * meta, + off_t offset) +{ + intptr_t l,r,m; + off_t * offsetv; + + l = 0; + r = meta->nentries - 1; + + offsetv = meta->offsetv; + + while (l != r) { + m = (l + r) / 2; + m += (l + r) % 2; + + if (offsetv[m] > offset) { + r = --m; + } else { + l = m; + } + } + + return (offsetv[l] == offset) ? meta->memberv[l] : 0; +} + +int pe_ar_get_archive_meta( + const struct pe_driver_ctx * dctx, + const struct pe_raw_archive * archive, + struct pe_archive_meta ** meta) +{ + const char * mark; + const char * cap; + struct pe_archive_meta_impl * m; + const char * slash; + const char * ch; + const char * fldcap; + size_t nelements; + uint64_t nentries; + uint64_t nmembers; + uint64_t stblsize; + uint64_t filesize; + uint64_t namelen; + uint64_t nameoff; + uint32_t attr; + void * s_addr; + void * m_addr; + const char * s_ptr; + const char * m_ptr; + struct ar_raw_file_header * arhdr; + struct ar_raw_file_header * arlongnames; + struct ar_meta_member_info * memberp; + char * longnamep; + size_t idx; + struct ar_meta_armap_ref_32 * symrefs_32; + struct ar_meta_armap_ref_64 * symrefs_64; + struct ar_header_info * hdrinfov; + struct ar_header_info * hdrinfov_cap; + struct ar_header_info * hdrinfov_next; + struct ar_header_info hdrinfobuf[AR_STACK_VECTOR_ELEMENTS]; + + /* init */ + hdrinfov = hdrinfobuf; + hdrinfov_cap = &hdrinfobuf[AR_STACK_VECTOR_ELEMENTS]; + nelements = AR_STACK_VECTOR_ELEMENTS; + + memset(hdrinfobuf,0,sizeof(hdrinfobuf)); + + mark = archive->map_addr; + cap = &mark[archive->map_size]; + + /* preliminary validation */ + if (archive->map_size < sizeof(struct ar_raw_signature)) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_SIGNATURE); + + else if (strncmp(mark,ar_signature,sizeof(struct ar_raw_signature))) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_SIGNATURE); + + /* alloc */ + if (!(m = calloc(1,sizeof(*m)))) + return PERK_SYSTEM_ERROR(dctx); + + /* associated driver context */ + m->dctx = dctx; + + /* archive map info */ + m->armeta.r_archive.map_addr = archive->map_addr; + m->armeta.r_archive.map_size = archive->map_size; + + /* archive signature */ + m->armeta.r_signature = (struct ar_raw_signature *)mark; + m->armeta.m_signature = (struct ar_meta_signature *)ar_signature; + + /* signature only? */ + if (archive->map_size == sizeof(struct ar_raw_signature)) { + *meta = &m->armeta; + return 0; + } + + mark += sizeof(struct ar_raw_signature); + + /* only trailing null characters past the signature? */ + if (cap < &mark[sizeof(*arhdr)]) + for (ch=mark; ch<cap; ch++) + if (*ch) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_HEADER)); + + /* count entries, calculate string table size */ + for (nentries=0,stblsize=0,arlongnames=0; mark<cap; nentries++) { + arhdr = (struct ar_raw_file_header *)mark; + + /* file size */ + if ((pe_ar_read_decimal_64( + arhdr->ar_file_size, + sizeof(arhdr->ar_file_size), + &filesize)) < 0) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_HEADER)); + + mark += sizeof(struct ar_raw_file_header); + + /* stblsize, member name type */ + fldcap = &arhdr->ar_file_id[sizeof(arhdr->ar_file_id)]; + + /* sysv long names table? */ + if ((arhdr->ar_file_id[0] == '/') && (arhdr->ar_file_id[1] == '/')) { + for (ch=&arhdr->ar_file_id[2]; ch<fldcap; ch++) + if (*ch != AR_DEC_PADDING) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_HEADER)); + + if (pe_ar_read_decimal_64( + arhdr->ar_file_size, + sizeof(arhdr->ar_file_size), + &namelen) < 0) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_HEADER)); + + + /* duplicate long names member? */ + if (arlongnames) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_DUPLICATE_LONG_NAMES)); + + attr = AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_SYSV; + + stblsize++; + stblsize++; + stblsize++; + + stblsize += namelen; + + arlongnames = arhdr; + + /* the /SYM64/ string must be special cased, also below when it gets copied */ + } else if (!strncmp(arhdr->ar_file_id,"/SYM64/",7)) { + for (ch=&arhdr->ar_file_id[7]; ch<fldcap; ch++) + if (*ch != AR_DEC_PADDING) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_HEADER)); + + attr = AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_SYSV; + stblsize += 8; + + /* sysv armap member or sysv long name reference? */ + } else if (arhdr->ar_file_id[0] == '/') { + if (pe_ar_read_decimal_64( + &arhdr->ar_file_id[1], + sizeof(arhdr->ar_file_id)-1, + &nameoff) < 0) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_HEADER)); + + if (arhdr->ar_file_id[1] == AR_DEC_PADDING) { + attr = AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_SYSV; + stblsize++; + stblsize++; + } else { + attr = AR_HEADER_ATTR_NAME_REF | AR_HEADER_ATTR_SYSV; + } + + /* bsd long name reference? */ + } else if ((arhdr->ar_file_id[0] == '#') + && (arhdr->ar_file_id[1] == '1') + && (arhdr->ar_file_id[2] == '/')) { + if (pe_ar_read_decimal_64( + &arhdr->ar_file_id[3], + sizeof(arhdr->ar_file_id)-3, + &namelen) < 0) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_HEADER)); + + attr = AR_HEADER_ATTR_NAME_REF | AR_HEADER_ATTR_BSD; + + stblsize += namelen + 1; + + /* must be either a sysv short member name, or a (legacy) bsd short name */ + } else { + for (ch=arhdr->ar_file_id,slash=0; (ch<fldcap) && !slash; ch++) + if (*ch == '/') + slash = ch; + + if (slash) { + attr = AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_SYSV; + stblsize += (slash - arhdr->ar_file_id) + 1; + } else { + attr = AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_BSD; + stblsize += sizeof(arhdr->ar_file_id) + 1; + } + + for (; ch<fldcap; ) + if (*ch++ != AR_DEC_PADDING) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_HEADER)); + + } + + /* truncated data? */ + if (cap < &mark[filesize]) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_TRUNCATED_DATA)); + + /* ar member alignment */ + filesize += 1; + filesize |= 1; + filesize ^= 1; + + mark += filesize; + + /* only trailing null characters past the signature? */ + if (cap < &mark[sizeof(*arhdr)]) + for (; mark<cap; ) + if (*mark++) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_HEADER)); + + /* transient header info vector */ + if (&hdrinfov[nentries] == hdrinfov_cap) { + nelements = (nelements == AR_STACK_VECTOR_ELEMENTS) + ? (nelements << 4) : (nelements << 1); + + if (!(hdrinfov_next = calloc(nelements,sizeof(*hdrinfov)))) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_TRUNCATED_DATA)); + + for (idx=0; idx<nentries; idx++) { + hdrinfov_next[idx].phdr = hdrinfov[idx].phdr; + hdrinfov_next[idx].attr = hdrinfov[idx].attr; + }; + + if (hdrinfov != hdrinfobuf) + free(hdrinfov); + + hdrinfov = hdrinfov_next; + hdrinfov_cap = &hdrinfov_next[nelements]; + m->hdrinfov = hdrinfov; + } + + hdrinfov[nentries].phdr = arhdr; + hdrinfov[nentries].attr = attr; + } + + /* allocate name strings, member vector */ + if (!(m->namestrs = calloc(1,stblsize))) + return pe_ar_free_archive_meta_impl( + m,PERK_SYSTEM_ERROR(dctx)); + + if (!(m->offsetv = calloc(nentries+1,sizeof(*m->offsetv)))) + return pe_ar_free_archive_meta_impl( + m,PERK_SYSTEM_ERROR(dctx)); + + if (!(m->memberv = calloc(nentries+1,sizeof(*m->memberv)))) + return pe_ar_free_archive_meta_impl( + m,PERK_SYSTEM_ERROR(dctx)); + + if (!(m->members = calloc(nentries,sizeof(*m->members)))) + return pe_ar_free_archive_meta_impl( + m,PERK_SYSTEM_ERROR(dctx)); + + /* archive signature reference */ + s_addr = archive->map_addr; + s_ptr = s_addr; + + /* iterate, store meta data in library-friendly form */ + for (idx=0,longnamep=m->namestrs; idx<nentries; idx++) { + arhdr = hdrinfov[idx].phdr; + attr = hdrinfov[idx].attr; + + m_addr = arhdr; + m_ptr = m_addr; + + memberp = &m->members[idx]; + m->offsetv[idx] = m_ptr - s_ptr; + m->memberv[idx] = memberp; + + memberp->ar_file_header.ar_header_attr = attr; + + pe_ar_read_decimal_64( + arhdr->ar_time_date_stamp, + sizeof(arhdr->ar_time_date_stamp), + &memberp->ar_file_header.ar_time_date_stamp); + + pe_ar_read_decimal_32( + arhdr->ar_uid, + sizeof(arhdr->ar_uid), + &memberp->ar_file_header.ar_uid); + + pe_ar_read_decimal_32( + arhdr->ar_gid, + sizeof(arhdr->ar_gid), + &memberp->ar_file_header.ar_gid); + + pe_ar_read_octal( + arhdr->ar_file_mode, + sizeof(arhdr->ar_file_mode), + &memberp->ar_file_header.ar_file_mode); + + pe_ar_read_decimal_64( + arhdr->ar_file_size, + sizeof(arhdr->ar_file_size), + &memberp->ar_file_header.ar_file_size); + + memberp->ar_file_header.ar_member_name = longnamep; + + if (attr == (AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_SYSV)) { + if ((arhdr->ar_file_id[0] == '/') && (arhdr->ar_file_id[1] == '/')) { + *longnamep++ = '/'; + *longnamep++ = '/'; + longnamep++; + + } else if ((arhdr->ar_file_id[0] == '/') && (arhdr->ar_file_id[1] == 'S')) { + *longnamep++ = '/'; + *longnamep++ = 'S'; + *longnamep++ = 'Y'; + *longnamep++ = 'M'; + *longnamep++ = '6'; + *longnamep++ = '4'; + *longnamep++ = '/'; + longnamep++; + + } else if (arhdr->ar_file_id[0] == '/') { + *longnamep++ = '/'; + longnamep++; + + } else { + ch = arhdr->ar_file_id; + + for (; (*ch != '/'); ) + *longnamep++ = *ch++; + + longnamep++; + } + + } else if (attr == (AR_HEADER_ATTR_FILE_ID | AR_HEADER_ATTR_BSD)) { + ch = arhdr->ar_file_id; + fldcap = &ch[sizeof(arhdr->ar_file_id)]; + + for (; (ch<fldcap) && (*ch != AR_DEC_PADDING); ) + *longnamep++ = *ch++; + + longnamep++; + + } else if (attr == (AR_HEADER_ATTR_NAME_REF | AR_HEADER_ATTR_SYSV)) { + pe_ar_read_decimal_64( + &arhdr->ar_file_id[1], + sizeof(arhdr->ar_file_id) - 1, + &nameoff); + + ch = arlongnames->ar_file_id; + ch += sizeof(*arlongnames); + ch += nameoff; + + for (; *ch && (*ch != '/') && (*ch != AR_OBJ_PADDING); ) + *longnamep++ = *ch++; + + longnamep++; + + } else if (attr == (AR_HEADER_ATTR_NAME_REF | AR_HEADER_ATTR_BSD)) { + pe_ar_read_decimal_64( + &arhdr->ar_file_id[3], + sizeof(arhdr->ar_file_id) - 3, + &namelen); + + mark = arhdr->ar_file_id; + mark += sizeof(*arhdr); + + memcpy(longnamep,mark,namelen); + + longnamep += namelen; + longnamep++; + } + + /* member raw header, object size, object data */ + mark = arhdr->ar_file_id; + mark += sizeof(*arhdr); + namelen = 0; + + if (attr == (AR_HEADER_ATTR_NAME_REF | AR_HEADER_ATTR_BSD)) { + pe_ar_read_decimal_64( + &arhdr->ar_file_id[3], + sizeof(arhdr->ar_file_id)-3, + &namelen); + + namelen += 1; + namelen |= 1; + namelen ^= 1; + + mark += namelen; + }; + + memberp->ar_member_data = arhdr; + memberp->ar_object_data = (void *)mark; + memberp->ar_object_size = memberp->ar_file_header.ar_file_size - namelen; + + /* member attribute */ + memberp->ar_member_attr = pe_ar_get_member_attr(memberp); + + /* pe/coff second linker member? */ + if ((idx == 1) && (memberp->ar_member_attr == AR_MEMBER_ATTR_ARMAP)) + if (hdrinfov[0].attr & AR_HEADER_ATTR_SYSV) + if (m->members[0].ar_member_attr == AR_MEMBER_ATTR_ARMAP) + if (attr & AR_HEADER_ATTR_SYSV) + memberp->ar_member_attr = AR_MEMBER_ATTR_LINKINFO; + + /* armap member must be the first */ + if ((memberp->ar_member_attr == AR_MEMBER_ATTR_ARMAP) && (idx > 0)) { + if (m->members[0].ar_member_attr == AR_MEMBER_ATTR_ARMAP) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_DUPLICATE_ARMAP_MEMBER)); + + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_MISPLACED_ARMAP_MEMBER)); + } + } + + /* number of archive members, including internal ones */ + m->nentries = nentries; + + /* primary armap (first linker member) */ + if (pe_ar_parse_primary_armap(dctx,m) < 0) + return pe_ar_free_archive_meta_impl( + m,PERK_NESTED_ERROR(dctx)); + + for (idx=0,ch=m->symstrs; idx<m->armaps.armap_nsyms; idx++) { + m->symstrv[idx] = ch; + ch += strlen(ch); + ch++; + } + + if (m->armaps.armap_common_32.ar_member) { + symrefs_32 = m->armaps.armap_symrefs_32; + + for (idx=0; idx<m->armaps.armap_nsyms; idx++) { + if (m->armaps.armap_common_32.ar_armap_attr & AR_ARMAP_ATTR_SYSV) + symrefs_32[idx].ar_name_offset = m->symstrv[idx] - m->symstrv[0]; + + if (!pe_archive_member_from_offset(m,symrefs_32[idx].ar_member_offset)) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_MEMBER_OFFSET)); + + if (symrefs_32[idx].ar_name_offset) { + ch = &m->symstrs[symrefs_32[idx].ar_name_offset]; + + if ((ch > m->symstrv[m->armaps.armap_nsyms - 1]) || *--ch) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_NAME_OFFSET)); + } + + } + } + + if (m->armaps.armap_common_64.ar_member) { + symrefs_64 = m->armaps.armap_symrefs_64; + + for (idx=0; idx<m->armaps.armap_nsyms; idx++) { + if (m->armaps.armap_common_64.ar_armap_attr & AR_ARMAP_ATTR_SYSV) + symrefs_64[idx].ar_name_offset = m->symstrv[idx] - m->symstrv[0]; + + if (!pe_archive_member_from_offset(m,symrefs_64[idx].ar_member_offset)) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_MEMBER_OFFSET)); + + if (symrefs_64[idx].ar_name_offset) { + ch = &m->symstrs[symrefs_64[idx].ar_name_offset]; + + if ((ch > m->symstrv[m->armaps.armap_nsyms - 1]) || *--ch) + return pe_ar_free_archive_meta_impl( + m,PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_NAME_OFFSET)); + } + + } + } + + /* number of public archive members */ + for (idx=0,nmembers=0; idx<nentries; idx++) { + switch (m->memberv[idx]->ar_member_attr) { + case AR_MEMBER_ATTR_ARMAP: + case AR_MEMBER_ATTR_LINKINFO: + case AR_MEMBER_ATTR_NAMESTRS: + break; + + default: + nmembers++; + } + } + + if (m->armaps.armap_common_32.ar_member) + m->armaps.armap_common_32.ar_num_of_members = nmembers; + + if (m->armaps.armap_common_64.ar_member) + m->armaps.armap_common_64.ar_num_of_members = nmembers; + + /* pe/coff armap attributes (second linker member) */ + (void)m->armeta.a_armap_pecoff; + + /* reference to the long names member */ + if (arlongnames) + for (idx=0; idx<nentries && !m->armeta.a_arref_longnames; idx++) + if (m->memberv[idx]->ar_member_data == arlongnames) + m->armeta.a_arref_longnames = m->memberv[idx]; + + /* common binary format (information only) */ + for (idx=0,nmembers=0; idx<nentries; idx++) { + if (m->memberv[idx]->ar_member_attr == AR_MEMBER_ATTR_OBJECT) { + if (m->ofmtattr && (m->ofmtattr != m->memberv[idx]->ar_object_attr)) { + m->ofmtattr = 0; + idx = nentries; + } else if (!m->ofmtattr) { + m->ofmtattr = m->memberv[idx]->ar_object_attr; + } + } + } + + /* member vector */ + m->armeta.a_memberv = m->memberv; + + /* all done */ + if (m->hdrinfov) { + free(m->hdrinfov); + m->hdrinfov = 0; + } + + *meta = &m->armeta; + + return 0; +} + +void pe_ar_free_archive_meta(struct pe_archive_meta * meta) +{ + struct pe_archive_meta_impl * m; + + if (meta) { + m = pe_archive_meta_ictx(meta); + pe_ar_free_archive_meta_impl(m,0); + } +} diff --git a/src/arbits/pe_armap_bsd_32.c b/src/arbits/pe_armap_bsd_32.c new file mode 100644 index 0000000..fdbc3a0 --- /dev/null +++ b/src/arbits/pe_armap_bsd_32.c @@ -0,0 +1,157 @@ +/***************************************************************/ +/* perk: PE Resource Kit */ +/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.PERK. */ +/***************************************************************/ + +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <perk/perk.h> +#include <perk/perk_arbits.h> +#include "perk_ar_impl.h" +#include "perk_driver_impl.h" +#include "perk_errinfo_impl.h" +#include "perk_visibility_impl.h" + +perk_hidden int pe_ar_parse_primary_armap_bsd_32( + const struct pe_driver_ctx * dctx, + struct pe_archive_meta_impl * m) + +{ + struct ar_raw_armap_bsd_32 * armap; + struct ar_meta_member_info * memberp; + struct ar_meta_armap_common_32 *armapref; + struct ar_meta_armap_ref_32 * symrefs; + uint32_t idx; + uint32_t uref; + uint32_t attr; + uint32_t nsyms; + uint32_t nstrs; + uint32_t sizeofrefs_le; + uint32_t sizeofrefs_be; + uint32_t sizeofrefs; + uint32_t sizeofstrs; + const char * ch; + const char * cap; + unsigned char * uch; + unsigned char (*mark)[0x04]; + + armap = &m->armaps.armap_bsd_32; + memberp = m->memberv[0]; + + mark = memberp->ar_object_data; + + armap->ar_size_of_refs = mark; + uch = *mark++; + + armap->ar_first_name_offset = mark; + + sizeofrefs_le = (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + sizeofrefs_be = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + + if (sizeofrefs_le < memberp->ar_object_size - sizeof(*mark)) { + sizeofrefs = sizeofrefs_le; + attr = AR_ARMAP_ATTR_LE_32; + + } else if (sizeofrefs_be < memberp->ar_object_size - sizeof(*mark)) { + sizeofrefs = sizeofrefs_be; + attr = AR_ARMAP_ATTR_BE_32; + } else { + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_SIZE_OF_REFS); + } + + nsyms = sizeofrefs / sizeof(struct ar_raw_armap_ref_32); + mark += (sizeofrefs / sizeof(*mark)); + + armap->ar_size_of_strs = mark; + uch = *mark++; + + sizeofstrs = (attr == AR_ARMAP_ATTR_LE_32) + ? (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0] + : (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + + if (sizeofstrs > memberp->ar_object_size - 2*sizeof(*mark) - sizeofrefs) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_SIZE_OF_STRS); + + m->symstrs = (const char *)mark; + + cap = m->symstrs; + cap += sizeofstrs; + + if ((cap == m->symstrs) && nsyms) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (nsyms && !m->symstrs[0]) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + for (ch=&m->symstrs[1],nstrs=0; ch<cap; ch++) { + if (!ch[0] && !ch[-1] && (nstrs < nsyms)) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!ch[0] && ch[-1] && (nstrs < nsyms)) + nstrs++; + } + + if (nstrs != nsyms) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!(m->symstrv = calloc(nsyms + 1,sizeof(const char *)))) + return PERK_SYSTEM_ERROR(dctx); + + if (!(m->armaps.armap_symrefs_32 = calloc(nsyms + 1,sizeof(*symrefs)))) + return PERK_SYSTEM_ERROR(dctx); + + mark = armap->ar_first_name_offset; + symrefs = m->armaps.armap_symrefs_32; + + for (idx=0; idx<nsyms; idx++) { + uch = *mark++; + + uref = (attr == AR_ARMAP_ATTR_BE_32) + ? (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3] + : (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + + symrefs[idx].ar_name_offset = uref; + + uch = *mark++; + + uref = (attr == AR_ARMAP_ATTR_BE_32) + ? (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3] + : (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + + symrefs[idx].ar_member_offset = uref; + } + + armap->ar_string_table = m->symstrv; + + armapref = &m->armaps.armap_common_32; + armapref->ar_member = memberp; + armapref->ar_symrefs = symrefs; + armapref->ar_armap_bsd = armap; + armapref->ar_armap_attr = AR_ARMAP_ATTR_BSD | attr; + armapref->ar_num_of_symbols = nsyms; + armapref->ar_size_of_refs = sizeofrefs; + armapref->ar_size_of_strs = sizeofstrs; + armapref->ar_string_table = m->symstrs; + + m->armaps.armap_nsyms = nsyms; + + m->armeta.a_armap_primary.ar_armap_common_32 = armapref; + + return 0; +} diff --git a/src/arbits/pe_armap_bsd_64.c b/src/arbits/pe_armap_bsd_64.c new file mode 100644 index 0000000..fc61eb2 --- /dev/null +++ b/src/arbits/pe_armap_bsd_64.c @@ -0,0 +1,174 @@ +/***************************************************************/ +/* perk: PE Resource Kit */ +/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.PERK. */ +/***************************************************************/ + +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <perk/perk.h> +#include <perk/perk_arbits.h> +#include "perk_ar_impl.h" +#include "perk_driver_impl.h" +#include "perk_errinfo_impl.h" +#include "perk_visibility_impl.h" + +perk_hidden int pe_ar_parse_primary_armap_bsd_64( + const struct pe_driver_ctx * dctx, + struct pe_archive_meta_impl * m) +{ + struct ar_raw_armap_bsd_64 * armap; + struct ar_meta_member_info * memberp; + struct ar_meta_armap_common_64 *armapref; + struct ar_meta_armap_ref_64 * symrefs; + uint64_t idx; + uint64_t uref_hi; + uint64_t uref_lo; + uint32_t attr; + uint64_t u64_lo; + uint64_t u64_hi; + uint64_t nsyms; + uint64_t nstrs; + uint64_t sizeofrefs_le; + uint64_t sizeofrefs_be; + uint64_t sizeofrefs; + uint64_t sizeofstrs; + const char * ch; + const char * cap; + unsigned char * uch; + unsigned char (*mark)[0x08]; + + armap = &m->armaps.armap_bsd_64; + memberp = m->memberv[0]; + + mark = memberp->ar_object_data; + + armap->ar_size_of_refs = mark; + uch = *mark++; + + armap->ar_first_name_offset = mark; + + u64_lo = (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + u64_hi = (uch[7] << 24) + (uch[6] << 16) + (uch[5] << 8) + uch[4]; + + sizeofrefs_le = u64_lo + (u64_hi << 32); + + u64_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + u64_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + + sizeofrefs_be = (u64_hi << 32) + u64_lo; + + if (sizeofrefs_le < memberp->ar_object_size - sizeof(*mark)) { + sizeofrefs = sizeofrefs_le; + attr = AR_ARMAP_ATTR_LE_64; + + } else if (sizeofrefs_be < memberp->ar_object_size - sizeof(*mark)) { + sizeofrefs = sizeofrefs_be; + attr = AR_ARMAP_ATTR_BE_64; + } else { + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_SIZE_OF_REFS); + } + + nsyms = sizeofrefs / sizeof(struct ar_raw_armap_ref_64); + mark += (sizeofrefs / sizeof(*mark)); + + armap->ar_size_of_strs = mark; + uch = *mark++; + + if (attr == AR_ARMAP_ATTR_LE_64) { + u64_lo = (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + u64_hi = (uch[7] << 24) + (uch[6] << 16) + (uch[5] << 8) + uch[4]; + } else { + u64_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + u64_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + } + + sizeofstrs = u64_lo + (u64_hi << 32); + m->symstrs = (const char *)mark; + + cap = m->symstrs; + cap += sizeofstrs; + + if ((cap == m->symstrs) && nsyms) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (nsyms && !m->symstrs[0]) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + for (ch=&m->symstrs[1],nstrs=0; ch<cap; ch++) { + if (!ch[0] && !ch[-1] && (nstrs < nsyms)) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!ch[0] && ch[-1] && (nstrs < nsyms)) + nstrs++; + } + + if (nstrs != nsyms) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!(m->symstrv = calloc(nsyms + 1,sizeof(const char *)))) + return PERK_SYSTEM_ERROR(dctx); + + if (!(m->armaps.armap_symrefs_64 = calloc(nsyms + 1,sizeof(*symrefs)))) + return PERK_SYSTEM_ERROR(dctx); + + mark = armap->ar_first_name_offset; + symrefs = m->armaps.armap_symrefs_64; + + for (idx=0; idx<nsyms; idx++) { + uch = *mark++; + + if (attr == AR_ARMAP_ATTR_BE_64) { + uref_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + uref_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + } else { + uref_lo = (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + uref_hi = (uch[7] << 24) + (uch[6] << 16) + (uch[5] << 8) + uch[4]; + } + + symrefs[idx].ar_name_offset = (uref_hi << 32) + uref_lo; + + uch = *mark++; + + if (attr == AR_ARMAP_ATTR_BE_64) { + uref_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + uref_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + } else { + uref_lo = (uch[3] << 24) + (uch[2] << 16) + (uch[1] << 8) + uch[0]; + uref_hi = (uch[7] << 24) + (uch[6] << 16) + (uch[5] << 8) + uch[4]; + } + + symrefs[idx].ar_member_offset = (uref_hi << 32) + uref_lo; + } + + armap->ar_string_table = m->symstrv; + + armapref = &m->armaps.armap_common_64; + armapref->ar_member = memberp; + armapref->ar_symrefs = symrefs; + armapref->ar_armap_bsd = armap; + armapref->ar_armap_attr = AR_ARMAP_ATTR_BSD | attr; + armapref->ar_num_of_symbols = nsyms; + armapref->ar_size_of_refs = sizeofrefs; + armapref->ar_size_of_strs = sizeofstrs; + armapref->ar_string_table = m->symstrs; + + m->armaps.armap_nsyms = nsyms; + + m->armeta.a_armap_primary.ar_armap_common_64 = armapref; + + return 0; +} diff --git a/src/arbits/pe_armap_sysv_32.c b/src/arbits/pe_armap_sysv_32.c new file mode 100644 index 0000000..91fd5ec --- /dev/null +++ b/src/arbits/pe_armap_sysv_32.c @@ -0,0 +1,120 @@ +/***************************************************************/ +/* perk: PE Resource Kit */ +/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.PERK. */ +/***************************************************************/ + +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <perk/perk.h> +#include <perk/perk_arbits.h> +#include "perk_ar_impl.h" +#include "perk_driver_impl.h" +#include "perk_errinfo_impl.h" +#include "perk_visibility_impl.h" + +perk_hidden int pe_ar_parse_primary_armap_sysv_32( + const struct pe_driver_ctx * dctx, + struct pe_archive_meta_impl * m) +{ + struct ar_raw_armap_sysv_32 * armap; + struct ar_meta_member_info * memberp; + struct ar_meta_armap_common_32 *armapref; + struct ar_meta_armap_ref_32 * symrefs; + uint32_t idx; + uint32_t uref; + uint32_t nsyms; + uint32_t nstrs; + const char * ch; + const char * cap; + unsigned char * uch; + unsigned char (*mark)[0x04]; + + armap = &m->armaps.armap_sysv_32; + memberp = m->memberv[0]; + + mark = memberp->ar_object_data; + + armap->ar_num_of_syms = mark; + uch = *mark++; + + armap->ar_first_ref_offset = mark; + + nsyms = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + mark += nsyms; + + if (memberp->ar_object_size < (sizeof(*mark) + (nsyms * sizeof(*mark)))) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_NUMBER_OF_SYMS); + + m->symstrs = (const char *)mark; + + cap = memberp->ar_object_data; + cap += memberp->ar_object_size; + + if ((cap == m->symstrs) && nsyms) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (nsyms && !m->symstrs[0]) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + for (ch=&m->symstrs[1],nstrs=0; ch<cap; ch++) { + if (!ch[0] && !ch[-1] && (nstrs < nsyms)) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!ch[0] && ch[-1]) + nstrs++; + } + + if (nstrs != nsyms) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (cap[-1]) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!(m->symstrv = calloc(nsyms + 1,sizeof(const char *)))) + return PERK_SYSTEM_ERROR(dctx); + + if (!(m->armaps.armap_symrefs_32 = calloc(nsyms + 1,sizeof(*symrefs)))) + return PERK_SYSTEM_ERROR(dctx); + + mark = armap->ar_first_ref_offset; + symrefs = m->armaps.armap_symrefs_32; + + for (idx=0,uch=*mark; idx<nsyms; idx++,uch=*++mark) { + uref = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + symrefs[idx].ar_member_offset = uref; + } + + armap->ar_string_table = m->symstrv; + + armapref = &m->armaps.armap_common_32; + armapref->ar_member = memberp; + armapref->ar_symrefs = symrefs; + armapref->ar_armap_sysv = armap; + armapref->ar_armap_attr = AR_ARMAP_ATTR_SYSV | AR_ARMAP_ATTR_BE_32; + armapref->ar_num_of_symbols = nsyms; + armapref->ar_size_of_refs = nsyms * sizeof(*mark); + armapref->ar_size_of_strs = cap - m->symstrs; + armapref->ar_string_table = m->symstrs; + + m->armaps.armap_nsyms = nsyms; + + m->armeta.a_armap_primary.ar_armap_common_32 = armapref; + + return 0; +} diff --git a/src/arbits/pe_armap_sysv_64.c b/src/arbits/pe_armap_sysv_64.c new file mode 100644 index 0000000..f46a4b7 --- /dev/null +++ b/src/arbits/pe_armap_sysv_64.c @@ -0,0 +1,128 @@ +/***************************************************************/ +/* perk: PE Resource Kit */ +/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.PERK. */ +/***************************************************************/ + +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <perk/perk.h> +#include <perk/perk_arbits.h> +#include "perk_ar_impl.h" +#include "perk_driver_impl.h" +#include "perk_errinfo_impl.h" +#include "perk_visibility_impl.h" + +perk_hidden int pe_ar_parse_primary_armap_sysv_64( + const struct pe_driver_ctx * dctx, + struct pe_archive_meta_impl * m) +{ + struct ar_raw_armap_sysv_64 * armap; + struct ar_meta_member_info * memberp; + struct ar_meta_armap_common_64 *armapref; + struct ar_meta_armap_ref_64 * symrefs; + uint64_t idx; + uint64_t uref_hi; + uint64_t uref_lo; + uint64_t nsyms_hi; + uint64_t nsyms_lo; + uint64_t nsyms; + uint64_t nstrs; + const char * ch; + const char * cap; + unsigned char * uch; + unsigned char (*mark)[0x08]; + + armap = &m->armaps.armap_sysv_64; + memberp = m->memberv[0]; + + mark = memberp->ar_object_data; + + armap->ar_num_of_syms = mark; + uch = *mark++; + + armap->ar_first_ref_offset = mark; + + nsyms_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + nsyms_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + + nsyms = (nsyms_hi << 32) + nsyms_lo; + mark += nsyms; + + if (memberp->ar_object_size < (sizeof(*mark) + (nsyms * sizeof(*mark)))) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_NUMBER_OF_SYMS); + + m->symstrs = (const char *)mark; + + cap = memberp->ar_object_data; + cap += memberp->ar_object_size; + + if ((cap == m->symstrs) && nsyms) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (nsyms && !m->symstrs[0]) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + for (ch=&m->symstrs[1],nstrs=0; ch<cap; ch++) { + if (!ch[0] && !ch[-1] && (nstrs < nsyms)) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!ch[0] && ch[-1]) + nstrs++; + } + + if (nstrs != nsyms) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (cap[-1]) + return PERK_CUSTOM_ERROR( + dctx, + PERK_ERR_AR_INVALID_ARMAP_STRING_TABLE); + + if (!(m->symstrv = calloc(nsyms + 1,sizeof(const char *)))) + return PERK_SYSTEM_ERROR(dctx); + + if (!(m->armaps.armap_symrefs_64 = calloc(nsyms + 1,sizeof(*symrefs)))) + return PERK_SYSTEM_ERROR(dctx); + + mark = armap->ar_first_ref_offset; + symrefs = m->armaps.armap_symrefs_64; + + for (idx=0,uch=*mark; idx<nsyms; idx++,uch=*++mark) { + uref_hi = (uch[0] << 24) + (uch[1] << 16) + (uch[2] << 8) + uch[3]; + uref_lo = (uch[4] << 24) + (uch[5] << 16) + (uch[6] << 8) + uch[7]; + + symrefs[idx].ar_member_offset = (uref_hi << 32) + uref_lo; + } + + armap->ar_string_table = m->symstrv; + + armapref = &m->armaps.armap_common_64; + armapref->ar_member = memberp; + armapref->ar_symrefs = symrefs; + armapref->ar_armap_sysv = armap; + armapref->ar_armap_attr = AR_ARMAP_ATTR_SYSV | AR_ARMAP_ATTR_BE_64; + armapref->ar_num_of_symbols = nsyms; + armapref->ar_size_of_refs = nsyms * sizeof(*mark); + armapref->ar_size_of_strs = cap - m->symstrs; + armapref->ar_string_table = m->symstrs; + + m->armaps.armap_nsyms = nsyms; + + m->armeta.a_armap_primary.ar_armap_common_64 = armapref; + + return 0; +} diff --git a/src/internal/perk_ar_impl.h b/src/internal/perk_ar_impl.h new file mode 100644 index 0000000..323dc56 --- /dev/null +++ b/src/internal/perk_ar_impl.h @@ -0,0 +1,100 @@ +/***************************************************************/ +/* perk: PE Resource Kit */ +/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */ +/* Released under GPLv2 and GPLv3; see COPYING.PERK. */ +/***************************************************************/ + +#ifndef SLIBTOOL_AR_IMPL_H +#define SLIBTOOL_AR_IMPL_H + +#include "argv/argv.h" +#include <perk/perk.h> +#include <perk/perk_arbits.h> + +/* decimal values in archive header are right padded with ascii spaces */ +#define AR_DEC_PADDING (0x20) + +/* archive file members are right padded as needed with ascii newline */ +#define AR_OBJ_PADDING (0x0A) + +/* initial number of elements in the transient, on-stack vector */ +# define AR_STACK_VECTOR_ELEMENTS (0x200) + +extern const struct argv_option pe_ar_options[]; + +struct ar_armaps_impl { + struct ar_meta_armap_ref_32 * armap_symrefs_32; + struct ar_meta_armap_ref_64 * armap_symrefs_64; + struct ar_raw_armap_bsd_32 armap_bsd_32; + struct ar_raw_armap_bsd_64 armap_bsd_64; + struct ar_raw_armap_sysv_32 armap_sysv_32; + struct ar_raw_armap_sysv_64 armap_sysv_64; + struct ar_meta_armap_common_32 armap_common_32; + struct ar_meta_armap_common_64 armap_common_64; + uint64_t armap_nsyms; +}; + +struct pe_archive_meta_impl { + const struct pe_driver_ctx * dctx; + struct pe_archive_ctx * actx; + size_t ofmtattr; + size_t nentries; + void * hdrinfov; + char * namestrs; + const char * symstrs; + const char ** symstrv; + const char ** mapstrv; + off_t * offsetv; + struct ar_meta_symbol_info * syminfo; + struct ar_meta_symbol_info ** syminfv; + struct ar_meta_member_info ** memberv; + struct ar_meta_member_info * members; + struct ar_armaps_impl armaps; + struct pe_txtfile_ctx * nminfo; + struct pe_archive_meta armeta; +}; + +struct ar_meta_member_info * pe_archive_member_from_offset( + struct pe_archive_meta_impl * meta, + off_t offset); + +int pe_ar_parse_primary_armap_bsd_32( + const struct pe_driver_ctx * dctx, + struct pe_archive_meta_impl * m); + +int pe_ar_parse_primary_armap_bsd_64( + const struct pe_driver_ctx * dctx, + struct pe_archive_meta_impl * m); + +int pe_ar_parse_primary_armap_sysv_32( + const struct pe_driver_ctx * dctx, + struct pe_archive_meta_impl * m); + +int pe_ar_parse_primary_armap_sysv_64( + const struct pe_driver_ctx * dctx, + struct pe_archive_meta_impl * m); + +int pe_update_mapstrv( + const struct pe_driver_ctx * dctx, + struct pe_archive_meta_impl * m); + +int pe_ar_update_syminfo( + struct pe_archive_ctx * actx); + +int pe_ar_update_syminfo_ex( + struct pe_archive_ctx * actx, + int fdout); + +static inline struct pe_archive_meta_impl * pe_archive_meta_ictx(const struct pe_archive_meta * meta) +{ + uintptr_t addr; + + if (meta) { + addr = (uintptr_t)meta - offsetof(struct pe_archive_meta_impl,armeta); + return (struct pe_archive_meta_impl *)addr; + } + + return 0; +} + +#endif |