Page 1 of 1

A little experiment

Posted: Thu Jun 10, 2010 8:11 pm
by rknox
A little experiment. Those who find this interesting probably already know the result. But I thought I would share anyway.

At one place, the Intel documentation implies that the relationship between segment registers and general registers does not exist in flat protected mode. This seemed unreasonable to me and I suspected they were saying because all segment registers are the same it is no longer material.

Being curious I ran a little experiment that confirmed what most of you probably know - that the relationship is still there, but just not material.

The experiment - code that deposited information into a variable whose address was in ebx. Ebx instructions are coupled with seg register ds. After I had loaded the address of the variable into ebx I modified ds either (1) to be what it already was ( 0x10 pointing to a read/write segment descriptior in the gdt) or (2) to be 0x08 (which pointed to the read only code descriptor).

As you probably know (and what I learned): the code ran when I used 0x10 and bombed with 0x08.

Conclusions:
1 - there is no harm in messing with ds after you load an address via lea as long as you fix ds before it is used.
2- the relationship between general registers and segment registers does not go away in flat mode, it just becomes immaterial.

Code: Select all

; experiment
	EXTERN timer_ticks ; variable in c code
	; system does not complain if you put offset of a r/o segment into ds
	; .. as long as you dont use it
	mov eax, 0x08	; code segment gdt offset
	mov ds, eax	; bad value - did not complain
	mov eax, 0x10	; data segment offset
	mov ds, eax	; fix

	; write to an arbitrarily selected variable - with good or bad value in ds
	lea ebx, [timer_ticks]	; addr of tt in ebx
	mov eax, 0x08	; bad  data seg selector 
	mov eax, 0x10	; good data seg selector - pgm runs ok unless line commented out
	mov ds, eax	; make ds either bad or good, depending on the ;
	mov [ebx], dword 0x1000 ; write 1000 to timer_ticks (or bomb .. and it does bomb)

	; prepare to show t_t in main program to see that we did what we wanted to do
	mov eax, [timer_ticks]
	mov [st], eax	; st is used to display 0x1000 as diagnostic "start" at run time
; end experiment


Re: A little experiment

Posted: Thu Jun 10, 2010 8:41 pm
by gerryg400
I think you'll find that when you load DS with a code selector, the code segment needs to be readable. So I'm guessing your code segment is of type 1xx1 1x1x ( 0x9a ??? ). I don't think you could load a selector for an execute-only code segment into DS. ( i.e. 0x98 wouldn't work).

I don't do these experiments enough. I'm usually so relieved when something finally works, I don't ever change it again !!

Re: A little experiment

Posted: Fri Jun 11, 2010 3:24 am
by qw
At one place, the Intel documentation implies that the relationship between segment registers and general registers does not exist in flat protected mode [...] the relationship is still there, but just not material.
I really don't get what you mean by the relationship between segment and general purpose registers. Your code is not testing a relationship, but whether you can load a selector for a code descriptor in DS. You can, but we already knew that.
Segment selectors for code segments that are not readable [...] cannot be loaded into data-segment registers
So selectors for code segments that are readable can.

Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 3A, Chapter 4.

Re: A little experiment

Posted: Fri Jun 11, 2010 9:44 am
by rknox
Experiment 2 - reading through reg ebx while ds points to the code descriptor in the gdt does not cause a problem.

Type for 0x08 is A - code execute read
Type for 0x10 is 2 - data read write

We are experimenting with operations that use register ebx - linked behind the scenes to seg reg ds. We are using ebx as a pointer to memory, to see how the value in ds effects whether we abend.

We change ds for one instruction only to point to the code segment descriptor in the gdt. That segment desecriptor specifies read only.

When we tried to write under that condition we raised a general protection fault.

Today we found that we can read under that condition.

Prompted by a question Gerry asked, I tried to read while ds points to the code descriptor. Maybe we can do it since the code area allows reads.

System did not abend.

Code: Select all

; experiment
global bomb
bomb:
	; call from main so we can see the exception raised
	EXTERN timer_ticks ; variable in c code
	; system does not complain if you put offset of a r/o segment into ds
	; .. as long as you dont use it
	mov eax, 0x08	; code segment gdt offset
	mov ds, eax	; bad value - did not complain
	mov eax, 0x10	; data segment offset
	mov ds, eax	; fix

	; write to an arbitrarily selected variable - with good or bad value in ds
	; gdt seg descriptor at 0x08 byte 5 :==: 9A - 0x10 byte 5 :==: 0x9C
	lea ebx, [timer_ticks]	; addr of tt in ebx
	mov eax, 0x08	; bad  data seg selector 
;	mov eax, 0x10	; good data seg selector - pgm runs ok unless line commented out
	mov ds, eax	; make ds either bad or good, depending on the ;
;	following 2 are either or
;	mov [ebx], dword 0x1000 ; write 1000 to timer_ticks,or bomb when ds :==: 0x08 
	mov eax  ,[ebx] ; read into eax from timer_ticks
	mov eax, 0x10	; good data seg selector
	mov ds, eax	; fix ds in case we survuve

	; show t_t in main program to see that we did what we wanted to do
	mov eax, [timer_ticks]
	mov [st], eax	; displays 0x1000 as diagnostic "start" at run time
	ret
; end experiment


Re: A little experiment

Posted: Fri Jun 11, 2010 9:59 am
by rknox
Hobbes - let me try to explain myself better.

When you program in real segmented mode I believe you need to spend some time worrying about the fact that if you use certain general registers as pointers in memory operations there is an unseen use of certain segment registers. If you use BX as a memory pointer, the resulting operation uses DS in constructing the address.

My premise was that this same relationship existed in protected flat mode and the experiment was intended to show this effect.

Yesterday I changed ds to a value that was incompatible with a write, and then tried an operation that used ebx in a write operation, and the instruction which used ebx as a pointer failed.

Today I changed ds to a value that was incompatible with a write, and tried to use ebx as a pointer in a read operation and there was no problem.

Re: A little experiment

Posted: Fri Jun 11, 2010 11:13 am
by qw
All right, I get it.

Default segment register for base [E]SP and [E]BP is SS. Default segment register for base EAX, [E]BX, ECX, EDX, [E]SI and [E]DI is DS. This is the same in both real and protected mode.

For this reason some say that the x86 doesn't really have a flat memory model. It is still segmented, it just seems flat because the segments overlap and span the entire address space. I don't mind calling it flat anyway, but you get the point.

In your second test the read will fail if the code segment is not readable.

Re: A little experiment

Posted: Fri Jun 11, 2010 8:27 pm
by Gigasoft
Why perform experiments when you can just read the manual? Of course, "flat protected mode" isn't a special mode, it just means setting up segment registers for flat addressing, therefore making it so that a program doesn't have to worry about segments. If it does manipulate or use segment registers anyway, then that assumption is no longer valid.

Re: A little experiment

Posted: Fri Jun 11, 2010 10:23 pm
by gerryg400
I think it's good to experiment. It will confirm (or not) your understanding of the manual. And some of the language in the manuals while accurate, can be a little imprecise. For example, we recently discovered on this list, largely by experiment, that the AMD and Intel chips differ in how they handle far jumps in long mode.

Re: A little experiment

Posted: Sat Jun 12, 2010 7:32 am
by Owen
gerryg400 wrote:I think it's good to experiment. It will confirm (or not) your understanding of the manual. And some of the language in the manuals while accurate, can be a little imprecise. For example, we recently discovered on this list, largely by experiment, that the AMD and Intel chips differ in how they handle far jumps in long mode.
Its stated quite clearly in the manuals...

Re: A little experiment

Posted: Sat Jun 12, 2010 7:56 am
by gerryg400
Its stated quite clearly in the manuals...
But I would argue that it is not entirely clear in both manuals that they are different. It was unclear enough that some of us tested it. If I recall correctly, the discussion was settled by your publishing the results of your 'experiments'.

All I'm saying is that there is no harm, and very often a lot to learn by experimenting. We should encourage this type of thing (rather than yet another boot-loader question)

Re: A little experiment

Posted: Sat Jun 12, 2010 8:43 am
by rknox
Two more experiments and an answer to Gigasoft's question.

Expt 3: What happens if I mess up DS and then write thru EBX with an ES seg override? Works ok.

Expt 4: What happens if I mess up DS, read thru EBX with a CS override? Works ok.

Gigasoft - you asked why I experiment when it's all in the manuals.

I found over the course of my career, that authorized sources (manuals, professors, presidents) are not always right!! IMHO there is value in finding out for yourself.

Even things that seem obvious sometimes have associated secrets. I would not have guessed that you could use the CS register to do a read. And you of course can't count on it since it is undocumented and may be different from processor to processor.

In my world, experiments are a useful adjunct to the documentation.

The manual may be wrong - sometimes it is.
The manual may be misleading.
The manual may be right but due to your strongly held biases you may interpret it incorrectly.
Often the experiment reveals additional information.
The manual may be too brief, and not tell the whole story.
The manual may be too long, and the data you need is hidden in 1000's of repetitive pages. example - I am currently trying to make my way thru the 5 volumes each with about 1000 pages of the Intel documention. That is where I found the misleading (or misunderstood by me) statement that started this experiment.
Finally, for some of us, fiddling with something causes us to internalize it. Makes it part of our firmware.

I am not alone. There are others who like to fiddle like this - or look over someone elses shoulder when he fiddles. Maybe in deference to those who know more than I, I should put this kind of thing in the 1 week auto delete forum.

Re: A little experiment

Posted: Sat Jun 12, 2010 10:48 am
by qw
rknox wrote:I would not have guessed that you could use the CS register to do a read. And you of course can't count on it since it is undocumented and may be different from processor to processor.
No, the CS segment override to do a read is in the manuals. But all together I agree with your post.

Re: A little experiment

Posted: Sat Jun 12, 2010 11:01 am
by Gigasoft
The CS override has been there since the 8086, and has of course always been documented. The fact that all assemblers recognize it should tell you something. What would be the purpose of a CS override, if not to access memory?