mirror of https://github.com/upx/upx.git
WIP: smallest executable on Apple M1 (aarch64 or arm64)
Requirements for success after snipping a loader_command: "codesign -s - my_app" must succeed. "lldb my_app; process launch -s; continue" must succeed. Optional loader_commands (macho-snip can remove these successfully): LC_UUID, LC_BUILD_VERSION, LC_SOURCE_VERISON, LC_DATA_IN_CODE (when 0==datasize) Apple "strip -N" clears out LC_SYMTAB and LC_DYSYMTAB, but leaves LC_DYLD_INFO_ONLY.export_size. Perhaps this could be zero if constructed that way; snipping seems tedious because codesign requires that __LINKEDIT must have no gaps. LC_FUNCTION_STARTS seems to be required by codesign. codesign wants offsets that point into __LINKEDIT to be in order: LC_DYLD_INFO_ONLY, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_SYMTAB MacOS seesm to require LC_LOAD_DYLINKER (else "zsh: Kiled"), which seems to require LD_LOAD_DYLIB (else SIGABRT). https://github.com/upx/upx/issues/446 ---- modified: macho-snip.c modified: udf.s
This commit is contained in:
parent
480ab51650
commit
252143d0bb
|
@ -247,10 +247,11 @@ main(int argc, char const * /*const*/ *const argv, char const *const *const envp
|
||||||
unsigned ncmds = mhdr->ncmds;
|
unsigned ncmds = mhdr->ncmds;
|
||||||
unsigned headway = mhdr->sizeofcmds;
|
unsigned headway = mhdr->sizeofcmds;
|
||||||
struct load_command *cmd = (struct load_command *)(1+ mhdr);
|
struct load_command *cmd = (struct load_command *)(1+ mhdr);
|
||||||
for (; ncmds; --ncmds) {
|
struct load_command *cmd_next;
|
||||||
|
unsigned delta_dataoff = 0;
|
||||||
|
for (; ncmds; --ncmds, cmd = cmd_next) {
|
||||||
unsigned end_dataoff = 0;
|
unsigned end_dataoff = 0;
|
||||||
unsigned end_datasize = 0;
|
unsigned end_datasize = 0;
|
||||||
struct load_command *cmd_next;
|
|
||||||
again: ;
|
again: ;
|
||||||
fprintf(stderr, "cmd@%p %s %d(%#x)\n",
|
fprintf(stderr, "cmd@%p %s %d(%#x)\n",
|
||||||
cmd, cmd_names[cmd->cmd&0xFF].name, cmd->cmd&0xFF, cmd->cmd);
|
cmd, cmd_names[cmd->cmd&0xFF].name, cmd->cmd&0xFF, cmd->cmd);
|
||||||
|
@ -271,15 +272,7 @@ again: ;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case LC_CODE_SIGNATURE: {
|
case LC_CODE_SIGNATURE: {
|
||||||
fprintf(stderr, "macho-snip: LC_CODE_SIGNATURE not implemented\n");
|
fprintf(stderr, "macho-snip: use 'codesign --remove-signature' to remove LC_CODE_SIGNATURE\n");
|
||||||
continue;
|
|
||||||
struct linkedit_data_command *cmd_LED = (struct linkedit_data_command *)cmd;
|
|
||||||
if (( cmd_LED->dataoff + cmd_LED->datasize )
|
|
||||||
== (linkedit->fileoff + linkedit->filesize)) {
|
|
||||||
linkedit->filesize -= cmd_LED->datasize;
|
|
||||||
}
|
|
||||||
memset(addr + linkedit->fileoff, 0, cmdsize);
|
|
||||||
goto snip;
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
//struct nlist_64 {
|
//struct nlist_64 {
|
||||||
|
@ -305,6 +298,8 @@ again: ;
|
||||||
// value per 4KB page) has offset (4 mod 16) instead of (0 mod 16).]
|
// value per 4KB page) has offset (4 mod 16) instead of (0 mod 16).]
|
||||||
|
|
||||||
case LC_SYMTAB: {
|
case LC_SYMTAB: {
|
||||||
|
fprintf(stderr, "macho-snip: LC_SYMTAB skipped\n");
|
||||||
|
continue;
|
||||||
struct symtab_command *symcmd = (struct symtab_command *)cmd;
|
struct symtab_command *symcmd = (struct symtab_command *)cmd;
|
||||||
if (( symcmd->strsize + symcmd->stroff)
|
if (( symcmd->strsize + symcmd->stroff)
|
||||||
!= (linkedit->filesize + linkedit->fileoff)) {
|
!= (linkedit->filesize + linkedit->fileoff)) {
|
||||||
|
@ -328,6 +323,8 @@ again: ;
|
||||||
symcmd->nsyms -= 1; // lop last symbol FIXME: generalize
|
symcmd->nsyms -= 1; // lop last symbol FIXME: generalize
|
||||||
} break;
|
} break;
|
||||||
case LC_DYSYMTAB: {
|
case LC_DYSYMTAB: {
|
||||||
|
fprintf(stderr, "macho-snip: LD_DYSYMTAB skipped\n");
|
||||||
|
continue;
|
||||||
struct dysymtab_command *dysym = (struct dysymtab_command *)cmd;
|
struct dysymtab_command *dysym = (struct dysymtab_command *)cmd;
|
||||||
if (0==(dysym->nundefsym -= 1)) { // FIXME: generalize
|
if (0==(dysym->nundefsym -= 1)) { // FIXME: generalize
|
||||||
dysym->iundefsym = 0;
|
dysym->iundefsym = 0;
|
||||||
|
@ -335,15 +332,25 @@ again: ;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case LC_BUILD_VERSION:
|
case LC_BUILD_VERSION:
|
||||||
|
case LC_DYLD_INFO: // also LC_DYLD_INFO_ONLY because low 8 bits
|
||||||
case LC_LOAD_DYLIB:
|
case LC_LOAD_DYLIB:
|
||||||
case LC_LOAD_DYLINKER:
|
case LC_LOAD_DYLINKER:
|
||||||
case LC_MAIN:
|
case LC_MAIN:
|
||||||
case LC_SOURCE_VERSION: {
|
case LC_SOURCE_VERSION:
|
||||||
|
case LC_UUID:
|
||||||
|
{
|
||||||
for (jargv = 2; jargv < argc; ++jargv) {
|
for (jargv = 2; jargv < argc; ++jargv) {
|
||||||
if (argv[jargv] && !strcmp(cmd_names[cmd->cmd & 0xFF].name, argv[jargv])) {
|
if (argv[jargv] && !strcmp(cmd_names[cmd->cmd & 0xFF].name, argv[jargv])) {
|
||||||
argv_done |= 1uL << jargv;
|
argv_done |= 1uL << jargv;
|
||||||
fprintf(stderr, "macho-snip: %#x, %s\n",
|
fprintf(stderr, "macho-snip: %#x, %s\n",
|
||||||
cmd_names[cmd->cmd & 0xFF].val, cmd_names[cmd->cmd & 0xFF].name);
|
cmd_names[cmd->cmd & 0xFF].val, cmd_names[cmd->cmd & 0xFF].name);
|
||||||
|
// EXPERIMENT:
|
||||||
|
if (cmd->cmd == LC_DYLD_INFO_ONLY) { // the "must process" case
|
||||||
|
struct dyld_info_command *dyldcmd = (struct dyld_info_command *)cmd;
|
||||||
|
dyldcmd->export_off = 0;
|
||||||
|
dyldcmd->export_size = 0;
|
||||||
|
goto next; // EXPERIMENT
|
||||||
|
}
|
||||||
goto snip;
|
goto snip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,8 +373,7 @@ again: ;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
cmd = (struct load_command *)(cmdsize + (void *)cmd);
|
continue; // no changes ==> advance
|
||||||
continue;
|
|
||||||
snip_linkedit_data_command: ;
|
snip_linkedit_data_command: ;
|
||||||
struct linkedit_data_command *ldc = (struct linkedit_data_command *)cmd;
|
struct linkedit_data_command *ldc = (struct linkedit_data_command *)cmd;
|
||||||
end_datasize = ldc->datasize;
|
end_datasize = ldc->datasize;
|
||||||
|
@ -379,12 +385,12 @@ snip_linkedit_data_command: ;
|
||||||
snip: ;
|
snip: ;
|
||||||
memmove(cmd, cmd_next, headway);
|
memmove(cmd, cmd_next, headway);
|
||||||
memset(headway + (char *)cmd, 0, cmdsize); // space that was vacated
|
memset(headway + (char *)cmd, 0, cmdsize); // space that was vacated
|
||||||
cmd_next = cmd; // we moved *cmd_next to *cmd
|
cmd_next = cmd; // we moved tail at *cmd_next to *cmd
|
||||||
mhdr->sizeofcmds -= cmdsize;
|
mhdr->sizeofcmds -= cmdsize;
|
||||||
mhdr->ncmds -= 1;
|
mhdr->ncmds -= 1;
|
||||||
argv[jargv] = 0; // snip only once per argv[]
|
argv[jargv] = 0; // snip only once per argv[]
|
||||||
} // switch
|
} // switch
|
||||||
cmd = cmd_next;
|
next: ;
|
||||||
} // ncmds
|
} // ncmds
|
||||||
argv_done |= (1<<1) | (1<<0); // argv[0,1] do not name linker_commands
|
argv_done |= (1<<1) | (1<<0); // argv[0,1] do not name linker_commands
|
||||||
if (~(~0uL << argc) != argv_done) {
|
if (~(~0uL << argc) != argv_done) {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
.align 4
|
.align 4
|
||||||
// start: .globl start # for standalone
|
// start: .globl start /* for standalone */
|
||||||
_main: .globl _main # for -lc
|
_main: .globl _main /* for -lc */
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
ud2
|
ud2
|
||||||
#endif
|
#endif
|
||||||
#ifdef __AARCH64EL__
|
#ifdef __AARCH64EL__
|
||||||
.int 0x7b # udf 123
|
.int 0x7b /* udf 123 */
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue