Skip to content

Commit

Permalink
[x86] Fix issues with a realigned stack in MSVC compiled applications
Browse files Browse the repository at this point in the history
Summary:
This patch fixes issues with a stack realignment.

MSVC maintains two frame pointers (`ebx` and `ebp`) for a realigned stack - one
is used for access to function parameters, while another is used for access to
locals. To support this the patch:
- adds an alternative frame pointer (`ebx`);
- considers stack realignment instructions (e.g. `and esp, -32`);
- along with CFA (Canonical Frame Address) which point to the position next to
  the saved return address (or to the first parameter on the stack) introduces
  AFA (Aligned Frame Address) which points to the position of the stack pointer
  right after realignment. AFA is used for access to registers saved after the
  realignment (see the test);

Here is an example of the code with the realignment:
```
struct __declspec(align(256)) OverAligned {
  char c;
};

void foo(int foo_arg) {
  OverAligned oa_foo = { 1 };
  auto aaa_foo = 1234;
}

void bar(int bar_arg) {
  OverAligned oa_bar = { 2 };
  auto aaa_bar = 5678;
  foo(1111);
}

int main() {
  bar(2222);
  return 0;
}
```
and here is the `bar` disassembly:
```
push    ebx
mov     ebx, esp
sub     esp, 8
and     esp, -100h
add     esp, 4
push    ebp
mov     ebp, [ebx+4]
mov     [esp+4], ebp
mov     ebp, esp
sub     esp, 200h
mov     byte ptr [ebp-200h], 2
mov     dword ptr [ebp-4], 5678
push    1111            ; foo_arg
call    j_?foo@@Yaxh@Z  ; foo(int)
add     esp, 4
mov     esp, ebp
pop     ebp
mov     esp, ebx
pop     ebx
retn
```

Reviewers: labath, zturner, jasonmolenda, stella.stamenova

Reviewed By: jasonmolenda

Subscribers: abidh, lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D53435

llvm-svn: 345577
  • Loading branch information
Aleksandr Urakov committed Oct 30, 2018
1 parent d62afe0 commit 4538ed3
Show file tree
Hide file tree
Showing 8 changed files with 484 additions and 150 deletions.
60 changes: 47 additions & 13 deletions lldb/include/lldb/Symbol/UnwindPlan.h
Expand Up @@ -28,14 +28,22 @@ namespace lldb_private {
// The UnwindPlan object specifies how to unwind out of a function - where this
// function saves the caller's register values before modifying them (for non-
// volatile aka saved registers) and how to find this frame's Canonical Frame
// Address (CFA).
// Address (CFA) or Aligned Frame Address (AFA).

// CFA is a DWARF's Canonical Frame Address.
// Most commonly, registers are saved on the stack, offset some bytes from the
// Canonical Frame Address, or CFA, which is the starting address of this
// function's stack frame (the CFA is same as the eh_frame's CFA, whatever that
// may be on a given architecture). The CFA address for the stack frame does
// not change during the lifetime of the function.

// AFA is an artificially introduced Aligned Frame Address.
// It is used only for stack frames with realignment (e.g. when some of the
// locals has an alignment requirement higher than the stack alignment right
// after the function call). It is used to access register values saved on the
// stack after the realignment (and so they are inaccessible through the CFA).
// AFA usually equals the stack pointer value right after the realignment.

// Internally, the UnwindPlan is structured as a vector of register locations
// organized by code address in the function, showing which registers have been
// saved at that point and where they are saved. It can be thought of as the
Expand All @@ -61,6 +69,8 @@ class UnwindPlan {
same, // reg is unchanged
atCFAPlusOffset, // reg = deref(CFA + offset)
isCFAPlusOffset, // reg = CFA + offset
atAFAPlusOffset, // reg = deref(AFA + offset)
isAFAPlusOffset, // reg = AFA + offset
inOtherRegister, // reg = other reg
atDWARFExpression, // reg = deref(eval(dwarf_expr))
isDWARFExpression // reg = eval(dwarf_expr)
Expand Down Expand Up @@ -90,6 +100,10 @@ class UnwindPlan {

bool IsAtCFAPlusOffset() const { return m_type == atCFAPlusOffset; }

bool IsAFAPlusOffset() const { return m_type == isAFAPlusOffset; }

bool IsAtAFAPlusOffset() const { return m_type == atAFAPlusOffset; }

bool IsInOtherRegister() const { return m_type == inOtherRegister; }

bool IsAtDWARFExpression() const { return m_type == atDWARFExpression; }
Expand All @@ -106,6 +120,16 @@ class UnwindPlan {
m_location.offset = offset;
}

void SetAtAFAPlusOffset(int32_t offset) {
m_type = atAFAPlusOffset;
m_location.offset = offset;
}

void SetIsAFAPlusOffset(int32_t offset) {
m_type = isAFAPlusOffset;
m_location.offset = offset;
}

void SetInRegister(uint32_t reg_num) {
m_type = inOtherRegister;
m_location.reg_num = reg_num;
Expand All @@ -120,9 +144,16 @@ class UnwindPlan {
RestoreType GetLocationType() const { return m_type; }

int32_t GetOffset() const {
if (m_type == atCFAPlusOffset || m_type == isCFAPlusOffset)
switch(m_type)
{
case atCFAPlusOffset:
case isCFAPlusOffset:
case atAFAPlusOffset:
case isAFAPlusOffset:
return m_location.offset;
return 0;
default:
return 0;
}
}

void GetDWARFExpr(const uint8_t **opcodes, uint16_t &len) const {
Expand Down Expand Up @@ -169,20 +200,20 @@ class UnwindPlan {
} m_location;
};

class CFAValue {
class FAValue {
public:
enum ValueType {
unspecified, // not specified
isRegisterPlusOffset, // CFA = register + offset
isRegisterDereferenced, // CFA = [reg]
isDWARFExpression // CFA = eval(dwarf_expr)
isRegisterPlusOffset, // FA = register + offset
isRegisterDereferenced, // FA = [reg]
isDWARFExpression // FA = eval(dwarf_expr)
};

CFAValue() : m_type(unspecified), m_value() {}
FAValue() : m_type(unspecified), m_value() {}

bool operator==(const CFAValue &rhs) const;
bool operator==(const FAValue &rhs) const;

bool operator!=(const CFAValue &rhs) const { return !(*this == rhs); }
bool operator!=(const FAValue &rhs) const { return !(*this == rhs); }

void SetUnspecified() { m_type = unspecified; }

Expand Down Expand Up @@ -279,7 +310,7 @@ class UnwindPlan {
uint16_t length;
} expr;
} m_value;
}; // class CFAValue
}; // class FAValue

public:
Row();
Expand All @@ -302,7 +333,9 @@ class UnwindPlan {

void SlideOffset(lldb::addr_t offset) { m_offset += offset; }

CFAValue &GetCFAValue() { return m_cfa_value; }
FAValue &GetCFAValue() { return m_cfa_value; }

FAValue &GetAFAValue() { return m_afa_value; }

bool SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num, int32_t offset,
bool can_replace);
Expand All @@ -329,7 +362,8 @@ class UnwindPlan {
typedef std::map<uint32_t, RegisterLocation> collection;
lldb::addr_t m_offset; // Offset into the function for this row

CFAValue m_cfa_value;
FAValue m_cfa_value;
FAValue m_afa_value;
collection m_register_locations;
}; // class Row

Expand Down

0 comments on commit 4538ed3

Please sign in to comment.