From 3ce6cc030c07fe6220ac8288b0d6d54108ad3f3e Mon Sep 17 00:00:00 2001 From: Zane Leung Date: Wed, 2 Sep 2026 16:10:39 +0800 Subject: [PATCH] [WRAPPER] Fix FTSENT layout mismatch Translate native FTSENT structures to x86-64 layout in fts_read wrapper. Handle nlink_t size difference (4 vs 8 bytes), stat structure translation, and linked-list pointer resolution via per-FTS mapping table. --- src/emu/x64printer.c | 2 + src/include/myalign.h | 36 ++++++ src/libtools/myalign.c | 148 +++++++++++++++++++++++ src/wrapped/generated/functions_list.txt | 10 ++ src/wrapped/generated/wrappedlibctypes.h | 9 ++ src/wrapped/generated/wrapper.c | 2 + src/wrapped/generated/wrapper.h | 1 + src/wrapped/wrappedlibc.c | 45 +++++++ src/wrapped/wrappedlibc_private.h | 16 +-- 9 files changed, 261 insertions(+), 8 deletions(-) diff --git a/src/emu/x64printer.c b/src/emu/x64printer.c index 10ab875a39..0d124dfa5e 100644 --- a/src/emu/x64printer.c +++ b/src/emu/x64printer.c @@ -2809,6 +2809,8 @@ void x64Print(x64emu_t* emu, char* buff, size_t buffsz, const char* func, int ti snprintf(buff, buffsz, "%04d|%p: Calling %s(%" PRIp ", %" PRIp ", %" PRIp ", %" PRIu64 ")", tid, *(void**)(R_RSP), func, (void*)R_RDI, (void*)R_RSI, (void*)R_RDX, (uintptr_t)R_RCX); } else if (w == LFpppp) { snprintf(buff, buffsz, "%04d|%p: Calling %s(%" PRIp ", %" PRIp ", %" PRIp ", %" PRIp ")", tid, *(void**)(R_RSP), func, (void*)R_RDI, (void*)R_RSI, (void*)R_RDX, (void*)R_RCX); + } else if (w == pFEipi) { + snprintf(buff, buffsz, "%04d|%p: Calling %s(%" PRIi32 ", %" PRIp ", %" PRIi32 ")", tid, *(void**)(R_RSP), func, (int32_t)R_RDI, (void*)R_RSI, (int32_t)R_RDX); } else if (w == pFEipp) { snprintf(buff, buffsz, "%04d|%p: Calling %s(%" PRIi32 ", %" PRIp ", %" PRIp ")", tid, *(void**)(R_RSP), func, (int32_t)R_RDI, (void*)R_RSI, (void*)R_RDX); } else if (w == pFEipV) { diff --git a/src/include/myalign.h b/src/include/myalign.h index d87b6c7795..11aa0656da 100644 --- a/src/include/myalign.h +++ b/src/include/myalign.h @@ -325,4 +325,40 @@ typedef struct my_x64_user_s } my_x64_user_t; +/* FTSENT layout difference between x86_64 and other architectures: + * - On x86_64: nlink_t is 8 bytes + * - On other architectures (ARM, RISC-V, etc.): nlink_t is 4 bytes + * See: https://github.com/gnutools/glibc/blob/master/sysdeps/unix/sysv/linux/x86/bits/typesizes.h + * - FTSENT definition: https://github.com/gnutools/glibc/blob/master/io/fts.h + */ +struct x64_ftsent { /* x86_64 other */ + struct x64_ftsent *fts_cycle; /* 0 */ /* 0 */ + struct x64_ftsent *fts_parent; /* 8 */ /* 8 */ + struct x64_ftsent *fts_link; /* 16 */ /* 16 */ + long fts_number; /* 24 */ /* 24 */ + void *fts_pointer; /* 32 */ /* 32 */ + char *fts_accpath; /* 40 */ /* 40 */ + char *fts_path; /* 48 */ /* 48 */ + int fts_errno; /* 56 */ /* 56 */ + int fts_symfd; /* 60 */ /* 60 */ + uint16_t fts_pathlen; /* 64 */ /* 64 */ + uint16_t fts_namelen; /* 66 */ /* 66 */ + uint32_t _pad0; /* 68 */ /* --- */ + uint64_t fts_ino; /* 72 */ /* 72 */ + uint64_t fts_dev; /* 80 */ /* 80 */ + uint64_t fts_nlink; /* 88 */ /* 88 */ + int16_t fts_level; /* 96 */ /* 92 */ + uint16_t fts_info; /* 98 */ /* 94 */ + uint16_t fts_flags; /* 100 */ /* 96 */ + uint16_t fts_instr; /* 102 */ /* 98 */ + struct x64_stat64 *fts_statp; /* 104 */ /* 104 */ + char fts_name[1]; /* 112 */ /* 112 */ +} __attribute__((packed)); /* 120 */ /* 120 */ + +struct x64_ftsent* get_mapped_x64_ftsent(void* fts, void* ent); +void cleanup_fts_mappings(void* fts); + +void UnalignFTSENT(void* dest, void* source); +void AlignFTSENT(void* dest, void* source); + #endif //__MY_ALIGN__H_ diff --git a/src/libtools/myalign.c b/src/libtools/myalign.c index 3b5aa8cb44..edc39c4733 100644 --- a/src/libtools/myalign.c +++ b/src/libtools/myalign.c @@ -1760,3 +1760,151 @@ void unregister_xcb_display(void* d) return; } } + +struct ftsent_node { + void* ent; + struct x64_ftsent* x64_ent; + struct ftsent_node* next; +}; + +struct fts_node { + void* fts; + struct ftsent_node* head; + struct fts_node* next; +}; + +static pthread_mutex_t fts_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct fts_node* fts_list = NULL; + +struct x64_ftsent* get_mapped_x64_ftsent(void* fts, void* ent) +{ + pthread_mutex_lock(&fts_mutex); + + struct x64_ftsent* x64_ent = NULL; + + struct fts_node *node; + for (node = fts_list; node; node = node->next) { + if (node->fts == fts) break; + } + if (!node) { + node = malloc(sizeof(struct fts_node)); + if (!node) goto out; + node->fts = fts; + node->head = NULL; + node->next = fts_list; + fts_list = node; + } + + struct ftsent_node* n; + for (struct ftsent_node* n = node->head; n; n = n->next) { + if (n->ent == ent) { + x64_ent = n->x64_ent; + goto out; + } + } + + n = malloc(sizeof(struct ftsent_node)); + if (!n) goto out; + x64_ent = malloc(sizeof(struct x64_ftsent) + ((FTSENT*)ent)->fts_namelen); + if (!x64_ent) { + free(n); + goto out; + } + n->ent = ent; + n->x64_ent = x64_ent; + n->next = node->head; + node->head = n; + +out: + pthread_mutex_unlock(&fts_mutex); + return x64_ent; +} + +void cleanup_fts_mappings(void* fts) +{ + pthread_mutex_lock(&fts_mutex); + + struct fts_node** pp = &fts_list; + while (*pp) { + if ((*pp)->fts == fts) { + struct fts_node* t = *pp; + struct ftsent_node *n = t->head; + while (n) { + struct ftsent_node *next = n->next; + free(n->x64_ent); + free(n); + n = next; + } + *pp = t->next; + free(t); + break; + } + pp = &(*pp)->next; + } + + pthread_mutex_unlock(&fts_mutex); +} + +void UnalignFTSENT(void* dest, void* source) +{ + struct x64_ftsent* d = (struct x64_ftsent*)dest; + FTSENT* s = (FTSENT*)source; + /* + * fts_cycle, fts_parent, fts_link and fts_statp are copied as raw + * pointers without layout conversion for now. Recursively calling + * UnalignFTSENT on them may walk up the fts_parent chain to the + * root, and fts_cycle may form a loop, causing unbounded recursion + */ + d->fts_cycle = ((struct x64_ftsent*)s)->fts_cycle; + d->fts_parent = ((struct x64_ftsent*)s)->fts_parent; + d->fts_link = ((struct x64_ftsent*)s)->fts_link; + d->fts_number = s->fts_number; + d->fts_pointer = s->fts_pointer; + d->fts_accpath = s->fts_accpath; + d->fts_path = s->fts_path; + d->fts_errno = s->fts_errno; + d->fts_symfd = s->fts_symfd; + d->fts_pathlen = s->fts_pathlen; + d->fts_namelen = s->fts_namelen; + d->_pad0 = 0; + d->fts_ino = s->fts_ino; + d->fts_dev = s->fts_dev; + d->fts_nlink = s->fts_nlink; + d->fts_level = s->fts_level; + d->fts_info = s->fts_info; + d->fts_flags = s->fts_flags; + d->fts_instr = s->fts_instr; + d->fts_statp = ((struct x64_ftsent*)s)->fts_statp; + memcpy(d->fts_name, s->fts_name, s->fts_namelen + 1); +} + +void AlignFTSENT(void* dest, void* source) +{ + FTSENT *d = (FTSENT*)dest; + struct x64_ftsent *s = (struct x64_ftsent*)source; + /* + * Reverse conversion (x64 -> native). Same rationale as UnalignFTSENT: + * fts_cycle, fts_parent, fts_link, fts_statp are copied verbatim + * without recursing. + */ + d->fts_cycle = ((FTSENT*)s)->fts_cycle; + d->fts_parent = ((FTSENT*)s)->fts_parent; + d->fts_link = ((FTSENT*)s)->fts_link; + d->fts_number = s->fts_number; + d->fts_pointer = s->fts_pointer; + d->fts_accpath = s->fts_accpath; + d->fts_path = s->fts_path; + d->fts_errno = s->fts_errno; + d->fts_symfd = s->fts_symfd; + d->fts_pathlen = s->fts_pathlen; + d->fts_namelen = s->fts_namelen; + d->fts_ino = s->fts_ino; + d->fts_dev = s->fts_dev; + d->fts_nlink = s->fts_nlink; + d->fts_level = s->fts_level; + d->fts_info = s->fts_info; + d->fts_flags = s->fts_flags; + d->fts_instr = s->fts_instr; + d->fts_statp = ((FTSENT*)s)->fts_statp; + memcpy(d->fts_name, s->fts_name, s->fts_namelen + 1); +} diff --git a/src/wrapped/generated/functions_list.txt b/src/wrapped/generated/functions_list.txt index 300c9bb19e..cef080edf2 100644 --- a/src/wrapped/generated/functions_list.txt +++ b/src/wrapped/generated/functions_list.txt @@ -1576,6 +1576,7 @@ #() LFpppl #() LFpppL #() LFpppp +#() pFEipi #() pFEipp #() pFEipV #() pFEuup @@ -5546,6 +5547,8 @@ wrappedlibc: - cnd_broadcast - cnd_init - cnd_signal + - fts64_close + - fts_close - getcontext - register_printf_type - setcontext @@ -5570,6 +5573,8 @@ wrappedlibc: - _ZGTtnam - pFp: - __deregister_frame_info + - fts64_read + - fts_read - mallinfo - mallinfo2 - vFpi: @@ -5656,6 +5661,7 @@ wrappedlibc: - pFpi: - __libc_dlopen_mode - backtrace_symbols + - fts64_children - pFpp: - __libc_dlsym - __realpath_chk @@ -5721,6 +5727,8 @@ wrappedlibc: - iFpLp: - init_module - iFppi: + - fts64_set + - fts_set - ftw - ftw64 - iFppp: @@ -5777,6 +5785,8 @@ wrappedlibc: - LFppL: - strlcat - strlcpy +- pFipi: + - fts_children - pFpip: - fts64_open - fts_open diff --git a/src/wrapped/generated/wrappedlibctypes.h b/src/wrapped/generated/wrappedlibctypes.h index 9bc7a49298..aab1fa5ca2 100644 --- a/src/wrapped/generated/wrappedlibctypes.h +++ b/src/wrapped/generated/wrappedlibctypes.h @@ -73,6 +73,7 @@ typedef intptr_t (*lFipL_t)(int32_t, void*, uintptr_t); typedef intptr_t (*lFipV_t)(int32_t, void*, ...); typedef intptr_t (*lFppL_t)(void*, void*, uintptr_t); typedef uintptr_t (*LFppL_t)(void*, void*, uintptr_t); +typedef void* (*pFipi_t)(int32_t, void*, int32_t); typedef void* (*pFpip_t)(void*, int32_t, void*); typedef void* (*pFppp_t)(void*, void*, void*); typedef void* (*pFppV_t)(void*, void*, ...); @@ -155,6 +156,8 @@ typedef int32_t (*iFppipppp_t)(void*, void*, int32_t, void*, void*, void*, void* GO(cnd_broadcast, iFp_t) \ GO(cnd_init, iFp_t) \ GO(cnd_signal, iFp_t) \ + GO(fts64_close, iFp_t) \ + GO(fts_close, iFp_t) \ GO(getcontext, iFp_t) \ GO(register_printf_type, iFp_t) \ GO(setcontext, iFp_t) \ @@ -171,6 +174,8 @@ typedef int32_t (*iFppipppp_t)(void*, void*, int32_t, void*, void*, void*, void* GO(_ZGTtnaX, pFL_t) \ GO(_ZGTtnam, pFL_t) \ GO(__deregister_frame_info, pFp_t) \ + GO(fts64_read, pFp_t) \ + GO(fts_read, pFp_t) \ GO(mallinfo, pFp_t) \ GO(mallinfo2, pFp_t) \ GO(__longjmp_chk, vFpi_t) \ @@ -240,6 +245,7 @@ typedef int32_t (*iFppipppp_t)(void*, void*, int32_t, void*, void*, void*, void* GO(sysv_signal, pFip_t) \ GO(__libc_dlopen_mode, pFpi_t) \ GO(backtrace_symbols, pFpi_t) \ + GO(fts64_children, pFpi_t) \ GO(__libc_dlsym, pFpp_t) \ GO(__realpath_chk, pFpp_t) \ GO(realpath, pFpp_t) \ @@ -284,6 +290,8 @@ typedef int32_t (*iFppipppp_t)(void*, void*, int32_t, void*, void*, void*, void* GO(mprotect, iFpLi_t) \ GO(posix_madvise, iFpLi_t) \ GO(init_module, iFpLp_t) \ + GO(fts64_set, iFppi_t) \ + GO(fts_set, iFppi_t) \ GO(ftw, iFppi_t) \ GO(ftw64, iFppi_t) \ GO(__cxa_atexit, iFppp_t) \ @@ -330,6 +338,7 @@ typedef int32_t (*iFppipppp_t)(void*, void*, int32_t, void*, void*, void*, void* GO(readlink, lFppL_t) \ GO(strlcat, LFppL_t) \ GO(strlcpy, LFppL_t) \ + GO(fts_children, pFipi_t) \ GO(fts64_open, pFpip_t) \ GO(fts_open, pFpip_t) \ GO(tdelete, pFppp_t) \ diff --git a/src/wrapped/generated/wrapper.c b/src/wrapped/generated/wrapper.c index bb49326f48..8e9aef61d3 100644 --- a/src/wrapped/generated/wrapper.c +++ b/src/wrapped/generated/wrapper.c @@ -1606,6 +1606,7 @@ typedef uintptr_t (*LFpppu_t)(void*, void*, void*, uint32_t); typedef uintptr_t (*LFpppl_t)(void*, void*, void*, intptr_t); typedef uintptr_t (*LFpppL_t)(void*, void*, void*, uintptr_t); typedef uintptr_t (*LFpppp_t)(void*, void*, void*, void*); +typedef void* (*pFEipi_t)(x64emu_t*, int32_t, void*, int32_t); typedef void* (*pFEipp_t)(x64emu_t*, int32_t, void*, void*); typedef void* (*pFEipV_t)(x64emu_t*, int32_t, void*, void*); typedef void* (*pFEuup_t)(x64emu_t*, uint32_t, uint32_t, void*); @@ -5889,6 +5890,7 @@ void LFpppu(x64emu_t *emu, uintptr_t fcn) { LFpppu_t fn = (LFpppu_t)fcn; R_RAX=( void LFpppl(x64emu_t *emu, uintptr_t fcn) { LFpppl_t fn = (LFpppl_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (void*)R_RSI, (void*)R_RDX, (intptr_t)R_RCX); } void LFpppL(x64emu_t *emu, uintptr_t fcn) { LFpppL_t fn = (LFpppL_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (void*)R_RSI, (void*)R_RDX, (uintptr_t)R_RCX); } void LFpppp(x64emu_t *emu, uintptr_t fcn) { LFpppp_t fn = (LFpppp_t)fcn; R_RAX=(uintptr_t)fn((void*)R_RDI, (void*)R_RSI, (void*)R_RDX, (void*)R_RCX); } +void pFEipi(x64emu_t *emu, uintptr_t fcn) { pFEipi_t fn = (pFEipi_t)fcn; R_RAX=(uintptr_t)fn(emu, (int32_t)R_RDI, (void*)R_RSI, (int32_t)R_RDX); } void pFEipp(x64emu_t *emu, uintptr_t fcn) { pFEipp_t fn = (pFEipp_t)fcn; R_RAX=(uintptr_t)fn(emu, (int32_t)R_RDI, (void*)R_RSI, (void*)R_RDX); } void pFEipV(x64emu_t *emu, uintptr_t fcn) { pFEipV_t fn = (pFEipV_t)fcn; R_RAX=(uintptr_t)fn(emu, (int32_t)R_RDI, (void*)R_RSI, (void*)(R_RSP + 8)); } void pFEuup(x64emu_t *emu, uintptr_t fcn) { pFEuup_t fn = (pFEuup_t)fcn; R_RAX=(uintptr_t)fn(emu, (uint32_t)R_RDI, (uint32_t)R_RSI, (void*)R_RDX); } diff --git a/src/wrapped/generated/wrapper.h b/src/wrapped/generated/wrapper.h index 689599c995..96df8fb157 100644 --- a/src/wrapped/generated/wrapper.h +++ b/src/wrapped/generated/wrapper.h @@ -1613,6 +1613,7 @@ void LFpppu(x64emu_t *emu, uintptr_t fnc); void LFpppl(x64emu_t *emu, uintptr_t fnc); void LFpppL(x64emu_t *emu, uintptr_t fnc); void LFpppp(x64emu_t *emu, uintptr_t fnc); +void pFEipi(x64emu_t *emu, uintptr_t fnc); void pFEipp(x64emu_t *emu, uintptr_t fnc); void pFEipV(x64emu_t *emu, uintptr_t fnc); void pFEuup(x64emu_t *emu, uintptr_t fnc); diff --git a/src/wrapped/wrappedlibc.c b/src/wrapped/wrappedlibc.c index c4575d243d..1b2b9eba8a 100644 --- a/src/wrapped/wrappedlibc.c +++ b/src/wrapped/wrappedlibc.c @@ -1988,6 +1988,51 @@ EXPORT void* my_fts64_open(x64emu_t* emu, void* path, int options, void* c) return my->fts64_open(path, options, findcompareFct(c)); } +EXPORT void* my_fts_children(x64emu_t* emu, void* fts, int instr) +{ + (void)emu; + FTSENT* ent = fts_children((FTS*)fts, instr); + if (!ent) return NULL; + + struct x64_ftsent* x64_ent = get_mapped_x64_ftsent(fts, ent); + if (!x64_ent) return ent; + UnalignFTSENT(x64_ent, ent); + return x64_ent; +} +EXPORT void* my_fts64_children(x64emu_t* emu, void* fts, int instr) __attribute__((alias("my_fts_children"))); + +EXPORT void* my_fts_read(x64emu_t* emu, void* fts) +{ + (void)emu; + FTSENT* ent = fts_read((FTS*)fts); + if (!ent) return NULL; + + struct x64_ftsent* x64_ent = get_mapped_x64_ftsent(fts, ent); + if (!x64_ent) return ent; + UnalignFTSENT(x64_ent, ent); + return x64_ent; +} +EXPORT void* my_fts64_read(x64emu_t* emu, void* fts) __attribute__((alias("my_fts_read"))); + +EXPORT int my_fts_set(x64emu_t* emu, void* fts, void* ftsent, int instr) +{ + (void)emu; + FTSENT ent; + AlignFTSENT(&ent, ftsent); + int r = fts_set(fts, &ent, instr); + if (!r) UnalignFTSENT(ftsent, &ent); + return r; +} +EXPORT int my_fts64_set(x64emu_t* emu, void* fts, void* ftsent, int instr) __attribute__((alias("my_fts_set"))); + +EXPORT int my_fts_close(x64emu_t* emu, void* fts) +{ + (void)emu; + cleanup_fts_mappings(fts); + return fts_close((FTS*)fts); +} +EXPORT int my_fts64_close(x64emu_t* emu, void* fts) __attribute__((alias("my_fts_close"))); + #if 0 struct i386_dirent { uint32_t d_ino; diff --git a/src/wrapped/wrappedlibc_private.h b/src/wrapped/wrappedlibc_private.h index 43b3c31092..7996a65732 100644 --- a/src/wrapped/wrappedlibc_private.h +++ b/src/wrapped/wrappedlibc_private.h @@ -517,16 +517,16 @@ GO(ftok, iFpi) GO(ftruncate, iFil) GO(ftruncate64, iFiI) GOW(ftrylockfile, iFS) -GOW(fts64_children, pFpi) -GOW(fts64_close, iFp) +GOWM(fts64_children, pFEpi) +GOWM(fts64_close, iFEp) GOWM(fts64_open, pFEpip) -GOW(fts64_read, pFp) -GOW(fts64_set, iFppi) -GO(fts_children, pFpi) -GO(fts_close, iFp) +GOWM(fts64_read, pFEp) +GOWM(fts64_set, iFEppi) +GOM(fts_children, pFEipi) +GOM(fts_close, iFEp) GOM(fts_open, pFEpip) -GO(fts_read, pFp) -GO(fts_set, iFppi) +GOM(fts_read, pFEp) +GOM(fts_set, iFEppi) GOM(ftw, iFEppi) GOM(ftw64, iFEppi) GOW(funlockfile, vFS)