How to load all registers into C struct?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
NeonLightions
Member
Member
Posts: 102
Joined: Wed Oct 20, 2021 6:00 pm
Location: Paraguay

How to load all registers into C struct?

Post by NeonLightions »

Like the title, how can I load all registers to struct like this:

Code: Select all

typedef struct {
    uint64_t carry                      : 1;
    uint64_t reserved0                  : 1;
    uint64_t parity                     : 1;
    uint64_t reserved1                  : 1;
    uint64_t auxiliary                  : 1;
    uint64_t reserved2                  : 1;
    uint64_t zero                       : 1;
    uint64_t sign                       : 1;
    uint64_t trap                       : 1;
    uint64_t interrupt_enable           : 1;
    uint64_t direction                  : 1;
    uint64_t overflow                   : 1;
    uint64_t io_privl_level             : 2;
    uint64_t nested_task                : 1;
    uint64_t reserved3                  : 1;
    uint64_t resume                     : 1;
    uint64_t v8086_mode                 : 1;
    uint64_t align_check_access_ctrl    : 1;
    uint64_t virtual_interrupt          : 1;
    uint64_t virtual_interrupt_pending  : 1;
    uint64_t id                         : 1;
    uint64_t reserved4                  : 42;
}__attribute__((packed)) flags_t;


typedef struct {
    uint64_t protected_mode_entable     : 1;
    uint64_t monitor_coprocessor        : 1;
    uint64_t emulation                  : 1;
    uint64_t task_switched              : 1;
    uint64_t extension_type             : 1;
    uint64_t numeric_error              : 1;
    uint64_t reserved0                  : 10;
    uint64_t write_protect              : 1;
    uint64_t reserved1                  : 1;
    uint64_t alignment_check            : 1;
    uint64_t reserved2                  : 10;
    uint64_t not_write_through          : 1;
    uint64_t cache_disable              : 1;
    uint64_t paging                     : 1;
    uint64_t reserved3                  : 32;
}__attribute__((packed)) cr0_t;

typedef struct {
    uint64_t vme                        : 1;
    uint64_t pvi                        : 1;
    uint64_t tsd                        : 1;
    uint64_t de                         : 1;
    uint64_t pse                        : 1;
    uint64_t pae                        : 1;
    uint64_t mce                        : 1;
    uint64_t pge                        : 1;
    uint64_t pce                        : 1; /** Performance Monitoring Counter Enable */
    uint64_t osfxsr                     : 1;
    uint64_t osxmmexcpt                 : 1;
    uint64_t umip                       : 1;
    uint64_t reserved0                  : 1;
    uint64_t vmxe                       : 1;
    uint64_t smxe                       : 1;
    uint64_t reserved1                  : 1;
    uint64_t pcide                      : 1;
    uint64_t osxsave                    : 1;
    uint64_t reserved2                  : 1;
    uint64_t smep                       : 1;
    uint64_t smap                       : 1;
    uint64_t pke                        : 1;
    uint64_t cet                        : 1;
    uint64_t pks                        : 1;
    uint64_t reserved3                  : 39;
}__attribute__((packed)) cr4_t;

typedef struct {
    uint64_t priority   : 4;
    uint64_t reserved   : 60;
}__attribute__((packed)) cr8_t;

typedef struct {
    uint64_t sce        : 1;
    uint64_t reserved0  : 7;
    uint64_t lme        : 1;
    uint64_t lma        : 1;
    uint64_t nxe        : 1;
    uint64_t svme       : 1;
    uint64_t lmsle      : 1;
    uint64_t ffxsr      : 1;
    uint64_t tce        : 1;
    uint64_t reserved1  : 48;
}__attribute__((packed)) ia32_efer_t;

typedef struct {
    /**
     * General purpose registers (8-bit form)
     */
    uint8_t ah;
    uint8_t al;
    uint8_t bh;
    uint8_t bl;
    uint8_t ch;
    uint8_t cl;
    uint8_t dh;
    uint8_t dl;
    uint8_t sil;
    uint8_t dil;
    uint8_t spl;
    uint8_t bpl;
    uint8_t r8b;
    uint8_t r9b;
    uint8_t r10b;
    uint8_t r11b;
    uint8_t r12b;
    uint8_t r13b;
    uint8_t r14b;
    uint8_t r15b;
    
    /**
     * General purpose registers (16-bit form)
     */
    uint16_t ax;
    uint16_t bx;
    uint16_t cx;
    uint16_t dx;
    uint16_t si;
    uint16_t di;
    uint16_t sp;
    uint16_t bp;
    uint16_t r8w;
    uint16_t r9w;
    uint16_t r10w;
    uint16_t r11w;
    uint16_t r12w;
    uint16_t r13w;
    uint16_t r14w;
    uint16_t r15w;

    /**
     * Segment registers
     */
    uint16_t cs;
    uint16_t ds;
    uint16_t es;
    uint16_t fs;
    uint16_t gs;
    uint16_t ss;

    /**
     * Instruction pointer (16-bit form)
     */
    uint16_t ip;

    /**
     * Instruction pointer (32-bit form)
     */
    uint32_t eip;

    /**
     * General purpose registers (32-bit form)
     */
    uint32_t eax;
    uint32_t ebx;
    uint32_t ecx;
    uint32_t edx;
    uint32_t esi;
    uint32_t edi;
    uint32_t esp;
    uint32_t ebp;
    uint32_t r8d;
    uint32_t r9d;
    uint32_t r10d;
    uint32_t r11d;
    uint32_t r12d;
    uint32_t r13d;
    uint32_t r14d;
    uint32_t r15d;

    /**
     * Instruction pointer (64-bit form)
     */
    uint64_t rax;
    uint64_t rbx;
    uint64_t rcx;
    uint64_t rdx;
    uint64_t rsi;
    uint64_t rdi;
    uint64_t rsp;
    uint64_t rbp;
    uint64_t r8;
    uint64_t r9;
    uint64_t r10;
    uint64_t r11;
    uint64_t r12;
    uint64_t r13;
    uint64_t r14;
    uint64_t r15;

    /**
     * Control registers
     */
    cr0_t cr0;
    uint64_t cr2;
    uint64_t cr3;
    cr4_t cr4;
    cr8_t cr8;

    /**
     * FLAGS register
     */
    flags_t rflags;

    /**
     * IA32_EFER MSR
     */
    ia32_efer_t ia32_efer;
} registers_t;
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: How to load all registers into C struct?

Post by iansjack »

Just write the appropriate assembly code. What exactly is your problem?
NeonLightions
Member
Member
Posts: 102
Joined: Wed Oct 20, 2021 6:00 pm
Location: Paraguay

Re: How to load all registers into C struct?

Post by NeonLightions »

iansjack wrote:Just write the appropriate assembly code. What exactly is your problem?
Oh, no problem in my code. I just want to get registers information for debug purpose. But i don't known how to "write approriate code" in Assembly. Using inline asm is too slow, all is want is: Somehow, i can embed the C struct to asm to write.
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: How to load all registers into C struct?

Post by iansjack »

To access the fields of a C struct in assembly language you put the base address of the struct variable in a register then use indexed addressing, with that register, to access the fields (with the offset of the field in the struct as the index). Obviously, if you want to put all registers into a C struct you have to save the address of the register you are going to use as the base (and any other registers that you use in your routine) - the stack would be a good place for this.

What you want to do is definitely (IMO) a candidate for a separate assembly language routine rather than inline assembly code.
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to load all registers into C struct?

Post by Octocontrabass »

NeonLightions wrote:Like the title, how can I load all registers to struct like this:
Having separate entries for AH, AL, AX, EAX, and RAX seems pretty wasteful. That could be a union.
NeonLightions wrote:I just want to get registers information for debug purpose.
What are you trying to debug? That will determine where and how you'll retrieve many of those register values. For example, if you retrieve the current RIP, you'll always get the address of the code that retrieves RIP. What you probably want is the value of RIP when a program was interrupted by your debugger.
NeonLightions wrote:Using inline asm is too slow
Compared to what?
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: How to load all registers into C struct?

Post by BenLunt »

I would also like to suggest that you do not use bit fields. Bit fields are not standard across brands of compilers, and may be non-standard among the same compiler with different versions.

For example, a known brand declares:
Microsoft Specific

The ordering of data declared as bit fields is from low to high bit...

END Microsoft Specific
A different brand may declare it from high to low. There are also restrictions with const's and referencing a bit field. For more, read the "NOTES" section at https://en.cppreference.com/w/cpp/language/bit_field.

With this in mind, you might think that 'flags_t::carry' is at bit 0, when it might be at bit 63.

If you do continue to use bit fields, use some compiler preprocessor defines to make sure you are using a known compiler (and version) that supports the bit fields as you need them. i.e.: Check that the current compiler (and version) is acceptable to the declaration of bit fields as defined by that compiler (and version).

As for calling an assembly routine to get these values, depending on the calling convention, the compiler will place the parameters passed into registers ultimately destroying the values you wish to preserve. For example, the Microsoft convention for 64-bit declares:

Code: Select all

The first four arguments are placed onto the registers. That means RCX, RDX, R8, R9 for integer...
For example, the following:

Code: Select all

  retrieve_general(&general_storage);
Will destroy (at least) RCX.

With this in mind, one way to do it would be to note that an interrupt will not destroy the register state. If you have a known stack, you could generate an (software) interrupt. Now all you have to do is know the placement of the EFLAGS, EIP, CS, SS, ESP, etc, on the given stack (Note that a software interrupt may be different than a hardware interrupt when placing items on the stack).

There may be other ways to do it--simpler as well as not so simple--but either way, you need to know what processor you are running on, what compiler calling convention is being used, is the address state a flat address or a linear address, as well as possibly a few other items.

To retrieve the register state, I simply invoke a GPF. All registers have the values given at the time of the interrupt. (Doing this, you could easily set a flag to return as normal or process the GPF.) To make it easier, you could use an Illegal Instruction sequence which will invoke a lesser used exception handler, though you will still need some way of knowing (within the handler) if this is a "retrieve all registers" call or an actual illegal instruction. A software interrupt might be better for your needs, depending on the platform you are using.

Ben
- http://www.fysnet.net/osdesign_book_series.htm
NeonLightions
Member
Member
Posts: 102
Joined: Wed Oct 20, 2021 6:00 pm
Location: Paraguay

Re: How to load all registers into C struct?

Post by NeonLightions »

Octocontrabass wrote:
NeonLightions wrote:Like the title, how can I load all registers to struct like this:
Having separate entries for AH, AL, AX, EAX, and RAX seems pretty wasteful. That could be a union.
I will keep in mind that. Thank you :D
Octocontrabass wrote:
NeonLightions wrote:I just want to get registers information for debug purpose.
What are you trying to debug? That will determine where and how you'll retrieve many of those register values. For example, if you retrieve the current RIP, you'll always get the address of the code that retrieves RIP. What you probably want is the value of RIP when a program was interrupted by your debugger.
I'm trying to debug this: "The RBX register doesn't give me where the .text section start (0x100000) so I can't get multiboot information from GRUB"
Octocontrabass wrote:
NeonLightions wrote:Using inline asm is too slow
Compared to what?
I just saw that, inline assembly took 1/4 second to execute, i think.
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to load all registers into C struct?

Post by Octocontrabass »

NeonLightions wrote:I'm trying to debug this: "The RBX register doesn't give me where the .text section start (0x100000) so I can't get multiboot information from GRUB"
GRUB starts your kernel in protected mode, but from the sound of things, you're expecting long mode. Does your code to switch to long mode overwrite EBX? How are you passing the value in EBX to your C code?
NeonLightions wrote:I just saw that, inline assembly took 1/4 second to execute, i think.
Sounds like you have a bug. Reading a register in inline assembly should be fast enough you have to measure it in CPU cycles, not seconds.
NeonLightions
Member
Member
Posts: 102
Joined: Wed Oct 20, 2021 6:00 pm
Location: Paraguay

Re: How to load all registers into C struct?

Post by NeonLightions »

Octocontrabass wrote:
NeonLightions wrote:I'm trying to debug this: "The RBX register doesn't give me where the .text section start (0x100000) so I can't get multiboot information from GRUB"
GRUB starts your kernel in protected mode, but from the sound of things, you're expecting long mode. Does your code to switch to long mode overwrite EBX? How are you passing the value in EBX to your C code?
I checked again the boot code and I'm sure there isn't any function that overwritten EBX
Octocontrabass wrote:
NeonLightions wrote:I just saw that, inline assembly took 1/4 second to execute, i think.
Sounds like you have a bug. Reading a register in inline assembly should be fast enough you have to measure it in CPU cycles, not seconds.
Do you have any suggestion to fix this bug?
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to load all registers into C struct?

Post by Octocontrabass »

NeonLightions wrote:I checked again the boot code and I'm sure there isn't any function that overwritten EBX
Are you sure? A lot of example code for switching to long mode uses CPUID, which overwrites EBX. You didn't answer my other question, either: how are you passing the value in EBX to your C code?
NeonLightions wrote:Do you have any suggestion to fix this bug?
Step through the offending code with a debugger.
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: How to load all registers into C struct?

Post by iansjack »

NeonLightions wrote:I checked again the boot code and I'm sure there isn't any function that overwritten EBX
Set a breakpoint in gdb for the entry code of your boot code (immediately after grub has loaded your kernel). Check the value of ebx. Then step through the code to see where it is changed. You could, of course, save the value of ebx immediately after grub loads the kernel and then later access this saved value.

It does seem that most of your current problems could be solved by learning how to use gdb effectively. Doing so would be very useful in future.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: How to load all registers into C struct?

Post by BenLunt »

I also agree, you should learn to use a debugger. You can see exactly what the machine state is at any given point.

You don't state what development platform you are using. I think most *nix platforms use GDB as mentioned before. If you are using a Windows version as your development platform, you can try Bochs.

I have a simple tutorial on how to use the Bochs debugger at http://www.fysnet.net/bochsdbg/index.htm.

Ben
NeonLightions
Member
Member
Posts: 102
Joined: Wed Oct 20, 2021 6:00 pm
Location: Paraguay

Re: How to load all registers into C struct?

Post by NeonLightions »

Octocontrabass wrote:
NeonLightions wrote:I checked again the boot code and I'm sure there isn't any function that overwritten EBX
Are you sure? A lot of example code for switching to long mode uses CPUID, which overwrites EBX. You didn't answer my other question, either: how are you passing the value in EBX to your C code?
NeonLightions wrote:Do you have any suggestion to fix this bug?
Step through the offending code with a debugger.
Hm... I didn't think before. I used cpuid instruction with EAX=1, is that instruction overwritten EBX? And I save EBX to somewhere in the memory, after switched to long mode, I move back to rdi (rsi used for multiboot bootloader magic value) and call kernel_main(unsigned long addr, unsigned long magic)
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to load all registers into C struct?

Post by Octocontrabass »

The CPUID instruction always overwrites EBX.
NeonLightions
Member
Member
Posts: 102
Joined: Wed Oct 20, 2021 6:00 pm
Location: Paraguay

Re: How to load all registers into C struct?

Post by NeonLightions »

Octocontrabass wrote:The CPUID instruction always overwrites EBX.
So how can I save EBX? I have moved EBX to somewhere in memory but it's useless, the EBX is always change
Post Reply