From 3fefaa73620b95fe10e59eb78fd1a1ab8a1a7620 Mon Sep 17 00:00:00 2001 From: "jreiser@BitWagon.com" Date: Sat, 10 Sep 2016 15:02:13 -0700 Subject: [PATCH] revamp MH_EXECUTE: try to meet expectations of MacOSX 10.12 Sierra changed src/p_mach.cpp changed src/stub/src/amd64-darwin.macho-upxmain.c --- src/p_mach.cpp | 214 ++++++++++++++++++---- src/stub/src/amd64-darwin.macho-upxmain.c | 106 ++++++++--- 2 files changed, 261 insertions(+), 59 deletions(-) diff --git a/src/p_mach.cpp b/src/p_mach.cpp index aa9dc411..eb7a50ae 100644 --- a/src/p_mach.cpp +++ b/src/p_mach.cpp @@ -328,7 +328,13 @@ void PackMachI386::addStubEntrySections(Filter const *ft) void PackMachAMD64::addStubEntrySections(Filter const * /*ft*/) { - addLoader("MACHMAINX", NULL); + if (my_filetype!=Mach_header::MH_EXECUTE) { + addLoader("MACHMAINX", NULL); + } + else { + addLoader("AMD64BXX", NULL); + } + addLoader("MACH_UNC", NULL); //addLoader(getDecompressorSections(), NULL); addLoader( ( M_IS_NRV2E(ph.method) ? "NRV_HEAD,NRV2E,NRV_TAIL" @@ -338,7 +344,10 @@ void PackMachAMD64::addStubEntrySections(Filter const * /*ft*/) : NULL), NULL); if (hasLoaderSection("CFLUSH")) addLoader("CFLUSH"); - addLoader("MACHMAINY,IDENTSTR,+40,MACHMAINZ,FOLDEXEC", NULL); + addLoader("MACHMAINY,IDENTSTR,+40,MACHMAINZ", NULL); + if (my_filetype!=Mach_header::MH_EXECUTE) { + addLoader("FOLDEXEC", NULL); + } } void PackMachARMEL::addStubEntrySections(Filter const * /*ft*/) @@ -457,11 +466,29 @@ PackMachI386::buildLoader(const Filter *ft) void PackMachAMD64::buildLoader(const Filter *ft) { - if (my_filetype==Mach_header::MH_EXECUTE) { + if (0 && my_filetype==Mach_header::MH_EXECUTE) { initLoader(NULL, 0); - linker->addSection("UPXMAIN", stub_amd64_darwin_macho_upxmain_exe, - sizeof(stub_amd64_darwin_macho_upxmain_exe), 0); - addLoader("UPXMAIN", NULL); + addStubEntrySections(ft); + + defineSymbols(ft); + relocateLoader(); +if (0) { + Mach_command const *ptr1 = (Mach_command const *)(1+ + (Mach_header const *)stub_amd64_darwin_macho_upxmain_exe); + for (unsigned j = 0; j < mhdro.ncmds; ++j, + ptr1 = (Mach_command const *)(ptr1->cmdsize + (char const *)ptr1)) + switch (ptr1->cmd) { + case Mach_segment_command::LC_SEGMENT_64: { + Mach_segment_command const *const segptr = (Mach_segment_command const *)ptr1; + if (!strcmp("__TEXT", segptr->segname)) { + Mach_section_command const *const secptr = (Mach_section_command const *)(1+ segptr); + linker->addSection("UPXMAIN", &stub_amd64_darwin_macho_upxmain_exe[secptr->offset], + secptr->size, 0); + addLoader("UPXMAIN", NULL); + } + } break; + } // end switch +} } else { buildMachLoader( @@ -748,25 +775,115 @@ void PackMachAMD64::pack4(OutputFile *fo, Filter &ft) // append PackHeader secXHDR.offset -= sizeof(linkitem); } secXHDR.addr += secXHDR.offset; - unsigned foff1 = (PAGE_MASK & (~PAGE_MASK + segTEXT.filesize)); - if (foff1 < segTEXT.vmsize) - foff1 += PAGE_SIZE; // codesign disallows overhang - segLINK.fileoff = foff1; - segLINK.vmaddr = segTEXT.vmaddr + foff1; - fo->seek(foff1 - 1, SEEK_SET); fo->write("", 1); - fo->seek(sizeof(mhdro), SEEK_SET); - fo->rewrite(&segZERO, sizeof(segZERO)); - fo->rewrite(&segXHDR, sizeof(segXHDR)); - fo->rewrite(&secXHDR, sizeof(secXHDR)); - fo->rewrite(&segTEXT, sizeof(segTEXT)); - fo->rewrite(&secTEXT, sizeof(secTEXT)); - fo->rewrite(&cmdUUID, sizeof(cmdUUID)); - fo->rewrite(&segLINK, sizeof(segLINK)); - fo->rewrite(&threado, sizeof(threado)); - if (my_filetype==Mach_header::MH_EXECUTE) { - fo->rewrite(&linkitem, sizeof(linkitem)); + unsigned offLINK = (PAGE_MASK & (~PAGE_MASK + segTEXT.filesize)); + if (offLINK < segTEXT.vmsize) + offLINK += PAGE_SIZE; // codesign disallows overhang + segLINK.fileoff = offLINK; + segLINK.vmaddr = segTEXT.vmaddr + offLINK; + if (0) { + fo->seek(offLINK - 1, SEEK_SET); fo->write("", 1); + fo->seek(sizeof(mhdro), SEEK_SET); + fo->rewrite(&segZERO, sizeof(segZERO)); + fo->rewrite(&segXHDR, sizeof(segXHDR)); + fo->rewrite(&secXHDR, sizeof(secXHDR)); + fo->rewrite(&segTEXT, sizeof(segTEXT)); + fo->rewrite(&secTEXT, sizeof(secTEXT)); + fo->rewrite(&cmdUUID, sizeof(cmdUUID)); + fo->rewrite(&segLINK, sizeof(segLINK)); + fo->rewrite(&threado, sizeof(threado)); + if (my_filetype==Mach_header::MH_EXECUTE) { + fo->rewrite(&linkitem, sizeof(linkitem)); + } + fo->rewrite(&linfo, sizeof(linfo)); + } + if (my_filetype == Mach_header::MH_EXECUTE) { + unsigned cmdsize = mhdro.sizeofcmds - sizeof(segXHDR); + Mach_header const *const ptr0 = (Mach_header const *)stub_amd64_darwin_macho_upxmain_exe; + Mach_command const *ptr1 = (Mach_command const *)(1+ ptr0); + unsigned delta = 0; + for (unsigned j = 0; j < mhdro.ncmds -1; ++j, + (cmdsize -= ptr1->cmdsize), + ptr1 = (Mach_command const *)(ptr1->cmdsize + (char const *)ptr1)) + switch (ptr1->cmd) { + case Mach_segment_command::LC_SEGMENT_64: { + Mach_segment_command const *const segptr = (Mach_segment_command const *)ptr1; + if (!strcmp("__TEXT", segptr->segname)) { + Mach_section_command const *const secptr = (Mach_section_command const *)(1+ segptr); + memcpy(&secTEXT, secptr, sizeof(secTEXT)); + secTEXT.align = 0; + unsigned const d = getLoaderSize(); + secTEXT.addr -= d; + secTEXT.size += d; + secTEXT.offset -= d; + fo->seek((char const *)secptr - (char const *)ptr0, SEEK_SET); + fo->rewrite(&secTEXT, sizeof(secTEXT)); + fo->seek(secTEXT.offset, SEEK_SET); + fo->rewrite(getLoader(), d); + fo->seek(0, SEEK_END); + } + if (!strcmp("__LINKEDIT", ((Mach_segment_command const *)ptr1)->segname)) { + memcpy(&segLINK, segptr, sizeof(segLINK)); + delta = offLINK - segLINK.fileoff; // relocation constant + + // Mach_command __LINKEDIT + // The contents remain the same, but at a different offset in the file + fo->seek(offLINK, SEEK_SET); + fo->write(&stub_amd64_darwin_macho_upxmain_exe[segLINK.fileoff], segLINK.filesize); + + // Update the __LINKEDIT header + segLINK.vmaddr += delta; + segLINK.fileoff = offLINK; + fo->seek((char const *)ptr1 - (char const *)ptr0, SEEK_SET); + fo->rewrite(&segLINK, sizeof(segLINK)); + + // Mach_segment_command for new segXHDR + segXHDR.cmdsize = sizeof(segXHDR); + segXHDR.nsects = 0; + fo->rewrite(&segXHDR, sizeof(segXHDR)); + } + } break; + case Mach_segment_command::LC_DYLD_INFO_ONLY: { + N_Mach::Mach_dyld_info_only_command blk; memcpy(&blk, ptr1, sizeof(blk)); + if (blk.rebase_off) blk.rebase_off += delta; + if (blk.bind_off) blk.bind_off += delta; + if (blk.lazy_bind_off) blk.lazy_bind_off += delta; + if (blk.export_off) blk.export_off += delta; + fo->seek(sizeof(segXHDR) + ((char const *)ptr1 - (char const *)ptr0), SEEK_SET); + fo->rewrite(&blk, sizeof(blk)); + } break; + case Mach_segment_command::LC_SYMTAB: { + Mach_symtab_command blk; memcpy(&blk, ptr1, sizeof(blk)); + if (blk.symoff) blk.symoff += delta; + if (blk.stroff) blk.stroff += delta; + fo->seek(sizeof(segXHDR) + ((char const *)ptr1 - (char const *)ptr0), SEEK_SET); + fo->rewrite(&blk, sizeof(blk)); + } break; + case Mach_segment_command::LC_DYSYMTAB: { + Mach_dysymtab_command blk; memcpy(&blk, ptr1, sizeof(blk)); + if (blk.tocoff) blk.tocoff += delta; + if (blk.modtaboff) blk.modtaboff += delta; + if (blk.extrefsymoff) blk.extrefsymoff += delta; + if (blk.indirectsymoff) blk.indirectsymoff += delta; + if (blk.extreloff) blk.extreloff += delta; + if (blk.locreloff) blk.locreloff += delta; + fo->seek(sizeof(segXHDR) + ((char const *)ptr1 - (char const *)ptr0), SEEK_SET); + fo->rewrite(&blk, sizeof(blk)); + } break; + case Mach_segment_command::LC_FUNCTION_STARTS: { + N_Mach::Mach_function_starts_command blk; memcpy(&blk, ptr1, sizeof(blk)); + if (blk.dataoff) blk.dataoff += delta; + fo->seek(sizeof(segXHDR) + ((char const *)ptr1 - (char const *)ptr0), SEEK_SET); + fo->rewrite(&blk, sizeof(blk)); + } break; + case Mach_segment_command::LC_DATA_IN_CODE: { + N_Mach::Mach_data_in_code_command blk; memcpy(&blk, ptr1, sizeof(blk)); + if (blk.dataoff) blk.dataoff += delta; + fo->seek(sizeof(segXHDR) + ((char const *)ptr1 - (char const *)ptr0), SEEK_SET); + fo->rewrite(&blk, sizeof(blk)); + } break; + } // end switch + fo->seek(0, SEEK_END); } - fo->rewrite(&linfo, sizeof(linfo)); } void PackMachARMEL::pack4(OutputFile *fo, Filter &ft) // append PackHeader @@ -1444,13 +1561,10 @@ void PackMachBase::pack1(OutputFile *const fo, Filter &/*ft*/) // generate e unsigned const lc_seg = lc_segment[sizeof(Addr)>>3]; mhdro = mhdri; if (my_filetype==Mach_header::MH_EXECUTE) { - mhdro.ncmds = 6; // __ZERO, __XHDR, __TEXT, UUID, __LINKEDIT, THREAD_STATE - mhdro.sizeofcmds = sizeof(segZERO) - + sizeof(segXHDR) + sizeof(secXHDR) - + sizeof(segTEXT) + sizeof(secTEXT) - + sizeof(cmdUUID) - + sizeof(segLINK) + my_thread_command_size /* + sizeof(linkitem) */ ; - mhdro.flags = Mach_header::MH_NOUNDEFS | Mach_header::MH_DYLDLINK; + memcpy(&mhdro, stub_amd64_darwin_macho_upxmain_exe, sizeof(mhdro)); + mhdro.ncmds += 1; // we add LC_SEGMENT{,_64} for UPX_DATA + mhdro.sizeofcmds += sizeof(segXHDR); + mhdro.flags &= ~Mach_header::MH_PIE; // we require fixed address } fo->write(&mhdro, sizeof(mhdro)); @@ -1501,13 +1615,15 @@ void PackMachBase::pack1(OutputFile *const fo, Filter &/*ft*/) // generate e | Mach_section_command::S_ATTR_PURE_INSTRUCTIONS; segXHDR = segTEXT; + segXHDR.cmdsize = sizeof(segXHDR) + sizeof(secXHDR); segXHDR.vmaddr = segZERO.vmsize; segXHDR.vmsize = PAGE_SIZE; segXHDR.filesize = PAGE_SIZE; - strncpy((char *)segXHDR.segname, "__XHDR", sizeof(segXHDR.segname)); + segXHDR.nsects = 1; + strncpy((char *)segXHDR.segname, "UPX_DATA", sizeof(segXHDR.segname)); memset(&secXHDR, 0, sizeof(secXHDR)); - strncpy((char *)secXHDR.sectname, "__xhdr", sizeof(secXHDR.sectname)); + strncpy((char *)secXHDR.sectname, "upx_data", sizeof(secXHDR.sectname)); memcpy(secXHDR.segname, segXHDR.segname, sizeof(secXHDR.segname)); secXHDR.addr = segXHDR.vmaddr; secXHDR.size = 0; // empty so far @@ -1520,7 +1636,33 @@ void PackMachBase::pack1(OutputFile *const fo, Filter &/*ft*/) // generate e segLINK.initprot = Mach_segment_command::VM_PROT_READ; // Adjust later: .vmaddr .vmsize .fileoff .filesize - if (my_filetype==Mach_header::MH_EXECUTE) { + if (my_filetype == Mach_header::MH_EXECUTE) { + unsigned cmdsize = mhdro.sizeofcmds - sizeof(segXHDR); + Mach_header const *const ptr0 = (Mach_header const *)stub_amd64_darwin_macho_upxmain_exe; + Mach_command const *ptr1 = (Mach_command const *)(1+ ptr0); + for (unsigned j = 0; j < mhdro.ncmds -1; ++j, + (cmdsize -= ptr1->cmdsize), + ptr1 = (Mach_command const *)(ptr1->cmdsize + (char const *)ptr1)) { + Mach_segment_command const *const segptr = (Mach_segment_command const *)ptr1; + if (lc_seg == ptr1->cmd && !strcmp("__LINKEDIT", segptr->segname)) { + // Mach_command before __LINKEDIT + fo->write((1+ ptr0), (char const *)ptr1 - (char const *)(1+ ptr0)); + // Mach_command __LINKEDIT + fo->write(segptr, segptr->cmdsize); + // LC_SEGMENT_64 for payload; steal space from -Wl,-headerpadsize + segXHDR.cmdsize = sizeof(segXHDR); + segXHDR.nsects = 0; + fo->write(&segXHDR, sizeof(segXHDR)); + // Mach_command after __LINKEDIT + fo->write(ptr1->cmdsize + (char const *)ptr1, cmdsize - ptr1->cmdsize); + // Contents before __LINKEDIT; put non-headers at same offset in file + unsigned pos = sizeof(mhdro) + mhdro.sizeofcmds; // includes sizeof(segXHDR) + fo->write(&stub_amd64_darwin_macho_upxmain_exe[pos], segptr->fileoff - pos); + break; + } + } + } + else { // not MH_EXECUTE; thus MH_DYLIB fo->write(&segZERO, sizeof(segZERO)); fo->write(&segXHDR, sizeof(segXHDR)); fo->write(&secXHDR, sizeof(secXHDR)); @@ -1531,8 +1673,6 @@ void PackMachBase::pack1(OutputFile *const fo, Filter &/*ft*/) // generate e pack1_setup_threado(fo); memset(&linkitem, 0, sizeof(linkitem)); fo->write(&linkitem, sizeof(linkitem)); - } - if (my_filetype==Mach_header::MH_DYLIB) { fo->write(rawmseg, mhdri.sizeofcmds); } sz_mach_headers = fo->getBytesWritten(); @@ -1608,7 +1748,7 @@ void PackMachBase::unpack(OutputFile *fo) unsigned char const *ptr = (unsigned char const *)(1+mhdr); for (unsigned j= 0; j < ncmds; ++j) { memcpy(&msegcmd[j], ptr, umin(sizeof(Mach_segment_command), - ((Mach_segment_command const *)ptr)->cmdsize)); + ((Mach_command const *)ptr)->cmdsize)); ptr += (unsigned) ((Mach_segment_command const *)ptr)->cmdsize; if ((unsigned)(ptr - (unsigned char const *)mhdr) > ph.u_len) { throwCantUnpack("cmdsize"); diff --git a/src/stub/src/amd64-darwin.macho-upxmain.c b/src/stub/src/amd64-darwin.macho-upxmain.c index 9b2a5520..7b9892e2 100644 --- a/src/stub/src/amd64-darwin.macho-upxmain.c +++ b/src/stub/src/amd64-darwin.macho-upxmain.c @@ -29,7 +29,8 @@ */ - +#include +#include #include "include/darwin.h" #ifndef DEBUG /*{*/ @@ -135,10 +136,10 @@ heximal(unsigned long x, char *ptr, int n) } -#define DPRINTF(a) dprintf a +#define DPRINTF(a) my_printf a static int -dprintf(char const *fmt, ...) +my_printf(char const *fmt, ...) { char c; int n= 0; @@ -397,11 +398,13 @@ typedef struct { unsigned cmdsize; } Mach_load_command; enum e4 { + LC_REQ_DYLD = 0x80000000, // OR'ed ==> must not ignore LC_SEGMENT = 0x1, LC_SEGMENT_64 = 0x19, LC_THREAD = 0x4, LC_UNIXTHREAD = 0x5, - LC_LOAD_DYLINKER = 0xe + LC_LOAD_DYLINKER = 0xe, + LC_MAIN = (0x28|LC_REQ_DYLD) }; typedef struct { @@ -423,6 +426,27 @@ typedef struct { VM_PROT_EXECUTE = 4 }; +typedef struct { + char sectname[16]; + char segname[16]; + uint64_t addr; /* memory address */ + uint64_t size; /* size in bytes */ + unsigned offset; /* file offset */ + unsigned align; /* power of 2 */ + unsigned reloff; /* file offset of relocation entries */ + unsigned nreloc; /* number of relocation entries */ + unsigned flags; /* section type and attributes */ + unsigned reserved1; /* for offset or index */ + unsigned reserved2; /* for count or sizeof */ +} Mach_section_command; + +typedef struct { + uint32_t cmd; // LC_MAIN; MH_EXECUTE only + uint32_t cmdsize; // 24 + uint64_t entryoff; // file offset of main() [expected in __TEXT] + uint64_t stacksize; // non-default initial stack size +} Mach_main_command; + typedef struct { uint64_t rax, rbx, rcx, rdx; uint64_t rdi, rsi, rbp, rsp; @@ -469,7 +493,7 @@ DEBUG_STRCON(STR_mmap, DEBUG_STRCON(STR_do_xmap, "do_xmap fdi=%%x mhdr=%%p xi=%%p(%%x %%p) f_unf=%%p\\n") -static Mach_AMD64_thread_state const * +static uint64_t // entry address do_xmap( Mach_header64 const *const mhdr, off_t const fat_offset, @@ -481,7 +505,8 @@ do_xmap( ) { Mach_segment_command const *sc = (Mach_segment_command const *)(1+ mhdr); - Mach_AMD64_thread_state const *entry = 0; + Mach_segment_command const *segTEXT = 0; + uint64_t entry = 0; unsigned j; DPRINTF((STR_do_xmap(), @@ -514,6 +539,7 @@ do_xmap( } if (xi && 0!=sc->filesize) { if (0==sc->fileoff /*&& 0!=mhdrpp*/) { + segTEXT = sc; *mhdrpp = (Mach_header64 *)(void *)addr; } unpackExtent(xi, &xo, f_decompress, f_unf); @@ -547,13 +573,18 @@ ERR_LAB Mach_thread_command const *const thrc = (Mach_thread_command const *)sc; if (AMD64_THREAD_STATE ==thrc->flavor && AMD64_THREAD_STATE_COUNT==thrc->count ) { - entry = &thrc->state; + entry = thrc->rip; + } + else if (LC_MAIN==sc->cmd) { + entry = ((Mach_main_command const *)sc)->entryoff; + if (segTEXT->fileoff <= entry && entry < segTEXT->filesize) { + entry += segTEXT->vmaddr; } + // XXX FIXME TODO: if entry not in segTEXT } return entry; } - /************************************************************************* // upx_main - called by our entry code // @@ -563,7 +594,7 @@ DEBUG_STRCON(STR_upx_main, "upx_main szc=%%x f_dec=%%p f_unf=%%p " " xo=%%p(%%x %%p) xi=%%p(%%x %%p) mhdrpp=%%p\\n") -Mach_AMD64_thread_state const * +uint64_t // entry address upx_main( struct l_info const *const li, size_t volatile sz_compressed, // total length @@ -574,7 +605,7 @@ upx_main( Mach_header64 **const mhdrpp // Out: *mhdrpp= &real Mach_header64 ) { - Mach_AMD64_thread_state const *entry; + uint64_t entry; off_t fat_offset = 0; Extent xi, xo, xi0; xi.buf = CONST_CAST(unsigned char *, 1+ (struct p_info const *)(1+ li)); // &b_info @@ -636,25 +667,56 @@ ERR_LAB return entry; } -f_expand *f_exp; -f_unfilter *f_unf; -void (*launch)(void const *, Mach_header64 **); +typedef struct { + uint32_t cmd; + uint32_t cmdsize; + uint32_t data[2]; // because cmdsize >= 16 +} Mach_command; // generic prefix -// Build on Mac OS X: -// gcc -o amd64-darwin.macho-upxmain.exe \ -// -fPIC amd64-darwin.macho-upxmain.c -Wl,-pagezero_size,0xffff0000 +// +// Build on Mac OS X: (where gcc is really clang) +// gcc -o amd64-darwin.macho-upxmain.exe -fno-stack-protector \ +// -Os -fPIC amd64-darwin.macho-upxmain.c -Wl,-pagezero_size,0xffff0000 \ +// -Wl,-no_pie -Wl,-no_uuid -Wl,-no_function_starts -Wl,-headerpad,0x400 int main(int argc, char *argv[]) { Mach_header64 *mhdrp; - unsigned long const base = 0xffff0000ul; - size_t const len = *(size_t *)(base - sizeof(size_t)); - void const *const ptr = (void const *)(base - len); + Mach_header64 const *mhdr0 = (Mach_header64 const *)((~0ul<<16) & (unsigned long)&main); + Mach_command const *ptr = (Mach_command const *)(1+ mhdr0); + f_unfilter *f_unf; + f_expand *f_exp; + unsigned char const *payload; + size_t paysize; + + unsigned j; + for (j=0; j < mhdr0->ncmds; ++j, + ptr = (Mach_command const *)(ptr->cmdsize + (char const *)ptr)) + if (LC_SEGMENT_64==ptr->cmd) { + Mach_segment_command const *const segptr = (Mach_segment_command const *)ptr; +//fprintf(stderr, "ptr=%p segptr=%p\n", ptr, segptr); + if ((long)0x0000545845545f5ful == *(long const *)segptr->segname) { // "__TEXT" + Mach_section_command const *const secptr = (Mach_section_command const *)(1+ segptr); + //if ((long)0x0000747865745f5ful == *(long const *)secptr->sectname) { // "__text" + f_unf = (f_unfilter *)(sizeof(unsigned short) + secptr->addr); + f_exp = (f_expand *)((char const *)f_unf + ((unsigned short *)f_unf)[-1]); +//fprintf(stderr, "f_unf=%p f_exp=%p\n", f_unf, f_exp); + //} + } + if ((long)0x415441445f585055ul == *(long const *)segptr->segname) { // "UPX_DATA" + payload = (unsigned char const *)(segptr->vmaddr); + paysize = segptr->filesize; +//fprintf(stderr, "payload=%p paysize=%lu\n", payload, paysize); + } + } char mhdr[2048]; - void const *entry = upx_main((struct l_info const *)ptr, len, - (Mach_header64 *)&mhdr, sizeof(mhdr), +//fprintf(stderr, "call upx_main(payload=%p paysize=%lu mhdr=%p f_exp=%p f_unf=%p mhdrp@%p)\n", +//payload, paysize, mhdr, f_exp, f_unf, &mhdrp); + uint64_t entry = upx_main((struct l_info const *)payload, paysize, + (Mach_header64 *)mhdr, sizeof(mhdr), f_exp, f_unf, &mhdrp); - launch(entry, &mhdrp); +//fprintf(stderr, "return to launch\n"); + //launch(entry, &mhdrp); return 0; }