Stack overflow checking with stack probes.
This adds a libcall name, a calling convention, and settings for emitting stack probes, and implements them for x86 system_v ABIs.
This commit is contained in:
@@ -591,12 +591,25 @@ stack_check = Instruction(
|
||||
|
||||
The global variable must be accessible and naturally aligned for a
|
||||
pointer-sized value.
|
||||
|
||||
`stack_check` is an alternative way to detect stack overflow, when using
|
||||
a calling convention that doesn't perform stack probes.
|
||||
""",
|
||||
ins=GV, can_trap=True)
|
||||
|
||||
delta = Operand('delta', Int)
|
||||
adjust_sp_down = Instruction(
|
||||
'adjust_sp_down', r"""
|
||||
Subtracts ``delta`` offset value from the stack pointer register.
|
||||
|
||||
This instruction is used to adjust the stack pointer by a dynamic amount.
|
||||
""",
|
||||
ins=(delta,),
|
||||
other_side_effects=True)
|
||||
|
||||
StackOffset = Operand('Offset', imm64, 'Offset from current stack pointer')
|
||||
adjust_sp_imm = Instruction(
|
||||
'adjust_sp_imm', r"""
|
||||
adjust_sp_up_imm = Instruction(
|
||||
'adjust_sp_up_imm', r"""
|
||||
Adds ``Offset`` immediate offset value to the stack pointer register.
|
||||
|
||||
This instruction is used to adjust the stack pointer, primarily in function
|
||||
@@ -606,6 +619,19 @@ adjust_sp_imm = Instruction(
|
||||
ins=(StackOffset,),
|
||||
other_side_effects=True)
|
||||
|
||||
StackOffset = Operand('Offset', imm64, 'Offset from current stack pointer')
|
||||
adjust_sp_down_imm = Instruction(
|
||||
'adjust_sp_down_imm', r"""
|
||||
Subtracts ``Offset`` immediate offset value from the stack pointer
|
||||
register.
|
||||
|
||||
This instruction is used to adjust the stack pointer, primarily in function
|
||||
prologues and epilogues. ``Offset`` is constrained to the size of a signed
|
||||
32-bit integer.
|
||||
""",
|
||||
ins=(StackOffset,),
|
||||
other_side_effects=True)
|
||||
|
||||
f = Operand('f', iflags)
|
||||
|
||||
ifcmp_sp = Instruction(
|
||||
|
||||
@@ -38,17 +38,27 @@ call_conv = EnumSetting(
|
||||
- system_v: System V-style convention used on many platforms
|
||||
- fastcall: Windows "fastcall" convention, also used for x64 and ARM
|
||||
- baldrdash: SpiderMonkey WebAssembly convention
|
||||
- probestack: specialized convention for the probestack function
|
||||
|
||||
The default calling convention may be overridden by individual
|
||||
functions.
|
||||
""",
|
||||
'fast', 'cold', 'system_v', 'fastcall', 'baldrdash')
|
||||
'fast', 'cold', 'system_v', 'fastcall', 'baldrdash', 'probestack')
|
||||
|
||||
# Note that Cretonne doesn't currently need an is_pie flag, because PIE is just
|
||||
# PIC where symbols can't be pre-empted, which can be expressed with the
|
||||
# `colocated` flag on external functions and global variables.
|
||||
is_pic = BoolSetting("Enable Position-Independent Code generation")
|
||||
|
||||
colocated_libcalls = BoolSetting(
|
||||
"""
|
||||
Use colocated libcalls.
|
||||
|
||||
Generate code that assumes that libcalls can be declared "colocated",
|
||||
meaning they will be defined along with the current function, such that
|
||||
they can use more efficient addressing.
|
||||
""")
|
||||
|
||||
return_at_end = BoolSetting(
|
||||
"""
|
||||
Generate functions with at most a single return instruction at the
|
||||
@@ -115,4 +125,31 @@ allones_funcaddrs = BoolSetting(
|
||||
Emit not-yet-relocated function addresses as all-ones bit patterns.
|
||||
""")
|
||||
|
||||
#
|
||||
# Stack probing options.
|
||||
#
|
||||
probestack_enabled = BoolSetting(
|
||||
"""
|
||||
Enable the use of stack probes, for calling conventions which support
|
||||
this functionality.
|
||||
""",
|
||||
default=True)
|
||||
|
||||
probestack_func_adjusts_sp = BoolSetting(
|
||||
"""
|
||||
Set this to true of the stack probe function modifies the stack pointer
|
||||
itself.
|
||||
""")
|
||||
|
||||
probestack_size_log2 = NumSetting(
|
||||
"""
|
||||
The log2 of the size of the stack guard region.
|
||||
|
||||
Stack frames larger than this size will have stack overflow checked
|
||||
by calling the probestack function.
|
||||
|
||||
The default is 12, which translates to a size of 4096.
|
||||
""",
|
||||
default=12)
|
||||
|
||||
group.close(globals())
|
||||
|
||||
@@ -136,29 +136,29 @@ for inst, rrr in [
|
||||
(base.band_imm, 4),
|
||||
(base.bor_imm, 1),
|
||||
(base.bxor_imm, 6)]:
|
||||
enc_i32_i64(inst, r.rib, 0x83, rrr=rrr)
|
||||
enc_i32_i64(inst, r.rid, 0x81, rrr=rrr)
|
||||
enc_i32_i64(inst, r.r_ib, 0x83, rrr=rrr)
|
||||
enc_i32_i64(inst, r.r_id, 0x81, rrr=rrr)
|
||||
|
||||
# TODO: band_imm.i64 with an unsigned 32-bit immediate can be encoded as
|
||||
# band_imm.i32. Can even use the single-byte immediate for 0xffff_ffXX masks.
|
||||
|
||||
# Immediate constants.
|
||||
X86_32.enc(base.iconst.i32, *r.puid(0xb8))
|
||||
X86_32.enc(base.iconst.i32, *r.pu_id(0xb8))
|
||||
|
||||
X86_64.enc(base.iconst.i32, *r.puid.rex(0xb8))
|
||||
X86_64.enc(base.iconst.i32, *r.puid(0xb8))
|
||||
X86_64.enc(base.iconst.i32, *r.pu_id.rex(0xb8))
|
||||
X86_64.enc(base.iconst.i32, *r.pu_id(0xb8))
|
||||
# The 32-bit immediate movl also zero-extends to 64 bits.
|
||||
X86_64.enc(base.iconst.i64, *r.puid.rex(0xb8),
|
||||
X86_64.enc(base.iconst.i64, *r.pu_id.rex(0xb8),
|
||||
instp=IsUnsignedInt(UnaryImm.imm, 32))
|
||||
X86_64.enc(base.iconst.i64, *r.puid(0xb8),
|
||||
X86_64.enc(base.iconst.i64, *r.pu_id(0xb8),
|
||||
instp=IsUnsignedInt(UnaryImm.imm, 32))
|
||||
# Sign-extended 32-bit immediate.
|
||||
X86_64.enc(base.iconst.i64, *r.uid.rex(0xc7, rrr=0, w=1))
|
||||
X86_64.enc(base.iconst.i64, *r.u_id.rex(0xc7, rrr=0, w=1))
|
||||
# Finally, the 0xb8 opcode takes an 8-byte immediate with a REX.W prefix.
|
||||
X86_64.enc(base.iconst.i64, *r.puiq.rex(0xb8, w=1))
|
||||
X86_64.enc(base.iconst.i64, *r.pu_iq.rex(0xb8, w=1))
|
||||
|
||||
# bool constants.
|
||||
enc_both(base.bconst.b1, r.puid_bool, 0xb8)
|
||||
enc_both(base.bconst.b1, r.pu_id_bool, 0xb8)
|
||||
|
||||
# Shifts and rotates.
|
||||
# Note that the dynamic shift amount is only masked by 5 or 6 bits; the 8-bit
|
||||
@@ -180,7 +180,7 @@ for inst, rrr in [
|
||||
(base.ishl_imm, 4),
|
||||
(base.ushr_imm, 5),
|
||||
(base.sshr_imm, 7)]:
|
||||
enc_i32_i64(inst, r.rib, 0xc1, rrr=rrr)
|
||||
enc_i32_i64(inst, r.r_ib, 0xc1, rrr=rrr)
|
||||
|
||||
# Population count.
|
||||
X86_32.enc(base.popcnt.i32, *r.urm(0xf3, 0x0f, 0xb8), isap=cfg.use_popcnt)
|
||||
@@ -254,11 +254,21 @@ enc_x86_64(x86.pop.i64, r.popq, 0x58)
|
||||
X86_64.enc(base.copy_special, *r.copysp.rex(0x89, w=1))
|
||||
X86_32.enc(base.copy_special, *r.copysp(0x89))
|
||||
|
||||
# Adjust SP Imm
|
||||
X86_32.enc(base.adjust_sp_imm, *r.adjustsp8(0x83))
|
||||
X86_32.enc(base.adjust_sp_imm, *r.adjustsp32(0x81))
|
||||
X86_64.enc(base.adjust_sp_imm, *r.adjustsp8.rex(0x83, w=1))
|
||||
X86_64.enc(base.adjust_sp_imm, *r.adjustsp32.rex(0x81, w=1))
|
||||
# Adjust SP down by a dynamic value (or up, with a negative operand).
|
||||
X86_32.enc(base.adjust_sp_down.i32, *r.adjustsp(0x29))
|
||||
X86_64.enc(base.adjust_sp_down.i64, *r.adjustsp.rex(0x29, w=1))
|
||||
|
||||
# Adjust SP up by an immediate (or down, with a negative immediate)
|
||||
X86_32.enc(base.adjust_sp_up_imm, *r.adjustsp_ib(0x83))
|
||||
X86_32.enc(base.adjust_sp_up_imm, *r.adjustsp_id(0x81))
|
||||
X86_64.enc(base.adjust_sp_up_imm, *r.adjustsp_ib.rex(0x83, w=1))
|
||||
X86_64.enc(base.adjust_sp_up_imm, *r.adjustsp_id.rex(0x81, w=1))
|
||||
|
||||
# Adjust SP down by an immediate (or up, with a negative immediate)
|
||||
X86_32.enc(base.adjust_sp_down_imm, *r.adjustsp_ib(0x83, rrr=5))
|
||||
X86_32.enc(base.adjust_sp_down_imm, *r.adjustsp_id(0x81, rrr=5))
|
||||
X86_64.enc(base.adjust_sp_down_imm, *r.adjustsp_ib.rex(0x83, rrr=5, w=1))
|
||||
X86_64.enc(base.adjust_sp_down_imm, *r.adjustsp_id.rex(0x81, rrr=5, w=1))
|
||||
|
||||
#
|
||||
# Float loads and stores.
|
||||
@@ -406,11 +416,11 @@ X86_64.enc(base.trapff, r.trapff, 0)
|
||||
# Comparisons
|
||||
#
|
||||
enc_i32_i64(base.icmp, r.icscc, 0x39)
|
||||
enc_i32_i64(base.icmp_imm, r.icsccib, 0x83, rrr=7)
|
||||
enc_i32_i64(base.icmp_imm, r.icsccid, 0x81, rrr=7)
|
||||
enc_i32_i64(base.icmp_imm, r.icscc_ib, 0x83, rrr=7)
|
||||
enc_i32_i64(base.icmp_imm, r.icscc_id, 0x81, rrr=7)
|
||||
enc_i32_i64(base.ifcmp, r.rcmp, 0x39)
|
||||
enc_i32_i64(base.ifcmp_imm, r.rcmpib, 0x83, rrr=7)
|
||||
enc_i32_i64(base.ifcmp_imm, r.rcmpid, 0x81, rrr=7)
|
||||
enc_i32_i64(base.ifcmp_imm, r.rcmp_ib, 0x83, rrr=7)
|
||||
enc_i32_i64(base.ifcmp_imm, r.rcmp_id, 0x81, rrr=7)
|
||||
# TODO: We could special-case ifcmp_imm(x, 0) to TEST(x, x).
|
||||
|
||||
X86_32.enc(base.ifcmp_sp.i32, *r.rcmp_sp(0x39))
|
||||
|
||||
@@ -480,8 +480,8 @@ mulx = TailRecipe(
|
||||
''')
|
||||
|
||||
# XX /n ib with 8-bit immediate sign-extended.
|
||||
rib = TailRecipe(
|
||||
'rib', BinaryImm, size=2, ins=GPR, outs=0,
|
||||
r_ib = TailRecipe(
|
||||
'r_ib', BinaryImm, size=2, ins=GPR, outs=0,
|
||||
instp=IsSignedInt(BinaryImm.imm, 8),
|
||||
emit='''
|
||||
PUT_OP(bits, rex1(in_reg0), sink);
|
||||
@@ -491,8 +491,8 @@ rib = TailRecipe(
|
||||
''')
|
||||
|
||||
# XX /n id with 32-bit immediate sign-extended.
|
||||
rid = TailRecipe(
|
||||
'rid', BinaryImm, size=5, ins=GPR, outs=0,
|
||||
r_id = TailRecipe(
|
||||
'r_id', BinaryImm, size=5, ins=GPR, outs=0,
|
||||
instp=IsSignedInt(BinaryImm.imm, 32),
|
||||
emit='''
|
||||
PUT_OP(bits, rex1(in_reg0), sink);
|
||||
@@ -502,8 +502,8 @@ rid = TailRecipe(
|
||||
''')
|
||||
|
||||
# XX /n id with 32-bit immediate sign-extended. UnaryImm version.
|
||||
uid = TailRecipe(
|
||||
'uid', UnaryImm, size=5, ins=(), outs=GPR,
|
||||
u_id = TailRecipe(
|
||||
'u_id', UnaryImm, size=5, ins=(), outs=GPR,
|
||||
instp=IsSignedInt(UnaryImm.imm, 32),
|
||||
emit='''
|
||||
PUT_OP(bits, rex1(out_reg0), sink);
|
||||
@@ -513,8 +513,8 @@ uid = TailRecipe(
|
||||
''')
|
||||
|
||||
# XX+rd id unary with 32-bit immediate. Note no recipe predicate.
|
||||
puid = TailRecipe(
|
||||
'puid', UnaryImm, size=4, ins=(), outs=GPR,
|
||||
pu_id = TailRecipe(
|
||||
'pu_id', UnaryImm, size=4, ins=(), outs=GPR,
|
||||
emit='''
|
||||
// The destination register is encoded in the low bits of the opcode.
|
||||
// No ModR/M.
|
||||
@@ -524,8 +524,8 @@ puid = TailRecipe(
|
||||
''')
|
||||
|
||||
# XX+rd id unary with bool immediate. Note no recipe predicate.
|
||||
puid_bool = TailRecipe(
|
||||
'puid_bool', UnaryBool, size=4, ins=(), outs=GPR,
|
||||
pu_id_bool = TailRecipe(
|
||||
'pu_id_bool', UnaryBool, size=4, ins=(), outs=GPR,
|
||||
emit='''
|
||||
// The destination register is encoded in the low bits of the opcode.
|
||||
// No ModR/M.
|
||||
@@ -535,8 +535,8 @@ puid_bool = TailRecipe(
|
||||
''')
|
||||
|
||||
# XX+rd iq unary with 64-bit immediate.
|
||||
puiq = TailRecipe(
|
||||
'puiq', UnaryImm, size=8, ins=(), outs=GPR,
|
||||
pu_iq = TailRecipe(
|
||||
'pu_iq', UnaryImm, size=8, ins=(), outs=GPR,
|
||||
emit='''
|
||||
PUT_OP(bits | (out_reg0 & 7), rex1(out_reg0), sink);
|
||||
let imm: i64 = imm.into();
|
||||
@@ -564,8 +564,15 @@ copysp = TailRecipe(
|
||||
modrm_rr(dst, src, sink);
|
||||
''')
|
||||
|
||||
adjustsp8 = TailRecipe(
|
||||
'adjustsp8', UnaryImm, size=2, ins=(), outs=(),
|
||||
adjustsp = TailRecipe(
|
||||
'adjustsp', Unary, size=1, ins=(GPR), outs=(),
|
||||
emit='''
|
||||
PUT_OP(bits, rex2(RU::rsp.into(), in_reg0), sink);
|
||||
modrm_rr(RU::rsp.into(), in_reg0, sink);
|
||||
''')
|
||||
|
||||
adjustsp_ib = TailRecipe(
|
||||
'adjustsp_ib', UnaryImm, size=2, ins=(), outs=(),
|
||||
instp=IsSignedInt(UnaryImm.imm, 8),
|
||||
emit='''
|
||||
PUT_OP(bits, rex1(RU::rsp.into()), sink);
|
||||
@@ -574,8 +581,8 @@ adjustsp8 = TailRecipe(
|
||||
sink.put1(imm as u8);
|
||||
''')
|
||||
|
||||
adjustsp32 = TailRecipe(
|
||||
'adjustsp32', UnaryImm, size=5, ins=(), outs=(),
|
||||
adjustsp_id = TailRecipe(
|
||||
'adjustsp_id', UnaryImm, size=5, ins=(), outs=(),
|
||||
instp=IsSignedInt(UnaryImm.imm, 32),
|
||||
emit='''
|
||||
PUT_OP(bits, rex1(RU::rsp.into()), sink);
|
||||
@@ -1217,8 +1224,8 @@ fcmp = TailRecipe(
|
||||
''')
|
||||
|
||||
# XX /n, MI form with imm8.
|
||||
rcmpib = TailRecipe(
|
||||
'rcmpib', BinaryImm, size=2, ins=GPR, outs=FLAG.rflags,
|
||||
rcmp_ib = TailRecipe(
|
||||
'rcmp_ib', BinaryImm, size=2, ins=GPR, outs=FLAG.rflags,
|
||||
instp=IsSignedInt(BinaryImm.imm, 8),
|
||||
emit='''
|
||||
PUT_OP(bits, rex1(in_reg0), sink);
|
||||
@@ -1228,8 +1235,8 @@ rcmpib = TailRecipe(
|
||||
''')
|
||||
|
||||
# XX /n, MI form with imm32.
|
||||
rcmpid = TailRecipe(
|
||||
'rcmpid', BinaryImm, size=5, ins=GPR, outs=FLAG.rflags,
|
||||
rcmp_id = TailRecipe(
|
||||
'rcmp_id', BinaryImm, size=5, ins=GPR, outs=FLAG.rflags,
|
||||
instp=IsSignedInt(BinaryImm.imm, 32),
|
||||
emit='''
|
||||
PUT_OP(bits, rex1(in_reg0), sink);
|
||||
@@ -1401,8 +1408,8 @@ icscc = TailRecipe(
|
||||
modrm_rr(out_reg0, 0, sink);
|
||||
''')
|
||||
|
||||
icsccib = TailRecipe(
|
||||
'icsccib', IntCompareImm, size=2 + 3, ins=GPR, outs=ABCD,
|
||||
icscc_ib = TailRecipe(
|
||||
'icscc_ib', IntCompareImm, size=2 + 3, ins=GPR, outs=ABCD,
|
||||
instp=IsSignedInt(IntCompareImm.imm, 8),
|
||||
emit='''
|
||||
// Comparison instruction.
|
||||
@@ -1429,8 +1436,8 @@ icsccib = TailRecipe(
|
||||
modrm_rr(out_reg0, 0, sink);
|
||||
''')
|
||||
|
||||
icsccid = TailRecipe(
|
||||
'icsccid', IntCompareImm, size=5 + 3, ins=GPR, outs=ABCD,
|
||||
icscc_id = TailRecipe(
|
||||
'icscc_id', IntCompareImm, size=5 + 3, ins=GPR, outs=ABCD,
|
||||
instp=IsSignedInt(IntCompareImm.imm, 32),
|
||||
emit='''
|
||||
// Comparison instruction.
|
||||
|
||||
Reference in New Issue
Block a user