I’ve already gotten my Willem programmer working in Windows 7 with a custom DLL, so I figured why not do the same in Wine under Linux? I meant to make directions for this earlier, but the last time I looked at this, Wine had a bug that made it really annoying to build 32-bit DLLs on 64-bit Linux. That’s all fixed now, so here we go. These instructions are tested with Ubuntu 14.04 with Wine 1.6. Some older versions of Wine had problems, so this is all I can test with.

Warning: This DLL is slow, and these instructions are fairly complicated. I don’t really want to supply prebuilt binaries, and it requires some special work that might have to be repeated when new versions of Wine are installed due to updates. I’m also too lazy to make it configurable, so you need to change a #define in the code if your parallel port is at an I/O address other than 0x378. If you’re still feeling brave, here’s what I have:

Get your parallel port address

First of all, figure out your parallel port address. Type the following command:

cat /proc/ioports | grep parport

You should see something like this:

  0378-037a : parport0

In this case, the output means my parallel port is at 0x378. Remember this address; we will customize it when we compile the DLL.

Download the DLL source code

Download the DLL source code here:


Extract it. If your parallel port is at an address other than 0x378, open up io_main.c and update the LPT_BASE_ADDR define near the top of the code to match your parallel port address.

Install prerequisites

These packages will probably be necessary to make everything work. I hope I got all of them, but I may have forgotten something. If I forgot something, I apologize. Let me know in the comments.

sudo apt-get install build-essential wine1.6-dev gcc-multilib

Build the DLL

Inside the DLL source directory, type:


Assuming everything goes without error, you should find the file io.dll.so which is the DLL we will be using.

Install the DLL

Remove any io.dll from your Wine installation. It’s useless inside of Wine anyway, so there’s no need for it. Make sure there’s no io.dll file next to EpromM51.exe either. I’m trying to make sure that Wine doesn’t look anywhere else for this DLL. Now, we’re going to put the io.dll.so file in a place where Wine can find it:

sudo cp io.dll.so /usr/lib/i386-linux-gnu/wine/

I messed around, but couldn’t get this to work without putting it right in Wine’s library directory. There might be a better way to do this, but this was the best I could find.

Even if you’re on a 64-bit system, you’ll find that this DLL gets compiled as a 32-bit DLL. So /usr/lib/i386-linux-gnu/wine is always the correct location for this library, regardless of whether your system is 32-bit or 64-bit.

Set up permissions

Here’s the weird part. Raw port access doesn’t work unless you’re root, or you allow the program to have capabilities to access I/O ports. Running things as root is a bad idea, so let’s give the program capabilities to access the I/O ports. Warning: This could pose a potential security threat to your system. We’re actually going to be giving all Wine programs access to the I/O ports.

Here’s the magic command that allows Wine programs to use raw I/O:

sudo setcap cap_sys_rawio=ep /usr/bin/wine-preloader

You may or may not have to re-run this command every time you update Wine. I just don’t know. Anybody else have a better solution for direct port access? I hate having to mess with wine-preloader, but I don’t want to create a separate daemon or whatever due to the added latency.

All done

If you’re followed all these directions, it’s very possible that your Willem programmer is all ready to go now. Give it a shot. I hope this helps someone out there.

Boring details for developers

For anyone who’s interested in how I did this, I created this DLL by taking the original io.dll, fixing its header file so it doesn’t use weird typedefs that confuse winedump, and then running this command:

winedump spec io.dll -I . -t -C

After that, I ran this command:

winemaker . --nosource-fix --dll --nomfc -D__WINESRC__ --wine32

This gave me everything I needed to get this to work. I also had to fix up the generated code–for example, I commented out the line: #include “config.h” in io_main.c.



  1. Hey Doug!

    Love the treatment you’ve developed here. I just verified your work in the 32 bit Mint Linux – Rebecca release. As a side note, my card lives way up at 0xbca4 and works in the burner program as lpt1. I’m pretty pleased. Thank you for all of your hard work.

    Now then, if you could make my DIY K150 programmer work under linux, you really would be my hero.


  2. Hi,

    I tried to buid it on Debian 8.1 64-bit but it failed with the following error:

    $ make
    winegcc -c -mno-cygwin -m32 -I. -D__WINESRC__ -o io_main.o io_main.c
    winegcc -shared io.spec -mno-cygwin -m32 -o io.dll.so io_main.o -lodbc32 -lole32 -loleaut32 -lwinspool -lodbccp32 -luuid
    /usr/bin/ld: Relocatable linking with relocations from format elf64-x86-64 (/usr/lib/x86_64-linux-gnu/wine/wine/libwinecrt0.a(dll_entry.o)) to format elf32-i386 (io.8Ccl3C.o) is not supported
    winebuild: /usr/bin/ld failed with status 1
    winegcc: winebuild failed
    Makefile:117: recipe for target ‘io.dll.so’ failed
    make: *** [io.dll.so] Error 2

  3. That is the same problem I saw in older Ubuntu versions. It looks like it’s trying to link against 64-bit libraries even though it’s building a 32-bit library. Can you try building it inside a 32-bit VM? That was the eventual solution for me back then.

  4. On Debian 8.1 32-bit works just fine. But after copy so file to 64-bit box I face such a problem:

    $ ldd /usr/lib/i386-linux-gnu/wine/io.dll.so
    ERROR: ld.so: object ‘/usr/lib/libtsocks.so’ from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS64): ignored.
    linux-gate.so.1 (0xf77cf000)
    libwine.so.1 => not found
    libm.so.6 => /lib/i386-linux-gnu/i686/cmov/libm.so.6 (0xf774c000)
    libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf75a1000)
    /lib/ld-linux.so.2 (0xf77d2000)

    I have libwine.so.1 (link to libwine.so.1.0) but it is 64-bit vrsion:

    file /usr/lib/x86_64-linux-gnu/wine/libwine.so.1.0
    /usr/lib/x86_64-linux-gnu/wine/libwine.so.1.0: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=504e416669c729494a877202809c64a48a7ff69c, stripped

    apt-file does not help in finding 32-bit version of the library:

    apt-file search libwine.so.1
    libwine: /usr/lib/x86_64-linux-gnu/wine/libwine.so.1
    libwine: /usr/lib/x86_64-linux-gnu/wine/libwine.so.1.0
    libwine-development: /usr/lib/x86_64-linux-gnu/wine-development/libwine.so.1
    libwine-development: /usr/lib/x86_64-linux-gnu/wine-development/libwine.so.1.0

  5. Hmm, I have no idea about that. Sounds like a Debian issue, but I guess I don’t know for sure. Here’s what ldd reports on a 64-bit Ubuntu 14.04 box:

    $ ldd io.dll.so
    linux-gate.so.1 => (0xf77a0000)
    libwine.so.1 => /usr/lib/i386-linux-gnu/libwine.so.1 (0xf75ae000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7400000)
    libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xf73fa000)
    /lib/ld-linux.so.2 (0xf77a1000)

    I think you are missing a 32-bit version of the library. On my 64-bit machine, apt-file also doesn’t say anything a 32-bit version of libwine.so.1. However, I do have it. dpkg -S libwine.so.1 indicates:

    wine1.7-i386: /usr/lib/i386-linux-gnu/libwine.so.1
    wine1.7-i386: /usr/lib/i386-linux-gnu/libwine.so.1.0

  6. In my case dpkg shows:

    dpkg -S libwine.so.1
    libwine:amd64: /usr/lib/x86_64-linux-gnu/wine/libwine.so.1.0
    libwine:i386: /usr/lib/i386-linux-gnu/wine/libwine.so.1.0
    libwine-development:i386: /usr/lib/i386-linux-gnu/wine-development/libwine.so.1.0
    libwine-development:i386: /usr/lib/i386-linux-gnu/wine-development/libwine.so.1
    libwine:i386: /usr/lib/i386-linux-gnu/wine/libwine.so.1
    libwine:amd64: /usr/lib/x86_64-linux-gnu/wine/libwine.so.1

    After adding the following line:


    to the following file:


    and executing “ldconfig”, ldd shows all libraries:

    ldd /usr/lib/i386-linux-gnu/wine/io.dll.so
    linux-gate.so.1 (0xf776d000)
    libwine.so.1 => /usr/lib/i386-linux-gnu/wine/libwine.so.1 (0xf757a000)
    libm.so.6 => /lib/i386-linux-gnu/i686/cmov/libm.so.6 (0xf7534000)
    libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf7389000)
    libdl.so.2 => /lib/i386-linux-gnu/i686/cmov/libdl.so.2 (0xf7384000)
    /lib/ld-linux.so.2 (0xf7770000)

    But during startup of the EPROMM51.EXE I see the following message:

    Could not open the IO.dll driver. My laptop has no parallel port yest – express card has not arrived yet. Can the message be related to lack of the parallel port?

  7. Executing bthe following command does not report any issue:

    LD_PRELOAD=/usr/lib/i386-linux-gnu/wine/io.dll.so ./32-bit-test-app

    The 32-bit-test-app is a just a tiny C 32-bit app – main function with couple of lines.

  8. Yet another test:

    LD_PRELOAD=/usr/lib/i386-linux-gnu/wine/io.dll.so wine ./EPROMM51.EXE
    ERROR: ld.so: object ‘/usr/lib/i386-linux-gnu/wine/io.dll.so’ from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS32): ignored.
    ERROR: ld.so: object ‘/usr/lib/i386-linux-gnu/wine/io.dll.so’ from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS32): ignored.
    ioperm: Success

  9. On this way no error message:

    LD_PRELOAD=/usr/lib/i386-linux-gnu/wine/io.dll.so wine32 ./EPROMM51.EXE
    ioperm: Success

  10. Interesting! I don’t know why I never have to do any of these things when I run it in Wine. Hopefully that last LD_PRELOAD trick you did will work for you when you have your card.

  11. I am back in Debian 32-bit. After upgrade to Debian 8.2 afer exeucting the following command:

    LD_PRELOAD=/usr/lib/i386-linux-gnu/wine/io.dll.so wine32 ./EPROMM51.EXE

    I receive “Could not open the IO.dll driver” and then “Access violation …”. Any help? Any ideas?

  12. Larry Kraemer @ 2017-10-25 13:10

    I just built your iodll file on Debian 8.x (32 bit) and finally got it to build properly. I’ve documented it in a detailed email.

    Please review, as this information will help others get the
    Willem Programmer working on Linux.



  13. Larry Kraemer @ 2017-10-27 20:07

    For those with errors in LD_PRELOAD=/usr/lib/i386-linux-gnu/wine/io.dll.so, try moving io.dll.so to /usr/lib/i386-linux-gnu/wine/wine/

    Then I just execute wine as follows for the PCB6.0E LPT Software:

    $wine pcb60\ lpt.exe

    I’ve also extracted the Willem Programmer Icon from the Software with Linux’s wrestool.

    More information will follow after I have a Parallel PCIe Card & actual PCB60E LPT Programmer.


  14. Phoebus1966 @ 2023-01-29 08:07

    Your instructions worked for me. Thanks a million Doug Brown!

    For anyone’s intrest: My system is an AMD Athlon 64X2 4800+x2 Desktop computer running 32-bit Ubuntu 14.04 LTS and Wine verson 1.6.2. Wine is running as a Windows Vista.

    The security issue is understood however I don’t use this desktop computer for critical stuff. I’ll check if I can set the network off.

Add your comment now