1

When I compile a C++ program that uses assembly (using Visual Studio 2022), some of the assembly code appears to be adjusted or at least optimized away (but I have optimizations disabled).

Does anyone know what's going on and how I can make it not do this?

The purpose behind this code (at least the non-summarized version) is to create a wrapper class for another class defined in a DLL that I do not have the source for. Essentially, I am calling the constructor of the DLL class which requires ECX to be an address in the stack that points to 0 (an empty pointer).

SampleClass.h

class SampleClass
{
public:
    SampleClass();

private:
    uintptr_t value1;
};

SampleClass.cpp

SampleClass::SampleClass() : 
    value1(0)
{
    __asm {
        // When I compile this converts to LEA ECX, 0...
        // I want it to hold the value for the address of value1.
        LEA ECX, DWORD PTR SS : [value1]
    }
}

If I adjust the function to this, it doesn't remove/change the code... but crashes at the end of the function saying that the stack data around value2 is corrupt.

SampleClass::SampleClass() : 
    value1(0)
{
    uintptr_t value2;

    __asm {
        LEA ECX, DWORD PTR SS : [value2]
    }

    value1 = value2;
    // Crashes here...
}
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Rick
  • 421
  • 3
  • 15
  • Do you have to use assembly? If not in general you probably won't write faster code than your compiler does : [CppCon 2017: Matt Godbolt “What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid”](https://www.youtube.com/watch?v=bSkpMdDe4g4). And you could argue that the compiler was right deducing that value1 always will be 0, I am curious what would happen if you made value1 `volatile` – Pepijn Kramer Jan 10 '23 at 05:08
  • @harold I'm trying to make a wrapper object for an object that I do not have the source for. In this case I'm trying to create the object and the value of ECX should be a stack address with a value of 0 (since the object doesn't exist yet). – Rick Jan 10 '23 at 05:11
  • 1
    Ok then my reply doesn't make much sense, it would have helped if in your question you would first explain what you wanted to do, and then show your "how". But I think this approach is not going to work, to get a valid instance (of the object you don't have the source for) you should call its constructor not just place some data in registers. – Pepijn Kramer Jan 10 '23 at 05:12
  • Thanks, I'll add that. @PepijnKramer.. also making it volatile changed it to DWORD PTR DS:[0] (instead of DWORD PTR SS : [address that should not be 0].) – Rick Jan 10 '23 at 05:15
  • What's the point of setting ECX inside an `_asm{}` block without doing anything with that value? MSVC's simplistic inline asm syntax doesn't have any way to tell the compiler about outputs in registers, so you have to `mov` to a C++ variable if you want it to do anything. – Peter Cordes Jan 10 '23 at 05:17
  • Also, crashes how, on what instruction when you disassemble in the debugger (so you also see compiler-generated asm)? Your code looks perfectly safe; like I said the inline asm does nothing. (Unless you're compiling with `thiscall` or `fastcall`, because [MSVC doesn't support inline asm for functions with register args](https://stackoverflow.com/questions/3323445/what-is-the-difference-between-asm-asm-and-asm#comment59576185_35959859) / https://msdn.microsoft.com/en-us/library/k1a8ss06.aspx. And overwriting ECX could be destroying its copy of `this` because MSVC is really dumb.) – Peter Cordes Jan 10 '23 at 05:17
  • Just thinking : Maybe in the end you're better of reverse engineering the class declaration of the class (you don't have the source for) and let the compiler do all the work. – Pepijn Kramer Jan 10 '23 at 05:19
  • @PeterCordes this was just a small version of the code, the real code calls the constructor of a class in a different DLL once the value of ECX is assigned to an address of a null pointer. I updated my original post to describe this. – Rick Jan 10 '23 at 05:19
  • @PepijnKramer Unfortunately I don't think I'm skilled enough to do something like that yet... – Rick Jan 10 '23 at 05:25
  • https://godbolt.org/z/8W1GjscEs shows that `lea reg, varname` works for actual local vars, including `this`. Also that by default it's using `thiscall`, since we can see it storing ECX to the stack, which means we're in undefined-behaviour territory. Also, it's not constant-propagation of the *value* `value1(0)`, since changing that to `value1(456)` still results in an LEA of address `0`. (@PepijnKramer). – Peter Cordes Jan 10 '23 at 05:30
  • 1
    What do you want to do with ECX that you can't do with `void *ptr = &value1;`? – Peter Cordes Jan 10 '23 at 05:31
  • @MichaelPetch: Wouldn't you want the *value* of `this` in a register, like `mov ecx, this`, if you want to add `offsetof(SampleClass, value1)` to it to get `&(this->value1)`? – Peter Cordes Jan 10 '23 at 05:33
  • @MichaelPetch: Yes, exactly. `this` is a variable of type `SampleClass*`, and you want that 4-byte pointer value in a register. Since the compiler spilled it from ECX to the stack (and won't accept `__cdecl` to take it on the stack in the first place), we need a load to get `this` instead of `&this`. – Peter Cordes Jan 10 '23 at 05:39
  • 1
    If you want to call a function with a specific value in `ecx`, better would be to declare a `__fastcall` function. https://gcc.godbolt.org/z/bfxjr7nPb – Raymond Chen Jan 10 '23 at 05:40
  • 2
    You would have to get the `this` address into a register with `mov` and then you would have to reference the `value1` member. Something like this might work `MOV ECX, this` `LEA ECX, [ecx].value1` . ECX would then contain the address of `this->value1` – Michael Petch Jan 10 '23 at 06:27
  • 2
    As for the other part of your question, `LEA ECX, DWORD PTR SS : [value1]` moves the offset of `value1` within the class. In your case the offset is 0. If you were to add other members before `value1` in `SampleClass` you'd see the offset change. You need to load the address of the `this` and then from there compute the offset of the member `value1` to get the address of `this->value` at runtime as I suggested. I'm not sure what you are doing with ECX after you get it, but I'm really answering the question in the title of your question. – Michael Petch Jan 10 '23 at 06:29
  • Thank you everyone, @MichaelPetch that was the solution! – Rick Jan 10 '23 at 11:39

0 Answers0