radiomanV / TL866

Open source software for TL866
GNU General Public License v2.0
334 stars 79 forks source link

wine/minipro on nixos #8

Closed evrim closed 6 years ago

evrim commented 7 years ago

Hello,

I have the following ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="e11c", MODE="664", GROUP="users" in my udev/99-local.rules.

I can access the device using the opensource minipro software.

[nix-shell:~/.wine/drive_c/MiniPro]$ ls ATMEGA8_LED config.dat drv img InfoIC.dll language.dat MiniPro.exe MiniProHelp.chm Serial25Index.dat Serialnumber Uninstall.exe update.dat UsbDrvInstall.exe

[nix-shell:~/.wine/drive_c/MiniPro]$ wine ./MiniPro.exe fixme:win:RegisterDeviceNotificationA (hwnd=0xb0096, filter=0x4ec710,flags=0x00000000) returns a fake device notification handle!

[nix-shell:~/.wine/drive_c/MiniPro]$ cp ~/Downloads/setupapi_v660.dll ./setupapi.dll

[nix-shell:~/.wine/drive_c/MiniPro]$ wine ./MiniPro.exe fixme:win:RegisterDeviceNotificationA (hwnd=0xa0194, filter=0x4ec710,flags=0x00000000) returns a fake device notification handle!

[nix-shell:~/.wine/drive_c/MiniPro]$ mv ./setupapi.dll ./SETUPAPI.dll

[nix-shell:~/.wine/drive_c/MiniPro]$ wine ./MiniPro.exe fixme:win:RegisterDeviceNotificationA (hwnd=0xe0098, filter=0x4ec710,flags=0x00000000) returns a fake device notification handle!

[nix-shell:~]$ cd minipro/TL866/wine/

[nix-shell:~/minipro/TL866/wine]$ make clean && make rm -f y.tab.c y.tab.h lex.yy.c core .orig .rej ## ~ % .# usb.o rm -f usb.dll.so
winegcc -c -o usb.o usb.c usb.c:112:1: warning: ‘stdcall’ attribute ignored [-Wattributes] HANDLE stdcall RegisterDeviceNotifications(HANDLE hRecipient,LPVOID NotificationFilter,DWORD Flags); ^ usb.c: In function ‘patch_minipro’: usb.c:141:26: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] ((DWORD ) &t[1]) = (DWORD)&open_devices; ^... ^ usb.c: At top level: usb.c:463:1: warning: ‘stdcall__’ attribute ignored [-Wattributes] { ^ winegcc -shared usb.spec -o usb.dll.so usb.o -lusb-1.0 -ludev -lpthread
/nix/store/iz98xp5p48jayn034wq89y30plpx5xns-binutils-2.27/bin/ld: Relocatable linking with relocations from format elf32-i386 (/nix/store/hx9vzpy682gjvpjcl1ysw3zzg79xxw6x-wine-2.0/lib/wine/libwinecrt0.a(dll_entry.o)) to format elf64-x86-64 (usb.C4ShH1.o) is not supported winebuild: ld failed with status 1 winegcc: winebuild failed make: *** [Makefile:112: usb.dll.so] Error 2

[nix-shell:~/minipro/TL866/wine]$ wine --version wine-2.0

[nix-shell:~/minipro/TL866/wine]$ winegcc -v gcc -v Using built-in specs. COLLECT_GCC=/nix/store/pbi1xsg79m0ap3qrdlvq4ilyxpirqlm8-gcc-5.4.0/bin/gcc COLLECT_LTO_WRAPPER=/nix/store/pbi1xsg79m0ap3qrdlvq4ilyxpirqlm8-gcc-5.4.0/libexec/gcc/x86_64-unknown-linux-gnu/5.4.0/lto-wrapper Target: x86_64-unknown-linux-gnu Configured with: Thread model: posix gcc version 5.4.0 (GCC)

radiomanV commented 7 years ago

Hi, The problem you have is because you're using an 64 bit OS. From what i see this NixOs is some kind of minimal exotic distro but this is not a problem. Because the Minipro.exe app. is an 32bit executable of course the setupapi.dll is also an 32bit dll. Because the setupapi.dll use libudev/libusb as dependencies, these libraries must be in 32bit architecture (see issue #2 ). The question here is: Can NixOS install 32bit libs? is multiarch supported? Also the compilation will fail if you build an 32 bit app in an 64 bit environment. In debian/*buntu/Mint you can set an 32bit build environment chroot jail and build from there.

evrim commented 7 years ago

OK, I'll try to follow your steps.

$ nix-shell --pure -p stdenv_32bit pkgsi686Linux.libusb pkgsi686Linux.wine pkgsi686Linux.binutils pkgsi686Linux.gcc

$ make clean && make rm -f y.tab.c y.tab.h lex.yy.c core .orig .rej ## ~ % .# usb.o rm -f usb.dll.so
winegcc -c -o usb.o usb.c winegcc -shared usb.spec -o usb.dll.so usb.o -lusb-1.0 -ludev -lpthread

After copying the dll into the dir, I get: $ wine ~/minipro/autoelectric/MiniPro.exe Dll Loaded. Open devices. Close devices. wine: Unhandled page fault on read access to 0x0000003c at address 0x7ef4515d (thread 0009), starting debugger... Unhandled exception: page fault on read access to 0x0000003c in 32-bit code (0x7ef4515d). fixme:dbghelp:i386_stack_walk new PC=32f2be different from Eip=32f358 Register dump: CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:006b EIP:7ef4515d ESP:0032f204 EBP:0032f2be EFLAGS:00210216( R- -- I -A-P- ) EAX:00000000 EBX:7ef58e10 ECX:7ef4f3a4 EDX:00000000 ESI:0032f2be EDI:00000000 Stack dump: 0x0032f204: 00000000 00000004 7ef4f3a4 7ef4e2b3 0x0032f214: 7ef6ef24 7ef6f284 0000000c 7ef6c742 0x0032f224: 00000000 0032f2be 7cd82d68 0032f2be 0x0032f234: 7cd8c960 0032f2be 7cd76e18 0032f2be 0x0032f244: 00000030 0032f2be 0000002c 0032f2be 0x0032f254: 00000028 0032f2be 00000024 0032f2be Backtrace: =>0 0x7ef4515d libusb_get_device_descriptor+0x2d() in libusb-1.0.so.0 (0x0032f2be) 1 0x7ef6c742 open_devices+0x101() in setupapi (0x0032f2be) 0x7ef4515d libusb_get_device_descriptor+0x2d in libusb-1.0.so.0: movl 0x3c(%edi),%edx System information: Wine build: wine-2.0 Platform: i386 Version: Windows XP Host system: Linux Host version: 4.9.27

evrim commented 7 years ago

Fiddled for a while yet no luck.

Q: In the case of 6 byte opcodes, push fun_addr; ret, what happens when the function rets? I mean what happens for example, when open_devices returns? I've plenty of stack smashing detected errors which i cannot disable (tried -D_FORTIFY_SOURCE=0 -fno-stack-protector) yet it seems they'r built into winegcc. I know why you didn't prefer jmp addr.

What if BYTE t[] is something like push addr; ret; ret; (like double rets?)

[nix-shell:~/minipro/autoelectric]$ wine ./MiniPro.exe fixme:winediag:start_process Wine Staging 2.1 is a testing version containing experimental patches. fixme:winediag:start_process Please mention your exact version when filing bug reports on winehq.org. Dll Loaded. Open devices. Close devices. count 10 i 0 �� j�6�>�����xwine: Unhandled page fault on read access to 0x0000000a at address 0xf743bd17 (thread 0009), starting debugger... Unhandled exception: page fault on read access to 0x0000000a in 32-bit code (0xf743bd17). Register dump: CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:006b EIP:f743bd17 ESP:0032c70c EBP:0032cbfc EFLAGS:00210202( R- -- I - - - ) EAX:0000000a EBX:f7575e68 ECX:00002525 EDX:25252525 ESI:0032cc3c EDI:fbad8004 Stack dump: 0x0032c70c: fbad8004 f7404456 0000000a 00000025 0x0032c71c: 0032cc44 7ed6c6ae 00000010 7ed6d3fa 0x0032c72c: 00000001 00000000 00000008 0000018f 0x0032c73c: 00000001 7d28f7b8 0032ec50 0032c700 0x0032c74c: ffffffb8 0032c7cc 00000000 0000000a 0x0032c75c: 00000000 00000000 00000000 00000000 Backtrace: =>0 0xf743bd17 strchrnul+0x17() in libc.so.6 (0x0032cbfc) 1 0xf7404456 _IO_vfprintf_internal+0xb5() in libc.so.6 (0x0032cbfc) 2 0xf7406bd1 buffered_vfprintf+0xa0() in libc.so.6 (0x0032f1dc) 3 0xf740457d _IO_vfprintfinternal+0x1dc() in libc.so.6 (0x0032f1dc) 4 0xf74b7d8e printf_chk+0x7d() in libc.so.6 (0x0032f21c) 5 0x7ed6c738 open_devices+0xf7() in setupapi (0x00000002) 6 0x005516f0 (0x005516f0) 7 0x00000001 (0x004aa9c8) 8 0x004424d0 in minipro (+0x424cf) (0x00442410) 0xf743bd17 __strchrnul+0x17 in libc.so.6: movb 0x0(%eax),%cl

radiomanV commented 7 years ago

Hi Evrim, Unfurtunately i have no time these days to play with this baby. You seems to have enough knowledge to dig into problem, yet from what i see the problem is complex. Because i have no experience with this NixOs i can't give you more info about it (for ex. switching to an 32bit environment) seems like: nix-shell --pure -p stdenv_32bit pkgsi686Linux.libusb pkgsi686Linux.wine pkgsi686Linux.binutils pkgsi686Linux.gcc do the thrick.

I played a bit in a virtual machine with the default "Vbox apliance" provided by the NixOs website without installing it and yes i can reproduce the crash. Seems like the LibUsb is the culprit.

In fact not the libusb_get_device_descriptor is the culprit but every LibUsb function; for example in the open_devices place an ret 1; after the libusb_init(&ctx);//initialize a new session line; this also will crash the whole thing! so every libusb function you'll acces will crash the program! why? i don't know. Maybe this is something binary compatibility or else.

Q: In the case of 6 byte opcodes, push fun_addr; ret, what happens when the function rets? I mean what happens for example, when open_devices returns?

The short answer is: will return to where the top of the stack points to! i.e. back in the minipro.exe where the original call was made! Let me explain: the Push address; ret; is used like a trampoline (like an jump to another address). In the very begining of the program the original functions from minipro.exe are patched to point to another address (patch is done by overwriting the first 6 bytes of the original function by a jump to another address, aka push xxxx; ret;); Now when minipro.exe will call his own open_devices function the original code will never be executed, instead the program flow will be detoured in our custom function (where the push address points). When the IP has landed into our function address, the stack will contain all the arguments originated from the minipro.exe and most important the top of the stack will point to the address where the original call was made!(in minipro.exe of course) so the IP will return in the minipro.exe, thus continuing the normal program flow like nothing wrong happen.

What if BYTE t[] is something like push addr; ret; ret; (like double rets?)

Sorry but this is nonsense! imagine this:

  1. Push 1234;//This will push the value 1234 to the stack, so the top of the stack will point to the value 1234! 2.Ret;// The Instruction Pointer (Aka IP) will be loaded with the value from the stack pointer ie 1234 and the stack is discarded. In this moment the program flow will run from the address 1234! 3.Ret;// this will never be executed because the program is already elsewhere (at the 1234 address).

More of why i used Push/Ret pair instead of the JMP is explained in detail here: http://www.ragestorm.net/blogs/?p=101

I have tried also V6.5 and V6.13 versions, same crash in the Libusb. I think that this is linked with the Libs provided by NixOs or dependencies not sure what one. Next week i will have more time to play with, until then, succes!

evrim commented 7 years ago

Our ABI problem is solved by gcc force_align_arg_pointer function attribute. It now works. Thanks for the explanation, much appreciated.

Best, evrim.

//Minipro replacement functions
attribute((force_align_arg_pointer)) int open_devices(GUID guid, int *devices) {

https://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Function-Attributes.html#Function-Attributes force_align_arg_pointer On the Intel x86, the force_align_arg_pointer attribute may be applied to individual function definitions, generating an alternate prologue and epilogue that realigns the runtime stack. This supports mixing legacy codes that run with a 4-byte aligned stack with modern codes that keep a 16-byte stack for SSE compatibility. The alternate prologue and epilogue are slower and bigger than the regular ones, and the alternate prologue requires a scratch register; this lowers the number of registers available if used in conjunction with the regparm attribute. The force_align_arg_pointer attribute is incompatible with nested functions; this is considered a hard error.

See also: https://bugs.archlinux.org/task/27560

radiomanV commented 7 years ago

What a good finding! a ha! Never tought that was a stack misalignment issue! damn gcc!

I was aware that was an abi/stack corruption problem . I never encountered this issue on other distros no mather if os was 32 or 64 bit; I think that this is wine related. For example i stubbed the open_devices function (by placing an ret 1 at the beginning of function), and after recompile there was no crash(sure no hardware was detected) but the minipro.exe started ok, device plug function worked ok, so the libudev was working ok, libpthread also no problem and the other dependencies libs was also ok. Only the libusb crashed no mather of what function you called(in fact libusb corupted the stack due to the missalignment). Next week i will investigate this and the setupapi.dll replacement will reflect my findings.

Here is another article on the subject: https://eigen.tuxfamily.org/dox/group__TopicWrongStackAlignment.html

Regarding to the "baby" you made me laugh ha ha, the "baby" was supposed to be this NixOs to play with, but thanks for the wishes anyway! i'm way too old and rusty for a real "baby", heh!

Once again thank you for finding the root cause, i'm sure that this will cause issues in the future if no care are taken now.