dosdefender-ld31 icon indicating copy to clipboard operation
dosdefender-ld31 copied to clipboard

Compilation seems to be ... off?

Open prozacgod opened this issue 5 years ago • 8 comments

First up, I'm on PopOS! latest gcc --version gcc (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

/etc/os-release NAME="Pop!_OS" VERSION="19.10" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Pop!_OS 19.10" VERSION_ID="19.10" HOME_URL="https://system76.com/pop" SUPPORT_URL="http://support.system76.com" BUG_REPORT_URL="https://github.com/pop-os/pop/issues" PRIVACY_POLICY_URL="https://system76.com/privacy" VERSION_CODENAME=eoan UBUNTU_CODENAME=eoan LOGO=distributor-logo-pop-os

It seems the compiled jump instructions are off by a few bytes.... ?

command

gcc -std=gnu99 -Os -nostdlib -m32 -march=i386 -fno-PIC -ffreestanding -o test.com -fno-PIE -Wl,--nmagic,--script=com.ld test.c

test.c

asm (".code16gcc\n"
  "call  main\n"
  "mov   $0x4C,%ah\n"
  "int   $0x21\n");
int main(void)
{
   return 0;
}

produces...

66 E8 04 00 00 00 B4 4C CD 21 F3 0F .....

00  callw 0x00000008 
04  add BYTE PTR [eax], al
06 mov ah, 0x4c
08 int 0x21;
0a repz nop ebx

Where it initially calls 0x108 from from the init, which is the address for CD 21 ... which.. is most assuredly not going to execute the main function :facepalm:

prozacgod avatar Apr 28 '20 07:04 prozacgod

  1. the symbol main can be missinterpreted by the compiler, so use something like _main instead
  2. to correctly disassemble the com executable use objdump with this command

objdump -D -b binary -m i8086 --adjust-vma=0x100 test.com

3)try to use this to compile, it works for a custom project of mine

gcc -o test.com -std=gnu99 -Wall -DDOS -Wextra -Os -nostdlib -m16 -march=i386 -Wno-unused-function -ffreestanding -fomit-frame-pointer -fwrapv -fno-strict-aliasing -fno-leading-underscore -fno-pic -fno-stack-protector -Wl,--nmagic,-static,-Tcom.ld main.c *.h

it defines a very usefoul DOS macro and the -m16 remooves the need for the .code16gcc in the startup assembly code (except for functions like naked assembly functions which will still require that).

that said, you also must have something going on in the program or the optimizer will just remoove everything.

  1. check out this guy's work because he used his knowledge of the gcc compiler to improove a lot everthing, https://www.patreon.com/posts/even-more-gcc-w-20376115.

he added a lot, including c++ support and a proper makefile, but for me one of his very best additions is a section to the executable file called .start which is placed right before the .text section, with that you can use something like this

static void __attribute__((section(".start"), naked, used)) start() { //asm code to start your program here }

to start your program without having to care about placing that asm before all the code, just place this function inside an header file.

I also strongly reccomend you to implement the exit function too because that start function had it's issues

`void attribute((noreturn)) exit(int ret){ asm volatile ("int $0x21\n" : : "a"(0x4c00 | (ret & 0xff)) :);

}`

the only problem i am having with this implementation of exit is that the compiler says that it returns, but i can't uderstand why, reguardless of that it works.

And also some IDEs might complain that a standalone implementation of exit or really any other important library function, might be off-standard, so be carefoul about that.

Feel free to also chat with me or contact me to compile dos programs in this way or really just talk about this kind of dos development, i am making my own does game in this same way too, i had a lot of challenges to overcome and so you might need some more advice.

ITzTravelInTime avatar Apr 28 '20 12:04 ITzTravelInTime

After going over this all again today I think part of what was wrong was a misunderstanding from me.

00 callw 0x00000008

is a relative 32 bit call, which would be 8 bytes after the instruction, and I was looking at it as if it were absolute.

it seems I was wrong, and that it jumps to the "repz nop ebx" which I think is the init code for the main function.

The symptom I was having was this program in dosbox would hard lock the cpu.

I tried dosbox/dosemu/qemu/virtualbox - qemu/virtualbox seem to behave better

dosbox would just lock up often. I wonder if it's not executing 32bit relative jumps correctly, but surely if that was an issue it would cause WAY more incompatibilities than just my toy gcc project?

I also grabbed the code you mentioned, built it with very much similar results, dosbox just not liking most of the code.

But some of the programs still locked up in virtualbox/qemu as well. BUT at few of them worked! so that's a start.

TBH I'm having fun with twisting gcc around, but I'd much rather just code up my toy DOS game/code instead of fighting the build process :/ - I might switch to bcc or something else.

prozacgod avatar Apr 28 '20 17:04 prozacgod

I wonder what other's environments are like. I've been using mtools and qemu this morning to make the experience more tollerable.

my hdd is raw disk image, 1gb

I created a .mtoolsrc

drive c: file="/absolute/path/to/file/hda.img" partition=1

I created a test.sh file that

#!/bin/bash
mdel c:/test/*
mdeltree c:/test
mmd c:/test

mcopy *.com c:/test
mcopy -t *.bat c:/test

qemu-system-i386 -vga std -m 64 -cpu pentium3 -soundhw sb16,adlib -hda /absolute/path/to/file/hda.img

mdeltree -y c:/test

inside the boot disk, I have an autoexec.bat file that does all of it's bits and bobs and then ends with this bit here..

IF EXIST C:\TEST\TEST.BAT echo Running TEST
IF EXIST C:\TEST\TEST.BAT C:\TEST\TEST.BAT

the next bit of magic, I'd used a atx command back "in the day" to turn off my machine, so I assumed it might close qemu - and it does! https://superuser.com/questions/1094409/how-do-i-auto-turn-off-a-dos-only-machine-using-software-the-pc-has-no-power-sw

so the test.bat can call that to exit the emulator automatically after the program exits, I've managed to get vga mode setting and a pixel routine to work, so ... atm things are looking up, thanks for the help.

prozacgod avatar Apr 28 '20 19:04 prozacgod

It's useless to give more than 16 mb to a dos vm since most programs are limited to the addressing modes of the 8086 or the 286, only when using the full i386 protected mode you have access to all the features of the emulated cpu, inlcuing the much improoved addressing modes. That said those dos com executables you are compiling with gcc are real mode i386 executables, so you are limited to the addressing modes of the 8086 but you have access to some 386 features, like the 2 extra segment registers, some 32 bit operations and the whole 32 bit registers.

I am also testing my programs on real hardware, i own a 386 dx 40 and a slot 1 motherboard with a pentium 3 (and i also own some slot 1 pentium 2s and celerons) and things are going good so far, the only problem i had on real hardware is that 32 bit memory operations (which for example i performed using assembly to read/write to some other memory segments) needs to be made to memory addresses which are multiple of 4 or otherwise the system will halt, that's something that the compiler can take care of for reading/writing within the 64k memory area of your program, but you will have to take care of while performing operations to other memory segments

ITzTravelInTime avatar Apr 30 '20 14:04 ITzTravelInTime

Looks like the problem is newer gcc (8.1 and up?) uses Intel CET and starts stuffing endbr32's in the code. Dosbox doesn't seem to like these even though they should just be treated as NOP on old CPU's. So the CPU seems to hang right away when it branches to dosmain and encounters the endbr32.

@prozacgod I believe that "repz nop ebx" you were encountering is the same as the "endbr32" instruction just in a disassembly before the knowledge of endbr32.

@ITzTravelInTime thanks for that objdump -D -b binary -m i8086 --adjust-vma=0x100 test.com tip -- it helped me disassemble the code to find the problematic endbr32.

The solution looks to be to add -fcf-protection=none to your CFLAGS and there will be no endbr32's and dosbox is happy.

agentbooth avatar Feb 23 '21 18:02 agentbooth

A little more info here if you're interested: https://stackoverflow.com/questions/56120231/how-do-old-cpus-execute-the-new-endbr64-and-endbr32-instructions

agentbooth avatar Feb 23 '21 19:02 agentbooth

hi guys, i know this is not related to the issue, but i'd like to have a place in wich we can all riunite to discuss development of ms-dos games using modern GCC on linux/WSL (or on macOS using an ELF-targeted copy of GCC).

I was wondering about opening some telegram group or a discord server for that.

ITzTravelInTime avatar Feb 24 '21 16:02 ITzTravelInTime

@ITzTravelInTime That sounds pretty cool. I don't really plan on doing much dev on this front but it's very cool being able to use currect gcc on linux to create old DOS executables. I was just trying to convert (to C) an old DOS graphics demo I wrote in Pascal back in the early 90s to see it run again and this was a great platform for doing that. Thanks @skeeto!

agentbooth avatar Feb 25 '21 17:02 agentbooth