hangover icon indicating copy to clipboard operation
hangover copied to clipboard

armeabi-v7a support?

Open skompc opened this issue 6 years ago • 30 comments

I'm trying to add support for 32 bit arm processors but I ran into a little problem... I don't know a lot about C code and qemu code is beyond my knowledge... I've made a hacky way to get around this problem by adding an arm folder into /qemu/windows-user/host folder... With some presumably good syscall files... It still complains about an unsupported architecture... How do I get around this?

skompc avatar Apr 10 '18 04:04 skompc

Are you trying to run hangover on an arm32 CPU (to run x86 programs on arm32), or are you trying to run arm32 apps in hangover (e.g. run arm32 on x86)?

For the former, this will be tricky, but not impossible. The challenge will be getting the right address space setup, and you'll have to write a bit of assembly code (the code has #error directives to show you where).

For the latter case I need to be convinced it is something worth spending time on. There are not many arm windows applications out there, and they all have x86 and x86_64 builds anyway.

stefand avatar Apr 10 '18 10:04 stefand

I'm trying to run hangover on an arm CPU... But I'm willing to wait if its something you're going to work on anyway... I'm not too fluent in C and assembly is barely understandable to me

On Tue, Apr 10, 2018, 5:00 AM Stefan Dösinger [email protected] wrote:

Are you trying to run hangover on an arm32 CPU (to run x86 programs on arm32), or are you trying to run arm32 apps in hangover (e.g. run arm32 on x86)?

For the former, this will be tricky, but not impossible. The challenge will be getting the right address space setup, and you'll have to write a bit of assembly code (the code has #error directives to show you where).

For the latter case I need to be convinced it is something worth spending time on. There are not many arm windows applications out there, and they all have x86 and x86_64 builds anyway.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/AndreRH/hangover/issues/3#issuecomment-380044563, or mute the thread https://github.com/notifications/unsubscribe-auth/AFCP7Z4yBMsXBpeMD4fmoX3JEtlmQcYxks5tnILMgaJpZM4TNkuw .

skompc avatar Apr 11 '18 08:04 skompc

In any event I know its off topic for this issue but can I have a build for android?... I'm getting a aarch64 phone soon so it'll be helpful... Oh and wine in general doesn't work on android Oreo... Any workaround for that?

skompc avatar Apr 11 '18 08:04 skompc

Unfortunately I'm not actively working on arm32 support, there are many more things to do than I have time for anyway.

Re an Android build: You can try your luck with build.android.sh, but I've never used it myself - André has been taking care of the Android parts.

Regarding Oreo, please ask the Wine project, e.g. file a bug or contact the mailing list.

stefand avatar Apr 11 '18 09:04 stefand

i just cant seem to get my build environment right... can you ask @AndreRH to provide me with an apk if he has one?

skompc avatar Apr 12 '18 00:04 skompc

I plan to remake the build system for Android as soon as I have time, then it'll be easier and we might do releases at some point, currently it involves some manual fiddling

AndreRH avatar Apr 12 '18 21:04 AndreRH

Hello, I'm just commenting to mark my interest as wel for a arm 32-bit host version. However I don't have the knowledge to do it. Specifically my goal is to run a windows x86 32-bit binnary on a 32-bit arm device (currently beaglebone). I've been trying to run qemu + wine as it seemed to work for some people, but I have not been able to.

flabou avatar Jul 13 '18 10:07 flabou

I'm writing this to acknowledge the request, but as I wrote last time I won't get around to it anytime soon. For at least the next few months arm32 support is something interested parties will have to do themselves.

I also noticed that qemu performance on an arm32 host is only about half the speed of an arm64 host (this compares running a Linux x86 binary in qemu vs running a Linux arm binary natively).

stefand avatar Jul 13 '18 19:07 stefand

Thank you for your answer, I'll keep on trying to run qemu + wine on the beaglebone using this guide in the meantime.

flabou avatar Jul 16 '18 20:07 flabou

Have a look at https://wiki.winehq.org/Emulation , especially https://github.com/AlbrechtL/RPi-QEMU-x86-wine

AndreRH avatar Jul 16 '18 21:07 AndreRH

Hi, I'm also spuer interested for a ARM32 support ! Hope you didn't abandoned the idea. Thanks

brunetton avatar Dec 16 '19 00:12 brunetton

There's no change that makes it more difficult than before, but I don't think I'll ever free up the time for doing it myself.

stefand avatar Dec 16 '19 09:12 stefand

I've been working on ARM32 support myself, but I'm somewhat confused with the callback_entry struct in callback_helper.h. It contains different variables depending on the architecture, but aside from in callback_helper_impl.h, I can't see any other file which checks architecture and distinguishes between the different variables used. The same goes for the hooked_function struct in main.c of dlls/kernel32 and qemu/windows-user.

Any advice on where to trace them, so that I can implement their use correctly, would be incredibly helpful.

mpbagot avatar Dec 21 '19 23:12 mpbagot

struct callback_entry contains (cpu-specific) opcodes. It is used for callback functions that do not have a dedicated user data parameter, e.g. WNDPROCs. Hangover has to pass a host function to Wine, then this host function has to somehow find the guest function to invoke. Without any extra parameters that is difficult.

So what callback_helper.c does is that it creates executable code at runtime that can find extra data relative to the instruction pointer. What this executable code does is that it invokes a fixed (not runtime-allocated, but just compiler created) function with an extra argument that allows the compiler generated function to find the per-callback private data. How exactly this works depends on the calling convention and the number of parameters the callback accepts.

On arm64 it loads a program-counter relative memory value into x4 (for a 4 parameter function).

For x86_64 it loads a %rip-relative memory value into %r9 for a 3 parameter function. A 5 param function is more complicated - it needs to push the %rip-relative value to the stack and clean up afterwards.

Similar code exists for 32 bit x86 on the guest side. You won't have to touch this, but see dlls/user32/main.c, init_reverse_wndproc() for a really complicated example. I think I stole some inspiration from gcc by compiling a function like this:

LRESULT WINAPI (*destination)(HWND win, UINT msg, WPARAM wp, LPARAM lp, void *data); LRESULT WINAPI thunk(HWND win, UINT msg, WPARAM wp, LPARAM lp) { selfptr: return destination(win, msg, wp, lp, &&selfptr); }

I wrote this from memory, and you'll have to adjust it a bit. Hint: Compile with optimizations, otherwise gcc might generate a lot of redundant stack ops.

hooked_function on the other hand is a trick to intercept calls to certain Wine host functions. What you do is to overwrite the existing opcodes with a jump to an absolute address. You don't have to be able to call the patched function afterwards. All places that hook Wine functions that way will redirect the calls to something else, e.g. the LoadLibraryW hook calls LoadLibraryExW and kernel32.SetCurrentDirectory will call ntdll.RtlSetCurrentDirectory_U

stefand avatar Dec 22 '19 14:12 stefand

I've gotten fairly far into implementing ARM32 support, with (I hope) just one major stumbling block left to solve. In the main function of qemu, the entire initialisation process works up until the call to qemu_LdrInitializeThunk.

This call mostly succeeds, with all of the dlls being attached to the qemu process as expected, but fails when attempting to initialise comctl32. Specifically, at some point, LoadCursorW is called. I've traced the exact point of failure down to the call to LoadResource in wine/dlls/user32/cursoricon.c:1817.

A printf statement immediately before this call works as intended, yet a printf statement placed at the beginning of the LoadResource function in kernelbase/loader.c will not execute. For some reason, execution never makes it to the LoadResource call site. Additionally, at this point, the wine debugger will start up, claiming an invalid Thumb-2 instruction is being executed. This exception occurs in the exception handling code in wine/dlls/ntdll/signal_arm.c:523. (N.B: I haven't modified any of this code)

After looking over the wine debugger output, the actual exception being handled is a SIGSEGV, which leads me to believe that the issue lies with how the LoadResource call is addressing the kernelbase DLL (LoadResource is a function in kernelbase).

Honestly, I'm completely stumped. I would greatly appreciate any ideas on where to look for issues, or anything I can try to find the root cause of the issue.

mpbagot avatar Jan 14 '20 12:01 mpbagot

I've gotten fairly far into implementing ARM32 support, with (I hope) just one major stumbling block left to solve. In the main function of qemu, the entire initialisation process works up until the call to qemu_LdrInitializeThunk. ... Honestly, I'm completely stumped. I would greatly appreciate any ideas on where to look for issues, or anything I can try to find the root cause of the issue.

I tried your code these days and needed to at least fix one minor thing, but now I'm missing your qemu changes, seems you never pushed them?

AndreRH avatar Jul 01 '20 09:07 AndreRH

That's correct. I had a few changes in Hangover itself that I never pushed (mainly just a couple of tweaks to the assembler in callback_helper_impl.h), as well as quite a few changes to qemu that I never committed or pushed, since I intended to do so once I solved the problem I had.

I've pushed what I had into a new branch on my fork of qemu. Note that it's pretty messy, and it's been quite a while since I looked at it or worked on it, so it may no longer be of any use.

mpbagot avatar Jul 04 '20 07:07 mpbagot

Took me some time to build and fix buildtime errors and now when I try to run 32-bit notepad++ it seems to crash when loading the guest msvcrt, might take me again some time to debug that

AndreRH avatar Jul 06 '20 14:07 AndreRH

fwiw the LoadResource printf might not be triggered because comctl32.dll calls kernel32.LoadResource instead of kernelbase.LoadResource. In Wine this is now a forward to kernelbase, but not in hangover's kernel32.dll. So it might be forwarded to the arm side kernel32.LoadResource and thus the arm side kernelbase.LoadResource instead of the guest-side function.

In the long run we should probably replace the wrappers we have in kernel32 with forwards to kernelbase, but many wrappers in ntdll are either missing or just the autogenerated thunks that might need 32/64 translation.

stefand avatar Jul 06 '20 15:07 stefand

I had success with a simple hello world program! good job @mpbagot ! If you can clean it up and fix remaining issues we are happy to accept a pull request :) Here are some notes:

  • Main thing was the hook functions in qemu/windows-user/main.c and dlls/kernel32/main.c, I fixed the offset calculation in the qemu version and copyied the whole thing to kernel32. Problem was that you only substracted 4, but you need to substract 8 as the PC on ARM points to the next instruction.
  • get rid of --enable-win64 in the Makefile
  • in the Makefile "getconf LONG_BIT" didn't work for me for some reason, so I hardcoded it to 32
  • I changed scanf_wrapper in dlls/msvcrt/scanf.c from CDECL to WINAPIV, not sure if that's correct for every arch...
  • call_dll_entry_point in qemu also has a minor issue to be fixed, compiler will tell you ;)

AndreRH avatar Jul 08 '20 19:07 AndreRH

Apologies, but I seem to be doing something wrong. When I try to build, with all of the changes you've mentioned, I come across an error in the build process. Specifically, this error occurs when attempting to build the mf dll for the wine-host.

The exact error that is output is:

/usr/bin/ld: topology.o: in function 'topology_generate_id':
/home/pi/build/hangover/build/wine-host/dlls/mf/../../../../wine/dlls/mf/topology.c:872: undefined reference to 'InterlockedCompareExchange64'
/usr/bin/ld: mf.dll.so: hidden symbol 'InterlockedCompareExchange64' isn't defined
/usr/bin/ld: final link failed: bad value

I've never seen an issue exactly like this before, so I checked out the preprocessor output, and I found that the InterlockedCompareExchange64 function is only included from winbase.h using the following line:

__attribute__((visibility ("hidden"))) LONGLONG __attribute__((pcs("aapcs-vfp"))) InterlockedCompareExchange64(LONGLONG volatile*,LONGLONG,LONGLONG);

From what I could find online, it's likely the hidden visibility attribute that causes the issues with the build, but given that you haven't experienced any issue, I'm not sure what I could have done wrong to cause this.

If it matters, the GCC version is 8.3.0, and Wine source tree is the same as the current base Hangover version, with no changes.

mpbagot avatar Jul 09 '20 15:07 mpbagot

The hidden attribute means it is not exported from the final .so file. It is kind of like the opposite of DLLEXPORT in msvc.

mf (mediafoundation) is under very active development right now and the most likely case is that this is a bug in the Wine code. ARM and ARM64 aren't regularly tested. I guess try to update gcc, or try to build with clang. If neither works try to build an unmodified Wine from source.winehq.org/git/wine.git and file a bug at bugs.winehq.org.

stefand avatar Jul 09 '20 17:07 stefand

I'm on a Cubox i4pro, with a Armbian based on Debian GNU/Linux 10 (buster) gcc version 8.3.0 (Debian 8.3.0-6) mingw-w64 6.0 with gcc version 8.3-win32 20190406 (GCC)

commits I based my changes on: hangover: 636fa9d0e462a69aaae863ab758965e0e11ae30d (from your repo @mpbagot ) wine: f4c57bbf71b26fb39bab528f21dce8c8f37fd945 (which itself is based on wine 5.11) qemu: d97091fc78b39c450df9e1dfb9d47339fc4ffcf7 (again from your repo Mitchell)

AndreRH avatar Jul 09 '20 17:07 AndreRH

Thanks for the suggestion @stefand, using clang fixed the build. Unmodified Wine from the winehq source tree failed to build at the same point as Hangover, so I'm assuming there is simply an issue with my gcc setup.

Regardless, with the build now working, I've tried a few programs, and I've noticed that I experience a segmentation fault on a call to CreateWindowExW in the user32 dll. This happens with notepad, wordpad and clock, all from the wine-guest32/programs folder.

I have a suspicion about why, but before I begin changing anything, I was wondering if you could give me a bit more information about the Hello World program that you successfully tested @AndreRH. Did the program you tested make use of a graphical interface at all? Would you be able to link to the source code?

mpbagot avatar Jul 10 '20 10:07 mpbagot

I have a suspicion about why, but before I begin changing anything, I was wondering if you could give me a bit more information about the Hello World program that you successfully tested @AndreRH. Did the program you tested make use of a graphical interface at all? Would you be able to link to the source code?

I compiled following code with i686-w64-mingw32-gcc test.c -Wl,--dynamicbase,--export-all-symbols to make it relocatable:

#include <windows.h> int main(void) { printf("hi i386\n"); return 123; }

AndreRH avatar Jul 10 '20 10:07 AndreRH

@mpbagot I am currently trying to apply some of your patches, but I have questions :) why do you change the contents of i386_wrapper in pe.c? are you ware that you are clobber r4 in wrapper_code3 in the callback code?

AndreRH avatar Oct 06 '20 19:10 AndreRH

Hey @AndreRH, it's been quite some time since I looked at this, but I think I can answer your questions.

why do you change the contents of i386_wrapper in pe.c?

I think initially, I modified the wrapper code because I thought that what I changed it to was equivalent, but shorter. In retrospect, it's actually slightly different. It's not a necessary change, it was unrelated and I accidentally committed and pushed it. Feel free to use the original wrapper code.

are you ware that you are clobber r4 in wrapper_code3 in the callback code?

It seems I missed that when I was working on the code before. That's probably the cause of most of the issues I was having with my implementation. I don't have time right now to test out fixing that myself, but I think it should be okay to just add a push and pop, like so:

0x04, 0x40, 0x2d, 0xe5, // push {r4}
0x14, 0x30, 0x9f, 0xe5, // ldr r3, =selfptr // NOTE: Offset is 24
0x14, 0x40, 0x9f, 0xe5, // ldr r4, =host_proc // NOTE: Offset is 24
0x14, 0xff, 0x2f, 0xe1, // bx r4
0x04, 0x40, 0x9d, 0xe4, // pop {r4}

mpbagot avatar Oct 09 '20 11:10 mpbagot

Hi @mpbagot Thanks for the explanations! I meanwhile pushed some of the modifications to hangover. The hook function should be done now Same for the callback Varargs should be handled by libffi (but I think there are remaining issues, as that code was implemented with 64-Bit in mind) The Makefile change is in place Vararg functions use WINAPIV instead of CDECL now

In qemu only the hostdep stuff is pushed. What we might wanna look at is the filling of the adress space, if I fill up to 0x60000000, the chances are better to get a non-relocatable binary loaded...

AndreRH avatar Oct 11 '20 13:10 AndreRH

I'm happy to start looking into the qemu side of this a bit more in a week or so when I've got some more spare time. I'll probably try finding/creating some non-relocatable binaries and see how it works with and without any address space filling. I'm wondering though, will this have any impact on whether a 32 bit host will work:

The host Wine is always built as a 64 bit application. Hangover handles 32 bit applications by translating structures passed between the application and Wine. The LLP64 model of Windows keeps most structures compatible between 32 and 64 bit, but translating is still a big effort.

If all structures are automatically translated under the assumption that the host Wine is 64-bit, won't this cause problems for the 32-bit host Wine?

mpbagot avatar Nov 16 '20 08:11 mpbagot

The struct conversion is dependent on ifdef guards. There are two defines GUEST_BIT and HOST_BIT, if they are equal no conversion is done. That's also necessary for running win64 applications on arm64 Wine.

Regardless of pointer size function pointers (e.g. callbacks, COM interfaces passed from applications to Win32 libs) are translated anyway.

stefand avatar Nov 16 '20 09:11 stefand