Discussion:
ss and lea on 8086
(too old to reply)
Paul Edwards
2023-06-24 07:35:07 UTC
Permalink
I have the following code:

void dumplong(unsigned long x)
{
static int y;
char *z = "0123456789abcdef";
char buf[9];

y = 0;
buf[0] = 'P';
buf[1] = 'P';
buf[2] = 'P';
buf[3] = 'P';
buf[3-y] = 'R';
buf[4] = 'P';
buf[4+y] = 'S';
buf[5] = 'P';
buf[6] = 'P';
buf[7] = 'P';

And Visual C++ 1.52 is generating this assembler:

; x = 0006
; buf = fff4
; z = fff0
; Line 5836
_dumplong:
push bp
mov bp,sp
sub sp,OFFSET L04604
; Line 5837
; Line 5838
mov ax,OFFSET L03767
mov dx,ds
mov WORD PTR -16[bp],ax
mov WORD PTR -14[bp],dx
; Line 5839
; Line 5841
mov WORD PTR ?y@?1??dumplong@@***@9,OFFSET 0
; Line 5842
mov BYTE PTR -12[bp],OFFSET 80
; Line 5843
mov BYTE PTR -11[bp],OFFSET 80
; Line 5844
mov BYTE PTR -10[bp],OFFSET 80
; Line 5845
mov BYTE PTR -9[bp],OFFSET 80
; Line 5846
mov ax,OFFSET 3
sub ax,WORD PTR ?y@?1??dumplong@@***@9
lea bx,WORD PTR -12[bp]
add bx,ax
mov BYTE PTR ss:[bx],OFFSET 82
; Line 5847
mov BYTE PTR -8[bp],OFFSET 80
; Line 5848
mov ax,WORD PTR ?y@?1??dumplong@@***@9
add ax,OFFSET 4
lea bx,WORD PTR -12[bp]
add bx,ax
mov BYTE PTR ss:[bx],OFFSET 83
; Line 5849
mov BYTE PTR -7[bp],OFFSET 80
; Line 5850
mov BYTE PTR -6[bp],OFFSET 80
; Line 5851
mov BYTE PTR -5[bp],OFFSET 80


I've been looking at this for hours.

Unless I've somehow stuffed up the test, this data is
all being set to 'P'.

Regardless of whether I do a subtraction or an addition
(of 0), the data is not changed.

I do have one unusual thing happening - my "ss" is set
to something a long way away from "ds" because this
is the PDOS/86 kernel and I didn't create a separate
stack for the kernel, I just reused the one from the
bootloader.

But I don't see any reason for that effect. The lea shouldn't
care, and the ss is explicitly used.

It is not easy to debug this because this IS what I use
to debug!

Original code, before I started debugging, is here:

https://sourceforge.net/p/pdos/gitcode/ci/master/tree/src/pdos.c


Any ideas?

Thanks. Paul.
Tavis Ormandy
2023-06-24 14:52:17 UTC
Permalink
[I've rearranged your code to intermix code with assembly]
Post by Paul Edwards
void dumplong(unsigned long x)
push bp
mov bp,sp
Note: Making space for locals, not an offset.
Post by Paul Edwards
char buf[9];
sub sp,OFFSET L04604
Note: Loading far pointer to "0123..." into z
Post by Paul Edwards
char *z = "0123456789abcdef";
mov ax,OFFSET L03767
mov dx,ds
mov WORD PTR -16[bp],ax
mov WORD PTR -14[bp],dx
y = 0;
buf[0] = 'P';
buf[1] = 'P';
buf[2] = 'P';
buf[3] = 'P';
mov BYTE PTR -12[bp],OFFSET 80
mov BYTE PTR -11[bp],OFFSET 80
mov BYTE PTR -10[bp],OFFSET 80
mov BYTE PTR -9[bp],OFFSET 80
buf[3-y] = 'R';
mov ax,OFFSET 3
lea bx,WORD PTR -12[bp]
add bx,ax
mov BYTE PTR ss:[bx],OFFSET 82
buf[4] = 'P';
mov BYTE PTR -8[bp],OFFSET 80
buf[4+y] = 'S';
add ax,OFFSET 4
lea bx,WORD PTR -12[bp]
add bx,ax
mov BYTE PTR ss:[bx],OFFSET 83
buf[5] = 'P';
buf[6] = 'P';
buf[7] = 'P';
mov BYTE PTR -7[bp],OFFSET 80
mov BYTE PTR -6[bp],OFFSET 80
mov BYTE PTR -5[bp],OFFSET 80
I've been looking at this for hours.
Unless I've somehow stuffed up the test, this data is
all being set to 'P'.
I don't follow, you set buf[3] to 'R' (shows as OFFSET 82 in the
disassembly) and buf[4] to 'S'.
Post by Paul Edwards
Regardless of whether I do a subtraction or an addition
(of 0), the data is not changed.
You don't really explain what problem you're seeing, here is a
Post by Paul Edwards
buf[3-y] = 'R';
mov ax,OFFSET 3 ; ax = 3
So now ax is 3-y
Post by Paul Edwards
lea bx,WORD PTR -12[bp] ; bx = &buf[0]
add bx,ax ; bx += ax
So now bx is &buf[3-y]
Post by Paul Edwards
mov BYTE PTR ss:[bx],OFFSET 82 ; *bx = 'R'
Set *bx = 'R', isn't that correct?

Tavis.
--
_o) $ lynx lock.cmpxchg8b.com
/\\ _o) _o) $ finger ***@sdf.org
_\_V _( ) _( ) @taviso
Tavis Ormandy
2023-06-24 15:39:49 UTC
Permalink
Post by Tavis Ormandy
Post by Paul Edwards
I've been looking at this for hours.
Unless I've somehow stuffed up the test, this data is
all being set to 'P'.
I don't follow, you set buf[3] to 'R' (shows as OFFSET 82 in the
disassembly) and buf[4] to 'S'.
Ah, as soon as I hit send I realized what you were saying - you don't
think there's a miscompilation. You're saying the write to ss:[bx] isn't
working, and you suspect the lea is to blame?

It seems correct to me, the base should be irrelevant and the actual
write has the correct override. I don't know what could be causing
that!

Tavis.
--
_o) $ lynx lock.cmpxchg8b.com
/\\ _o) _o) $ finger ***@sdf.org
_\_V _( ) _( ) @taviso
Paul Edwards
2023-06-25 00:44:20 UTC
Permalink
On Saturday, June 24, 2023 at 11:55:00 PM UTC+8, Tavis Ormandy wrote:

Hi Tavis. Thanks for your reply.
Post by Tavis Ormandy
Ah, as soon as I hit send I realized what you were saying - you don't
think there's a miscompilation. You're saying the write to ss:[bx] isn't
working, and you suspect the lea is to blame?
Correct.
Post by Tavis Ormandy
It seems correct to me, the base should be irrelevant and the actual
write has the correct override. I don't know what could be causing
that!
Ok, thanks for the analysis. I thought I might have been doing
something that was fundamentally and obviously wrong by
having ss != ds, and the lea showed that up.

A piece of information I forgot to give was that making the
buffer static (so referenced by ds instead of ss), made the
problem go away.

So now that no-one is aware of anything that is obviously
wrong, I thought of a way of getting more information by
creating a second set of dump functions, one of them using
"static".

Here is what I see when I use the static version, and the
new debug shown below, and Bochs instead of qemu
so that I can capture a screen:

Booting from Floppy...
5bb3351253525050PPRSPPPP

which is:

5bb3:3512 (data section of PDOS)

53525050 (SRPP - first 4 bytes treated as a 32-bit long)

PPRSPPPP (the expected buffer printing correctly)


And here is what I see when the buffer is on the stack:


Booting from Floppy...
0a6bfed850505050PPPPPPPP

which is:

0a6b:fed8

the stack area from the loader:

0A6B:0 DGROUP (from pload.map)

50505050 (no sign of my 52 and 53)

PPPPPPPP (and this is not what I want)


Any ideas? Current debug code below.

I really am expecting that the "lea" gives an address
relative to ds rather than ss. Although I read that
lea is a simple arithmetic operation, which would
mean it is unrelated to either the ds or ss value.
But that doesn't sound right to me. I think it needs
to be relative to something by definition, and I
would hope that a bp reference is relative to ss.

Also note that I changed this so that it is within
the first 4 bytes so that a "long" can see both
bytes.

+ buf[2-y] = 'R';
+ buf[3] = 'P';
+ buf[3+y] = 'S';

Thanks. Paul.



diff --git a/src/pdos.c b/src/pdos.c
index 0459c3ab..c35bc927 100644
--- a/src/pdos.c
+++ b/src/pdos.c
@@ -885,6 +885,10 @@ void pdosRun(void)
#ifdef __32BIT__
printf("Welcome to PDOS/386 (aka PD-Windows)\n");
#else
+/*dumpbuf("XY", 2);*/
+dumplong((long)5);
+for (;;) ;
+printf("Z");
printf("Welcome to PDOS/86\n");
#endif
PosSetVideoAttribute(0x7);
@@ -5828,17 +5832,71 @@ static void accessDisk(int drive)
return;
}

-void dumplong(unsigned long x)
+void dumpbuf2(unsigned char *buf, int len);
+
+void dumplong2(unsigned long x)
{
- int y;
+ static int y;
char *z = "0123456789abcdef";
- char buf[9];
+ static char buf[9];
+
+ for (y = 0; y < 8; y++)
+ {
+ buf[7 - y] = z[x & 0x0f];
+ x /= 16;
+ }
+ buf[8] = '\0';
+ dumpbuf2(buf, 8);
+ return;
+}
+
+void dumpbuf2(unsigned char *buf, int len)
+{
+ int x;
+
+ for (x = 0; x < len; x++)
+ {
+ pdosWriteText(buf[x]);
+ }
+ return;
+}

+void dumplong(unsigned long x)
+{
+ static int y;
+ char *z = "0123456789abcdef";
+ char buf[9]; /* using static makes it work */
+
+ y = 0;
+ buf[0] = 'P';
+ buf[1] = 'P';
+ buf[2] = 'P';
+ buf[2-y] = 'R';
+ buf[3] = 'P';
+ buf[3+y] = 'S';
+ buf[4] = 'P';
+ buf[5] = 'P';
+ buf[6] = 'P';
+ buf[7] = 'P';
+#if 0
for (y = 0; y < 8; y++)
{
buf[7 - y] = z[x & 0x0f];
x /= 16;
+#if 0
+ buf[7 - y] = 'V'; /* z[x & 0x0f]; */
+ /* x /= 16; */
+ if (x != 333)
+ {
+ buf[7] = 'H';
+ dumpbuf(&buf[7], 1);
+ buf[7-y] = 'G';
+ dumpbuf(&buf[7-y], 1);
+ dumpbuf(&buf[7], 1);
+ }
+#endif
}
+#endif
buf[8] = '\0';
dumpbuf(buf, 8);
return;
@@ -5848,6 +5906,9 @@ void dumpbuf(unsigned char *buf, int len)
{
int x;

+/* buf is 0a6b:fedf */
+ dumplong2((long)buf);
+ dumplong2((long)*(long *)buf);
for (x = 0; x < len; x++)
{
pdosWriteText(buf[x]);
Paul Edwards
2023-06-25 03:26:19 UTC
Permalink
Next thing I tried was putting the generated assembler
into one of my own assembler files. It didn't directly
assemble, so I modified it like this:

public dumplong3
; x = 0006
; buf = fffa
; Line 5910
dumplong3:
push bp
mov bp,sp
; sub sp,OFFSET L04632
sub sp,06H
; Line 5911
; Line 5912
; Line 5914
mov BYTE PTR [bp-6],80
; Line 5915
mov BYTE PTR [bp-5],OFFSET 80
; Line 5916
mov BYTE PTR [bp-4],OFFSET 80
; Line 5917
mov ax,OFFSET 2
sub ax,WORD PTR y
lea bx,WORD PTR [bp-6]
add bx,ax
mov BYTE PTR ss:[bx],OFFSET 82
; Line 5918
mov BYTE PTR [bp-3],OFFSET 80
; Line 5919
mov ax,WORD PTR y
add ax,OFFSET 3
lea bx,WORD PTR [bp-6]
add bx,ax
mov BYTE PTR ss:[bx],OFFSET 83
; Line 5920
mov ax,OFFSET 4
push ax
lea ax,WORD PTR [bp-6]
mov dx,ss
push dx
push ax
call FAR PTR dumpbuf
add sp,OFFSET 6
; Line 5921
; Line 5922
; Line 5922
L03803:
mov sp,bp
pop bp
retf
int 3
int 3
int 3
int 3
int 3
int 3
int 3

.data
y dw 0


The int 3 is an eyecatcher.

The most suspicious change I made was this:

; sub sp,OFFSET L04632
sub sp,06H

That label doesn't actually appear in the generated assembler.

Anyway, the handwritten assembler works!

So lea is fundamentally fine.

I then went looking to match the machine code. Here is
the handwritten assembler:

558B EC83EC06 ....XX.L.!U....
000150 C646FA50 C646FB50 C646FC50 B802002B .F.P.F.P.F.P...+
000160 0600008D 5EFA03D8 36C60752 C646FD50 ....^...6..R.F.P
000170 A1000083 C0038D5E FA03D836 C60753B8 .......^...6..S.
000180 0400508D 46FA8CD2 52509A00 00000083 ..P.F...RP......
000190 C4068BE5 5DCB

And here is the C code:

55 8BEC81EC 0600C646 .......U.......F
013310 FA50C646 FB50C646 FC50B802 002B0600 .P.F.P.F.P...+..
013320 008D5EFA 03D8C607 52C646FD 50A10000 ..^.....R.F.P...
013330 0503008D 5EFA03D8 C60753B8 0400508D ....^.....S...P.
013340 46FA8CD2 52509A00 00000083 C4068BE5 F...RP..........
013350 5DCB

The first thing I noticed was that the x'83' (subtract):

http://sparksandflames.com/files/x86InstructionChart.html

is strangely an "add" ('x81') in C, which isn't going to work.

I tried zapping the add into a subtract, on the assumption
that it was a compiler bug, since I think I am using 1.52
but I think there is a 1.52C available via patch.

But the C code still didn't work.

I suspect that the add is correct, and that label has a
value of -6 in it, and so it is meant to work, and may
explain why the number of bytes of code is different
between the two, as it is perhaps a reference rather
than an immediate.

Still investigating.

BFN. Paul.
Paul Edwards
2023-06-25 05:24:34 UTC
Permalink
Post by Paul Edwards
I tried zapping the add into a subtract, on the assumption
that it was a compiler bug, since I think I am using 1.52
but I think there is a 1.52C available via patch.
I appear to have 1.52C as per the name of the CD,
even though the label says 1.52.

Anyway, I believe I have found a compiler bug:

/*

This program demonstrates a presumed bug in Visual C++ 1.52C

When compiled with:

cl -Fa -c -AH -Ox -f foo.c

it says it generated:

add bx,ax
mov BYTE PTR ss:[bx],OFFSET 88

but looking at the object code shows:

03D8 C60758

Manually assembling with masm shows:

03 D8 add bx,ax
36: C6 07 58 mov BYTE PTR ss:[bx],OFFSET 88

the '36' is the SS override. That is missing from the object code.
This only becomes a problem if ds and ss are not the same. Normally
they are the same.

*/
void foo(int y)
{
char buf[4];

buf[3-y] = 'X';
bar(buf);
return;
}

Loading...