summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/perk/perk.h52
-rw-r--r--include/perk/perk_output.h7
-rw-r--r--project/common.mk4
-rw-r--r--project/extras.mk6
-rw-r--r--project/headers.mk1
-rw-r--r--project/tree.mk1
-rw-r--r--src/arbits/output/pe_ar_list_members.c388
-rw-r--r--src/arbits/output/pe_ar_print_members.c181
-rw-r--r--src/cmds/pe_cmd_ar.c134
-rw-r--r--src/cmds/pe_cmd_perk.c50
-rw-r--r--src/driver/pe_amain.c27
-rw-r--r--src/driver/pe_driver_ctx.c114
-rw-r--r--src/driver/pe_unit_ctx.c141
-rw-r--r--src/internal/perk_ar_impl.h52
-rw-r--r--src/internal/perk_driver_impl.h16
-rw-r--r--src/internal/perk_synopsis_impl.h46
-rw-r--r--src/internal/perk_visibility_impl.h16
-rw-r--r--src/output/pe_output_error.c15
-rw-r--r--src/skin/pe_skin_ar.c35
-rw-r--r--src/skin/pe_skin_default.c2
20 files changed, 1197 insertions, 91 deletions
diff --git a/include/perk/perk.h b/include/perk/perk.h
index 312e145..5c057da 100644
--- a/include/perk/perk.h
+++ b/include/perk/perk.h
@@ -35,13 +35,32 @@ extern "C" {
#define PERK_DRIVER_VERSION 0x0010
#define PERK_DRIVER_DRY_RUN 0x0020
+#define PERK_DRIVER_MAP_WRITE_ACCESS 0x0040
#define PERK_DRIVER_ANNOTATE_ALWAYS 0x1000
#define PERK_DRIVER_ANNOTATE_NEVER 0x2000
#define PERK_DRIVER_ANNOTATE_FULL 0x4000
-/* unit action flags */
-#define PERK_ACTION_MAP_READWRITE 0x0001
+#define PERK_DRIVER_AR_STRICT_PE 0x10000
+#define PERK_DRIVER_AR_STRICT_PE_ARCH 0x20000
+#define PERK_DRIVER_AR_OBJECT_VECTOR 0x40000
+#define PERK_DRIVER_AR_LIST_MEMBERS 0x80000 /* [-t] */
+
+#define PERK_DRIVER_AR_SUPPRESS_MESSAGE 0X100000 /* [-c] */
+#define PERK_DRIVER_AR_VERBOSE_OUTPUT 0X200000 /* [-v] */
+#define PERK_DRIVER_AR_UPDATE_SYMTBL 0X400000 /* [-s] */
+#define PERK_DRIVER_AR_UPDATE_MEMBERS 0x800000 /* [-u] */
+#define PERK_DRIVER_AR_PRESERVE_NEWER 0X800000 /* [-C] */
+
+#define PERK_DRIVER_AR_DELETE_MEMBERS 0X1000000 /* [-d] */
+#define PERK_DRIVER_AR_APPEND_MEMBERS 0X2000000 /* [-q] */
+#define PERK_DRIVER_AR_REPLACE_MEMBERS 0X4000000 /* [-r] */
+#define PERK_DRIVER_AR_EXTRACT_MEMBERS 0X8000000 /* [-x] */
+
+#define PERK_DRIVER_AR_MOVE_MEMBERS 0X10000000 /* [-m] */
+#define PERK_DRIVER_AR_POSITION_AFTER 0X20000000 /* [-a] */
+#define PERK_DRIVER_AR_POSITION_BEFORE 0X40000000 /* [-b] */
+#define PERK_DRIVER_AR_PRINT_MEMBERS 0X80000000 /* [-p] */
/* error flags */
#define PERK_ERROR_TOP_LEVEL 0x0001
@@ -83,12 +102,24 @@ enum pe_custom_error {
PERK_ERR_AR_DLUNIT_NOT_SPECIFIED,
PERK_ERR_AR_OUTPUT_NOT_SPECIFIED,
PERK_ERR_AR_OUTPUT_NOT_APPLICABLE,
+ PERK_ERR_AR_NON_ARCHIVE_IMAGE,
+ PERK_ERR_AR_NON_PE_MEMBERS,
+ PERK_ERR_AR_MIXED_PE_MEMBERS,
+ PERK_ERR_AR_NESTED_ARCHIVE,
+ PERK_ERR_AR_MISSING_ACTION,
+ PERK_ERR_AR_MULTIPLE_ACTIONS,
+ PERK_ERR_AR_MULTIPLE_ANCHORS,
+ PERK_ERR_AR_INVALID_ANCHORS,
+ PERK_ERR_AR_MISSING_ANCHOR,
+ PERK_ERR_AR_NULL_POSNAME,
+ PERK_ERR_AR_NULL_ARNAME,
PERK_ERR_CAP,
};
enum pe_cmd {
PERK_CMD_DEFAULT,
PERK_CMD_PERK,
+ PERK_CMD_AR,
PERK_CMD_CAP,
};
@@ -207,7 +238,6 @@ struct pe_error_info {
struct pe_common_ctx {
uint64_t drvflags;
- uint64_t actflags;
uint64_t fmtflags;
uint64_t hdrdump;
enum pe_cmd cmd;
@@ -224,8 +254,9 @@ struct pe_driver_ctx {
struct pe_unit_ctx {
const char * const * path;
- const struct pe_raw_image * map;
const struct pe_image_meta * meta;
+ const struct pe_archive_meta * armeta;
+ const struct pe_image_meta ** objmeta;
void * any;
};
@@ -251,6 +282,8 @@ perk_api int pe_lib_set_driver_fdctx (struct pe_driver_ctx *, const s
/* cmd api */
perk_api int pe_cmd_perk (const struct pe_driver_ctx *, const char *);
+perk_api int pe_cmd_ar (const struct pe_driver_ctx *, uint64_t,
+ const char *, const char *, const char **);
/* utility api */
perk_api int pe_main (char **, char **, const struct pe_fd_ctx *);
@@ -320,6 +353,17 @@ perk_api int pe_read_coff_symbol (const struct pe_raw_coff_symbol
perk_api int pe_read_import_lookup (const unsigned char *, struct pe_meta_import_lookup *,
uint32_t magic);
+/* archiver api */
+perk_api int pe_ar_get_archive_meta (const struct pe_driver_ctx *,
+ const struct pe_raw_archive *,
+ struct pe_archive_meta **);
+
+perk_api void pe_ar_free_archive_meta (struct pe_archive_meta *);
+
+/* archiver utility api */
+perk_api int pe_ar_list_members (const struct pe_archive_meta *, const char **);
+perk_api int pe_ar_print_members (const struct pe_archive_meta *, const char **);
+
/* package info */
perk_api const struct pe_source_version * pe_source_version(void);
diff --git a/include/perk/perk_output.h b/include/perk/perk_output.h
index d6e2529..313f609 100644
--- a/include/perk/perk_output.h
+++ b/include/perk/perk_output.h
@@ -35,7 +35,12 @@
#define PERK_PRETTY_TABLE PERK_PRETTY(0x00000002)
#define PERK_PRETTY_READOBJ PERK_PRETTY(0x00000004)
#define PERK_PRETTY_OBJDUMP PERK_PRETTY(0x00000008)
+
#define PERK_PRETTY_YAML PERK_PRETTY(0x00000010)
-#define PERK_PRETTY_DLLTOOL PERK_PRETTY(0x00000020)
+#define PERK_PRETTY_POSIX PERK_PRETTY(0x00000020)
+#define PERK_PRETTY_HEXDATA PERK_PRETTY(0x00000040)
+#define PERK_PRETTY_VERBOSE PERK_PRETTY(0x00000080)
+
+#define PERK_PRETTY_DLLTOOL PERK_PRETTY(0x10000000)
#endif
diff --git a/project/common.mk b/project/common.mk
index 0ad5c41..3fb1971 100644
--- a/project/common.mk
+++ b/project/common.mk
@@ -4,6 +4,9 @@ API_SRCS = \
src/arbits/pe_armap_bsd_64.c \
src/arbits/pe_armap_sysv_32.c \
src/arbits/pe_armap_sysv_64.c \
+ src/arbits/output/pe_ar_list_members.c \
+ src/arbits/output/pe_ar_print_members.c \
+ src/cmds/pe_cmd_ar.c \
src/cmds/pe_cmd_perk.c \
src/driver/pe_amain.c \
src/driver/pe_driver_ctx.c \
@@ -37,6 +40,7 @@ API_SRCS = \
src/reader/pe_read_optional_header.c \
src/reader/pe_read_section_header.c \
src/skin/pe_skin_default.c \
+ src/skin/pe_skin_ar.c \
src/skin/pe_skin_perk.c \
INTERNAL_SRCS = \
diff --git a/project/extras.mk b/project/extras.mk
index cae7dcc..b4af0c3 100644
--- a/project/extras.mk
+++ b/project/extras.mk
@@ -6,3 +6,9 @@ CFLAGS_CONFIG += $(CFLAGS_ATTR_VISIBILITY_HIDDEN)
src/driver/pe_driver_ctx.o: version.tag
src/driver/pe_driver_ctx.lo: version.tag
+
+install-app-extras:
+ mkdir -p $(DESTDIR)$(BINDIR)
+ rm -f bin/$(NICKNAME)-ar$(OS_APP_SUFFIX).tmp
+ ln -s ./$(NICKNAME)$(OS_APP_SUFFIX) bin/$(NICKNAME)-ar$(OS_APP_SUFFIX).tmp
+ mv bin/$(NICKNAME)-ar$(OS_APP_SUFFIX).tmp $(DESTDIR)$(BINDIR)/$(NICKNAME)-ar$(OS_APP_SUFFIX)
diff --git a/project/headers.mk b/project/headers.mk
index fc1f132..e87271b 100644
--- a/project/headers.mk
+++ b/project/headers.mk
@@ -16,6 +16,7 @@ INTERNAL_HEADERS = \
$(SOURCE_DIR)/src/internal/$(PACKAGE)_errinfo_impl.h \
$(SOURCE_DIR)/src/internal/$(PACKAGE)_hexdump_impl.h \
$(SOURCE_DIR)/src/internal/$(PACKAGE)_reader_impl.h \
+ $(SOURCE_DIR)/src/internal/$(PACKAGE)_synopsis_impl.h \
$(SOURCE_DIR)/src/internal/$(PACKAGE)_visibility_impl.h \
ALL_HEADERS = $(API_HEADERS) $(INTERNAL_HEADERS)
diff --git a/project/tree.mk b/project/tree.mk
index 0fa72e3..63d4c97 100644
--- a/project/tree.mk
+++ b/project/tree.mk
@@ -1,6 +1,7 @@
tree.tag:
mkdir -p src
mkdir -p src/arbits
+ mkdir -p src/arbits/output
mkdir -p src/cmds
mkdir -p src/driver
mkdir -p src/hexdump
diff --git a/src/arbits/output/pe_ar_list_members.c b/src/arbits/output/pe_ar_list_members.c
new file mode 100644
index 0000000..3e0aaa4
--- /dev/null
+++ b/src/arbits/output/pe_ar_list_members.c
@@ -0,0 +1,388 @@
+/***************************************************************/
+/* perk: PE Resource Kit */
+/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */
+/* Released under GPLv2 and GPLv3; see COPYING.PERK. */
+/***************************************************************/
+
+#include <time.h>
+#include <locale.h>
+#include <inttypes.h>
+#include <perk/perk.h>
+#include <perk/perk_output.h>
+#include "perk_driver_impl.h"
+#include "perk_dprintf_impl.h"
+#include "perk_errinfo_impl.h"
+#include "perk_ar_impl.h"
+
+#define PERK_PRETTY_FLAGS (PERK_PRETTY_YAML \
+ | PERK_PRETTY_POSIX \
+ | PERK_PRETTY_HEXDATA)
+
+#define PPRIU64 "%"PRIu64
+
+const char pe_ar_perm_strs[8][4] = {
+ {'-','-','-','\0'},
+ {'-','-','x','\0'},
+ {'-','w','-','\0'},
+ {'-','w','x','\0'},
+ {'r','-','-','\0'},
+ {'r','-','x','\0'},
+ {'r','w','-','\0'},
+ {'r','w','x','\0'}
+};
+
+static unsigned pe_ar_output_decimal_len_from_val(size_t val, unsigned min)
+{
+ unsigned ret;
+
+ for (ret=0; val; ret++)
+ val /= 10;
+
+ return (ret > min) ? ret : min;
+}
+
+static void pe_ar_member_not_found(
+ const struct pe_archive_meta * meta,
+ const char * name)
+{
+ const struct pe_driver_ctx * dctx;
+ int fderr;
+
+ dctx = (pe_archive_meta_ictx(meta))->dctx;
+
+ if ((fderr = pe_driver_fderr(dctx)) >= 0)
+ pe_dprintf(
+ fderr,
+ "%s: error: member '%s' was not found in the current archive\n",
+ dctx->program,name);
+}
+
+static struct ar_meta_member_info * pe_ar_get_member_info(
+ const struct pe_archive_meta * meta,
+ const char * name)
+{
+ struct ar_meta_member_info ** memberp;
+ const char * slash;
+
+ if ((slash = strrchr(name,'/')))
+ name = ++slash;
+
+ for (memberp=meta->a_memberv; *memberp; memberp++)
+ if (!strcmp(memberp[0]->ar_file_header.ar_member_name,name))
+ return *memberp;
+
+ pe_ar_member_not_found(meta,name);
+
+ return 0;
+}
+
+static int pe_ar_member_in_members(
+ struct ar_meta_member_info * minfo,
+ const char ** members)
+{
+ const char ** pname;
+
+ for (pname=members; *pname; pname++)
+ if (!strcmp(*pname,minfo->ar_file_header.ar_member_name))
+ return 1;
+
+ return 0;
+}
+
+static int pe_ar_list_one_member_posix(
+ int fdout,
+ struct ar_meta_member_info * minfo)
+{
+ return pe_dprintf(
+ fdout,"%s\n",
+ minfo->ar_file_header.ar_member_name);
+}
+
+static int pe_ar_list_one_member_posix_verbose(
+ int fdout,
+ struct ar_meta_member_info * minfo,
+ const char * fmtstr,
+ locale_t arlocale)
+{
+ unsigned ownerbits;
+ unsigned groupbits;
+ unsigned worldbits;
+ time_t artimeval;
+ struct tm artimeloc;
+ char artimestr[64] = {0};
+
+ ownerbits = (minfo->ar_file_header.ar_file_mode & 0700) >> 6;
+ groupbits = (minfo->ar_file_header.ar_file_mode & 0070) >> 3;
+ worldbits = (minfo->ar_file_header.ar_file_mode & 0007);
+ artimeval = minfo->ar_file_header.ar_time_date_stamp;
+
+ if (localtime_r(&artimeval,&artimeloc))
+ strftime_l(
+ artimestr,sizeof(artimestr),
+ "%b %e %H:%M %Y",&artimeloc,
+ arlocale);
+
+ return pe_dprintf(
+ fdout,fmtstr,
+ pe_ar_perm_strs[ownerbits],
+ pe_ar_perm_strs[groupbits],
+ pe_ar_perm_strs[worldbits],
+ minfo->ar_file_header.ar_uid,
+ minfo->ar_file_header.ar_gid,
+ minfo->ar_object_size,
+ artimestr,
+ minfo->ar_file_header.ar_member_name);
+}
+
+static int pe_ar_list_members_posix(
+ const struct pe_driver_ctx * dctx,
+ const struct pe_archive_meta * meta,
+ const struct pe_fd_ctx * fdctx,
+ const char ** members)
+{
+ struct ar_meta_member_info ** memberp;
+ struct ar_meta_member_info * minfo;
+ const char ** pname;
+ int fdout;
+ size_t testval;
+ size_t sizelen;
+ size_t uidlen;
+ size_t gidlen;
+ locale_t arloc;
+ char fmtstr[64];
+
+ fdout = fdctx->fdout;
+ arloc = 0;
+
+ if (dctx->cctx->fmtflags & PERK_PRETTY_VERBOSE) {
+ for (sizelen=0,memberp=meta->a_memberv; *memberp; memberp++)
+ if ((testval = memberp[0]->ar_object_size) > sizelen)
+ if (!members || pe_ar_member_in_members(*memberp,members))
+ sizelen = testval;
+
+ for (uidlen=0,memberp=meta->a_memberv; *memberp; memberp++)
+ if ((testval = memberp[0]->ar_file_header.ar_uid) > uidlen)
+ if (!members || pe_ar_member_in_members(*memberp,members))
+ uidlen = testval;
+
+ for (gidlen=0,memberp=meta->a_memberv; *memberp; memberp++)
+ if ((testval = memberp[0]->ar_file_header.ar_gid) > gidlen)
+ if (!members || pe_ar_member_in_members(*memberp,members))
+ gidlen = testval;
+
+ sizelen = pe_ar_output_decimal_len_from_val(sizelen,6);
+ uidlen = pe_ar_output_decimal_len_from_val(uidlen,1);
+ gidlen = pe_ar_output_decimal_len_from_val(gidlen,1);
+ arloc = newlocale(LC_ALL,setlocale(LC_ALL,0),0);
+
+ sprintf(
+ fmtstr,
+ "%%s%%s%%s "
+ "%%" PPRIU64 "u"
+ "/%%-" PPRIU64 "u "
+ "%%" PPRIU64 "u "
+ "%%s "
+ "%%s\n",
+ uidlen,
+ gidlen,
+ sizelen);
+ }
+
+ for (pname=members; pname && *pname; pname++) {
+ if ((minfo = pe_ar_get_member_info(meta,*pname))) {
+ switch (minfo->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (arloc) {
+ if (pe_ar_list_one_member_posix_verbose(
+ fdout,minfo,fmtstr,arloc) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ } else {
+ if (pe_ar_list_one_member_posix(
+ fdout,minfo) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ }
+ }
+ }
+ }
+
+ for (memberp=meta->a_memberv; !members && *memberp; memberp++) {
+ switch ((*memberp)->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (arloc) {
+ if (pe_ar_list_one_member_posix_verbose(
+ fdout,*memberp,fmtstr,arloc) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ } else {
+ if (pe_ar_list_one_member_posix(
+ fdout,*memberp) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ }
+ }
+ }
+
+ if (arloc)
+ freelocale(arloc);
+
+ return 0;
+}
+
+static int pe_ar_list_one_member_yaml(
+ int fdout,
+ struct ar_meta_member_info * minfo)
+{
+ return pe_dprintf(
+ fdout,
+ " - [ member: %s ]\n",
+ minfo->ar_file_header.ar_member_name);
+}
+
+static int pe_ar_list_one_member_yaml_verbose(
+ int fdout,
+ struct ar_meta_member_info * minfo,
+ locale_t arlocale)
+{
+ time_t artimeval;
+ struct tm artimeloc;
+ char artimestr[64] = {0};
+
+ artimeval = minfo->ar_file_header.ar_time_date_stamp;
+
+ if (localtime_r(&artimeval,&artimeloc))
+ strftime_l(
+ artimestr,sizeof(artimestr),
+ "%Y/%m/%d @ %H:%M",&artimeloc,
+ arlocale);
+
+ return pe_dprintf(
+ fdout,
+ " - Member:\n"
+ " - [ name: " "%s" " ]\n"
+ " - [ timestamp: " "%s" " ]\n"
+ " - [ filesize: " PPRIU64 " ]\n"
+ " - [ uid: " "%d" " ]\n"
+ " - [ gid: " "%d" " ]\n"
+ " - [ mode: " "%d" " ]\n\n",
+ minfo->ar_file_header.ar_member_name,
+ artimestr,
+ minfo->ar_object_size,
+ minfo->ar_file_header.ar_uid,
+ minfo->ar_file_header.ar_gid,
+ minfo->ar_file_header.ar_file_mode);
+}
+
+static int pe_ar_list_members_yaml(
+ const struct pe_driver_ctx * dctx,
+ const struct pe_archive_meta * meta,
+ const struct pe_fd_ctx * fdctx,
+ const char ** members)
+{
+ struct ar_meta_member_info ** memberp;
+ struct ar_meta_member_info * minfo;
+ const char ** pname;
+ int fdout;
+ locale_t arloc;
+
+ fdout = fdctx->fdout;
+ arloc = 0;
+
+ if (dctx->cctx->fmtflags & PERK_PRETTY_VERBOSE) {
+ arloc = newlocale(LC_ALL,setlocale(LC_ALL,0),0);
+ }
+
+ if (pe_dprintf(fdctx->fdout," - Members:\n") < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+
+ for (pname=members; pname && *pname; pname++) {
+ if ((minfo = pe_ar_get_member_info(meta,*pname))) {
+ switch (minfo->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (arloc) {
+ if (pe_ar_list_one_member_yaml_verbose(
+ fdout,minfo,arloc) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ } else {
+ if (pe_ar_list_one_member_yaml(
+ fdout,minfo) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ }
+ }
+ }
+ }
+
+ for (memberp=meta->a_memberv; !members && *memberp; memberp++) {
+ switch ((*memberp)->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (arloc) {
+ if (pe_ar_list_one_member_yaml_verbose(
+ fdout,*memberp,arloc) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ } else {
+ if (pe_ar_list_one_member_yaml(
+ fdout,*memberp) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+ }
+ }
+ }
+
+ if (arloc)
+ freelocale(arloc);
+
+ return 0;
+}
+
+int pe_ar_list_members(const struct pe_archive_meta * meta, const char ** members)
+{
+ const struct pe_driver_ctx * dctx;
+ struct pe_fd_ctx fdctx;
+
+ const char ** pmember;
+
+ dctx = (pe_archive_meta_ictx(meta))->dctx;
+
+ if (pe_lib_get_driver_fdctx(dctx,&fdctx) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ if (members && !members[0])
+ members = 0;
+
+ if (!meta->a_memberv) {
+ for (pmember=members; pmember && *pmember; pmember++)
+ pe_ar_member_not_found(meta,*pmember);
+
+ return members ? 1 : 0;
+ }
+
+ switch (dctx->cctx->fmtflags & PERK_PRETTY_FLAGS) {
+ case PERK_PRETTY_YAML:
+ return pe_ar_list_members_yaml(
+ dctx,meta,&fdctx,members);
+
+ case PERK_PRETTY_POSIX:
+ return pe_ar_list_members_posix(
+ dctx,meta,&fdctx,members);
+
+ default:
+ return pe_ar_list_members_posix(
+ dctx,meta,&fdctx,members);
+ }
+}
diff --git a/src/arbits/output/pe_ar_print_members.c b/src/arbits/output/pe_ar_print_members.c
new file mode 100644
index 0000000..3bdf539
--- /dev/null
+++ b/src/arbits/output/pe_ar_print_members.c
@@ -0,0 +1,181 @@
+/***************************************************************/
+/* perk: PE Resource Kit */
+/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */
+/* Released under GPLv2 and GPLv3; see COPYING.PERK. */
+/***************************************************************/
+
+#include <perk/perk.h>
+#include <perk/perk_output.h>
+#include "perk_driver_impl.h"
+#include "perk_dprintf_impl.h"
+#include "perk_errinfo_impl.h"
+#include "perk_ar_impl.h"
+
+static void pe_ar_member_not_found(
+ const struct pe_archive_meta * meta,
+ const char * name)
+{
+ const struct pe_driver_ctx * dctx;
+ int fderr;
+
+ dctx = (pe_archive_meta_ictx(meta))->dctx;
+
+ if ((fderr = pe_driver_fderr(dctx)) >= 0)
+ pe_dprintf(
+ fderr,
+ "%s: error: member '%s' was not found in the current archive\n",
+ dctx->program,name);
+}
+
+static struct ar_meta_member_info * pe_ar_get_member_info(
+ const struct pe_archive_meta * meta,
+ const char * name)
+{
+ struct ar_meta_member_info ** memberp;
+ const char * slash;
+
+ if ((slash = strrchr(name,'/')))
+ name = ++slash;
+
+ for (memberp=meta->a_memberv; *memberp; memberp++)
+ if (!strcmp(memberp[0]->ar_file_header.ar_member_name,name))
+ return *memberp;
+
+ pe_ar_member_not_found(meta,name);
+
+ return 0;
+}
+
+static int pe_ar_print_one_member_posix(
+ int fdout,
+ const struct pe_driver_ctx * dctx,
+ struct ar_meta_member_info * minfo)
+{
+ ssize_t ret;
+ size_t cnt;
+ const char * ch;
+
+ ret = 0;
+ cnt = minfo->ar_file_header.ar_file_size;
+ ch = minfo->ar_object_data;
+
+ for (; cnt && ret>=0; ) {
+ ch += ret;
+ cnt -= ret;
+
+ ret = write(fdout,ch,cnt);
+
+ while ((ret < 0) && (errno == EINTR))
+ ret = write(fdout,ch,cnt);
+ }
+
+ if (ret < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+
+ return 0;
+}
+
+static int pe_ar_print_one_member_posix_verbose(
+ int fdout,
+ const struct pe_driver_ctx * dctx,
+ struct ar_meta_member_info * minfo,
+ const char * aname)
+{
+ const char * name = aname ? aname : minfo->ar_file_header.ar_member_name;
+
+ if (pe_dprintf(fdout,"\n<%s>\n\n",name) < 0)
+ return PERK_SYSTEM_ERROR(dctx);
+
+ if (pe_ar_print_one_member_posix(fdout,dctx,minfo) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ return 0;
+}
+
+static int pe_ar_print_members_posix(
+ const struct pe_driver_ctx * dctx,
+ const struct pe_archive_meta * meta,
+ const struct pe_fd_ctx * fdctx,
+ const char ** members)
+{
+ struct ar_meta_member_info ** memberp;
+ struct ar_meta_member_info * minfo;
+ const char ** pname;
+ int fdout;
+ bool fname;
+
+ fdout = fdctx->fdout;
+ fname = dctx->cctx->fmtflags & PERK_PRETTY_VERBOSE;
+
+ for (pname=members; pname && *pname; pname++) {
+ if ((minfo = pe_ar_get_member_info(meta,*pname))) {
+ switch (minfo->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (fname) {
+ if (pe_ar_print_one_member_posix_verbose(
+ fdout,dctx,minfo,*pname) < 0)
+ return PERK_NESTED_ERROR(dctx);
+ } else {
+ if (pe_ar_print_one_member_posix(
+ fdout,dctx,minfo) < 0)
+ return PERK_NESTED_ERROR(dctx);
+ }
+ }
+ }
+ }
+
+ for (memberp=meta->a_memberv; !members && *memberp; memberp++) {
+ switch ((*memberp)->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_LINKINFO:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ break;
+
+ default:
+ if (fname) {
+ if (pe_ar_print_one_member_posix_verbose(
+ fdout,dctx,*memberp,0) < 0)
+ return PERK_NESTED_ERROR(dctx);
+ } else {
+ if (pe_ar_print_one_member_posix(
+ fdout,dctx,*memberp) < 0)
+ return PERK_NESTED_ERROR(dctx);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int pe_ar_print_members(const struct pe_archive_meta * meta, const char ** members)
+{
+ const struct pe_driver_ctx * dctx;
+ struct pe_fd_ctx fdctx;
+
+ const char ** pmember;
+
+ dctx = (pe_archive_meta_ictx(meta))->dctx;
+
+ if (pe_lib_get_driver_fdctx(dctx,&fdctx) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ if (members && !members[0])
+ members = 0;
+
+ if (!meta->a_memberv) {
+ for (pmember=members; pmember && *pmember; pmember++)
+ pe_ar_member_not_found(meta,*pmember);
+
+ return members ? 1 : 0;
+ }
+
+ if (pe_ar_print_members_posix(dctx,meta,&fdctx,members) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ return 0;
+}
diff --git a/src/cmds/pe_cmd_ar.c b/src/cmds/pe_cmd_ar.c
new file mode 100644
index 0000000..97e94c7
--- /dev/null
+++ b/src/cmds/pe_cmd_ar.c
@@ -0,0 +1,134 @@
+/***************************************************************/
+/* perk: PE Resource Kit */
+/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */
+/* Released under GPLv2 and GPLv3; see COPYING.PERK. */
+/***************************************************************/
+
+#include <perk/perk.h>
+#include "perk_driver_impl.h"
+#include "perk_errinfo_impl.h"
+#include "perk_ar_impl.h"
+
+int pe_ar_list_members(const struct pe_archive_meta *, const char **);
+
+static int pe_cmd_ar_perform_unit_actions(
+ const struct pe_driver_ctx * dctx,
+ const char * arname,
+ const char ** members,
+ uint64_t action)
+{
+ struct pe_unit_ctx * arctx = 0;
+ int (*pe_ar_fn)(const struct pe_archive_meta *,const char **);
+
+ if (action == PERK_DRIVER_AR_LIST_MEMBERS) {
+ pe_ar_fn = pe_ar_list_members;
+
+ } else if (action == PERK_DRIVER_AR_PRINT_MEMBERS) {
+ pe_ar_fn = pe_ar_print_members;
+
+ } else {
+ return 0;
+ }
+
+ if (pe_lib_get_unit_ctx(dctx,arname,&arctx) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ if (arctx->armeta == 0)
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_NON_ARCHIVE_IMAGE);
+
+ if (pe_ar_fn(arctx->armeta,members) < 0) {
+ pe_lib_free_unit_ctx(arctx);
+ return PERK_NESTED_ERROR(dctx);
+ }
+
+ pe_lib_free_unit_ctx(arctx);
+
+ return 0;
+}
+
+static int pe_cmd_ar_verify_cmdline(
+ const struct pe_driver_ctx * dctx,
+ uint64_t flags,
+ const char * posname,
+ const char * arname,
+ const char ** members)
+{
+ uint64_t action;
+ uint64_t poscmd;
+ uint64_t vercmd;
+
+ action = (flags & AR_ACTION_MASK);
+ poscmd = (flags & AR_POSNAME_MASK);
+ vercmd = (flags & PERK_DRIVER_VERSION);
+
+ if (vercmd && !posname && !arname && !members)
+ return 0;
+
+ switch (action) {
+ case 0:
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_MISSING_ACTION);
+
+ case PERK_DRIVER_AR_LIST_MEMBERS:
+ case PERK_DRIVER_AR_DELETE_MEMBERS:
+ case PERK_DRIVER_AR_APPEND_MEMBERS:
+ case PERK_DRIVER_AR_EXTRACT_MEMBERS:
+ case PERK_DRIVER_AR_PRINT_MEMBERS:
+ if (poscmd || posname)
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_INVALID_ANCHORS);
+
+ break;
+
+ case AR_UPDATE_MASK:
+ case PERK_DRIVER_AR_REPLACE_MEMBERS:
+ case PERK_DRIVER_AR_MOVE_MEMBERS:
+ switch (poscmd) {
+ case 0:
+ if (posname)
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_MISSING_ANCHOR);
+ break;
+
+ case PERK_DRIVER_AR_POSITION_AFTER:
+ case PERK_DRIVER_AR_POSITION_BEFORE:
+ if (!posname)
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_NULL_POSNAME);
+ break;
+
+ default:
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_MULTIPLE_ANCHORS);
+ }
+
+ default:
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_MULTIPLE_ACTIONS);
+ }
+
+ if (!arname)
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_NULL_ARNAME);
+
+ return 0;
+}
+
+int pe_cmd_ar(
+ const struct pe_driver_ctx * dctx,
+ uint64_t flags,
+ const char * posname,
+ const char * arname,
+ const char ** members)
+{
+ uint64_t action = (flags & AR_ACTION_MASK);
+
+ if (pe_cmd_ar_verify_cmdline(dctx,flags,posname,arname,members) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ if (pe_cmd_ar_perform_unit_actions(dctx,arname,members,action) < 0)
+ return PERK_NESTED_ERROR(dctx);
+
+ return 0;
+}
diff --git a/src/cmds/pe_cmd_perk.c b/src/cmds/pe_cmd_perk.c
index 5b6f371..6fe6112 100644
--- a/src/cmds/pe_cmd_perk.c
+++ b/src/cmds/pe_cmd_perk.c
@@ -13,59 +13,73 @@
static void pe_perform_hdrdump_actions(
const struct pe_driver_ctx * dctx,
- const struct pe_unit_ctx * uctx)
+ const struct pe_image_meta * meta)
{
if (dctx->cctx->hdrdump & PERK_HDRDUMP_IMAGE_DOS_HEADER)
- pe_hexdump_image_dos_hdr(dctx,uctx->meta);
+ pe_hexdump_image_dos_hdr(dctx,meta);
if (dctx->cctx->hdrdump & PERK_HDRDUMP_COFF_IMAGE_HEADER)
- pe_hexdump_coff_image_hdr(dctx,uctx->meta);
+ pe_hexdump_coff_image_hdr(dctx,meta);
if (dctx->cctx->hdrdump & PERK_HDRDUMP_COFF_OBJECT_HEADER)
- pe_hexdump_coff_object_hdr(dctx,uctx->meta);
+ pe_hexdump_coff_object_hdr(dctx,meta);
if (dctx->cctx->hdrdump & PERK_HDRDUMP_COFF_OPT_HEADER)
- pe_hexdump_coff_opt_hdr(dctx,uctx->meta);
+ pe_hexdump_coff_opt_hdr(dctx,meta);
if (dctx->cctx->hdrdump & PERK_HDRDUMP_SECTION_TABLE)
- pe_hexdump_sec_tbl(dctx,uctx->meta);
+ pe_hexdump_sec_tbl(dctx,meta);
if (dctx->cctx->hdrdump & PERK_HDRDUMP_EXPORT_HEADER)
- pe_hexdump_export_hdr(dctx,uctx->meta);
+ pe_hexdump_export_hdr(dctx,meta);
if (dctx->cctx->hdrdump & PERK_HDRDUMP_IMPORT_TABLE)
- pe_hexdump_import_tbl(dctx,uctx->meta);
+ pe_hexdump_import_tbl(dctx,meta);
}
-static void pe_perform_unit_actions(
+static void pe_perform_unit_actions_impl(
const struct pe_driver_ctx * dctx,
- const struct pe_unit_ctx * uctx)
+ const struct pe_image_meta * meta)
{
uint64_t flags = dctx->cctx->fmtflags;
if (flags & PERK_OUTPUT_IMAGE_CATEGORY)
- pe_output_image_category(dctx,uctx->meta);
+ pe_output_image_category(dctx,meta);
if (flags & PERK_OUTPUT_IMAGE_SECTIONS)
- pe_output_image_sections(dctx,uctx->meta);
+ pe_output_image_sections(dctx,meta);
if (flags & PERK_OUTPUT_IMAGE_SYMBOLS)
- pe_output_image_symbols(dctx,uctx->meta);
+ pe_output_image_symbols(dctx,meta);
if (flags & PERK_OUTPUT_IMAGE_STRINGS)
- pe_output_image_strings(dctx,uctx->meta);
+ pe_output_image_strings(dctx,meta);
if (flags & PERK_OUTPUT_EXPORT_SYMS)
- pe_output_export_symbols(dctx,uctx->meta);
+ pe_output_export_symbols(dctx,meta);
if ((flags & PERK_OUTPUT_IMPORT_LIBS) || (flags & PERK_OUTPUT_IMPORT_SYMS))
- pe_output_import_libraries(dctx,uctx->meta);
+ pe_output_import_libraries(dctx,meta);
if ((flags & PERK_OUTPUT_MDSO_LIBS) || (flags & PERK_OUTPUT_MDSO_SYMS))
- pe_output_mdso_libraries(dctx,uctx->meta);
+ pe_output_mdso_libraries(dctx,meta);
if (dctx->cctx->hdrdump)
- pe_perform_hdrdump_actions(dctx,uctx);
+ pe_perform_hdrdump_actions(dctx,meta);
+}
+
+static void pe_perform_unit_actions(
+ const struct pe_driver_ctx * dctx,
+ const struct pe_unit_ctx * uctx)
+{
+ const struct pe_image_meta ** objmeta;
+
+ if (uctx->meta)
+ pe_perform_unit_actions_impl(dctx,uctx->meta);
+
+ if ((objmeta = uctx->objmeta))
+ for (; *objmeta; )
+ pe_perform_unit_actions_impl(dctx,*objmeta++);
}
int pe_cmd_perk(const struct pe_driver_ctx * dctx, const char * path)
diff --git a/src/driver/pe_amain.c b/src/driver/pe_amain.c
index 173f391..a766dba 100644
--- a/src/driver/pe_amain.c
+++ b/src/driver/pe_amain.c
@@ -10,13 +10,15 @@
#include <perk/perk_output.h>
#include "perk_driver_impl.h"
#include "perk_dprintf_impl.h"
+#include "perk_ar_impl.h"
#ifndef PERK_DRIVER_FLAGS
#define PERK_DRIVER_FLAGS PERK_DRIVER_VERBOSITY_ERRORS \
- | PERK_DRIVER_VERBOSITY_UTILITY
+ | PERK_DRIVER_VERBOSITY_UTILITY \
+ | PERK_DRIVER_AR_OBJECT_VECTOR
#endif
-static const char vermsg[] = "%s%s%s (git://midipix.org/perk): "
+static const char vermsg[] = "%s%s%s (https://git.foss21.org/perk): "
"version %s%d.%d.%d%s.\n"
"[commit reference: %s%s%s]\n";
@@ -62,6 +64,10 @@ int pe_main(char ** argv, char ** envp, const struct pe_fd_ctx * fdctx)
uint64_t flags;
struct pe_driver_ctx * dctx;
const char ** unit;
+ const char * posname;
+ const char * arname;
+ const char ** arfiles;
+ uint64_t arflags;
flags = PERK_DRIVER_FLAGS;
fdout = fdctx ? fdctx->fdout : STDOUT_FILENO;
@@ -81,6 +87,23 @@ int pe_main(char ** argv, char ** envp, const struct pe_fd_ctx * fdctx)
pe_cmd_perk(dctx,*unit);
break;
+ case PERK_CMD_AR:
+ arflags = dctx->cctx->drvflags;
+
+ if (arflags & AR_POSNAME_MASK) {
+ posname = dctx->units[0];
+ arname = posname ? dctx->units[1] : 0;
+ arfiles = arname ? &dctx->units[2] : 0;
+ } else {
+ posname = 0;
+ arname = dctx->units[0];
+ arfiles = arname ? &dctx->units[1] : 0;
+ }
+
+ pe_cmd_ar(dctx,arflags,posname,arname,arfiles);
+
+ break;
+
default:
break;
}
diff --git a/src/driver/pe_driver_ctx.c b/src/driver/pe_driver_ctx.c
index afa46e7..11c2e27 100644
--- a/src/driver/pe_driver_ctx.c
+++ b/src/driver/pe_driver_ctx.c
@@ -14,6 +14,7 @@
#include <perk/perk_output.h>
#include "perk_version.h"
#include "perk_driver_impl.h"
+#include "perk_synopsis_impl.h"
#include "argv/argv.h"
/* package info */
@@ -27,12 +28,14 @@ static const struct pe_source_version pe_src_version = {
/* perk command names */
static const char * const perk_cmd_name[PERK_CMD_CAP] = {
[PERK_CMD_PERK] = "perk",
+ [PERK_CMD_AR] = "ar",
};
/* perk command options */
static const struct argv_option * perk_cmd_options[PERK_CMD_CAP] = {
[PERK_CMD_DEFAULT] = pe_default_options,
[PERK_CMD_PERK] = pe_perk_options,
+ [PERK_CMD_AR] = pe_ar_options,
};
/* default fd context */
@@ -74,30 +77,40 @@ static int pe_driver_usage(
const char * arg,
const struct argv_option ** optv,
struct argv_meta * meta,
+ const char * cmdarg,
enum pe_cmd cmd)
{
- char header[512];
- char * cmdarg[2];
- const char * cmdname;
-
- if (cmd == PERK_CMD_DEFAULT) {
- cmdarg[0] = "";
- cmdarg[1] = "";
- cmdname = "";
- } else {
- cmdarg[0] = " (--cmd=";
- cmdarg[1] = ")";
- cmdname = perk_cmd_name[cmd];
+ char header [2048];
+ char cmdname[128];
+
+ snprintf(cmdname,sizeof(cmdname),"%s%s%s",
+ program,
+ cmdarg ? " --cmd=" : "",
+ cmdarg ? perk_cmd_name[cmd] : "");
+
+ switch (cmd) {
+ case PERK_CMD_AR:
+ snprintf(header,sizeof(header),
+ PERK_AR_CMD_SYNOPSIS,
+ cmdname,cmdname,cmdname,cmdname,
+ cmdname,cmdname,cmdname,cmdname,
+ cmdname,cmdname,cmdname,cmdname,
+ cmdname,cmdname,cmdname);
+ break;
+
+ case PERK_CMD_PERK:
+ snprintf(header,sizeof(header),
+ PERK_PERK_CMD_SYNOPSIS,
+ cmdname,cmdname,cmdname);
+ break;
+
+ default:
+ snprintf(header,sizeof(header),
+ PERK_DEFAULT_CMD_SYNOPSIS,
+ program,program,program,program);
+ break;
}
- snprintf(header,sizeof(header),
- "Usage: %s [options] ...\n"
- "Usage: %s [options] [--cmd=<command>] <arg> <arg> ...\n\n"
- "Notes: --cmd must precede all non-option arguments, as well as\n"
- " all arguments that are specific to the selected command.\n\n"
- "Options%s%s%s:\n",
- program,program,cmdarg[0],cmdname,cmdarg[1]);
-
argv_usage(fdout,header,optv,arg);
argv_free(meta);
@@ -160,6 +173,9 @@ static int pe_cmd_from_program(const char * program)
if (!strcmp(mark,"perk")) {
return PERK_CMD_PERK;
+ } else if (!strcmp(mark,"ar")) {
+ return PERK_CMD_AR;
+
} else {
return PERK_CMD_DEFAULT;
}
@@ -171,7 +187,8 @@ static int pe_cctx_update(
struct argv_meta * meta,
const struct pe_fd_ctx * fdctx,
struct pe_common_ctx * cctx,
- size_t * nunits)
+ size_t * nunits,
+ const char * cmdarg)
{
struct argv_entry * entry;
const char * pretty;
@@ -186,7 +203,7 @@ static int pe_cctx_update(
return pe_driver_usage(
fdctx->fdout,
program,entry->arg,
- optv,0,cctx->cmd);
+ optv,0,cmdarg,cctx->cmd);
break;
case TAG_CMD:
@@ -194,9 +211,10 @@ static int pe_cctx_update(
return pe_driver_usage(
fdctx->fderr,
program,0,
- optv,0,cctx->cmd);
+ optv,0,cmdarg,cctx->cmd);
cctx->cmd = pe_cmd_from_program(entry->arg);
+ cmdarg = entry->arg;
break;
case TAG_VERSION:
@@ -207,6 +225,10 @@ static int pe_cctx_update(
pretty = entry->arg;
break;
+ case TAG_VERBOSE:
+ cctx->fmtflags |= PERK_PRETTY_VERBOSE;
+ break;
+
case TAG_CATEGORY:
cctx->fmtflags |= PERK_OUTPUT_IMAGE_CATEGORY;
break;
@@ -279,6 +301,15 @@ static int pe_cctx_update(
cctx->hdrdump = PERK_HDRDUMP_IMPORT_TABLE;
}
break;
+
+ /*---ar---*/
+ case TAG_AR_LIST_MEMBERS:
+ cctx->drvflags |= PERK_DRIVER_AR_LIST_MEMBERS;
+ break;
+
+ case TAG_AR_PRINT_MEMBERS:
+ cctx->drvflags |= PERK_DRIVER_AR_PRINT_MEMBERS;
+ break;
}
} else {
(*nunits)++;
@@ -288,6 +319,12 @@ static int pe_cctx_update(
if (pretty && !strcmp(pretty,"yaml")) {
cctx->fmtflags |= PERK_PRETTY_YAML;
+ } else if (pretty && !strcmp(pretty,"posix")) {
+ cctx->fmtflags |= PERK_PRETTY_POSIX;
+
+ } else if (pretty && !strcmp(pretty,"hexdata")) {
+ cctx->fmtflags |= PERK_PRETTY_HEXDATA;
+
} else if (pretty && !strcmp(pretty,"dlltool")) {
cctx->fmtflags |= PERK_PRETTY_DLLTOOL;
}
@@ -314,10 +351,12 @@ int pe_lib_get_driver_ctx(
struct argv_meta * meta;
size_t nunits;
const char * program;
+ const char * cmdarg;
char ** parg;
char ** pargcap;
char ** cmdargv;
char * cmdmark;
+ char stckarg[64];
struct argv_ctx actx = {ARGV_VERBOSITY_NONE,
ARGV_MODE_SCAN,
0,0,0,0,0,0,0,0};
@@ -336,6 +375,7 @@ int pe_lib_get_driver_ctx(
cctx.cmd = pe_cmd_from_program(program);
cctx.drvflags = flags;
nunits = 0;
+ cmdarg = 0;
/* missing arguments? */
argv_optv_init(perk_cmd_options[cctx.cmd],optv);
@@ -344,7 +384,18 @@ int pe_lib_get_driver_ctx(
return pe_driver_usage(
fdctx->fderr,
program,0,
- optv,0,cctx.cmd);
+ optv,0,0,cctx.cmd);
+
+ /* historic ar usage (vector will be cloned, so stack var is fine) */
+ if (cctx.cmd == PERK_CMD_AR) {
+ if (argv && argv[0] && argv[1] && (argv[1][0] != '-')) {
+ if (strlen(argv[1]) < (sizeof(stckarg) - 1)) {
+ stckarg[0] = '-';
+ strcpy(&stckarg[1],argv[1]);
+ argv[1] = stckarg;
+ }
+ }
+ }
/* initial argv scan: ... --cmd=xxx ... */
argv_scan(argv,optv,&actx,0);
@@ -365,9 +416,11 @@ int pe_lib_get_driver_ctx(
if (!strcmp(*parg,"--cmd") && parg[1]) {
cmdargv = &parg[2];
cmdmark = parg[2];
+ cmdarg = parg[1];
} else if (!strncmp(*parg,"--cmd=",6)) {
cmdargv = &parg[1];
cmdmark = parg[1];
+ cmdarg = &parg[0][6];
}
}
@@ -399,7 +452,8 @@ int pe_lib_get_driver_ctx(
if (pe_cctx_update(
program,optv,meta,
- fdctx,&cctx,&nunits)) {
+ fdctx,&cctx,&nunits,
+ cmdarg)) {
argv_free(meta);
return -1;
}
@@ -417,6 +471,9 @@ int pe_lib_get_driver_ctx(
} else if (cctx.cmd == PERK_CMD_PERK) {
argv_optv_init(pe_perk_options,optv);
+
+ } else if (cctx.cmd == PERK_CMD_AR) {
+ argv_optv_init(pe_ar_options,optv);
}
/* process the selected tool's command-line arguments */
@@ -428,7 +485,8 @@ int pe_lib_get_driver_ctx(
if (pe_cctx_update(
program,optv,meta,
- fdctx,&cctx,&nunits)) {
+ fdctx,&cctx,&nunits,
+ cmdarg)) {
argv_free(meta);
return -1;
}
@@ -438,8 +496,8 @@ int pe_lib_get_driver_ctx(
if (!nunits && !(cctx.drvflags & PERK_DRIVER_VERSION))
return pe_driver_usage(
fdctx->fdout,
- program,0,
- optv,meta,cctx.cmd);
+ program,0,optv,meta,
+ cmdarg,cctx.cmd);
/* context allocation */
if (!(ctx = pe_driver_ctx_alloc(meta,fdctx,&cctx,nunits)))
diff --git a/src/driver/pe_unit_ctx.c b/src/driver/pe_unit_ctx.c
index d1fea0e..b165226 100644
--- a/src/driver/pe_unit_ctx.c
+++ b/src/driver/pe_unit_ctx.c
@@ -11,20 +11,127 @@
#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"
static int pe_lib_free_unit_ctx_impl(struct pe_unit_ctx_impl * ctx, int ret)
{
+ struct pe_image_meta ** pmeta;
if (ctx) {
+ for (pmeta=ctx->objmeta; pmeta && *pmeta; pmeta++)
+ pe_meta_free_image_meta(*pmeta);
+
+ pe_ar_free_archive_meta(ctx->armeta);
pe_meta_free_image_meta(ctx->meta);
pe_raw_unmap_raw_image(&ctx->map);
+ free(ctx->usrobjmeta);
+ free(ctx->objmeta);
free(ctx);
}
return ret;
}
+static int pe_lib_create_object_vector_or_verify_archive(
+ const struct pe_driver_ctx * dctx,
+ struct pe_unit_ctx_impl * ctx)
+{
+ struct pe_archive_meta * actx;
+ struct pe_archive_meta_impl * ictx;
+ struct pe_driver_ctx_impl * idctx;
+ bool fvector;
+ bool fstrict;
+ bool fpurearch;
+ struct pe_image_meta * meta;
+ struct pe_image_meta ** objmeta;
+ const struct pe_image_meta ** usrobjmeta;
+ size_t veclen;
+ struct pe_raw_image map;
+ struct ar_meta_member_info ** pmember;
+ struct pe_error_info ** errinfp;
+ enum pe_abi m_abi;
+ enum pe_subtype m_subtype;
+
+ fvector = (dctx->cctx->drvflags & PERK_DRIVER_AR_OBJECT_VECTOR);
+ fstrict = (dctx->cctx->drvflags & PERK_DRIVER_AR_STRICT_PE);
+ fpurearch = (dctx->cctx->drvflags & PERK_DRIVER_AR_STRICT_PE_ARCH);
+
+ if (!fvector && !fstrict && !fpurearch)
+ return 0;
+
+ actx = ctx->armeta;
+ ictx = pe_archive_meta_ictx(actx);
+ idctx = pe_get_driver_ictx(dctx);
+ veclen = ictx->nentries + 1;
+
+ if (fvector && !(ctx->objmeta = calloc(veclen,sizeof(meta))))
+ return PERK_BUFFER_ERROR(dctx);
+
+ if (fvector && !(ctx->usrobjmeta = calloc(veclen,sizeof(*usrobjmeta))))
+ return PERK_BUFFER_ERROR(dctx);
+
+ if (!actx->a_memberv)
+ return 0;
+
+ objmeta = ctx->objmeta;
+ usrobjmeta = ctx->usrobjmeta;
+
+ for (pmember=actx->a_memberv; *pmember; pmember++) {
+ errinfp = idctx->errinfp;
+
+ switch (pmember[0]->ar_member_attr) {
+ case AR_MEMBER_ATTR_ARCHIVE:
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_NESTED_ARCHIVE);
+
+ case AR_MEMBER_ATTR_ARMAP:
+ case AR_MEMBER_ATTR_NAMESTRS:
+ case AR_MEMBER_ATTR_LINKINFO:
+ break;
+
+ default:
+ map.map_addr = pmember[0]->ar_object_data;
+ map.map_size = pmember[0]->ar_object_size;
+
+ if (!pe_meta_get_image_meta(dctx,&map,&meta)) {
+ if (fpurearch && (objmeta == ctx->objmeta)) {
+ m_abi = meta->m_abi;
+ m_subtype = meta->m_subtype;
+
+ } else if (fpurearch) {
+ if ((meta->m_abi != m_abi) || (meta->m_subtype != m_subtype))
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_MIXED_PE_MEMBERS);
+ }
+
+ if (fvector) {
+ *objmeta++ = meta;
+ *usrobjmeta++ = meta;
+ } else {
+ pe_meta_free_image_meta(meta);
+ };
+
+
+ } else if (fstrict || fpurearch) {
+ return PERK_CUSTOM_ERROR(dctx,
+ PERK_ERR_AR_NON_PE_MEMBERS);
+
+ } else {
+ idctx->errinfp = errinfp;
+
+ for (; *errinfp; )
+ *errinfp++ = 0;
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
int pe_lib_get_unit_ctx(
const struct pe_driver_ctx * dctx,
const char * path,
@@ -32,6 +139,8 @@ int pe_lib_get_unit_ctx(
{
struct pe_unit_ctx_impl * ctx;
int prot;
+ char * mark;
+ size_t siglen;
if (!dctx)
return PERK_CUSTOM_ERROR(
@@ -43,7 +152,7 @@ int pe_lib_get_unit_ctx(
pe_driver_set_ectx(
dctx,0,path);
- prot = (dctx->cctx->actflags & PERK_ACTION_MAP_READWRITE)
+ prot = (dctx->cctx->drvflags & PERK_DRIVER_MAP_WRITE_ACCESS)
? PROT_READ | PROT_WRITE
: PROT_READ;
@@ -51,14 +160,34 @@ int pe_lib_get_unit_ctx(
return pe_lib_free_unit_ctx_impl(ctx,
PERK_NESTED_ERROR(dctx));
- if (pe_meta_get_image_meta(dctx,&ctx->map,&ctx->meta))
+ if (ctx->map.map_size < (siglen = sizeof(struct ar_raw_signature)))
+ return pe_lib_free_unit_ctx_impl(ctx,
+ PERK_CUSTOM_ERROR(
+ dctx,
+ PERK_ERR_INVALID_IMAGE));
+
+ if (!strncmp((mark = ctx->map.map_addr),AR_SIGNATURE,siglen)) {
+ ctx->armap.map_addr = ctx->map.map_addr;
+ ctx->armap.map_size = ctx->map.map_size;
+
+ if (pe_ar_get_archive_meta(dctx,&ctx->armap,&ctx->armeta))
+ return pe_lib_free_unit_ctx_impl(ctx,
+ PERK_NESTED_ERROR(dctx));
+
+ if (pe_lib_create_object_vector_or_verify_archive(dctx,ctx) < 0)
+ return pe_lib_free_unit_ctx_impl(ctx,
+ PERK_NESTED_ERROR(dctx));
+
+ } else if (pe_meta_get_image_meta(dctx,&ctx->map,&ctx->meta)) {
return pe_lib_free_unit_ctx_impl(ctx,
PERK_NESTED_ERROR(dctx));
+ }
- ctx->path = path;
- ctx->uctx.path = &ctx->path;
- ctx->uctx.map = &ctx->map;
- ctx->uctx.meta = ctx->meta;
+ ctx->path = path;
+ ctx->uctx.path = &ctx->path;
+ ctx->uctx.meta = ctx->meta;
+ ctx->uctx.armeta = ctx->armeta;
+ ctx->uctx.objmeta = ctx->usrobjmeta;
*pctx = &ctx->uctx;
return 0;
diff --git a/src/internal/perk_ar_impl.h b/src/internal/perk_ar_impl.h
index 323dc56..0da7559 100644
--- a/src/internal/perk_ar_impl.h
+++ b/src/internal/perk_ar_impl.h
@@ -20,6 +20,22 @@
/* initial number of elements in the transient, on-stack vector */
# define AR_STACK_VECTOR_ELEMENTS (0x200)
+/* bit mask to indicate that the first non-option argument is <posname> */
+#define AR_POSNAME_MASK (PERK_DRIVER_AR_POSITION_AFTER \
+ |PERK_DRIVER_AR_POSITION_BEFORE)
+
+#define AR_UPDATE_MASK (PERK_DRIVER_AR_UPDATE_MEMBERS \
+ |PERK_DRIVER_AR_REPLACE_MEMBERS)
+
+#define AR_ACTION_MASK (PERK_DRIVER_AR_LIST_MEMBERS \
+ |PERK_DRIVER_AR_MOVE_MEMBERS \
+ |PERK_DRIVER_AR_UPDATE_MEMBERS \
+ |PERK_DRIVER_AR_DELETE_MEMBERS \
+ |PERK_DRIVER_AR_APPEND_MEMBERS \
+ |PERK_DRIVER_AR_REPLACE_MEMBERS \
+ |PERK_DRIVER_AR_EXTRACT_MEMBERS \
+ |PERK_DRIVER_AR_PRINT_MEMBERS)
+
extern const struct argv_option pe_ar_options[];
struct ar_armaps_impl {
@@ -35,8 +51,8 @@ struct ar_armaps_impl {
};
struct pe_archive_meta_impl {
- const struct pe_driver_ctx * dctx;
- struct pe_archive_ctx * actx;
+ const struct pe_driver_ctx * dctx;
+ struct pe_archive_ctx * actx;
size_t ofmtattr;
size_t nentries;
void * hdrinfov;
@@ -50,40 +66,40 @@ struct pe_archive_meta_impl {
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 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,
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ const struct pe_driver_ctx * dctx,
+ struct pe_archive_meta_impl * m);
int pe_ar_update_syminfo(
- struct pe_archive_ctx * actx);
+ struct pe_archive_ctx * actx);
int pe_ar_update_syminfo_ex(
- struct pe_archive_ctx * actx,
- int fdout);
+ struct pe_archive_ctx * actx,
+ int fdout);
static inline struct pe_archive_meta_impl * pe_archive_meta_ictx(const struct pe_archive_meta * meta)
{
diff --git a/src/internal/perk_driver_impl.h b/src/internal/perk_driver_impl.h
index e50d08a..22e7894 100644
--- a/src/internal/perk_driver_impl.h
+++ b/src/internal/perk_driver_impl.h
@@ -14,12 +14,14 @@
extern const struct argv_option pe_default_options[];
extern const struct argv_option pe_perk_options[];
+extern const struct argv_option pe_ar_options[];
enum app_tags {
TAG_HELP,
TAG_VERSION,
TAG_CMD,
TAG_PRETTY,
+ TAG_VERBOSE,
TAG_CATEGORY,
TAG_SECTIONS,
TAG_SYMBOLS,
@@ -30,6 +32,8 @@ enum app_tags {
TAG_DSOLIBS,
TAG_DSOSYMS,
TAG_HDRDUMP,
+ TAG_AR_LIST_MEMBERS,
+ TAG_AR_PRINT_MEMBERS,
};
struct pe_driver_ctx_impl {
@@ -45,10 +49,14 @@ struct pe_driver_ctx_impl {
};
struct pe_unit_ctx_impl {
- const char * path;
- struct pe_raw_image map;
- struct pe_image_meta * meta;
- struct pe_unit_ctx uctx;
+ const char * path;
+ struct pe_raw_image map;
+ struct pe_image_meta * meta;
+ struct pe_raw_archive armap;
+ struct pe_archive_meta * armeta;
+ struct pe_image_meta ** objmeta;
+ const struct pe_image_meta**usrobjmeta;
+ struct pe_unit_ctx uctx;
};
diff --git a/src/internal/perk_synopsis_impl.h b/src/internal/perk_synopsis_impl.h
new file mode 100644
index 0000000..f3d0a3a
--- /dev/null
+++ b/src/internal/perk_synopsis_impl.h
@@ -0,0 +1,46 @@
+/***************************************************************/
+/* perk: PE Resource Kit */
+/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */
+/* Released under GPLv2 and GPLv3; see COPYING.PERK. */
+/***************************************************************/
+
+#ifndef SLIBTOOL_SYNOPSIS_IMPL_H
+#define SLIBTOOL_SYNOPSIS_IMPL_H
+
+#define PERK_DEFAULT_CMD_SYNOPSIS \
+ "%s — PE/COFF Resource Kit\n\n" \
+ "Synopsis:\n" \
+ " %s [option] ...\n" \
+ " %s --cmd=<command> [option] ...\n" \
+ " %s --cmd=<command> [option] ... <file> ...\n\n" \
+ "Options:\n"
+
+#define PERK_PERK_CMD_SYNOPSIS \
+ "%s — PE/COFF Resource Kit\n\n" \
+ "Synopsis:\n" \
+ " %s [option] ...\n" \
+ " %s [option] ... <file> ...\n\n" \
+ "Options:\n"
+
+#define PERK_AR_CMD_SYNOPSIS \
+ "%s — the PE/COFF Resource Kit Archiver\n\n" \
+ "Synopsis:\n" \
+ " %s -d [-v] <archive> <file> ...\n" \
+ " %s -p [-v] [-s] <archive> <file> ...\n" \
+ " %s -q [-v] [-c] <archive> <file> ...\n" \
+ " %s -r [-v] [-c] [-u] <archive> <file> ...\n" \
+ " %s -t [-v] [-s] <archive> [<file> ...]\n" \
+ " %s -x [-v] [-s] [-C] [-T] <archive> [<file> ...]\n\n" \
+ \
+ " %s -m [-v] <archive> <file> ...\n" \
+ " %s -m -a [-v] <posname> <archive> <file> ...\n" \
+ " %s -m -b [-v] <posname> <archive> <file> ...\n" \
+ " %s -m -i [-v] <posname> <archive> <file> ...\n\n" \
+ \
+ " %s -r [-v] [-c] [-u] <archive> <file> ...\n" \
+ " %s -r -a [-v] [-c] [-u] <posname> <archive> <file> ...\n" \
+ " %s -r -b [-v] [-c] [-u] <posname> <archive> <file> ...\n" \
+ " %s -r -i [-v] [-c] [-u] <posname> <archive> <file> ...\n\n" \
+ "Options:\n"
+
+#endif
diff --git a/src/internal/perk_visibility_impl.h b/src/internal/perk_visibility_impl.h
index df45efc..0057b8b 100644
--- a/src/internal/perk_visibility_impl.h
+++ b/src/internal/perk_visibility_impl.h
@@ -7,20 +7,20 @@
/* attribute not only redundant, but also tricky if not properly */
/* supported by the toolchain. */
/* */
-/* When targeting Midipix, __PE__, __dllexport and __dllimport are */
-/* always defined by the toolchain. Otherwise, the absnece of these */
-/* macros has been detected by sofort's ccenv.sh during ./configure, */
-/* and they have accordingly been added to CFLAGS_OS. */
+/* When targeting Midipix hosts, where elf-like visibility is fully */
+/* supported and may be detected via the __PE_VISIBILITY__ macro, */
+/* we utilize the attribute to render private symbols invisibile */
+/* to dlsym(), as well as reduce the size of the .gotstrs section. */
/**********************************************************************/
-#ifdef __PE__
+#if defined(__PE_VISIBILITY__)
+#define perk_hidden _ATTR_VISIBILITY_HIDDEN
+#elif defined(__PE__)
#define perk_hidden
-#else
-#ifdef _ATTR_VISIBILITY_HIDDEN
+#elif defined(_ATTR_VISIBILITY_HIDDEN)
#define perk_hidden _ATTR_VISIBILITY_HIDDEN
#else
#define perk_hidden
#endif
-#endif
#endif
diff --git a/src/output/pe_output_error.c b/src/output/pe_output_error.c
index 84a867b..50c7357 100644
--- a/src/output/pe_output_error.c
+++ b/src/output/pe_output_error.c
@@ -27,13 +27,26 @@ static const char * const pe_error_strings[PERK_ERR_CAP] = {
[PERK_ERR_NULL_CONTEXT] = "null driver or unit context",
[PERK_ERR_NULL_IMAGE] = "null image base pointer",
[PERK_ERR_INVALID_CONTEXT] = "invalid driver or unit context",
- [PERK_ERR_INVALID_IMAGE] = "invalid PE image",
+ [PERK_ERR_INVALID_IMAGE] = "invalid PE image, object, or common archive",
[PERK_ERR_IMAGE_SIZE_ZERO] = "PE image size cannot be zero",
[PERK_ERR_IMAGE_MALFORMED] = "malformed PE image detected",
[PERK_ERR_BAD_DOS_HEADER] = "bad DOS header",
[PERK_ERR_BAD_COFF_HEADER] = "bad COFF header",
[PERK_ERR_BAD_IMAGE_TYPE] = "bad PE image type",
[PERK_ERR_UNSUPPORTED_ABI] = "unsupported image abi",
+
+ [PERK_ERR_AR_NON_ARCHIVE_IMAGE]= "the parsed PE/COFF object or image is not an archive",
+ [PERK_ERR_AR_NON_PE_MEMBERS] = "format of current archive member is not PE/COFF",
+ [PERK_ERR_AR_MIXED_PE_MEMBERS] = "archive mixes objects of different architectures",
+ [PERK_ERR_AR_NESTED_ARCHIVE] = "nested archives are currently not supported",
+
+ [PERK_ERR_AR_MISSING_ACTION] = "missing action, which should be exactly one of [dqmrxpt]",
+ [PERK_ERR_AR_MULTIPLE_ACTIONS] = "exactly one action permitted, multiple actions specified",
+ [PERK_ERR_AR_MULTIPLE_ANCHORS] = "multiple anchors: may specify _before_ or _after_, but not both",
+ [PERK_ERR_AR_INVALID_ANCHORS] = "mismatched arguments: anchors are incompatible with the selected action",
+ [PERK_ERR_AR_MISSING_ANCHOR] = "missing anchor: <posname> provided, but no anchor specified",
+ [PERK_ERR_AR_NULL_POSNAME] = "null <posname> argument with [-a], [-b], or [-i]",
+ [PERK_ERR_AR_NULL_ARNAME] = "null <arname> argument",
};
static const char * pe_output_error_header(const struct pe_error_info * erri)
diff --git a/src/skin/pe_skin_ar.c b/src/skin/pe_skin_ar.c
new file mode 100644
index 0000000..4bdce0e
--- /dev/null
+++ b/src/skin/pe_skin_ar.c
@@ -0,0 +1,35 @@
+#include "perk_driver_impl.h"
+#include "perk_visibility_impl.h"
+#include "argv/argv.h"
+
+const perk_hidden struct argv_option pe_ar_options[] = {
+ {"Wversion", 'V',TAG_VERSION,ARGV_OPTARG_NONE,
+ ARGV_OPTION_HYBRID_ONLY,0,0,
+ "show version information"},
+
+ {"Whelp", 'h',TAG_HELP,ARGV_OPTARG_NONE,
+ ARGV_OPTION_HYBRID_ONLY,0,0,
+ "show usage information"},
+
+ {"Wverbose", 'v',TAG_VERBOSE,ARGV_OPTARG_NONE,
+ ARGV_OPTION_HYBRID_ONLY,0,0,
+ "provide verbose output"},
+
+ {"Wprint", 'p',TAG_AR_PRINT_MEMBERS,ARGV_OPTARG_NONE,
+ ARGV_OPTION_HYBRID_ONLY,0,0,
+ "print archive members"},
+
+ {"Wlist", 't',TAG_AR_LIST_MEMBERS,ARGV_OPTARG_NONE,
+ ARGV_OPTION_HYBRID_ONLY,0,0,
+ "list archive members; names only be default, "
+ "or otherwise a detailed table of contents in "
+ "verbose mode"},
+
+ {"Wpretty", 0,TAG_PRETTY,ARGV_OPTARG_REQUIRED,
+ ARGV_OPTION_HYBRID_ONLY|ARGV_OPTION_HYBRID_EQUAL,
+ "yaml|posix|hexdata",0,
+ "list archive members or print content "
+ "using the %s format specification"},
+
+ {0,0,0,0,0,0,0,0}
+};
diff --git a/src/skin/pe_skin_default.c b/src/skin/pe_skin_default.c
index 5ca7387..95024c7 100644
--- a/src/skin/pe_skin_default.c
+++ b/src/skin/pe_skin_default.c
@@ -10,7 +10,7 @@ const perk_hidden struct argv_option pe_default_options[] = {
"show usage information [listing %s options only]"},
{"cmd", 0,TAG_CMD,ARGV_OPTARG_REQUIRED,0,
- "perk",0,
+ "perk|ar",0,
"invoke one of the following perk commands: {%s}"},
{0,0,0,0,0,0,0,0}