This is an archive of the discontinued LLVM Phabricator instance.

[Windows] Define generic arguments registers for Windows x64
ClosedPublic

Authored by aleksandr.urakov on Oct 26 2018, 4:34 AM.

Diff Detail

Repository
rL LLVM

Event Timeline

This revision was not accepted when it landed; it landed in state Needs Review.Oct 26 2018, 7:17 AM
This revision was automatically updated to reflect the committed changes.
lanza added a subscriber: lanza.May 15 2019, 2:26 PM

@aleksandr.urakov Microsoft's docs state

The first four integer arguments are passed in registers. Integer values are passed in left-to-right order in RCX, RDX, R8, and R9, respectively. Arguments five and higher are passed on the stack. All arguments are right-justified in registers, so the callee can ignore the upper bits of the register and access only the portion of the register necessary.

What's the reason for having R10 and R11 as LLDB_REGNUM_GENERIC_ARG5 and 6?

Herald added a project: Restricted Project. · View Herald TranscriptMay 15 2019, 2:26 PM

@lanza Hello! AFAIU, these values have nothing to do with the Microsoft x64 calling convention. They are used by ABISysV_x86_64, which specifies ABI for working with code injectable into a process being debugged. It requires six arguments to be passed through registers (see GetArgumentValues).

If I understand right, SymbolFile retrieves the information about arguments location on the stack, so there's no need to support different calling conventions in LLDB for this purpose. Please, correct me if I'm wrong.

@lanza Hello! AFAIU, these values have nothing to do with the Microsoft x64 calling convention. They are used by ABISysV_x86_64, which specifies ABI for working with code injectable into a process being debugged. It requires six arguments to be passed through registers (see GetArgumentValues).

If I understand right, SymbolFile retrieves the information about arguments location on the stack, so there's no need to support different calling conventions in LLDB for this purpose. Please, correct me if I'm wrong.

That definitely doesn't sound right. The reason ABISysV_x86_64 requires 6 registers is because the "SysV" x86_64 ABI specifies 6 argument registers. If the microsoft ABI is different, then you probably need a new ABI plugin to correctly describe it. (Also, we use the abi plugin to call mmap, and mmap takes 6 arguments).

I'm not sure what exactly are the consequences of not using the correct ABI definition here. I think the case where the difference may start to become obvious is if you try to get argument values of a function for which you don't have debug info for. Probably the reason that expression evaluation works for you is that we only ever pass one argument into the generated expression (by arranging all inputs into a struct and then passing the struct pointer as an argument), and the first register argument happens to be the same for SysV and microsoft ABIs.

I'm not sure what exactly are the consequences of not using the correct ABI definition here. I think the case where the difference may start to become obvious is if you try to get argument values of a function for which you don't have debug info for.

It sounds strange to me... If we don't have symbols for a function, then we can't even know amount of its arguments, so how can we retrieve them? Also e.g. on Windows x86 both stdcall, ccall, thiscall and fastcall are commonly used, so it would be strange to use some "default" ABI...

But I don't know why the SysV ABI is used on Windows now (it was already used before my commit). It looks like nothing bad should happen if we will use it for expressions evaluation (and even with six arguments - r10 and r11 are volatile on Windows x64), but I have no objections to creating a separate ABI plugin.

(Also, we use the abi plugin to call mmap, and mmap takes 6 arguments).

Can you explain me, please, when does such mmap being called? Just for the purpose of general education :)

I'm not sure what exactly are the consequences of not using the correct ABI definition here. I think the case where the difference may start to become obvious is if you try to get argument values of a function for which you don't have debug info for.

It sounds strange to me... If we don't have symbols for a function, then we can't even know amount of its arguments, so how can we retrieve them? Also e.g. on Windows x86 both stdcall, ccall, thiscall and fastcall are commonly used, so it would be strange to use some "default" ABI...

That's good point. I may be misremembering things here. I never dealt with these things directly, and I'm just relaying what I remember from past discussions.

I had a brief look at the source code, and it looks like there's only a handful of callers to the GetArgumentValues method. The main use case seems to be when you already have some external knowledge that a certain function has some signature, but you may not have debug info for it (e.g. because it's a system function, and you don't have debug info for system libraries). AppleObjCRuntime seems to use that to extract some information about the exception being thrown..

So it's quite possible that this function is never actually called on windows..

(Also, we use the abi plugin to call mmap, and mmap takes 6 arguments).

Can you explain me, please, when does such mmap being called? Just for the purpose of general education :)

When we evaluate an expression, we jit a bunch of opcodes into the inferior memory and then have it execute them. For that to work, we need to allocate some memory in order to store the opcodes. We cannot use the general expression engine to jit that expression, as we would be back to square one, so we manually set the PC to the entry point of the mmap function, and set the argument values as if it was being called. Then we just let the inferior loose and have it allocate the memory for us and return it. For this to work, we need abi knowledge both to correctly set the arguments of mmap, and to retrieve its result.

When we evaluate an expression, we jit a bunch of opcodes into the inferior memory and then have it execute them. For that to work, we need to allocate some memory in order to store the opcodes. We cannot use the general expression engine to jit that expression, as we would be back to square one, so we manually set the PC to the entry point of the mmap function, and set the argument values as if it was being called. Then we just let the inferior loose and have it allocate the memory for us and return it. For this to work, we need abi knowledge both to correctly set the arguments of mmap, and to retrieve its result.

Got it, thanks! There's a different approach on Windows: for now we just call VirtualAllocEx, which can allocate memory in another process. But it will not work for the remote debugging case.

When we evaluate an expression, we jit a bunch of opcodes into the inferior memory and then have it execute them. For that to work, we need to allocate some memory in order to store the opcodes. We cannot use the general expression engine to jit that expression, as we would be back to square one, so we manually set the PC to the entry point of the mmap function, and set the argument values as if it was being called. Then we just let the inferior loose and have it allocate the memory for us and return it. For this to work, we need abi knowledge both to correctly set the arguments of mmap, and to retrieve its result.

Got it, thanks! There's a different approach on Windows: for now we just call VirtualAllocEx, which can allocate memory in another process. But it will not work for the remote debugging case.

Sure it will. You just need to call that function from lldb-server in response to the _M packet. The _M packet is our primary method of allocating memory, and the mmap thingy is a fallback for platforms where allocating memory is not that easy.

(Actually, there is a relatively easy way to allocate memory from lldb-server on linux too, which is to (set up appropriate registers and then) have the inferior execute the int 0x80 instruction. That would bypass the mmap function and jump straight to the kernel syscall. It would have the advantage of working in situations where the mmap libc function may not be available, but it's a bit of pain to set up so, i haven't tried yet to implement that.)

Ah, yes, sure, it will just be moved on the lldb-server side. Thanks again for the clarification!

So the ABI plugin for Windows x64 seems to be necessary for proper unwinding. (Also, proper parsing of xdata and pdata sections might be necessary, too.) I suspect that this could be related to https://bugs.llvm.org/show_bug.cgi?id=32343, but would need to look more into this.