Sorun giderme
Bir hata mesajını tek başına değil, çağrının hangi aşamasında oluştuğuyla birlikte değerlendirin. NThread, accessor ve hook katmanları aynı...
Bir hata mesajını tek başına değil, çağrının hangi aşamasında oluştuğuyla birlikte değerlendirin. NThread, accessor ve hook katmanları aynı process üzerinde çalıştığı için aynı timeout belirtisi farklı nedenlerden çıkabilir.
Başlatma hataları
NoSleepAddressError / NoPushretAddressError / NoJumpAddressError
NThread'in ihtiyaç duyduğu küçük talimat stub'ları sistem modüllerinde bulunamadı veya tarama tamamlanmadan kullanıma geçildi.
Kontrol sırası:
- Process'in Windows x64 olduğundan emin olun.
ntdll,kernel32vekernelbasemodüllerinin hedefte yüklü olduğunu kontrol edin.- Async başlatmada
await memory.init()tamamlanmadancallyapılmadığını doğrulayın. - Wine kullanıyorsanız Windows Bun sürümünü ve Wine prefix mimarisini kontrol edin.
Access denied
Bu hata her zaman izin eksikliği anlamına gelmez. Hedef process kapanıyor olabilir, thread ID artık geçerli olmayabilir veya istenen Win32 erişim maskesi hedefin güvenlik politikasına uymuyor olabilir. Önce process/thread yaşamını, sonra erişim haklarını inceleyin.
Çağrı hataları
CallTimeoutError
Hedef fonksiyon dönüş zinciriyle spin stub'a dönmedi. En yaygın nedenler:
- fonksiyon uzun sürüyor veya bloke oluyor,
- yanlış adres ya da yanlış calling convention kullanılıyor,
- hedef thread lock/loader bölgesinde askıya alınmış durumda,
- thread başka bir kod yolu tarafından sonlandırıldı,
- hook veya başka bir suspend/resume sahibi ile suspend sayacı çakıştı.
Timeout sonrasında aynı context'i tekrar yazmadan önce hedef thread'in exit code, RIP ve RSP durumunu okuyun. timeoutMs değerini yükseltmek yalnızca çağrının gerçekten uzun sürdüğü doğrulanmışsa anlamlıdır.
CallThreadDiedError
Thread, çağrı sırasında öldü veya context okunamaz hale geldi. ExitThread veya noreturn fonksiyonları NThread çağrısı olarak kullanmayın. Hedefi yeniden başlatıp aynı çağrıyı daha küçük ve geri dönen bir fonksiyonla izole edin.
Stack mismatch uyarısı
expectedRsp ayarlanmışsa bu denetim hem async call() hem callSync() dönüşünde yapılır. Çağrı sonrası gerçek RSP, beklenen değerden farklıysa yanlış ABI, eksik stack argümanı, çağrının stack'i değiştirmesi veya dönüş stub'ının beklenmeyen bir yoldan çalışması söz konusu olabilir. Uyarıyı görmezden gelip aynı thread'de art arda çağrılar yapmak bir sonraki ret sırasında crash'e dönüşebilir.
Hook hataları
Hook hit alınmıyor
Hedef fonksiyon hiç çağrılmıyor olabilir; yanlış modül sürümündeki adres kullanılmış olabilir veya hook yanlış process'e yazılmış olabilir. Önce originalBytes ile hedefteki byte'ları ve process PID'sini doğrulayın.
Hook disable sonrası crash
Disable sırasında park edilmiş thread'ler hâlâ eski EB FE konumunda olabilir veya özgün byte'lar tam talimat sınırında geri yüklenmemiş olabilir. poll() ile bekleyen hit'leri tüketin, disable() tamamlanmadan process'i kapatmayın ve destroy() çağrısını en sona bırakın.
MinHook detour çalışmıyor
Detour adresi 32-bit relative jump sınırının dışında olabilir. MinHook bunu yakın relay ile çözmeye çalışır; relay allocation/protection başarısızsa hedefin adres alanını ve allocNear yeteneğini kontrol edin. Ayrıca detour machine-code'un hedef process'e gerçekten taşındığını doğrulayın.
Tanı için loglama
Sorunu tekrar üretirken şu değerleri aynı çağrı için kaydedin:
- hedef PID ve driving thread ID,
- hedef fonksiyon adresi ve ilk byte'ları,
- çağrı öncesi/sonrası
RIP,RSP,RCX,RDX,RAX, - argüman türleri ve stack argümanı sayısı,
- timeout sonucu ve thread exit durumu.
Adresleri loglarken hex formatı kullanın; pointer değerlerini JavaScript number içine zorlayarak 64-bit hassasiyetini kaybetmeyin.
CI / Wine ortamı sorunları
Taze thread üzerinde VirtualAlloc/malloc/fopen güvenilir değil (yalnızca GitHub Actions)
GitHub Actions'ın sanallaştırılmış Wine runner'larında (gerçek donanımda görülmez), az önce oluşturulmuş bir thread üzerinde çalıştırılan gerçek WinAPI/CRT çağrıları (VirtualAlloc, malloc, fopen, ...) güvenilir değildir — thread ister yerel ister uzak process'te olsun. Native.Thread.create() bozuk lpParameter iletir, yerel VirtualAlloc ERROR_INVALID_PARAMETER ile başarısız olur. Ayrıca RemoteCallableMemoryAccessor.call()/callSync(), CreateRemoteThread'i tek argümanlı (lpParameter) başlangıç rutini olarak kullandığından birden fazla argümanla asla çağrılmamalı — ikinci ve sonraki argümanlar sessizce düşer.
Çözüm: gerçek bir process spawn edin (new TestProcess()), var olan bir thread'ini alın (Native.Thread.getThreads(pid)[0]) ve tüm çağrıları IndirectNThreadHostAccessor(pid, tid) üzerinden sürün. Bu, zaten canlı olan thread'in register context'ini hijack eder (SetThreadContext/ResumeThread), CreateThread/CreateRemoteThread/yerel VirtualAlloc'ı tamamen atlar. Gerçek bir thread'in hook'lanmış fonksiyona doğrudan girmesi gerekiyorsa argümanı cmachinecode() sarmalayıcısında derleme-zamanı literali olarak gömün (lpParameter'a güvenmeyin).
Tam yerel Wine suite'inde ara sıra Bun segfault'u
bun-wine test bazen Bun'ın kendisini çökertebilir (Wine'a özgü, ~1/4-2/5 oranında). İki bilinen kök neden çözüldü:
- Double
CloseHandle: bir accessor{ handle: tp.handle }ileTestProcess'in handle'ını paylaşıpcloseHandle: falsegeçmezse, hem accessor hemtp.stop()aynı handle'ı kapatır — Wine altında fatal. Kural:tp.handle'ı paylaşan her accessorcloseHandle: falsealmalı (ya da handle'ı hiç paylaşmayıp accessor'ın kendiOpenProcess'ini kullanmasına izin verin). RemoteCallableMemoryAccessorargüman düşürme: yukarıdaki maddeyle aynı kök neden — birden fazla argümanlı bir çağrıCreateRemoteThread'in teklpParameterslotundan geçirilince ikinci argüman register çöpü olarak kalır ve sayfa hatasına yol açabilir.IndirectNThreadHostAccessor'a taşıyın.
Cross-package src/ importları eski dist/ derlemesine düşebilir
Her paketin package.json "main"'i ./dist/index.js'e işaret eder, ./src/index.ts'e değil. tests/ içindeki dosyalar tsconfig paths üzerinden canlı src/'i görür, ama bir paketin kendi kaynak kodu başka bir paketi import ederken (packages/nthread/src/*.ts'ten bun-xffi import etmek gibi) node_modules symlink'i üzerinden derlenmiş dist/'e düşer. packages/*/src/ içinde başka bir paketin import ettiği bir dosyayı değiştirdikten sonra, o paketi tüketen bir Wine testine güvenmeden önce bun run build çalıştırın — temiz typecheck/lint bunu garanti etmez, çünkü ikisi de her zaman canlı src/'i görür.
Başka bir paketten gelen sınıfa karşı instanceof Wine altında güvenilmez
Bun'ın izole linker'ı her tüketici pakete kendi node_modules/<dep> symlink'ini verir (hepsi aynı dizine işaret etse de). bun.exe Wine altında her farklı symlink yolunu ayrı bir modül örneği olarak değerlendirir, bu yüzden farklı paketlerden import edilen aynı sınıf instanceof ile eşleşmeyebilir. Bunun yerine instance.constructor.name ile isim bazlı kontrol yapın. Kod altında inşa edilen sınıfla aynı pakette tanımlı sınıflara karşı instanceof (ör. IndirectNThreadHostAccessor) güvenilirdir — risk yalnızca farklı bir pakette yaşayan sınıflarda.
Testler hiç çıktı vermeden asılı kalıyorsa: önceki çalışmadan kalan process
bun-wine test sıfır stdout ile sonsuza kadar asılı kalabilir. Genellikle önceki bir çalışmanın bun.exe'si çökmüş/kesilmiş ama spawn ettiği ping.exe çocuğu hâlâ yaşıyordur; bu process bun.exe'nin stdout pipe'ına handle tutar ve yeni çalışmanın çıktısı hiç EOF almaz. ps -eo pid,ppid,stat,etime,pcpu,cmd | grep -iE "wine|ping.exe" ile önceki çalışmadan kalan process'leri bulup kill -9 edin (yüksek CPU'da takılı kalanlar Wine scheduler'ını açlığa sürükleyip alakasız testlerde bile timeout'a yol açar).