From: Indu Bhagat <indu.bhagat@oracle.com>
To: Jan Beulich <jbeulich@suse.com>
Cc: binutils@sourceware.org
Subject: Re: [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm
Date: Mon, 18 Dec 2023 23:07:32 -0800 [thread overview]
Message-ID: <6a6c837a-46d5-4fe1-945d-45621834631c@oracle.com> (raw)
In-Reply-To: <a21de675-2aef-4a02-bbc6-c72da9a36f96@suse.com>
On 12/10/23 23:59, Jan Beulich wrote:
> On 10.12.2023 11:22, Indu Bhagat wrote:
>> On 11/2/23 04:39, Jan Beulich wrote:
>>> On 02.11.2023 09:15, Indu Bhagat wrote:
>>>> On 10/31/23 07:10, Jan Beulich wrote:
>>>>> On 30.10.2023 17:51, Indu Bhagat wrote:
>>>>>> gas/
>>>>>> * Makefile.am: Add new files.
>>>>>> * Makefile.in: Regenerated.
>>>>>> * as.c (defined): Handle documentation and listing option for
>>>>>> ginsns and SCFI.
>>>>>> * config/obj-elf.c (obj_elf_size): Invoke ginsn_data_end.
>>>>>> (obj_elf_type): Invoke ginsn_data_begin.
>>>>>> * config/tc-i386.c (ginsn_new): New functionality to generate
>>>>>> ginsns.
>>>>>> (x86_scfi_callee_saved_p): New function.
>>>>>> (ginsn_dw2_regnum): Likewise.
>>>>>> (ginsn_set_where): Likewise.
>>>>>> (x86_ginsn_alu): Likewise.
>>>>>> (x86_ginsn_move): Likewise.
>>>>>> (x86_ginsn_lea): Likewise.
>>>>>> (x86_ginsn_jump): Likewise.
>>>>>> (x86_ginsn_jump_cond): Likewise.
>>>>>> (md_assemble): Invoke ginsn_new.
>>>>>> (s_insn): Likewise.
>>>>>> (i386_target_format): Add hard error for usage of --scfi with non AMD64 ABIs.
>>>>>> * config/tc-i386.h (TARGET_USE_GINSN): New definition.
>>>>>> (TARGET_USE_SCFI): Likewise.
>>>>>> (SCFI_NUM_REGS): Likewise.
>>>>>> (REG_FP): Likewise.
>>>>>> (REG_SP): Likewise.
>>>>>> (SCFI_INIT_CFA_OFFSET): Likewise.
>>>>>> (SCFI_CALLEE_SAVED_REG_P): Likewise.
>>>>>> (x86_scfi_callee_saved_p): Likewise.
>>>>>
>>>>> For this arch-specific code there's a fundamental question of maintenance
>>>>> cost here: It doesn't look very reasonable to me to demand of people adding
>>>>> support for new ISA extensions to also take into consideration all of this
>>>>> new machinery. Yet if any such addition affects SCFI, things will go out-
>>>>> of-sync without updating this code as well. It may not be very often that
>>>>> such updating is necessary, but right now there's APX work in progress
>>>>> which I expect will need dealing with here as well.
>>>>>
>>>>
>>>> I understand your concerns. FWIW, for SCFI, we will need to translate
>>>> only a subset of instructions into ginsns (change of flow insns,
>>>> save/restore and arith on REG_SP/REG_FP).
>>>
>>> Considering what you say further down regarding untraceability, I'm afraid
>>> you will need to care about all insns which have SP/FP as their destination.
>>>
>>
>> Yeah, All insns which have SP/FP as their destination must be seen by
>> the SCFI machinery. If any instruction which has SP/FP as destination is
>> missed, and the user has specified a --scfi command line argument, my
>> thinking is that GAS should spit out an error or a diagnostic.
>>
>> That said, the current implementation handles the most commonly used
>> instructions to manage stack pointer for dynamic and static stack
>> allocation. For unhandled instructions which end up _possibly_ affecting
>> REG_FP/REG_SP (I say _possibly_ because at this time I check both
>> i.op[0].regs and i.op[1].regs for REG_SP/REG_FP), there is a warning:
>> "Warning: SCFI: unhandled op 0x63 may cause incorrect CFI"
>
> Yet neither i.op[0] nor i.op[1] may represent the destination register of
> an insn, when there are more than 2 operands.
>
I have now handled this case properly I think (will include this in V4).
Thanks for your comment below.
>>>>>> +static ginsnS *
>>>>>> +x86_ginsn_alu (i386_insn insn, symbolS *insn_end_sym)
>>>>>> +{
>>>>>> + offsetT src_imm;
>>>>>> + uint32_t dw2_regnum;
>>>>>> + enum ginsn_src_type src_type;
>>>>>> + enum ginsn_dst_type dst_type;
>>>>>> + ginsnS *ginsn = NULL;
>>>>>> +
>>>>>> + /* FIXME - create ginsn for REG_SP target only ? */
>>>>>> + /* Map for insn.tm.extension_opcode
>>>>>> + 000 ADD 100 AND
>>>>>> + 001 OR 101 SUB
>>>>>> + 010 ADC 110 XOR
>>>>>> + 011 SBB 111 CMP */
>>>>>> +
>>>>>> + /* add/sub imm, %reg.
>>>>>> + and imm, %reg only at this time for SCFI. */
>>>>>> + if (!(insn.tm.extension_opcode == 0
>>>>>> + || insn.tm.extension_opcode == 4
>>>>>> + || insn.tm.extension_opcode == 5))
>>>>>> + return ginsn;
>>>>>
>>>>> Why is AND permitted, but OR/XOR aren't?
>>>>>
>>>>> Also this is about ALU insns with immediate operands only, yet that
>>>>> fact isn't reflected in the function name.
>>>>>
>>>>
>>>> OR/XOR should be handled. I will fix this.
>>>>
>>
>> Spoke too soon. I remembered later why OR/XOR is not handled but AND is
>> (I have now added a code comment around this).
>>
>> An OR/XOR on REG_SP/REG_FP as destination makes the destination
>> untraceable. So these operations are skipped here. At a later point
>> (towards the end of x86_ginsn_new): the ginsn generation machinery will
>> complain about these OR/XOR as "unhandled opcode for SCFI" and bail out
>> if destination was REG_SP/REG_FP.
>>
>> On that note, AND operation also makes REG_SP/REG_FP untraceable, but
>> they are being generated currently. This is because supporting DRAP
>> pattern is something we plan to look into. Currently these are
>> generated, but the SCFI machinery later bails out with a message "Error:
>> SCFI: unsupported stack manipulation pattern"
>
> But why would you need special handling just to mark a register untraceable?
> Generic code merely looking at the destination register ought to be enough?
>
Yes, now a ginsn on type GINSN_TYPE_OTHER with destination REG_SP/REG_FP
as applicable is generated. The SCFI machinery then bails out if this
ginsn makes REG_FP untraceable, when REG_FP based CFA tracking is
active. If CFA tracking is _not_ REG_FP based, it is fine to manipulate
REG_FP in a untraceable way.
>>>>>> +static ginsnS *
>>>>>> +x86_ginsn_lea (i386_insn insn, symbolS *insn_end_sym)
>>>>>> +{
>>>>>> + offsetT src_disp = 0;
>>>>>> + ginsnS *ginsn = NULL;
>>>>>> + uint32_t base_reg;
>>>>>> + uint32_t index_reg;
>>>>>> + offsetT index_scale;
>>>>>> + uint32_t dst_reg;
>>>>>> +
>>>>>> + if (!insn.index_reg && !insn.base_reg)
>>>>>> + {
>>>>>> + /* lea symbol, %rN. */
>>>>>> + dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>>>>> + /* FIXME - Skip encoding information about the symbol.
>>>>>> + This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
>>>>>> + GINSN_GEN_SCFI. */
>>>>>> + ginsn = ginsn_new_mov (insn_end_sym, false,
>>>>>> + GINSN_SRC_IMM, 0xf /* arbitrary const. */, 0,
>>>>>> + GINSN_DST_REG, dst_reg, 0);
>>>>>> + }
>>>>>> + else if (insn.base_reg && !insn.index_reg)
>>>>>> + {
>>>>>> + /* lea -0x2(%base),%dst. */
>>>>>> + base_reg = ginsn_dw2_regnum (insn.base_reg);
>>>>>> + dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
>>>>>> +
>>>>>> + if (insn.disp_operands)
>>>>>> + src_disp = insn.op[0].disps->X_add_number;
>>>>>
>>>>> What if the displacement expression is other than O_constant?
>>>>>
>>>>
>>>> For SCFI, a "complex" lea instruction will imply some untraceable change
>>>> to the destination reg. For SCFI, the extent of untraceable change is
>>>> not of interest, hence, in general, some information loss in ginsn is
>>>> tolerable.
>>>
>>> As a fundamental request: For anything that's of no interest, can you
>>> please try to cover this with as little (and thus clear) code as possible?
>>> No taking apart of sub-cases when those are indifferent in the end anyway.
>>>
>>
>> In general, this is something I have battled with on and off. In the end
>> I chose the principle "Encode as precise ginsn as possible given its
>> current representation". I have tried to follow this mostly
>> (x86_ginsn_lea is the known outlier). For all cases known to be
>> imprecise, it is my intention to have them marked with
>> TBD_GINSN_INFO_LOSS etc. And to not have too many of those..
>>
>> If I leave out more sub-cases (elsewhere) because they are indifferent
>> in the end for SCFI at this time, two problems:
>> - More of the currently generated ginsn will look imprecise.
>> - More code will need to be revisited when there is other ginsn
>> generation modes to be supported.
>
> I can see your view. Still there's the other view of code not really
> being needed right now potentially also net being in the exact shape it
> may be needed down the road, at which point it would similarly need
> touching again.
>
>>>>>> +{
>>>>>> + uint16_t opcode;
>>>>>> + uint32_t dw2_regnum;
>>>>>> + uint32_t src2_dw2_regnum;
>>>>>> + int32_t gdisp = 0;
>>>>>> + ginsnS *ginsn = NULL;
>>>>>> + ginsnS *ginsn_next = NULL;
>>>>>> + ginsnS *ginsn_last = NULL;
>>>>>> +
>>>>>> + /* FIXME - Need a way to check whether the decoding is sane. The specific
>>>>>> + checks around i.tm.opcode_space were added as issues were seen. Likely
>>>>>> + insufficient. */
>>>>>
>>>>> Furthermore opcode encoding space (SPACE_...) need to be taken into
>>>>> account in all cases.
>>>>>
>>>>>> + /* Currently supports generation of selected ginsns, sufficient for
>>>>>> + the use-case of SCFI only. To remove this condition will require
>>>>>> + work on this target-specific process of creation of ginsns. Some
>>>>>> + of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
>>>>>> + examples. */
>>>>>> + if (gmode != GINSN_GEN_SCFI)
>>>>>> + return ginsn;
>>>>>> +
>>>>>> + opcode = i.tm.base_opcode;
>>>>>> +
>>>>>> + switch (opcode)
>>>>>> + {
>>>>>> + case 0x1:
>>>>>> + /* add reg, reg. */
>>>>>> + dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>>>>>
>>>>> You don't care about opcode 0 (byte operation). Then what about 16-bit
>>>>> operand size? Or, since we're talking of a 64-bit-ABI-only feature,
>>>>> even 32-bit operand size?
>>>>>
>>>>
>>>> Operand size in some cases does not affect SCFI. So we dont keep
>>>> information about operand sizes. The case that I should handle operand
>>>> size is when they are pushed to / popped from stack.
>>>
>>> So you care about recognizing when %rbp is overwritten by an insn. And
>>> you also care about the same for %ebp and %bp. In that sense operand
>>> size may indeed be unnecessary to determine. Except that then you also
>>> want to deal with byte operations, i.e. %bpl being the destination.
>>>
>>
>> I have adjusted the ginsn_dw2_regnum to handle correctly 8-bit register
>> usages so that any writes to REG_SP/REG_FP are detected. I also added a
>> new test "ginsn-dw2-regnum" to exercise this API.
>>
>>>>> Also what about opcode 0x3?
>>>>
>>>> LSL instruction ? It doesnt look like it can affect DWARF CFI of a
>>>> function. Please correct me if this is wrong.
>>>
>>> LSL is 0f03, i.e. opcode 0x3 in SPACE_0F. What my comment was about is
>>> ADD with inversed operands (ModRM.rm soure, ModRM.reg destination). All
>>> four of these flavors of ADD are covered by a single template, using the
>>> D and W attributes to establish opcode bits 0 and 1 based on actual
>>> operands (and possibly pseudo-prefixes).
>>>
>>
>> I added handling for 0x3 ADD.
>>
>> Can you elaborate on the "using the D and W attributes to establish
>> opcode bits 0 and 1 based on actual operands (and possibly
>> pseudo-prefixes)." a bit ?
>
> Well, reg <-> reg/mem ADDs (taking the specific example) come in two
> forms: ModR/M.reg reprsenting the source and the same representing the
> destination. They're represented by a single opcode template, with D
> indicating that both directions can be used.
>
> Similarly there are multiple operand sizes supported by several such
> templates, with W indicating that both byte nad word/dword/qword
> operation is possible to encode by OR-ing in a single opcode bit.
>
> For your purposes, W is likely of no direct interest. D may be, yet
> then I take it that you work on actual operands, not on what templates
> permit. In actual operands, destination (due to following to AT&T
> syntax layout in the internal representation) is last at all stages of
> assembly (for Intel syntax input after swapping operands, of course).
>
Thanks. We will now take the last reg operand, i.op[i.operands -
1].regs (after checks on i.operands and i.reg_operands) as destination
and if the register is REG_SP/REG_FP, we generate GINSN_TYPE_OTHER with
the appropriate destination register.
I will include this change in a V4 posting.
>>>>>> + dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
>>>>>> + /* push fs / push gs. */
>>>>>> + ginsn = ginsn_new_sub (insn_end_sym, false,
>>>>>> + GINSN_SRC_REG, REG_SP, 0,
>>>>>> + GINSN_SRC_IMM, 0, 8,
>>>>>
>>>>> Note how the 8 here assumes flag_code == CODE_64BIT. (You further assume
>>>>> no operand size override here.)
>>>>>
>>>>
>>>> Hmm. Yes, I do assume no operand size override here. I will need to
>>>> understand how to calculate the operand size here. Looks like I need to
>>>> check for instruction prefixes 66H or REX.W. Is there an API in the
>>>> backend that be used for this ?
>>>
>>> i.prefixes[] and (depending on the phase of assembly) i.rex should be
>>> telling you.
>>>
>>
>> I have added checks for prefix 66H to detect 16-bit ops now. IIUC, I
>> dont need to detect REX.W prefix specifically.
>
> I'm not sure. REX.W clear likely means the destination becomes untraceable?
> Whereas REX.W set means to full register is updated (upper half not zeroed)
> and hence it may remain traceable?
I see that Push, Pop instructions do not need the REX prefix. Hence,
checking for 66H to detect 16-bit operand size suffices. Default
operand size is 64-bit in 64-bit mode.
next prev parent reply other threads:[~2023-12-19 7:07 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-30 16:51 [PATCH, V2 00/10] Synthesize " Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 01/10] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 02/10] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 03/10] gas: dw2gencfi: expose a new cfi_set_last_fde API Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 04/10] gas: dw2gencfi: move some tc_* defines to the header file Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 05/10] gas: add new command line option --scfi[=all,none] Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 06/10] gas: scfidw2gen: new functionality to prepapre for SCFI Indu Bhagat
2023-10-31 11:28 ` Jan Beulich
2023-10-31 22:06 ` Indu Bhagat
2023-11-02 10:35 ` Jan Beulich
2023-11-02 16:32 ` Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 07/10] gas: synthesize CFI for hand-written asm Indu Bhagat
2023-10-31 14:10 ` Jan Beulich
2023-11-02 8:15 ` Indu Bhagat
2023-11-02 11:39 ` Jan Beulich
2023-12-10 10:22 ` Indu Bhagat
2023-12-11 7:59 ` Jan Beulich
2023-12-19 7:07 ` Indu Bhagat [this message]
2023-11-02 15:53 ` Jan Beulich
2023-11-04 7:29 ` Indu Bhagat
2023-11-06 11:03 ` Jan Beulich
2023-12-10 10:23 ` Indu Bhagat
2023-12-11 8:02 ` Jan Beulich
2023-10-30 16:51 ` [PATCH, V2 08/10] gas: doc: update documentation for the new listing option Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 09/10] gas: testsuite: add a x86_64 testsuite for SCFI Indu Bhagat
2023-10-31 16:13 ` Jan Beulich
2023-11-01 6:24 ` Indu Bhagat
2023-11-02 12:28 ` Jan Beulich
2023-11-03 5:45 ` Indu Bhagat
2023-10-30 16:51 ` [PATCH, V2 10/10] gas/NEWS: announce the new command line option Indu Bhagat
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=6a6c837a-46d5-4fe1-945d-45621834631c@oracle.com \
--to=indu.bhagat@oracle.com \
--cc=binutils@sourceware.org \
--cc=jbeulich@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).