x86: Fix implicit stack addressing in 64-bit mode

When in 64-bit mode, if the stack is accessed implicitly by an instruction,
the alternate address prefix should be ignored if present.

This patch adds an extra flag to the ldstop which signifies when the
address override should be ignored. Then, for all of the affected
instructions, this patch adds two options to the ld and st opcode to
use the current stack addressing mode for all addresses and to ignore the
AddressSizeFlagBit.
Finally, this patch updates the x86 TLB to not truncate the address if it
is in 64-bit mode and the IgnoreAddrSizeFlagBit is set.

This fixes a problem when calling __libc_start_main with a binary that
is linked with a recent version of ld. This version of ld uses the
address override prefix (0x67) on the call instruction instead of a nop.

Note: This has not been tested in compatibility mode and only the call
instruction with the address override prefix has been tested.

See [1] page 9 (pdf page 45)

For instructions that are affected see [1] page 519 (pdf page 555).

[1] http://support.amd.com/TechDocs/24594.pdf

Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
This commit is contained in:
Jason Lowe-Power 2017-01-28 13:37:58 +05:30 committed by Sanchayan Maity
parent 964652f8ff
commit 3370059668
3 changed files with 71 additions and 35 deletions

View file

@ -45,7 +45,7 @@ def macroop CALL_NEAR_I
limm t1, imm limm t1, imm
rdip t7 rdip t7
# Check target of call # Check target of call
st t7, ss, [0, t0, rsp], "-env.dataSize" st_stack t7, ss, [0, t0, rsp], "-env.dataSize"
subi rsp, rsp, ssz subi rsp, rsp, ssz
wrip t7, t1 wrip t7, t1
}; };
@ -58,7 +58,7 @@ def macroop CALL_NEAR_R
rdip t1 rdip t1
# Check target of call # Check target of call
st t1, ss, [0, t0, rsp], "-env.dataSize" st_stack t1, ss, [0, t0, rsp], "-env.dataSize"
subi rsp, rsp, ssz subi rsp, rsp, ssz
wripi reg, 0 wripi reg, 0
}; };

View file

@ -40,7 +40,7 @@ def macroop POP_R {
# Make the default data size of pops 64 bits in 64 bit mode # Make the default data size of pops 64 bits in 64 bit mode
.adjust_env oszIn64Override .adjust_env oszIn64Override
ld t1, ss, [1, t0, rsp], dataSize=ssz ld_stack t1, ss, [1, t0, rsp], dataSize=ssz
addi rsp, rsp, ssz, dataSize=asz addi rsp, rsp, ssz, dataSize=asz
mov reg, reg, t1 mov reg, reg, t1
}; };
@ -49,7 +49,7 @@ def macroop POP_M {
# Make the default data size of pops 64 bits in 64 bit mode # Make the default data size of pops 64 bits in 64 bit mode
.adjust_env oszIn64Override .adjust_env oszIn64Override
ld t1, ss, [1, t0, rsp], dataSize=ssz ld_stack t1, ss, [1, t0, rsp], dataSize=ssz
cda seg, sib, disp, dataSize=ssz cda seg, sib, disp, dataSize=ssz
addi rsp, rsp, ssz, dataSize=asz addi rsp, rsp, ssz, dataSize=asz
st t1, seg, sib, disp, dataSize=ssz st t1, seg, sib, disp, dataSize=ssz
@ -70,7 +70,7 @@ def macroop PUSH_R {
# Make the default data size of pops 64 bits in 64 bit mode # Make the default data size of pops 64 bits in 64 bit mode
.adjust_env oszIn64Override .adjust_env oszIn64Override
st reg, ss, [1, t0, rsp], "-env.stackSize", dataSize=ssz st_stack reg, ss, [1, t0, rsp], "-env.stackSize", dataSize=ssz
subi rsp, rsp, ssz subi rsp, rsp, ssz
}; };
@ -79,7 +79,7 @@ def macroop PUSH_I {
.adjust_env oszIn64Override .adjust_env oszIn64Override
limm t1, imm limm t1, imm
st t1, ss, [1, t0, rsp], "-env.stackSize", dataSize=ssz st_stack t1, ss, [1, t0, rsp], "-env.stackSize", dataSize=ssz
subi rsp, rsp, ssz subi rsp, rsp, ssz
}; };
@ -138,7 +138,7 @@ def macroop LEAVE {
.adjust_env oszIn64Override .adjust_env oszIn64Override
mov t1, t1, rbp, dataSize=ssz mov t1, t1, rbp, dataSize=ssz
ld rbp, ss, [1, t0, t1], dataSize=ssz ld_stack rbp, ss, [1, t0, t1], dataSize=ssz
mov rsp, rsp, t1, dataSize=ssz mov rsp, rsp, t1, dataSize=ssz
addi rsp, rsp, ssz, dataSize=ssz addi rsp, rsp, ssz, dataSize=ssz
}; };
@ -156,7 +156,7 @@ def macroop ENTER_I_I {
# t1 is now the masked nesting level, and t2 is the amount of storage. # t1 is now the masked nesting level, and t2 is the amount of storage.
# Push rbp. # Push rbp.
st rbp, ss, [1, t0, rsp], "-env.dataSize" st_stack rbp, ss, [1, t0, rsp], "-env.dataSize"
subi rsp, rsp, ssz subi rsp, rsp, ssz
# Save the stack pointer for later # Save the stack pointer for later
@ -172,8 +172,8 @@ def macroop ENTER_I_I {
limm t4, "ULL(-1)", dataSize=8 limm t4, "ULL(-1)", dataSize=8
topOfLoop: topOfLoop:
ld t5, ss, [dsz, t4, rbp] ld_stack t5, ss, [dsz, t4, rbp]
st t5, ss, [1, t0, rsp], "-env.dataSize" st_stack t5, ss, [1, t0, rsp], "-env.dataSize"
subi rsp, rsp, ssz subi rsp, rsp, ssz
# If we're not done yet, loop # If we're not done yet, loop
@ -183,7 +183,7 @@ topOfLoop:
bottomOfLoop: bottomOfLoop:
# Push the old rbp onto the stack # Push the old rbp onto the stack
st t6, ss, [1, t0, rsp], "-env.dataSize" st_stack t6, ss, [1, t0, rsp], "-env.dataSize"
subi rsp, rsp, ssz subi rsp, rsp, ssz
skipLoop: skipLoop:

View file

@ -315,7 +315,8 @@ def template MicroLdStSplitOpConstructor {{
let {{ let {{
class LdStOp(X86Microop): class LdStOp(X86Microop):
def __init__(self, data, segment, addr, disp, def __init__(self, data, segment, addr, disp,
dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec): dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec,
implicitStack):
self.data = data self.data = data
[self.scale, self.index, self.base] = addr [self.scale, self.index, self.base] = addr
self.disp = disp self.disp = disp
@ -331,6 +332,9 @@ let {{
self.instFlags += " | (1ULL << StaticInst::IsDataPrefetch)" self.instFlags += " | (1ULL << StaticInst::IsDataPrefetch)"
if nonSpec: if nonSpec:
self.instFlags += " | (1ULL << StaticInst::IsNonSpeculative)" self.instFlags += " | (1ULL << StaticInst::IsNonSpeculative)"
# For implicit stack operations, we should use *not* use the
# alternative addressing mode for loads/stores if the prefix is set
if not implicitStack:
self.memFlags += " | (machInst.legacy.addr ? " + \ self.memFlags += " | (machInst.legacy.addr ? " + \
"(AddrSizeFlagBit << FlagShift) : 0)" "(AddrSizeFlagBit << FlagShift) : 0)"
@ -351,7 +355,8 @@ let {{
class BigLdStOp(X86Microop): class BigLdStOp(X86Microop):
def __init__(self, data, segment, addr, disp, def __init__(self, data, segment, addr, disp,
dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec): dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec,
implicitStack):
self.data = data self.data = data
[self.scale, self.index, self.base] = addr [self.scale, self.index, self.base] = addr
self.disp = disp self.disp = disp
@ -367,6 +372,9 @@ let {{
self.instFlags += " | (1ULL << StaticInst::IsDataPrefetch)" self.instFlags += " | (1ULL << StaticInst::IsDataPrefetch)"
if nonSpec: if nonSpec:
self.instFlags += " | (1ULL << StaticInst::IsNonSpeculative)" self.instFlags += " | (1ULL << StaticInst::IsNonSpeculative)"
# For implicit stack operations, we should use *not* use the
# alternative addressing mode for loads/stores if the prefix is set
if not implicitStack:
self.memFlags += " | (machInst.legacy.addr ? " + \ self.memFlags += " | (machInst.legacy.addr ? " + \
"(AddrSizeFlagBit << FlagShift) : 0)" "(AddrSizeFlagBit << FlagShift) : 0)"
@ -395,9 +403,11 @@ let {{
class LdStSplitOp(LdStOp): class LdStSplitOp(LdStOp):
def __init__(self, data, segment, addr, disp, def __init__(self, data, segment, addr, disp,
dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec): dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec,
implicitStack):
super(LdStSplitOp, self).__init__(0, segment, addr, disp, super(LdStSplitOp, self).__init__(0, segment, addr, disp,
dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec) dataSize, addressSize, baseFlags, atCPL0, prefetch, nonSpec,
implicitStack)
(self.dataLow, self.dataHi) = data (self.dataLow, self.dataHi) = data
def getAllocator(self, microFlags): def getAllocator(self, microFlags):
@ -435,7 +445,8 @@ let {{
calculateEA = 'EA = SegBase + ' + segmentEAExpr calculateEA = 'EA = SegBase + ' + segmentEAExpr
def defineMicroLoadOp(mnemonic, code, bigCode='', def defineMicroLoadOp(mnemonic, code, bigCode='',
mem_flags="0", big=True, nonSpec=False): mem_flags="0", big=True, nonSpec=False,
implicitStack=False):
global header_output global header_output
global decoder_output global decoder_output
global exec_output global exec_output
@ -460,17 +471,26 @@ let {{
exec_output += MicroLoadInitiateAcc.subst(iop) exec_output += MicroLoadInitiateAcc.subst(iop)
exec_output += MicroLoadCompleteAcc.subst(iop) exec_output += MicroLoadCompleteAcc.subst(iop)
if implicitStack:
# For instructions that implicitly access the stack, the address
# size is the same as the stack segment pointer size, not the
# address size if specified by the instruction prefix
addressSize = "env.stackSize"
else:
addressSize = "env.addressSize"
base = LdStOp base = LdStOp
if big: if big:
base = BigLdStOp base = BigLdStOp
class LoadOp(base): class LoadOp(base):
def __init__(self, data, segment, addr, disp = 0, def __init__(self, data, segment, addr, disp = 0,
dataSize="env.dataSize", dataSize="env.dataSize",
addressSize="env.addressSize", addressSize=addressSize,
atCPL0=False, prefetch=False, nonSpec=nonSpec): atCPL0=False, prefetch=False, nonSpec=nonSpec,
implicitStack=implicitStack):
super(LoadOp, self).__init__(data, segment, addr, super(LoadOp, self).__init__(data, segment, addr,
disp, dataSize, addressSize, mem_flags, disp, dataSize, addressSize, mem_flags,
atCPL0, prefetch, nonSpec) atCPL0, prefetch, nonSpec, implicitStack)
self.className = Name self.className = Name
self.mnemonic = name self.mnemonic = name
@ -478,6 +498,10 @@ let {{
defineMicroLoadOp('Ld', 'Data = merge(Data, Mem, dataSize);', defineMicroLoadOp('Ld', 'Data = merge(Data, Mem, dataSize);',
'Data = Mem & mask(dataSize * 8);') 'Data = Mem & mask(dataSize * 8);')
# Load micro-op used in macro-ops that implicitly access the stack
defineMicroLoadOp('Ld_stack', 'Data = merge(Data, Mem, dataSize);',
'Data = Mem & mask(dataSize * 8);',
implicitStack=True)
defineMicroLoadOp('Ldst', 'Data = merge(Data, Mem, dataSize);', defineMicroLoadOp('Ldst', 'Data = merge(Data, Mem, dataSize);',
'Data = Mem & mask(dataSize * 8);', 'Data = Mem & mask(dataSize * 8);',
'(StoreCheck << FlagShift)') '(StoreCheck << FlagShift)')
@ -544,10 +568,11 @@ let {{
def __init__(self, data, segment, addr, disp = 0, def __init__(self, data, segment, addr, disp = 0,
dataSize="env.dataSize", dataSize="env.dataSize",
addressSize="env.addressSize", addressSize="env.addressSize",
atCPL0=False, prefetch=False, nonSpec=nonSpec): atCPL0=False, prefetch=False, nonSpec=nonSpec,
implicitStack=False):
super(LoadOp, self).__init__(data, segment, addr, super(LoadOp, self).__init__(data, segment, addr,
disp, dataSize, addressSize, mem_flags, disp, dataSize, addressSize, mem_flags,
atCPL0, prefetch, nonSpec) atCPL0, prefetch, nonSpec, implicitStack)
self.className = Name self.className = Name
self.mnemonic = name self.mnemonic = name
@ -574,7 +599,8 @@ let {{
'(StoreCheck << FlagShift) | Request::LOCKED_RMW', '(StoreCheck << FlagShift) | Request::LOCKED_RMW',
nonSpec=True) nonSpec=True)
def defineMicroStoreOp(mnemonic, code, completeCode="", mem_flags="0"): def defineMicroStoreOp(mnemonic, code, completeCode="", mem_flags="0",
implicitStack=False):
global header_output global header_output
global decoder_output global decoder_output
global exec_output global exec_output
@ -594,20 +620,31 @@ let {{
exec_output += MicroStoreInitiateAcc.subst(iop) exec_output += MicroStoreInitiateAcc.subst(iop)
exec_output += MicroStoreCompleteAcc.subst(iop) exec_output += MicroStoreCompleteAcc.subst(iop)
if implicitStack:
# For instructions that implicitly access the stack, the address
# size is the same as the stack segment pointer size, not the
# address size if specified by the instruction prefix
addressSize = "env.stackSize"
else:
addressSize = "env.addressSize"
class StoreOp(LdStOp): class StoreOp(LdStOp):
def __init__(self, data, segment, addr, disp = 0, def __init__(self, data, segment, addr, disp = 0,
dataSize="env.dataSize", dataSize="env.dataSize",
addressSize="env.addressSize", addressSize=addressSize,
atCPL0=False, nonSpec=False): atCPL0=False, nonSpec=False, implicitStack=implicitStack):
super(StoreOp, self).__init__(data, segment, addr, disp, super(StoreOp, self).__init__(data, segment, addr, disp,
dataSize, addressSize, mem_flags, atCPL0, False, dataSize, addressSize, mem_flags, atCPL0, False,
nonSpec) nonSpec, implicitStack)
self.className = Name self.className = Name
self.mnemonic = name self.mnemonic = name
microopClasses[name] = StoreOp microopClasses[name] = StoreOp
defineMicroStoreOp('St', 'Mem = pick(Data, 2, dataSize);') defineMicroStoreOp('St', 'Mem = pick(Data, 2, dataSize);')
# Store micro-op used in macro-ops that implicitly access the stack
defineMicroStoreOp('St_stack', 'Mem = pick(Data, 2, dataSize);',
implicitStack=True)
defineMicroStoreOp('Stul', 'Mem = pick(Data, 2, dataSize);', defineMicroStoreOp('Stul', 'Mem = pick(Data, 2, dataSize);',
mem_flags="Request::LOCKED_RMW") mem_flags="Request::LOCKED_RMW")
@ -655,10 +692,10 @@ let {{
def __init__(self, data, segment, addr, disp = 0, def __init__(self, data, segment, addr, disp = 0,
dataSize="env.dataSize", dataSize="env.dataSize",
addressSize="env.addressSize", addressSize="env.addressSize",
atCPL0=False, nonSpec=False): atCPL0=False, nonSpec=False, implicitStack=False):
super(StoreOp, self).__init__(data, segment, addr, disp, super(StoreOp, self).__init__(data, segment, addr, disp,
dataSize, addressSize, mem_flags, atCPL0, False, dataSize, addressSize, mem_flags, atCPL0, False,
nonSpec) nonSpec, implicitStack)
self.className = Name self.className = Name
self.mnemonic = name self.mnemonic = name
@ -694,7 +731,7 @@ let {{
def __init__(self, data, segment, addr, disp = 0, def __init__(self, data, segment, addr, disp = 0,
dataSize="env.dataSize", addressSize="env.addressSize"): dataSize="env.dataSize", addressSize="env.addressSize"):
super(LeaOp, self).__init__(data, segment, addr, disp, super(LeaOp, self).__init__(data, segment, addr, disp,
dataSize, addressSize, "0", False, False, False) dataSize, addressSize, "0", False, False, False, False)
self.className = "Lea" self.className = "Lea"
self.mnemonic = "lea" self.mnemonic = "lea"
@ -715,7 +752,7 @@ let {{
addressSize="env.addressSize"): addressSize="env.addressSize"):
super(TiaOp, self).__init__("InstRegIndex(NUM_INTREGS)", segment, super(TiaOp, self).__init__("InstRegIndex(NUM_INTREGS)", segment,
addr, disp, dataSize, addressSize, "0", False, False, addr, disp, dataSize, addressSize, "0", False, False,
False) False, False)
self.className = "Tia" self.className = "Tia"
self.mnemonic = "tia" self.mnemonic = "tia"
@ -727,10 +764,9 @@ let {{
addressSize="env.addressSize", atCPL0=False): addressSize="env.addressSize", atCPL0=False):
super(CdaOp, self).__init__("InstRegIndex(NUM_INTREGS)", segment, super(CdaOp, self).__init__("InstRegIndex(NUM_INTREGS)", segment,
addr, disp, dataSize, addressSize, "Request::NO_ACCESS", addr, disp, dataSize, addressSize, "Request::NO_ACCESS",
atCPL0, False, False) atCPL0, False, False, False)
self.className = "Cda" self.className = "Cda"
self.mnemonic = "cda" self.mnemonic = "cda"
microopClasses["cda"] = CdaOp microopClasses["cda"] = CdaOp
}}; }};