Linux Assembly Question

  • Follow


Hi,

I am writing some inline assembly code to practice with Linux and
Assembly, currently I ma trying to recognize if the cpu
supports the cpuid instruction and for this reason I am pushing/
popping the EFLAGS register.
Using this simple code :

        __asm__ __volatile__("pushf\n\t"
            "pop %eax\n\t"
            "mov %eax, %ecx\n\t"
            );

And inspecting the eax register with the debugger immediately after
the pop instruction  I notice that the integer value is 838 or
001101000110 in binary but here I have a doubt, should I see at least
a 32 register instead of this ?
Am I doing something wrong?
I have to add that Linux ubuntu 64 bit is running on a virtual machine
and I am generating 32bit code using the -m32.

cheers

0
Reply francesco.garelli (12) 2/12/2012 5:07:19 AM

On Feb 12, 5:07=A0am, fgarelli <francesco.gare...@nospicedham.gmail.com>
wrote:
> Hi,
>
> I am writing some inline assembly code to practice with Linux and
> Assembly, currently I ma trying to recognize if the cpu
> supports the cpuid instruction and for this reason I am pushing/
> popping the EFLAGS register.
> Using this simple code :
>
> =A0 =A0 =A0 =A0 __asm__ __volatile__("pushf\n\t"
> =A0 =A0 =A0 =A0 =A0 =A0 "pop %eax\n\t"
> =A0 =A0 =A0 =A0 =A0 =A0 "mov %eax, %ecx\n\t"
> =A0 =A0 =A0 =A0 =A0 =A0 );
>
> And inspecting the eax register with the debugger immediately after
> the pop instruction =A0I notice that the integer value is 838 or
> 001101000110 in binary but here I have a doubt, should I see at least
> a 32 register instead of this ?
> Am I doing something wrong?
> I have to add that Linux ubuntu 64 bit is running on a virtual machine
> and I am generating 32bit code using the -m32.
>
> cheers

Ok I tried with VS2010 and I get the same result so in Linux works but
I still don't understand
why I get only 12 bits, maybe because if the left bits are zero the
debugger don't display them?

cheers
0
Reply francesco.garelli (12) 2/12/2012 7:11:43 PM


"fgarelli" <francesco.garelli@nospicedham.gmail.com> wrote in message
news:5a36efce-e95e-4a07-9425-29248df6b911@f30g2000yqh.googlegroups.com...
> On Feb 12, 5:07 am, fgarelli <francesco.gare...@nospicedham.gmail.com>
> wrote:
> > I am writing some inline assembly code to practice with Linux and
> > Assembly, currently I ma trying to recognize if the cpu
> > supports the cpuid instruction and for this reason I am pushing/
> > popping the EFLAGS register.
>

Ok.

CPUID is documented in the AMD and Intel manuals.  Detecting CPUID is also
documented.  AMD at least also has a manual specifying the CPUID supported
functions called "CPUID Specification".

> > Using this simple code :
> >
> > __asm__ __volatile__("pushf\n\t"
> > "pop %eax\n\t"
> > "mov %eax, %ecx\n\t"
> > );
> >

Is that _really_ the entire sequence ... ?

The reason I ask is usually two percent signs %% are used to indicate
registers and one percent sign % is used for parameters.  If you don't have
any parameters, then only one percent is needed for registers.  However, if
you don't have parameters and that's what you posted, then you can't have
ecx's value passed back into a C or C++ variable ...  So, how are you using
ecx?  C and C++ don't support direct access of registers.  I.e., use -
non-use actually - of ecx in the sample code implies the sequence was
snipped and reduced from a larger code piece, therefore my question.  I.e.,
you're not able to check the flags with that since the C or C++ code can't
view ecx's contents, unless the assembly copied ecx into a C or C++ variable
which it didn't.  I'd guess that may be why you're using a debugger ... to
check ecx's contents?

The AMD manuals use 'pushfd' in their example in the description for CPUID.
GCC inline assembly uses 'pushf' and allows size qualifiers, like 'w', 'l',
'q', for word, long, quad, depending on the code size.  While all pushf
instructions encode to the same instruction, it's possible you may be
getting overrides you didn't want.  So, you'd want to try 'pushfl'.  You
should check though.  Are there any override prefixes that don't belong
there when disassembled via objdump etc?  Do you need to specify the size
for 'pushf'?  The same question goes for the other instructions too.  They
all should default to the correct size as long as you're actually
assemblying or compiling as 32-bits.  It's possible you're actually
compiling 32-bit instructions for 64-bit long-mode, which only supports
encodings for 16-bit and 64-bit instructions.  If so, it's possible your
sequence could actually be being executed as 16-bit, not 32-bit as you've
expected ... (more below)

You probably have a file called "opcode/i386.h" which has the info on size
specifiers on a per instruction bases.  If it has been renamed, search *.h
files for "wlq_Suf" or "movzwl" etc.

> > And inspecting the eax register with the debugger immediately
> > after the pop instruction I notice that the integer value is 838 or
> > 001101000110 in binary but here I have a doubt, should I see
> > at least a 32 register instead of this ?

What debugger?  GDB?  What Linux distro?  Is this under DOSEMU? etc.

> > Am I doing something wrong?

If you're asking, then we must assume so ...  ;-)  I.e., people who are
doing "something correct" don't usually ask.

Seriously, should we really know the answer to that from what little you've
posted?  You might be doing everything correct.  However, all of us don't
have your exact setup and probably none of us do.  So, we must guess about
it based on what our experiences are.

For what cpu mode is the 32-bit code being executed?  Long-mode
(unsupported, i.e., probably treated as 16-bit)?  32-bit compatibility mode?
32-bit protected mode?

> > I have to add that Linux ubuntu 64 bit is running on a
> > virtual machine and I am generating 32bit code using the -m32.

Can you run 32-bit code under a 64-bit Linux while in a VM?  AIUI, 64-bit
long-mode supports 64-bit and 16-bit instructions, but not 32-bit.  Somehow,
you managed to get into a debugger, so I'm assuming your 32-bit code is
executable.  To properly do so, Linux would have to switch from long-mode to
32-bit compatibility-mode.  Is either 16-bit or 32-bit compatibility-mode
supported in Linux?  I would've thought that you couldn't execute 32-bit
code in a 64-bit Linux ...  I no longer have a 64-bit Linux installed, so I
can't even check to see if 32-bit code will execute on a 64-bit Linux, nor
determine for which mode it's being executed.  What I'm saying is that it's
true that the x86 processor supports 32-bit code in compatibility mode while
in 64-bit mode, but I don't know if Linux supports 32-bit compatibility
mode.  I.e., while you may be able to encode 32-bit instructions it's seems
likely to me you might not be able to actually execute some of them, or
those 32-bit encodings may be executing as 16-bit if still in long-mode ...

> Ok I tried with VS2010 and I get the same result

Previously you said a VM ...  Visual Studio is not a VM.  What VM were you
using previously?  VMWare?  VirtualBox?  QEMU?  DOSEMU?

> so in Linux works but I still don't understand why I get
> only 12 bits, maybe because if the left bits are zero the
> debugger don't display them?

Could be ...

Even if I had your specific setup, there is insufficient information for me
to anwer.  You'll need to perform some simple tests.  Originally, you said
you were using an environment with a debugger.  If so, you should be able to
load a register with a value, yes?  Put 0FFFFFFFFh into a 32-bit register
and display it.  Do you still get 12-bits?  Put 838 into the same register
and display it.  Do you get 12-bits?  Repeat.  If you're getting 12-bits for
838 and 32-bits for 0FFFFFFFFh, then it's the debugger.  Are you sure it's
only 12-bits, and not 16-bits?  16-bits would imply the debugger is not in a
mode which supports 32-bit instructions.  E.g., you may actually be
executing the code in long-mode and not compatibility mode.


Rod Pemberton


0
Reply do_not_have7664 (117) 2/12/2012 10:07:48 PM

On 2/12/2012 11:11 AM, fgarelli wrote:
> On Feb 12, 5:07 am, fgarelli<francesco.gare...@nospicedham.gmail.com>
> wrote:
>> Hi,
>>
>> I am writing some inline assembly code to practice with Linux and
>> Assembly, currently I ma trying to recognize if the cpu
>> supports the cpuid instruction and for this reason I am pushing/
>> popping the EFLAGS register.
>> Using this simple code :
>>
>>          __asm__ __volatile__("pushf\n\t"
>>              "pop %eax\n\t"
>>              "mov %eax, %ecx\n\t"
>>              );
>>
>> And inspecting the eax register with the debugger immediately after
>> the pop instruction  I notice that the integer value is 838 or
>> 001101000110 in binary but here I have a doubt, should I see at least
>> a 32 register instead of this ?
>> Am I doing something wrong?
>> I have to add that Linux ubuntu 64 bit is running on a virtual machine
>> and I am generating 32bit code using the -m32.
>>
>> cheers
>
> Ok I tried with VS2010 and I get the same result so in Linux works but
> I still don't understand
> why I get only 12 bits, maybe because if the left bits are zero the
> debugger don't display them?

So, you ask your debugger to display %eax and only get 12 bits? This 
can't have anything to do with CPUID. Try storing different values to 
%eax and see how your debugger displays them.

I should also note that your asm code clobbers %eax and %ecx and the 
compiler is not informed of it. This will likely cause Bad Things to 
happen after your code is executed. If you want to store the flags to a 
variable, you should do something like

uint32_t flags;

asm volatile ("pushf\n pop %0" : "=rm"(flags) : :);
0
Reply qmbmnp3799 (16) 2/12/2012 10:43:31 PM

Rod Pemberton wrote:
> It's possible you're actually
> compiling 32-bit instructions for 64-bit long-mode, which only supports
> encodings for 16-bit and 64-bit instructions.  If so, it's possible your
> sequence could actually be being executed as 16-bit, not 32-bit as you've
> expected ...

In 64-bit long mode, most instructions are actually 32-bit, unless a 64-
bit prefix is present. However, you're right that only 16- and 64-bit 
versions of push and pop are supported. For those instructions, the 
normal 32-bit encoding is interpreted as a 64-bit instruction when in 
long mode, so I'm sure he's not getting the 16-bit encoding.


> Is either 16-bit or 32-bit compatibility-mode
> supported in Linux?  I would've thought that you couldn't execute 32-bit
> code in a 64-bit Linux.

Support for 32-bit compatibility mode is a compile-time kernel option. I 
think it is usually enabled in the standard distributions. If it isn't 
supported, the executable won't load. I don't think you could accidently 
get 16-bit operations.
0
Reply prl (29) 2/13/2012 9:24:17 AM

On Feb 13, 9:24=A0am, Philip Lantz <p...@nospicedham.canterey.us> wrote:
> Rod Pemberton wrote:
> > It's possible you're actually
> > compiling 32-bit instructions for 64-bit long-mode, which only supports
> > encodings for 16-bit and 64-bit instructions. =A0If so, it's possible y=
our
> > sequence could actually be being executed as 16-bit, not 32-bit as you'=
ve
> > expected ...
>
> In 64-bit long mode, most instructions are actually 32-bit, unless a 64-
> bit prefix is present. However, you're right that only 16- and 64-bit
> versions of push and pop are supported. For those instructions, the
> normal 32-bit encoding is interpreted as a 64-bit instruction when in
> long mode, so I'm sure he's not getting the 16-bit encoding.
>
> > Is either 16-bit or 32-bit compatibility-mode
> > supported in Linux? =A0I would've thought that you couldn't execute 32-=
bit
> > code in a 64-bit Linux.
>
> Support for 32-bit compatibility mode is a compile-time kernel option. I
> think it is usually enabled in the standard distributions. If it isn't
> supported, the executable won't load. I don't think you could accidently
> get 16-bit operations.

Hi Guys,

thank you very much for your help, for sure the debugger has some
problem because using this simple code :

int main()
{
__asm__ __volatile__("mov 0FFFFFFFFh, %%eax\n\t ::: );

}

Stepping using the debugger the register eax contains "1" after the
move instruction so I need to investigate this.
I did try to make the same sample using VS2010 and correctly the
debugger after the mov instruction shows FFFFFFFF
Now I need to understand where the problem come from.

My setup is this Rod :

Windows 7 64 Bit Running vmware workstation
Ubuntu Linux 11.10 64 bit runs in a virtual box
I use GCC and Code:Blocks and I generate 32 bit code using the option -
m32(apparently seems working fine..apparently)

Obviously the code gets executed because I can see for example if I
print something etc..etc..etc...

Probably my configuration is a little bit exotic, do you think guys I
should run a 32 bit version of Linux in a virtual machine
or simply getting rid of the virtual machine and installing a proper
32 bit Linux system?
My goal is to learn assembly language in Linux 32/64 bit.

cheers
0
Reply francesco.garelli (12) 2/13/2012 10:54:50 AM

fgarelli wrote:
> int main()
> {
> __asm__ __volatile__("mov 0FFFFFFFFh, %%eax\n\t ::: );
> 
> }

I'm not very familiar with inline asm syntax. In "plain Gas", this would 
attempt to move the contents of memory at address 0FFFFFFFFh into eax, 
which I would expect to segfault. To indicate an immediate value, you'd 
want:

movl $0xFFFFFFFF, %eax

I could be wrong, when it comes to inline syntax...

> Stepping using the debugger the register eax contains "1" after the
> move instruction so I need to investigate this.
> I did try to make the same sample using VS2010 and correctly the
> debugger after the mov instruction shows FFFFFFFF
> Now I need to understand where the problem come from.
> 
> My setup is this Rod :
> 
> Windows 7 64 Bit Running vmware workstation
> Ubuntu Linux 11.10 64 bit runs in a virtual box
> I use GCC and Code:Blocks and I generate 32 bit code using the option -
> m32(apparently seems working fine..apparently)
> 
> Obviously the code gets executed because I can see for example if I
> print something etc..etc..etc...
> 
> Probably my configuration is a little bit exotic, do you think guys I
> should run a 32 bit version of Linux in a virtual machine
> or simply getting rid of the virtual machine and installing a proper
> 32 bit Linux system?

I would, just to eliminate any "extra" complications, but what you 
describe "should" work fine.

> My goal is to learn assembly language in Linux 32/64 bit.

IMHO, inline asm syntax is "more complicated" than "plain asm". If 
that's what you're trying to learn, fine, but when you're just starting 
to learn, the fewer complications the better! Maybe try "CN"s suggested 
code... You might want to take a look at Jonathan Bartlett's 
"Programming from the Ground, Up". I can dig up a link if you can't find 
it. Good luck and "Hang in there!"

Best,
Frank

0
Reply fbkotler9831 (124) 2/13/2012 8:35:28 PM

"fgarelli" <francesco.garelli@nospicedham.gmail.com> wrote in message
news:6f80c0a4-78a6-4270-a4b0-cc49add1dbbc@k10g2000yqk.googlegroups.com...
>
> [snip]
....

> My goal is to learn assembly language in Linux 32/64 bit.
>

Ok, well that means you'll probably be using GAS, i.e., Gnu ASssembler.
Although, NASM is available too and it has a simpler syntax.  I don't
believe it can be inlined within C though ...

To "learn assembly language in Linux 32/64-bit", you can start by reading
about GAS assembly syntax, AT&T syntax, or Linux assembly, etc.  They all
cover GAS assembly syntax.  I've provided a bunch of links below for that.

Inline assembly for GNU C or C++ is basically GAS syntax except it's been
wrapped in quotes with newlines, has an 'asm' directive, and has a method to
specify C/C++ inputs, outputs, and destroyed registers.

The actual description of inline assembly as used in GNU C and C++ is buried
in an obscure and oddly titled section of the GNU C/C++ manual called:

  "6.41 Assembler Instructions with C Expression Operands"

It's aptly titled, but no one is going to locate that when searching for
"inline assembly syntax".  That's the link I provided at the very bottom.
It's better left for reading by someone already skilled with GAS.
Fortunately, a number of the other links do a better job at describing the
inline syntax.   However, they usually also compare GAS syntax to another
assembler at the same time.


GCC-Inline-Assembly-HOWTO
http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html

DJGPP (i.e., GCC) QuickAsm Programming Guide
http://personales.mundivia.es/jap/djasm.htm

Brennan's Guide to Inline Assembly
http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html

Introduction to UNIX assembly programming
http://asm.sourceforge.net//intro/Assembly-Intro.html

Linux Assembly HOWTO
http://asm.sourceforge.net/howto/Assembly-HOWTO.html

AT&T Syntax versus Intel Syntax
http://www.cs.utah.edu/dept/old/texinfo/as/as.html#SEC148

Linux assemblers: A comparison of GAS and NASM
http://www.ibm.com/developerworks/linux/library/l-gas-nasm/index.html

Using Assembly Language in Linux
http://asm.sourceforge.net/articles/linasm.html

DOS .COM files with GNU AS and LD
http://www.ludd.luth.se/~ams/gnu_dos_com/

GAS (GNU AS) Assembler Directives
http://www.cs.utah.edu/dept/old/texinfo/as/as.html#SEC58

Using AS
http://sourceware.org/binutils/docs-2.19/as/index.html

GCC's official inline C assembly description
http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Extended-Asm

HTH,


Rod Pemberton
PS.  I added alt.os.development and alt.lang.asm for the links.  Follow-ups
set to c.l.a.x.


0
Reply do_not_have7664 (117) 2/13/2012 9:45:10 PM

Thanks for your help guys really much appreciated :)


0
Reply francesco.garelli (12) 2/14/2012 12:26:03 AM

By the way, this is the output of the gdb debugger window

Code :

uint32_t flags;
asm volatile ("pushf\n pop %0" : "=rm"(flags) : :);



For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /home/fra/DEV/Perforce/HardwareLayer/bin/Debug/
HardwareLayer...done.
(gdb) break main
Breakpoint 1 at 0x80484f0: file
(gdb) run
Starting program:

Breakpoint 1,
81	asm volatile ("pushf\n pop %0" : "=rm"(flags) : :);
(gdb) watch flags
Hardware watchpoint 2: flags
(gdb) c
Continuing.
Hardware watchpoint 2: flags

Old value = 134514016
New value = 902
84	return 0;

do you consider this output correct?

cheers
0
Reply francesco.garelli (12) 2/14/2012 2:21:00 AM

fgarelli wrote:
> By the way, this is the output of the gdb debugger window
> 
> Code :
> 
> uint32_t flags;
> asm volatile ("pushf\n pop %0" : "=rm"(flags) : :);
> 
> 
> 
> For bug reporting instructions, please see:
> <http://bugs.launchpad.net/gdb-linaro/>...
> Reading symbols from /home/fra/DEV/Perforce/HardwareLayer/bin/Debug/
> HardwareLayer...done.
> (gdb) break main
> Breakpoint 1 at 0x80484f0: file
> (gdb) run
> Starting program:
> 
> Breakpoint 1,
> 81	asm volatile ("pushf\n pop %0" : "=rm"(flags) : :);
> (gdb) watch flags
> Hardware watchpoint 2: flags
> (gdb) c
> Continuing.
> Hardware watchpoint 2: flags
> 
> Old value = 134514016
> New value = 902
> 84	return 0;

Yeah, but I think it's in decimal. Decimal 902 is 386 hex, and that's a 
value I see in a debugger commonly. Lessee... 001110000110 (did I get 
that right?). I confess I'll have to look up which bit is which flag... 
and when I do... I came across this:

http://wiki.osdev.org/CPUID

There's some code there that may help you. Looks like you're on the 
right track, but there's more to do. Maybe you knew that. If you need 
help translating it to (G)as, ask (I'm better at Nasm, but this doesn't 
look too hard). See what you see after attempting to "flip" that bit, as 
shown...

After determining that you've got cpuid (I just ASSume it's available, 
these days), check the maximum level (zero in eax). If I exceed that on 
my machine, it tells me that I've got 16 cores on my funky old P4, which 
just ain't true! :)

Best,
Frank
0
Reply fbkotler9831 (124) 2/14/2012 5:12:14 AM

Hi Frank,

thank you very much yes you got it right and you see it's a 12 bit
value
and I don't understand why I see a 12 bit value, this completely let
me
believe that I haven't really understood how everything works :)

cheers

On Feb 14, 5:12=A0am, Frank Kotler
<fbkot...@nospicedham.myfairpoint.net> wrote:
> fgarelli wrote:
> > By the way, this is the output of the gdb debugger window
>
> > Code :
>
> > uint32_t flags;
> > asm volatile ("pushf\n pop %0" : "=3Drm"(flags) : :);
>
> > For bug reporting instructions, please see:
> > <http://bugs.launchpad.net/gdb-linaro/>...
> > Reading symbols from /home/fra/DEV/Perforce/HardwareLayer/bin/Debug/
> > HardwareLayer...done.
> > (gdb) break main
> > Breakpoint 1 at 0x80484f0: file
> > (gdb) run
> > Starting program:
>
> > Breakpoint 1,
> > 81 asm volatile ("pushf\n pop %0" : "=3Drm"(flags) : :);
> > (gdb) watch flags
> > Hardware watchpoint 2: flags
> > (gdb) c
> > Continuing.
> > Hardware watchpoint 2: flags
>
> > Old value =3D 134514016
> > New value =3D 902
> > 84 return 0;
>
> Yeah, but I think it's in decimal. Decimal 902 is 386 hex, and that's a
> value I see in a debugger commonly. Lessee... 001110000110 (did I get
> that right?). I confess I'll have to look up which bit is which flag...
> and when I do... I came across this:
>
> http://wiki.osdev.org/CPUID
>
> There's some code there that may help you. Looks like you're on the
> right track, but there's more to do. Maybe you knew that. If you need
> help translating it to (G)as, ask (I'm better at Nasm, but this doesn't
> look too hard). See what you see after attempting to "flip" that bit, as
> shown...
>
> After determining that you've got cpuid (I just ASSume it's available,
> these days), check the maximum level (zero in eax). If I exceed that on
> my machine, it tells me that I've got 16 cores on my funky old P4, which
> just ain't true! :)
>
> Best,
> Frank

0
Reply francesco.garelli (12) 2/14/2012 10:22:11 AM

fgarelli wrote:

> for sure the debugger has some
> problem because using this simple code :
> 
> int main()
> {
> __asm__ __volatile__("mov 0FFFFFFFFh, %%eax\n\t ::: );
> 
> }
> 
> Stepping using the debugger the register eax contains "1" after the
> move instruction so I need to investigate this.

I suggest that instead of trying things where you can only see the 
result in the debugger, you do it in such a way that you can print the 
result:

#include <stdio.h>

int main()
{
    unsigned x;
    __asm__ ("mov 0FFFFFFFFh, %0" : "=r"(x) : : );
    printf("x=%u\n", x);
    return 0;
}

Notice also that the "__volatile__" isn't necessary here, because the 
compiler can tell that the result is used.
0
Reply prl (29) 2/14/2012 5:51:41 PM

fgarelli wrote:
> Hi Frank,
> 
> thank you very much yes you got it right and you see it's a 12 bit
> value
> and I don't understand why I see a 12 bit value, this completely let
> me
> believe that I haven't really understood how everything works :)

Naw, just an artifact of what gdb's showing you. You're doing fine. The 
code Phillip showed you is probably better for your purposes, but I 
knocked off a little example in Nasm syntax, mostly as an "exercise" for 
myself. Shows all 32 bits, in binary, and returns whether we've got 
cpuid or not (untested for "not"). No gdb, no gcc, no C, no libraries... 
just "pure asm" using system calls (my personal preference). If you're 
happy with gcc's inline syntax, just ignore it.

Best,
Frank

;-----------------------------------------
; nasm -f elf32 myfile.asm [-Fdwarf for gdb]
; ld -o myfile myfile.o

global _start

section .text
_start:
     nop
     pushf
     pop eax ; get flags
; display original flags
     call showeaxbin
     mov ecx, eax ; save original flags
; try to flip ID bit
     xor eax, 200000h
     push eax
     popf
; see if we could set it
     pushf
     pop eax
; restore original flags
     push ecx
     popfd
; show the "flipped" flags
     call showeaxbin
; return 1 if we've got cpuid, else 0
; observe with "echo $?"
     mov ebx, eax
     shr ebx, 21
     and ebx, 1
     mov eax, 1 ; __NR_exit
     int 80h
;---------------------

showeaxbin:
; save all used registers
     push eax
     push ebx
     push ecx
     push edx
; make a buffer
     sub esp, 48

     mov ebx, esp
     mov ecx, 32
..top:
; throw in some underscores, for readability
     cmp ecx, 32
     jz .skip
     test ecx, 3
     jnz .skip
     mov byte [ebx], '_'
     inc ebx
..skip:
     mov dl, '0'
     rol eax, 1
     adc dl, 0
     mov [ebx], dl
     inc ebx
     loop .top
; throw in a linefeed, for readability
     mov byte [ebx], 10
; print it
     mov eax, 4 ; __NR_write
     mov ebx, 1 ; stdout
     mov ecx, esp ; buffer
     mov edx, 40 ; length
     int 80h
; clean up and go home
     add esp, 48
     pop edx
     pop ecx
     pop ebx
     pop eax
     ret
;----------------------

0
Reply fbkotler9831 (124) 2/14/2012 8:19:44 PM

"Frank Kotler" <fbkotler@nospicedham.myfairpoint.net> wrote in message
news:jhefk5$4gn$1@speranza.aioe.org...
> [SNIP]
....

> ;-----------------------------------------
> ; nasm -f elf32 myfile.asm [-Fdwarf for gdb]
> ; ld -o myfile myfile.o
>
> global _start
>
> section .text
> _start:
>      nop
>      pushf
>      pop eax ; get flags
> ; display original flags
>      call showeaxbin
>      mov ecx, eax ; save original flags
> ; try to flip ID bit
>      xor eax, 200000h
>      push eax
>      popf

"flip"

> ; see if we could set it
>      pushf
>      pop eax

"set it" ...  ?

You do mean "flip it" like the prior comment ... Yes?

> ; restore original flags
>      push ecx
>      popfd
> ; show the "flipped" flags
>      call showeaxbin

"flipped"

> ; return 1 if we've got cpuid, else 0
> ; observe with "echo $?"
>      mov ebx, eax
>      shr ebx, 21
>      and ebx, 1

Um, is this an "implied set" ... ?

Is this the same bit in eax that was inverted or toggled, but not set, via
xor ... ?

;-)


Rod Pemberton



0
Reply do_not_have7664 (117) 2/15/2012 1:23:24 AM

After all the answers there is a question that i have to ask,
basing on your experience which Assembler should I use?
I started with GAS only because it comes with GCC and I use to believe
that was the standard but it seems that there are better assemblers
around?
For somebody that is studying such subject would you recommend GAS or
any other
assembler?

cheers
0
Reply francesco.garelli (12) 2/15/2012 2:14:10 AM

Hi Philip,

yea you are right, printf shows the right content :)
I tried to print eflag as well from the code I have been given
by CN and I get the usual 12 bit value :)


cheers,
0
Reply francesco.garelli (12) 2/15/2012 2:20:03 AM

On Feb 15, 1:23=A0am, "Rod Pemberton"
<do_not_h...@nospicedham.noavailemail.cmm> wrote:
> "Frank Kotler" <fbkot...@nospicedham.myfairpoint.net> wrote in message
>
> news:jhefk5$4gn$1@speranza.aioe.org...> [SNIP]
>
> ...
>
>
>
>
>
>
>
>
>
> > ;-----------------------------------------
> > ; nasm -f elf32 myfile.asm [-Fdwarf for gdb]
> > ; ld -o myfile myfile.o
>
> > global _start
>
> > section .text
> > _start:
> > =A0 =A0 =A0nop
> > =A0 =A0 =A0pushf
> > =A0 =A0 =A0pop eax ; get flags
> > ; display original flags
> > =A0 =A0 =A0call showeaxbin
> > =A0 =A0 =A0mov ecx, eax ; save original flags
> > ; try to flip ID bit
> > =A0 =A0 =A0xor eax, 200000h
> > =A0 =A0 =A0push eax
> > =A0 =A0 =A0popf
>
> "flip"
>
> > ; see if we could set it
> > =A0 =A0 =A0pushf
> > =A0 =A0 =A0pop eax
>
> "set it" ... =A0?
>
> You do mean "flip it" like the prior comment ... Yes?
>
> > ; restore original flags
> > =A0 =A0 =A0push ecx
> > =A0 =A0 =A0popfd
> > ; show the "flipped" flags
> > =A0 =A0 =A0call showeaxbin
>
> "flipped"
>
> > ; return 1 if we've got cpuid, else 0
> > ; observe with "echo $?"
> > =A0 =A0 =A0mov ebx, eaxnasm
> > =A0 =A0 =A0shr ebx, 21
> > =A0 =A0 =A0and ebx, 1
>
> Um, is this an "implied set" ... ?
>
> Is this the same bit in eax that was inverted or toggled, but not set,
> ;-)
>
> Rod Pemberton

Hi Frank,

obviously I got nasm installed and I compiled your sample, the the
system call prints everything even if the
content is zero so at the beginning you get a 12 bit value
0000_0000_0000_0000_0000_0010_0000_0010 and
after you flip the bit 0000_0000_0010_0000_0000_0010_0000_0010 the
value is bigger, probably the debugger hides the reserved registers
and
the zero value registers.

cheers.
0
Reply francesco.garelli (12) 2/15/2012 2:51:13 AM

fgarelli wrote:

> yea you are right, printf shows the right content :)
> I tried to print eflag as well from the code I have been given
> by CN and I get the usual 12 bit value :)

You keep mentioning 12 bits as if that were an indication of something 
wrong. Which bits in eflags above bit 11 would you expect to be set?
0
Reply prl (29) 2/15/2012 4:30:09 AM

fgarelli wrote:
> 
> Hi Philip,
> 
> yea you are right, printf shows the right content :)

Did it really work the way I wrote it? Because, as Frank pointed out, I 
forgot the $ in front of the constant. I would expect a SIGSEGV from the 
code I wrote.
0
Reply prl (29) 2/15/2012 4:36:41 AM

"fgarelli" <francesco.garelli@nospicedham.gmail.com> wrote in message
news:36f91e52-814b-4c57-81cd-4aa80b87cad4@do4g2000vbb.googlegroups.com...
....

> For somebody that is studying such subject would you
> recommend GAS or any other assembler?

It's likely you'll have to learn about a few at some point.  The most common
probably are MASM, GAS, and NASM.  Most other x86 assemblers seem
to use the same syntax as one of them.

I like NASM because the syntax is easier.  NASM has the simplest syntax and
is closest to what's in the AMD and Intel manuals.  It allows you to specify
the encoding of instructions more precisely than other assemblers.  NASM
also has a powerful C like preprocessor.

MASM is the x86 assembler that most x86 code was written with.  MASM
requires a bunch of keywords to specify operand sizes.  I don't like MASM.
It can be really difficult to determine the correct syntax for some
instructions.

GAS is available anywhere GNU software is.  GAS probably has the most
complicated syntax.  GAS uses AT&T syntax, so the operand order is the
reverse of what is in the Intel and AMD manuals.  GAS can also have size
specifiers on the instructions, so instead of "mov" you may see "movl" etc.
This can produce some confusing names for instructions and in some cases
name collisions.  GAS will only generate instructions that it's front ends,
like C, C++, etc, need generated.  So, some instructions are not available.
However, it has a rich set of pseudo-instructions and assembler directives.
GAS can also assemble Intel syntax and switch between Intel and AT&T
syntax ...  Certain versions of GAS also support mixed-mode x86 code,
e.g., 16-bit code with 32-bit control-flow instructions.


Rod Pemberton


0
Reply do_not_have7664 (117) 2/15/2012 7:40:01 AM

Rod Pemberton wrote:
> "Frank Kotler" <fbkotler@nospicedham.myfairpoint.net> wrote in message
> news:jhefk5$4gn$1@speranza.aioe.org...
>> [SNIP]
> ...
> 
>> ;-----------------------------------------
>> ; nasm -f elf32 myfile.asm [-Fdwarf for gdb]
>> ; ld -o myfile myfile.o

Oops! I neglected to mention an important part of the command line for 
ld. With 64-bit capable ld, we need to tell it we want 32-bit code - 
same purpose as the "-m32" switch to gcc. I believe it's "-melf_i386" 
(there may be aliases). Apparently Francesco figured it out, so no harm 
done, but I'm *trying* to remember to mention that.

....
> "flip"
....
> "set it" ...  ?
> 
> You do mean "flip it" like the prior comment ... Yes?
....
> "flipped"
....
> Um, is this an "implied set" ... ?
> 
> Is this the same bit in eax that was inverted or toggled, but not set, via
> xor ... ?
> 
> ;-)

Good point! I ASSumed that the bit was clear when we found it (else why 
would be seeing only 12 bits?). IF it was clear, "flipping" 
(complementing) the bit would have the effect of setting it, but they 
are not the same thing! Sloppy language and sloppy programming on my 
part. The code I used as a rough guide actually does an xor of ecx 
(original flags) with eax (altered - or not - flags) to set that bit 
that we attempted to "flip" (toggle, complement) if it differed, 
regardless whether it was set or clear to begin with. Apparently (this 
is new to me), it is the ability to alter this "ID bit", rather than its 
original state, which indicates the availability of "cpuid". What I 
showed "amounts to the same thing" (in this case), but isn't strictly 
speaking "right".

xor eax, 200000h ; toggle, complement, or "flip" the bit

or eax, 200000h ; set the bit - no change if already set

Thanks for the clarification!

Best,
Frank
0
Reply fbkotler9831 (124) 2/15/2012 11:31:31 AM

On Feb 15, 11:31=A0am, Frank Kotler
<fbkot...@nospicedham.myfairpoint.net> wrote:
> Rod Pemberton wrote:
> > "Frank Kotler" <fbkot...@nospicedham.myfairpoint.net> wrote in message
> >news:jhefk5$4gn$1@speranza.aioe.org...
> >> [SNIP]
> > ...
>
> >> ;-----------------------------------------
> >> ; nasm -f elf32 myfile.asm [-Fdwarf for gdb]
> >> ; ld -o myfile myfile.o
>
> Oops! I neglected to mention an important part of the command line for
> ld. With 64-bit capable ld, we need to tell it we want 32-bit code -
> same purpose as the "-m32" switch to gcc. I believe it's "-melf_i386"
> (there may be aliases). Apparently Francesco figured it out, so no harm
> done, but I'm *trying* to remember to mention that.
>
> ...
>
> > "flip"
> ...
> > "set it" ... =A0?
>
> > You do mean "flip it" like the prior comment ... Yes?
> ...
> > "flipped"
> ...
> > Um, is this an "implied set" ... ?
>
> > Is this the same bit in eax that was inverted or toggled, but not set, =
via
> > xor ... ?
>
> > ;-)
>
> Good point! I ASSumed that the bit was clear when we found it (else why
> would be seeing only 12 bits?). IF it was clear, "flipping"
> (complementing) the bit would have the effect of setting it, but they
> are not the same thing! Sloppy language and sloppy programming on my
> part. The code I used as a rough guide actually does an xor of ecx
> (original flags) with eax (altered - or not - flags) to set that bit
> that we attempted to "flip" (toggle, complement) if it differed,
> regardless whether it was set or clear to begin with. Apparently (this
> is new to me), it is the ability to alter this "ID bit", rather than its
> original state, which indicates the availability of "cpuid". What I
> showed "amounts to the same thing" (in this case), but isn't strictly
> speaking "right".
>
> xor eax, 200000h ; toggle, complement, or "flip" the bit
>
> or eax, 200000h ; set the bit - no change if already set
>
> Thanks for the clarification!
>
> Best,
> Frank

Yes Philip yes the code was perfectly working I've just added the 32
bit option and voila',
I now understand also why I see this 12 bit number, is because all the
other bit are not set and I've got confused.

0
Reply francesco.garelli (12) 2/15/2012 12:45:16 PM

On Feb 15, 11:31=A0am, Frank Kotler
<fbkot...@nospicedham.myfairpoint.net> wrote:
> Rod Pemberton wrote:
> > "Frank Kotler" <fbkot...@nospicedham.myfairpoint.net> wrote in message
> >news:jhefk5$4gn$1@speranza.aioe.org...
> >> [SNIP]
> > ...
>
> >> ;-----------------------------------------
> >> ; nasm -f elf32 myfile.asm [-Fdwarf for gdb]
> >> ; ld -o myfile myfile.o
>
> Oops! I neglected to mention an important part of the command line for
> ld. With 64-bit capable ld, we need to tell it we want 32-bit code -
> same purpose as the "-m32" switch to gcc. I believe it's "-melf_i386"
> (there may be aliases). Apparently Francesco figured it out, so no harm
> done, but I'm *trying* to remember to mention that.
>
> ...
>
> > "flip"
> ...
> > "set it" ... =A0?
>
> > You do mean "flip it" like the prior comment ... Yes?
> ...
> > "flipped"
> ...
> > Um, is this an "implied set" ... ?
>
> > Is this the same bit in eax that was inverted or toggled, but not set, =
via
> > xor ... ?
>
> > ;-)
>
> Good point! I ASSumed that the bit was clear when we found it (else why
> would be seeing only 12 bits?). IF it was clear, "flipping"
> (complementing) the bit would have the effect of setting it, but they
> are not the same thing! Sloppy language and sloppy programming on my
> part. The code I used as a rough guide actually does an xor of ecx
> (original flags) with eax (altered - or not - flags) to set that bit
> that we attempted to "flip" (toggle, complement) if it differed,
> regardless whether it was set or clear to begin with. Apparently (this
> is new to me), it is the ability to alter this "ID bit", rather than its
> original state, which indicates the availability of "cpuid". What I
> showed "amounts to the same thing" (in this case), but isn't strictly
> speaking "right".
>
> xor eax, 200000h ; toggle, complement, or "flip" the bit
>
> or eax, 200000h ; set the bit - no change if already set
>
> Thanks for the clarification!
>
> Best,
> Frank

Yes Rod Gas is very confusing because for example the movl instruction
is something I've never seen before
and if you search on google "x86 instruction set" there is not such
instruction, I was suspecting that every
assembler has his proper set of mnemonics but I was not sure.
0
Reply francesco.garelli (12) 2/15/2012 12:47:33 PM

By the way thank you very much for your help guys.



0
Reply francesco.garelli (12) 2/15/2012 12:47:56 PM

"Frank Kotler" <fbkotler@nospicedham.myfairpoint.net> wrote in message
news:jhg51r$np9$1@speranza.aioe.org...
> [SNIP]
>
> xor eax, 200000h ; toggle, complement, or "flip" the bit
>

If you flip and the bit was originally set, you don't get the correct status
....

> or eax, 200000h ; set the bit - no change if already set
>

As noted, if you change 'xor' to 'or' and the bit was originally set, you
can't tell that the bit has been *changed* ...

Wasn't detecting that the bit changes the goal?  Doesn't that require two
checks?  One attempt to clear the bit and one attempt to set the bit?  If it
fails both attempts, then the check fails, otherwise succeeds.  I'd think
you'd want to have both an 'and' and an 'or'.  E.g., 'and eax, FFDFFFFFh'
near the beginning and change the 'xor' to 'or' later on.  You may need to
fix other logic too.


Rod Pemberton


0
Reply do_not_have7664 (117) 2/16/2012 9:03:10 AM

Rod and Frank discuss detection of CPUID support...

>> xor eax, 200000h ; toggle, complement, or "flip" the bit

> If you flip and the bit was originally set, you don't get the correct 
> status
> ...

>> or eax, 200000h ; set the bit - no change if already set

> As noted, if you change 'xor' to 'or' and the bit was originally set, you
> can't tell that the bit has been *changed* ...

> Wasn't detecting that the bit changes the goal?  Doesn't that require two
> checks?  One attempt to clear the bit and one attempt to set the bit?  If 
> it
> fails both attempts, then the check fails, otherwise succeeds.  I'd think
> you'd want to have both an 'and' and an 'or'.  E.g., 'and eax, FFDFFFFFh'
> near the beginning and change the 'xor' to 'or' later on.  You may need to
> fix other logic too.

My solution:
(assume 32-bit code)

PUSHF
XOR eax,eax
BTR [esp],15h    ;(TEST) XOR bit 21(dec)
ADC eax,0        ;=1 if bit 21 was set before
POPF             ;this may not affect "all" flag-bits
PUSHF
BTR [esp],15h    ;TEST (XOR) bit 21(dec)
SBB eax,0        ;=0 (set Zeroflag) if bit 21 didn't change
;POPF            ;no need to restore EFLAG
POP eax          ;just clear up stack and keep flags alive
JZ NO_CPUID
CPUID_OK:
....

Using XOR instead of BTR would need a few lines more because
we haven't got an "ADZ" (ADD Zeroflag).

__
wolfgang


0
Reply nowhere583 (184) 2/16/2012 12:02:25 PM

Rod Pemberton wrote:

....
> Wasn't detecting that the bit changes the goal?  Doesn't that require two
> checks?  One attempt to clear the bit and one attempt to set the bit?  If it
> fails both attempts, then the check fails, otherwise succeeds.  I'd think
> you'd want to have both an 'and' and an 'or'.  E.g., 'and eax, FFDFFFFFh'
> near the beginning and change the 'xor' to 'or' later on.  You may need to
> fix other logic too.

Right. I ASSumed that the bit was clear, and screwed up the logic. 
Should have been:

pushf
pop eax
mov ecx, eax
xor eax, 200000h
push eax
popf
xor eax, ecx
push ecx
popf
; isolate the bit in eax and return it

Something like that...

Best,
Frank

0
Reply fbkotler9831 (124) 2/16/2012 12:39:43 PM

Frank Kotler wrote:

....
> pushf
> pop eax
> mov ecx, eax
> xor eax, 200000h
> push eax
> popf
Damn!
pushf
pop eax
> xor eax, ecx
> push ecx
> popf
> ; isolate the bit in eax and return it
> 
> Something like that...

Does anyone have a machine *without* cpuid to test this on?

Best,
Frank

0
Reply fbkotler9831 (124) 2/16/2012 12:58:10 PM

Frank Kotler asked:
[...]

> Does anyone have a machine *without* cpuid to test this on?

Sorry not me anymore...
I remember my last 286 bought about 1983 didn't know CPUID at all.
And as I skipped all 386-shit and direct swapped from Intel286 to
an early AMD486 (which already got CPUID) I never needed this
little code piece I posted in this thread anymore.
But I kept this few lines in my OS just in case someone once try
my OS at the museum :)

__
wolfgang




 


0
Reply nowhere583 (184) 2/17/2012 4:59:50 PM

On 16.02.2012 13:39, Frank Kotler wrote:
> Rod Pemberton wrote:
> 
> ...
>> Wasn't detecting that the bit changes the goal?  Doesn't that require two
>> checks?  One attempt to clear the bit and one attempt to set the bit?  If it
>> fails both attempts, then the check fails, otherwise succeeds.  I'd think
>> you'd want to have both an 'and' and an 'or'.  E.g., 'and eax, FFDFFFFFh'
>> near the beginning and change the 'xor' to 'or' later on.  You may need to
>> fix other logic too.
> 
> Right. I ASSumed that the bit was clear, and screwed up the logic. 
> Should have been:
> 
> pushf
> pop eax
> mov ecx, eax
> xor eax, 200000h
> push eax
> popf
> xor eax, ecx
> push ecx
> popf
> ; isolate the bit in eax and return it
> 
> Something like that...
> 
> Best,
> Frank
> 

How about:

pushf
pop eax
xor eax, 200000h
mov ecx, eax
push eax
popf
pushf
pop eax
cmp eax, ecx
jnz .no_cpuid

But why go such lengths? CPUs without CPUID are extremely rare, so if
you really need to, you can simply use your operating system's handler
for #UD or install one yourself and then just use CPUID. (On Linux, a
#UD just causes a SIGILL to the current process if in user mode or a
kernel panic if in kernel mode).

And what are you gonna do, anyway? Most of us don't write the same code
twice, so a failing feature check usually just results in an error
message. In that case, just install a handler for SIGILL/#UD that does
that. No need to call CPUID there. Of course, it is still needed when
you have different code paths for different setups, but there aren't too
many applications for that (truecrypt using AES-NI, if available, for
instance, or several multi-media editors and players with several
filters and renderers for different hardware capabilities. But beyond that?)

Ciao,
Markus
0
Reply nullplan1 (27) 2/18/2012 10:57:07 AM

Markus Wichmann wrote:
> On 16.02.2012 13:39, Frank Kotler wrote:
>> Rod Pemberton wrote:
>>
>> ...
>>> Wasn't detecting that the bit changes the goal?  Doesn't that require two
>>> checks?  One attempt to clear the bit and one attempt to set the bit?  If it
>>> fails both attempts, then the check fails, otherwise succeeds.  I'd think
>>> you'd want to have both an 'and' and an 'or'.  E.g., 'and eax, FFDFFFFFh'
>>> near the beginning and change the 'xor' to 'or' later on.  You may need to
>>> fix other logic too.
>> Right. I ASSumed that the bit was clear, and screwed up the logic. 
>> Should have been:
>>
>> pushf
>> pop eax
>> mov ecx, eax
>> xor eax, 200000h
>> push eax
>> popf
>> xor eax, ecx
>> push ecx
>> popf
>> ; isolate the bit in eax and return it
>>
>> Something like that...
>>
>> Best,
>> Frank
>>
> 
> How about:
> 
> pushf
> pop eax
> xor eax, 200000h
> mov ecx, eax
> push eax
> popf
> pushf
> pop eax
> cmp eax, ecx
> jnz .no_cpuid
> 
> But why go such lengths? CPUs without CPUID are extremely rare, so if
> you really need to, you can simply use your operating system's handler
> for #UD or install one yourself and then just use CPUID. (On Linux, a
> #UD just causes a SIGILL to the current process if in user mode or a
> kernel panic if in kernel mode).
> 
> And what are you gonna do, anyway? Most of us don't write the same code
> twice, so a failing feature check usually just results in an error
> message. In that case, just install a handler for SIGILL/#UD that does
> that. No need to call CPUID there. Of course, it is still needed when
> you have different code paths for different setups, but there aren't too
> many applications for that (truecrypt using AES-NI, if available, for
> instance, or several multi-media editors and players with several
> filters and renderers for different hardware capabilities. But beyond that?)

Good point. There's an interesting discussion of the #UD method here:

http://www.rcollins.org/ddj/Nov96/Nov96.html

I just ASSume that cpuid is available. Of course there's the "Unix way":

cat /proc/cpuinfo

I think Francesco was looking for an "exercise" in asm programming, 
rather than whether he had cpuid or not...

Best,
Frank

0
Reply fbkotler9831 (124) 2/18/2012 3:38:48 PM

31 Replies
48 Views

(page loaded in 0.337 seconds)

5/23/2013 11:58:42 PM


Reply: