From 62db3cb96a998f819fd6ee5aae8a565c01ee4c34 Mon Sep 17 00:00:00 2001 From: Thorsten Otto Date: Mon, 11 Sep 2023 16:21:55 +0200 Subject: [PATCH] Add stripex tool, and bump version to 0.4 --- .github/workflows/build.yml | 2 +- .gitignore | 1 + configure | 20 +- configure.in | 2 +- src/Makefile.am | 3 +- src/Makefile.in | 25 +- src/stripex.c | 684 ++++++++++++++++++++++++++++++++++++ 7 files changed, 718 insertions(+), 19 deletions(-) create mode 100644 src/stripex.c diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f7576ff..8b9803c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: ./.scripts/install-freemint.sh mintlib - name: Setup environment env: - PROJECT_VERSION: "0.3" + PROJECT_VERSION: "0.4" run: ./.scripts/setup_env.sh - name: build run: ./.scripts/build.sh diff --git a/.gitignore b/.gitignore index a764971..b29d24f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ src/flags src/mintbin src/stack src/symex +src/stripex stamp-* .deps *.o diff --git a/configure b/configure index 5fc95cd..a4a0a90 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for mintbin 0.3. +# Generated by GNU Autoconf 2.71 for mintbin 0.4. # # Report bugs to . # @@ -611,8 +611,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='mintbin' PACKAGE_TARNAME='mintbin' -PACKAGE_VERSION='0.3' -PACKAGE_STRING='mintbin 0.3' +PACKAGE_VERSION='0.4' +PACKAGE_STRING='mintbin 0.4' PACKAGE_BUGREPORT='https://github.com/freemint/mintbin/issues/' PACKAGE_URL='https://github.com/freemint/mintbin/' @@ -1344,7 +1344,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures mintbin 0.3 to adapt to many kinds of systems. +\`configure' configures mintbin 0.4 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1415,7 +1415,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of mintbin 0.3:";; + short | recursive ) echo "Configuration of mintbin 0.4:";; esac cat <<\_ACEOF @@ -1517,7 +1517,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -mintbin configure 0.3 +mintbin configure 0.4 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -1835,7 +1835,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by mintbin $as_me 0.3, which was +It was created by mintbin $as_me 0.4, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3190,7 +3190,7 @@ fi # Define the identity of the package. PACKAGE='mintbin' - VERSION='0.3' + VERSION='0.4' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -7950,7 +7950,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by mintbin $as_me 0.3, which was +This file was extended by mintbin $as_me 0.4, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -8023,7 +8023,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -mintbin config.status 0.3 +mintbin config.status 0.4 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/configure.in b/configure.in index 2c9f45b..da2bd9e 100644 --- a/configure.in +++ b/configure.in @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(mintbin, 0.3, [https://github.com/freemint/mintbin/issues/],,[https://github.com/freemint/mintbin/]) +AC_INIT(mintbin, 0.4, [https://github.com/freemint/mintbin/issues/],,[https://github.com/freemint/mintbin/]) AC_CONFIG_SRCDIR(src/mintbin.c) AC_CANONICAL_HOST AM_CONFIG_HEADER(config.h) diff --git a/src/Makefile.am b/src/Makefile.am index 8eed9ea..1d93ced 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,7 +17,7 @@ LIBS = $(INTLLIBS) @LIBS@ noinst_HEADERS = exec.h symbols.h targets.h ar.h -bin_PROGRAMS = arconv cnm csize cstrip flags mintbin stack symex +bin_PROGRAMS = arconv cnm csize cstrip flags mintbin stack symex stripex arconv_SOURCES = arconv.c targets.c version.c cnm_SOURCES = cnm.c symbols.c targets.c version.c @@ -27,6 +27,7 @@ flags_SOURCES = flags.c targets.c version.c mintbin_SOURCES = mintbin.c targets.c version.c stack_SOURCES = stack.c symbols.c targets.c version.c symex_SOURCES = symex.c targets.c version.c +stripex_SOURCES = stripex.c install-exec-local: @$(NORMAL_INSTALL) diff --git a/src/Makefile.in b/src/Makefile.in index 457c889..a33c1d6 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -93,7 +93,7 @@ build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = arconv$(EXEEXT) cnm$(EXEEXT) csize$(EXEEXT) \ cstrip$(EXEEXT) flags$(EXEEXT) mintbin$(EXEEXT) stack$(EXEEXT) \ - symex$(EXEEXT) + symex$(EXEEXT) stripex$(EXEEXT) subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ @@ -142,6 +142,10 @@ am_stack_OBJECTS = stack.$(OBJEXT) symbols.$(OBJEXT) targets.$(OBJEXT) \ stack_OBJECTS = $(am_stack_OBJECTS) stack_LDADD = $(LDADD) stack_DEPENDENCIES = ../lib/libmb.a +am_stripex_OBJECTS = stripex.$(OBJEXT) +stripex_OBJECTS = $(am_stripex_OBJECTS) +stripex_LDADD = $(LDADD) +stripex_DEPENDENCIES = ../lib/libmb.a am_symex_OBJECTS = symex.$(OBJEXT) targets.$(OBJEXT) version.$(OBJEXT) symex_OBJECTS = $(am_symex_OBJECTS) symex_LDADD = $(LDADD) @@ -164,9 +168,10 @@ am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/arconv.Po ./$(DEPDIR)/cnm.Po \ ./$(DEPDIR)/csize.Po ./$(DEPDIR)/cstrip.Po \ ./$(DEPDIR)/flags.Po ./$(DEPDIR)/mintbin.Po \ - ./$(DEPDIR)/stack.Po ./$(DEPDIR)/symbols.Po \ - ./$(DEPDIR)/symex.Po ./$(DEPDIR)/targets.Po \ - ./$(DEPDIR)/tempname.Po ./$(DEPDIR)/version.Po + ./$(DEPDIR)/stack.Po ./$(DEPDIR)/stripex.Po \ + ./$(DEPDIR)/symbols.Po ./$(DEPDIR)/symex.Po \ + ./$(DEPDIR)/targets.Po ./$(DEPDIR)/tempname.Po \ + ./$(DEPDIR)/version.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -182,10 +187,10 @@ am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(arconv_SOURCES) $(cnm_SOURCES) $(csize_SOURCES) \ $(cstrip_SOURCES) $(flags_SOURCES) $(mintbin_SOURCES) \ - $(stack_SOURCES) $(symex_SOURCES) + $(stack_SOURCES) $(stripex_SOURCES) $(symex_SOURCES) DIST_SOURCES = $(arconv_SOURCES) $(cnm_SOURCES) $(csize_SOURCES) \ $(cstrip_SOURCES) $(flags_SOURCES) $(mintbin_SOURCES) \ - $(stack_SOURCES) $(symex_SOURCES) + $(stack_SOURCES) $(stripex_SOURCES) $(symex_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -350,6 +355,7 @@ flags_SOURCES = flags.c targets.c version.c mintbin_SOURCES = mintbin.c targets.c version.c stack_SOURCES = stack.c symbols.c targets.c version.c symex_SOURCES = symex.c targets.c version.c +stripex_SOURCES = stripex.c all: all-am .SUFFIXES: @@ -470,6 +476,10 @@ stack$(EXEEXT): $(stack_OBJECTS) $(stack_DEPENDENCIES) $(EXTRA_stack_DEPENDENCIE @rm -f stack$(EXEEXT) $(AM_V_CCLD)$(LINK) $(stack_OBJECTS) $(stack_LDADD) $(LIBS) +stripex$(EXEEXT): $(stripex_OBJECTS) $(stripex_DEPENDENCIES) $(EXTRA_stripex_DEPENDENCIES) + @rm -f stripex$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(stripex_OBJECTS) $(stripex_LDADD) $(LIBS) + symex$(EXEEXT): $(symex_OBJECTS) $(symex_DEPENDENCIES) $(EXTRA_symex_DEPENDENCIES) @rm -f symex$(EXEEXT) $(AM_V_CCLD)$(LINK) $(symex_OBJECTS) $(symex_LDADD) $(LIBS) @@ -487,6 +497,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flags.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mintbin.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stack.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stripex.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symbols.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symex.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/targets.Po@am__quote@ # am--include-marker @@ -646,6 +657,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/flags.Po -rm -f ./$(DEPDIR)/mintbin.Po -rm -f ./$(DEPDIR)/stack.Po + -rm -f ./$(DEPDIR)/stripex.Po -rm -f ./$(DEPDIR)/symbols.Po -rm -f ./$(DEPDIR)/symex.Po -rm -f ./$(DEPDIR)/targets.Po @@ -703,6 +715,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/flags.Po -rm -f ./$(DEPDIR)/mintbin.Po -rm -f ./$(DEPDIR)/stack.Po + -rm -f ./$(DEPDIR)/stripex.Po -rm -f ./$(DEPDIR)/symbols.Po -rm -f ./$(DEPDIR)/symex.Po -rm -f ./$(DEPDIR)/targets.Po diff --git a/src/stripex.c b/src/stripex.c new file mode 100644 index 0000000..8cf2446 --- /dev/null +++ b/src/stripex.c @@ -0,0 +1,684 @@ +/* + * stripex.c + * strip symbol table, GNU binutils aexec header, and ELF headers from TOS executables. + * Needed e.g. for SLB shared library files or CPX modules to get the header back + * to the start of the text segment. + * + * BUGS: + * - relocation table is not checked for validity. If it is corrupted, + * this program might crash and/or create corrupted output files. + * Should rarely be a problem, since the kernel does not check the table either. + */ + +#include +#include +#include +#include +#include +#include + +#ifndef O_BINARY +# ifdef _O_BINARY +# define O_BINARY _O_BINARY +# else +# define O_BINARY 0 +# endif +#endif + +#define NEWBUFSIZ 16384L + +static unsigned char mybuf[NEWBUFSIZ]; +static int verbose; +static int force; + +static char tmpname[1024]; + +#if defined(__atarist__) && defined(__GNUC__) +long _stksize = 30000; +#endif + +struct aexec +{ + short a_magic; /* magic number */ + unsigned long a_text; /* size of text segment */ + unsigned long a_data; /* size of initialized data */ + unsigned long a_bss; /* size of uninitialized data */ + unsigned long a_syms; /* size of symbol table */ + unsigned long a_res1; /* see below */ + unsigned long a_AZero2; /* always zero */ + unsigned short a_isreloc; /* is reloc info present */ +}; + +#define CMAGIC 0x601A /* contiguous text */ + +/* + * magic values in a_res1 field + */ +#define EXEC_FMT_TOS 0 /* original TOS */ +#define EXEC_FMT_MINT 0x4d694e54UL /* binutils a.out format */ +#define EXEC_FMT_ELF 0x454c4600UL /* new binutils PRG+ELF format */ + + +#define SIZEOF_SHORT 2 +#define SIZEOF_LONG 4 + +/* + * sizeof(struct aexec) + */ +#define SIZEOF_AEXEC (SIZEOF_SHORT + (6 * SIZEOF_LONG) + SIZEOF_SHORT) +/* + * extra header added by a.out-mintprg format + */ +#define SIZEOF_BINUTILS_AEXEC 228 + + +static char *f_basename(char *name) +{ + char *p1, *p2; + + p1 = strrchr(name, '/'); + p2 = strrchr(name, '\\'); + if (p1 == NULL || p2 > p1) + p1 = p2; + if (p1 == NULL) + p1 = name; + else + p1++; + return p1; +} + + +static unsigned short read_beword(const unsigned char *p) +{ + return (p[0] << 8) | p[1]; +} + + +static unsigned long read_belong(const unsigned char *p) +{ + return ((unsigned long)read_beword(p) << 16) | read_beword(p + 2); +} + + +static void write_beword(unsigned char *p, unsigned short v) +{ + p[0] = (v >> 8) & 0xff; + p[1] = (v ) & 0xff; +} + + +static void write_belong(unsigned char *p, unsigned long v) +{ + p[0] = (unsigned char)((v >> 24) & 0xff); + p[1] = (unsigned char)((v >> 16) & 0xff); + p[2] = (unsigned char)((v >> 8) & 0xff); + p[3] = (unsigned char)((v ) & 0xff); +} + + +/* + * read header -- return -1 on error + */ +static int read_head(int fd, struct aexec *a) +{ + unsigned char buf[SIZEOF_AEXEC]; + + if (read(fd, buf, SIZEOF_AEXEC) != SIZEOF_AEXEC) + return -1; + a->a_magic = read_beword(buf); + a->a_text = read_belong(buf + 2); + a->a_data = read_belong(buf + 6); + a->a_bss = read_belong(buf + 10); + a->a_syms = read_belong(buf + 14); + a->a_res1 = read_belong(buf + 18); + a->a_AZero2 = read_belong(buf + 22); + a->a_isreloc = read_beword(buf + 26); + + return SIZEOF_AEXEC; +} + + +static int write_head(int fd, const struct aexec *a) +{ + unsigned char buf[SIZEOF_AEXEC]; + + write_beword(buf, a->a_magic); + write_belong(buf + 2, a->a_text); + write_belong(buf + 6, a->a_data); + write_belong(buf + 10, a->a_bss); + write_belong(buf + 14, a->a_syms); + write_belong(buf + 18, a->a_res1); + write_belong(buf + 22, a->a_AZero2); + write_beword(buf + 26, a->a_isreloc); + + if (write(fd, buf, SIZEOF_AEXEC) != SIZEOF_AEXEC) + return -1; + return SIZEOF_AEXEC; +} + + +/* + * copy from, to in NEWBUFSIZ chunks upto bytes or EOF whichever occurs first + * returns # of bytes copied + */ +static long copy(int from, int to, long bytes) +{ + long todo; + long done = 0; + long remaining = bytes; + long actual; + + while (done != bytes) + { + todo = (remaining > NEWBUFSIZ) ? NEWBUFSIZ : remaining; + if ((actual = read(from, mybuf, todo)) != todo) + { + if (actual < 0) + { + fprintf(stderr, "Error Reading\n"); + return -done; + } + } + if (write(to, mybuf, actual) != actual) + { + fprintf(stderr, "Error Writing\n"); + return -done; + } + done += actual; + if (actual != todo) /* eof reached */ + return done; + remaining -= actual; + } + return done; +} + + +static int relocate(const char *name, int fd, long reloc_pos, long bytes_to_delete) +{ + if (lseek(fd, reloc_pos, SEEK_SET) != reloc_pos) + { + perror(name); + return 0; + } + if (read(fd, mybuf, SIZEOF_LONG) != SIZEOF_LONG) + { + perror(name); + return 0; + } + write_belong(mybuf, read_belong(mybuf) - bytes_to_delete); + if (lseek(fd, reloc_pos, SEEK_SET) != reloc_pos) + { + perror(name); + return 0; + } + if (write(fd, mybuf, SIZEOF_LONG) != SIZEOF_LONG) + { + perror(name); + return 0; + } + return 1; +} + + +/* copy TOS relocation table from `from` to `to`. Copy bytes until NUL byte or + first 4 bytes if == 0. + returns length of relocation table or -1 in case of an error */ + +static long copy_relocs(const char *name, int from, int to, long bytes_to_delete, long image_size) +{ + long res = 0; + long bytes; + long rbytes = 0; + long first_relo; + long reloc_pos; + long limit; + + res = read(from, mybuf, SIZEOF_LONG); + if (res != 0 && res != SIZEOF_LONG) + { + fprintf(stderr, "Error reading\n"); + return -1; + } + + if (res == 0) + { + /* I think empty relocation tables are allowed, + but could cause trouble with certain programs */ + fprintf(stderr, "Warning: %s: No relocation table\n", name); + return 0; + } + first_relo = read_belong(mybuf); + if (first_relo != 0) + { + /* + * The initially designed elf format had some relocations in the header + * that we are about to remove. + * Supporting that would require to rebuild the relocation table. + * The current format does not have such relocations anymore, + * but better check for it. + */ + if (first_relo <= bytes_to_delete) + { + fprintf(stderr, "%s: first relocation at 0x%08lx is in area to be deleted\n", name, first_relo); + fprintf(stderr, "update your binutils\n"); + return -1; + } + first_relo -= bytes_to_delete; + write_belong(mybuf, first_relo); + } + + if (write(to, mybuf, res) != res) + { + fprintf(stderr, "%s: Error writing\n", name); + return -1; + } + + rbytes = SIZEOF_LONG; + if (first_relo == 0) + return rbytes; /* This is a clean version of an empty + relocation table */ + + reloc_pos = SIZEOF_AEXEC + first_relo; + if (relocate(name, to, reloc_pos, bytes_to_delete) == 0) + return -1; + limit = SIZEOF_AEXEC + image_size; + + for (;;) + { + lseek(to, 0, SEEK_END); + if ((bytes = read(from, mybuf, 1)) < 0) + { + fprintf(stderr, "%s: Error reading\n", name); + return -1; + } + if (bytes == 0) + { + fprintf(stderr, "Warning: %s: Unterminated relocation table\n", name); + return rbytes; + } + if (write(to, mybuf, bytes) != bytes) + { + fprintf(stderr, "%s: Error writing\n", name); + return -1; + } + rbytes += bytes; + if (mybuf[0] == 0) + break; + if (mybuf[0] == 1) + { + reloc_pos += 254; + } else + { + reloc_pos += mybuf[0]; + /* + * check that we don't try to write beyond the new image size. + * This too, could happen with early version of new elf binutils + */ + if (reloc_pos >= limit) + { + fprintf(stderr, "%s: relocation at 0x%08lx is outside image\n", name, reloc_pos); + fprintf(stderr, "update your binutils\n"); + return -1; + } + if (relocate(name, to, reloc_pos, bytes_to_delete) == 0) + return -1; + } + } + return rbytes; +} + + +static int strip(const char *name) +{ + int fd; + int tfd; + long count, rbytes, sbytes; + struct aexec ahead; + unsigned char buf[13 * SIZEOF_LONG]; + unsigned long magic1, magic2; + long bytes_to_delete; + + if ((fd = open(name, O_RDONLY | O_BINARY, 0755)) < 0) + { + perror(name); + return 1; + } + if ((tfd = open(tmpname, O_RDWR | O_BINARY | O_TRUNC | O_CREAT, 0755)) < 0) + { + perror(tmpname); + close(fd); + return 1; + } + + /* + * read g_jump_entry and first 8 longs of exec header + */ + if (read_head(fd, &ahead) < 0 || + (int)read(fd, buf, 8 * SIZEOF_LONG) != 8 * SIZEOF_LONG) + { + perror(name); + close(tfd); + close(fd); + return 1; + } + if (ahead.a_magic != CMAGIC) + { + fprintf(stderr, "%s: Bad Magic number\n", name); + close(tfd); + close(fd); + return 1; + } + if (ahead.a_res1 == EXEC_FMT_MINT) /* 'MiNT' extended exec header magic */ + { + long e_entry; + + bytes_to_delete = SIZEOF_BINUTILS_AEXEC; + + /* + * read trampoline code + */ + magic1 = read_belong(buf); + magic2 = read_belong(buf + SIZEOF_LONG); + if (!((magic1 == 0x283a001aUL && magic2 == 0x4efb48faUL) || /* Original binutils */ + (magic1 == 0x203a001aUL && magic2 == 0x4efb08faUL))) /* binutils >= 2.18-mint-20080209 */ + { + fprintf(stderr, "%s: no aexec header\n", name); + close(tfd); + close(fd); + return 1; + } +#if 0 + magic1 = read_belong(buf + 2 * SIZEOF_LONG); + printf("e_info: %08lx\n", magic1); + magic1 = read_belong(buf + 3 * SIZEOF_LONG); + printf("e_text: %08lx\n", magic1); + magic1 = read_belong(buf + 4 * SIZEOF_LONG); + printf("e_data: %08lx\n", magic1); + magic1 = read_belong(buf + 5 * SIZEOF_LONG); + printf("e_bss: %08lx\n", magic1); + magic1 = read_belong(buf + 6 * SIZEOF_LONG); + printf("e_syms: %08lx\n", magic1); +#endif + e_entry = read_belong(buf + 7 * SIZEOF_LONG); +#if 0 + printf("e_entry: %08lx\n", e_entry); +#endif + if (e_entry != bytes_to_delete) + { + fprintf(stderr, "%s: warning: entry %08lx not at start of text segment\n", name, e_entry); + if (!force) + { + fprintf(stderr, "use -f to override\n"); + close(tfd); + close(fd); + return 1; + } + } + } else if ((ahead.a_res1 & 0xffffff00UL) == EXEC_FMT_ELF && (ahead.a_res1 & 0xff) >= 40) + { + long elf_offset; + long e_phoff; + long e_entry; + long p_offset; + unsigned short e_phnum; + + /* + * offset of ELF header is determined by last byte of a_res1 (normally 40) + */ + elf_offset = (ahead.a_res1 & 0xff); + /* + * read trampoline code + */ + magic1 = read_belong(buf); + magic2 = read_belong(buf + SIZEOF_LONG); + if (magic1 != (0x203a0000UL | (elf_offset + 24 - 30)) || magic2 != 0x4efb08faUL) /* binutils >= 2.41-mintelf */ + { + fprintf(stderr, "%s: no exec header\n", name); + close(tfd); + close(fd); + return 1; + } +#if 0 + magic1 = read_belong(buf + 2 * SIZEOF_LONG); + printf("_stksize: @%08lx\n", magic1); +#endif + if (lseek(fd, elf_offset, SEEK_SET) < 0 || + (int)read(fd, buf, 13 * SIZEOF_LONG) != 13 * SIZEOF_LONG) + { + perror(name); + close(tfd); + close(fd); + return 1; + } + /* + * We will find a complete ELF header there, + * but only care here about e_magic & e_entry + */ + magic1 = read_belong(buf); + e_phnum = read_beword(buf + 44); + if (magic1 != 0x7f454c46UL || e_phnum == 0) + { + fprintf(stderr, "%s: no ELF header\n", name); + close(tfd); + close(fd); + return 1; + } + e_entry = read_belong(buf + 6 * SIZEOF_LONG); + e_phoff = read_belong(buf + 7 * SIZEOF_LONG); + /* + * read first program header + */ + if (lseek(fd, e_phoff, SEEK_SET) < 0 || + (int)read(fd, buf, 8 * SIZEOF_LONG) != 8 * SIZEOF_LONG) + { + perror(name); + close(tfd); + close(fd); + return 1; + } + /* + * get start of text segment + */ + p_offset = read_belong(buf + SIZEOF_LONG); + bytes_to_delete = e_phoff + e_phnum * 32 - SIZEOF_AEXEC; + if (p_offset != SIZEOF_AEXEC || e_entry != bytes_to_delete) + { + fprintf(stderr, "%s: warning: entry %08lx not at start of text segment %08lx\n", name, e_entry, bytes_to_delete); + if (!force) + { + fprintf(stderr, "use -f to override\n"); + close(tfd); + close(fd); + return 1; + } + } + } else if (ahead.a_res1 == EXEC_FMT_TOS) + { + if (ahead.a_syms == 0) + { + fprintf(stderr, "%s: already stripped\n", name); + close(tfd); + close(fd); + return 0; + } + bytes_to_delete = 0; + } else + { + fprintf(stderr, "%s: unsupported file format\n", name); + close(tfd); + close(fd); + return 1; + } + + sbytes = ahead.a_syms; + if (verbose) + { + printf("%s: text=0x%lx, data=0x%lx, syms=0x%lx\n", name, ahead.a_text, ahead.a_data, ahead.a_syms); + } + + ahead.a_syms = 0; + ahead.a_text -= bytes_to_delete; + ahead.a_res1 = 0; + if (write_head(tfd, &ahead) < 0) + { + perror(name); + close(tfd); + close(fd); + return 1; + } + if (lseek(fd, bytes_to_delete + SIZEOF_AEXEC, SEEK_SET) < 0) + { + perror(name); + close(tfd); + close(fd); + return 1; + } + if (verbose) + { + printf("%s: skipped 0x%lx bytes extra header\n", name, bytes_to_delete); + } + + count = ahead.a_text + ahead.a_data; + if (copy(fd, tfd, count) != count) + { + close(tfd); + close(fd); + return 1; + } + if (verbose) + { + printf("%s: copied 0x%lx bytes text+data\n", name, count); + } + if (lseek(fd, sbytes, SEEK_CUR) < 0) + { + perror(name); + close(tfd); + close(fd); + return 1; + } + if (sbytes != 0 && verbose) + { + printf("%s: skipped 0x%lx bytes symbols\n", name, sbytes); + } + rbytes = 0; + if (ahead.a_isreloc == 0) + { + if ((rbytes = copy_relocs(name, fd, tfd, bytes_to_delete, count)) < 0) + { + close(tfd); + close(fd); + return 1; + } + if (verbose) + { + printf("%s: copied 0x%lx bytes relocation table\n", name, rbytes); + } + } + if (verbose) + { + unsigned long pos, size; + + pos = lseek(fd, 0, SEEK_CUR); + lseek(fd, 0, SEEK_END); + size = lseek(fd, 0, SEEK_CUR); + lseek(fd, pos, SEEK_SET); + if (size > pos) + { + /* could be pure-c debug information, after end of relocation table */ + printf("%s: skipped 0x%lx bytes trailer\n", name, size - pos); + } + } + + close(tfd); + close(fd); + if (rename(tmpname, name) == 0) + return 0; /* try to rename it */ + if ((fd = open(name, O_WRONLY | O_BINARY | O_TRUNC | O_CREAT, 0755)) < 0) + { + perror(name); + return 1; + } + if ((tfd = open(tmpname, O_RDONLY | O_BINARY, 0755)) < 0) + { + perror(tmpname); + close(fd); + return 1; + } + + count = SIZEOF_AEXEC + ahead.a_text + ahead.a_data + rbytes; + if (copy(tfd, fd, count) != count) + { + close(tfd); + close(fd); + return 1; + } + close(tfd); + close(fd); + return 0; +} + + +static void usage(const char *s) +{ + fprintf(stderr, "%s", s); + fprintf(stderr, "Usage: stripex [-f] files ...\n"); + fprintf(stderr, "strip GNU-binutils aexec/elf header from executables\n"); +} + + +int main(int argc, char **argv) +{ + int status = 0; + + verbose = 0; + + /* process arguments */ + while (argv++, --argc) + { + if ('-' != **argv) + break; + (*argv)++; + switch (**argv) + { + case 'v': + verbose = 1; + break; + case 'f': + force = 1; + break; + default: + usage(""); + return 1; + } + } + + if (argc < 1) + { + usage(""); + return 1; + } + + do + { + const char *filename = *argv++; + char *base; + int fd; + + strcpy(tmpname, filename); + base = f_basename(tmpname); + strcpy(base, "STXXXXXX"); + fd = mkstemp(tmpname); + if (fd < 0) + { + perror(tmpname); + status |= 1; + } else + { + close(fd); + status |= strip(filename); + unlink(tmpname); + } + } while (--argc > 0); + + return status; +}