Adding a new RegisterContext for Windows

I’m looking into adding a new RegisterContext for windows. Virgile’s patch that I was working on merging inherited from RegisterContext_POSIX, but on the surface this seems like the wrong thing to do, and I wonder if we need an entirely new one for Windows (or need to change the name of RegisterContext_POSIX to something else).

What are all the steps involved here? From what I can tell at a minimum I need to implement a RegisterContextWindows_x86 and RegisterContextWindows_x86_64, but there’s also RegisterInfoInterface and a few other things I need to figure out.

A few other specific questions:

  1. Why is all this stuff for different platforms is in Plugins/Process/Utility, instead of in the individual process plugins like Plugins/Process/Linux, or Plugins/Process/FreeBSD?

  2. Some code seems to be dead. Like in RegisterContextPosix, there’s a long list of static variables, g_contained_eax, g_invalidate_eax, etc. But none of this stuff seems to be used for anything. Am I overlooking something obvious?

  3. What is the difference between a RegisterInfoInterface and a RegisterContext?

I'm looking into adding a new RegisterContext for windows. Virgile's patch that I was working on merging inherited from RegisterContext_POSIX, but on the surface this seems like the wrong thing to do, and I wonder if we need an entirely new one for Windows (or need to change the name of RegisterContext_POSIX to something else).

What are all the steps involved here? From what I can tell at a minimum I need to implement a RegisterContextWindows_x86 and RegisterContextWindows_x86_64, but there's also RegisterInfoInterface and a few other things I need to figure out.

A few other specific questions:
1) Why is all this stuff for different platforms is in Plugins/Process/Utility, instead of in the individual process plugins like Plugins/Process/Linux, or Plugins/Process/FreeBSD?

These were being used by multiple plug-ins. FreeBSD and Linux are closely related (they use ptrace) so they can sometimes share their register contexts.

2) Some code seems to be dead. Like in RegisterContextPosix, there's a long list of static variables, g_contained_eax, g_invalidate_eax, etc. But none of this stuff seems to be used for anything. Am I overlooking something obvious?

If they are dead then remove them. Looking at the RegisterInfo struct:

    typedef struct
    {
        const char *name; // Name of this register, can't be NULL
        const char *alt_name; // Alternate name of this register, can be NULL
        uint32_t byte_size; // Size in bytes of the register
        uint32_t byte_offset; // The byte offset in the register context data where this register's value is found
        lldb::Encoding encoding; // Encoding of the register bits
        lldb::Format format; // Default display format
        uint32_t kinds[lldb::kNumRegisterKinds]; // Holds all of the various register numbers for all register kinds
        uint32_t *value_regs; // List of registers that must be terminated with LLDB_INVALID_REGNUM
        uint32_t *invalidate_regs; // List of registers that must be invalidated when this register is modified, list must be terminated with LLDB_INVALID_REGNUM
    } RegisterInfo;

Registers can define registers that make up the value for this register (like "d0" on ARM can say it is made up from "s0" and "s1" since "d0" is a 64 bit register). Then this register is read from, it will not issue a register read for its own register number, but it will request all registers in "value_regs" be read instead.

Likewise, you might want to invalidate registers when this register is modified, like "eax" (if you define one in your register context) would invalidate "rax" "ax" "al" if "eax" is modified.

The static arrays seem to be left over from a copy and paste where g_contained_eax represented the register for "value_regs" or "invalidate_regs"...

3) What is the difference between a RegisterInfoInterface and a RegisterContext?

I am not sure, this class was implemented, but it isn't referenced currently.

For register contexts you will just need to override all pure virtual functions in lldb_private::RegisterContext.

You must define all of your registers by returning a count:

    virtual size_t
    RegisterContextWindows::GetRegisterCount () = 0;

And supply a register info for each register. Register numbers are zero based index identifiers that must have no gaps. Each register must have a RegisterInfo struct that describes it:

    virtual const RegisterInfo *
    RegisterContextWindows::GetRegisterInfoAtIndex (size_t reg) = 0;

You must also describe your register sets:

    virtual size_t
    GetRegisterSetCount () = 0;

    virtual const RegisterSet *
    GetRegisterSet (size_t reg_set) = 0;

You must also be able to convert register numbers of various kinds into actual zero based register index IDs:

    virtual uint32_t
    ConvertRegisterKindToRegisterNumber (lldb::RegisterKind kind, uint32_t num) = 0;

And read and write the registers (see all remaining pure virtuals).

The common way to define your register infos is to just have a static array in your RegisterContextWindows.cpp:

static RegisterInfo g_x86_64_register_infos[] =
{
  ...
}

Then your RegisterContextWindows::GetRegisterInfoAtIndex() just returns

const RegisterInfo *
RegisterContextWindows::GetRegisterInfoAtIndex (size_t reg)
{
  if (reg < sizeof_array(g_x86_64_register_infos))
      return g_x86_64_register_infos[reg];
  return NULL;
}

So there is nothing that special about the register context class, just implement the pure virtual and abstract it underneath so you have different classes for i386 and x86_64. The register context defines a bunch of bytes for all registers and the register info structs contain the byte_offset into the big data buffer that can contain all of your registers.

Let me know if you have any questions.

Greg

Thanks.

I’m working on a very not-fancy implementation for Windows. When I get it done, who would be the best person to take a look at it if I put up a review?

Even if I’m the only person that understands the Windows-specific stuff, I think it would help to have someone familiar with these classes take a look at my implementation to make sure I’ve understood each of the functions correctly, and perhaps point out any gotchas that I might have fallen victim to or not be aware of.

I would be the best person to review it. Let me know when you have a patch.

Sounds good.

One question. Why does each RegisterInfo need the array of kinds? Wouldn’t it be possible, in theory, to store a single integer here – the lldb register kind – and then have an abstract interface which converts from eRegisterKindLLDB to another arbitrary kind, and from some arbitrary register kind to eRegisterKindLLDB? Then, there would be exactly one implementation of this interface per CPU. So it would be independent of the OS. Seems to me like no matter what OS you’re on, EAX is still just EAX. So even if there’s some confusing stuff like ESP and EBP being mixed up on old versions of GCC, that shouldn’t have anything to do with the OS.

There’s a lot of very similar sounding enums scattered around through the existing RegisterContext files which leads to some confusion figuring out what’s what, and it seems like that would make the definitions of the static arrays and register infos much simpler.

Not actually wanting to do something like that, just want make sure understanding of the different register kinds and stuff is correct.