mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:51:15 +08:00
385 lines
13 KiB
C
385 lines
13 KiB
C
|
/* ginsn.h - GAS instruction representation.
|
||
|
Copyright (C) 2023 Free Software Foundation, Inc.
|
||
|
|
||
|
This file is part of GAS, the GNU Assembler.
|
||
|
|
||
|
GAS is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 3, or (at your option)
|
||
|
any later version.
|
||
|
|
||
|
GAS is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with GAS; see the file COPYING. If not, write to the Free
|
||
|
Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
|
||
|
02110-1301, USA. */
|
||
|
|
||
|
#ifndef GINSN_H
|
||
|
#define GINSN_H
|
||
|
|
||
|
#include "as.h"
|
||
|
|
||
|
/* Maximum number of source operands of a ginsn. */
|
||
|
#define GINSN_NUM_SRC_OPNDS 2
|
||
|
|
||
|
/* A ginsn in printed in the following format:
|
||
|
"ginsn: OPCD SRC1, SRC2, DST"
|
||
|
"<-5-> <--------125------->"
|
||
|
where each of SRC1, SRC2, and DST are in the form:
|
||
|
"%rNN," (up to 5 chars)
|
||
|
"imm," (up to int32_t+1 chars)
|
||
|
"[%rNN+-imm]," (up to int32_t+9 chars)
|
||
|
Hence a max of 19 chars. */
|
||
|
|
||
|
#define GINSN_LISTING_OPND_LEN 40
|
||
|
#define GINSN_LISTING_LEN 156
|
||
|
|
||
|
enum ginsn_gen_mode
|
||
|
{
|
||
|
GINSN_GEN_NONE,
|
||
|
/* Generate ginsns for program validation passes. */
|
||
|
GINSN_GEN_FVAL,
|
||
|
/* Generate ginsns for synthesizing DWARF CFI. */
|
||
|
GINSN_GEN_SCFI,
|
||
|
};
|
||
|
|
||
|
/* ginsn types.
|
||
|
|
||
|
GINSN_TYPE_PHANTOM are phantom ginsns. They are used where there is no real
|
||
|
machine instruction counterpart, but a ginsn is needed only to carry
|
||
|
information to GAS. For example, to carry an SCFI Op.
|
||
|
|
||
|
Note that, ginsns do not have a push / pop instructions.
|
||
|
Instead, following are used:
|
||
|
type=GINSN_TYPE_LOAD, src=GINSN_SRC_INDIRECT, REG_SP: Load from stack.
|
||
|
type=GINSN_TYPE_STORE, dst=GINSN_DST_INDIRECT, REG_SP: Store to stack.
|
||
|
*/
|
||
|
|
||
|
#define _GINSN_TYPES \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_SYMBOL, "SYM") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_PHANTOM, "PHANTOM") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_ADD, "ADD") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_AND, "AND") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_CALL, "CALL") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_JUMP, "JMP") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_JUMP_COND, "JCC") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_MOV, "MOV") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_LOAD, "LOAD") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_STORE, "STORE") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_RETURN, "RET") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_SUB, "SUB") \
|
||
|
_GINSN_TYPE_ITEM (GINSN_TYPE_OTHER, "OTH")
|
||
|
|
||
|
enum ginsn_type
|
||
|
{
|
||
|
#define _GINSN_TYPE_ITEM(NAME, STR) NAME,
|
||
|
_GINSN_TYPES
|
||
|
#undef _GINSN_TYPE_ITEM
|
||
|
};
|
||
|
|
||
|
enum ginsn_src_type
|
||
|
{
|
||
|
GINSN_SRC_UNKNOWN,
|
||
|
GINSN_SRC_REG,
|
||
|
GINSN_SRC_IMM,
|
||
|
GINSN_SRC_INDIRECT,
|
||
|
GINSN_SRC_SYMBOL,
|
||
|
};
|
||
|
|
||
|
/* GAS instruction source operand representation. */
|
||
|
|
||
|
struct ginsn_src
|
||
|
{
|
||
|
enum ginsn_src_type type;
|
||
|
/* DWARF register number. */
|
||
|
unsigned int reg;
|
||
|
/* Immediate or disp for indirect memory access. */
|
||
|
offsetT immdisp;
|
||
|
/* Src symbol. May be needed for some control flow instructions. */
|
||
|
const symbolS *sym;
|
||
|
};
|
||
|
|
||
|
enum ginsn_dst_type
|
||
|
{
|
||
|
GINSN_DST_UNKNOWN,
|
||
|
GINSN_DST_REG,
|
||
|
GINSN_DST_INDIRECT,
|
||
|
};
|
||
|
|
||
|
/* GAS instruction destination operand representation. */
|
||
|
|
||
|
struct ginsn_dst
|
||
|
{
|
||
|
enum ginsn_dst_type type;
|
||
|
/* DWARF register number. */
|
||
|
unsigned int reg;
|
||
|
/* Disp for indirect memory access. */
|
||
|
offsetT disp;
|
||
|
};
|
||
|
|
||
|
/* Various flags for additional information per GAS instruction. */
|
||
|
|
||
|
/* Function begin or end symbol. */
|
||
|
#define GINSN_F_FUNC_MARKER 0x1
|
||
|
/* Identify real or implicit GAS insn.
|
||
|
Some targets employ CISC-like instructions. Multiple ginsn's may be used
|
||
|
for a single machine instruction in some ISAs. For some optimizations,
|
||
|
there is need to identify whether a ginsn, e.g., GINSN_TYPE_ADD or
|
||
|
GINSN_TYPE_SUB is a result of an user-specified instruction or not. */
|
||
|
#define GINSN_F_INSN_REAL 0x2
|
||
|
/* Identify if the GAS insn of type GINSN_TYPE_SYMBOL is due to a user-defined
|
||
|
label. Each user-defined labels in a function will cause addition of a new
|
||
|
ginsn. This simplifies control flow graph creation.
|
||
|
See htab_t label_ginsn_map usage. */
|
||
|
#define GINSN_F_USER_LABEL 0x4
|
||
|
/* Max bit position for flags (uint32_t). */
|
||
|
#define GINSN_F_MAX 0x20
|
||
|
|
||
|
#define GINSN_F_FUNC_BEGIN_P(ginsn) \
|
||
|
((ginsn != NULL) \
|
||
|
&& (ginsn->type == GINSN_TYPE_SYMBOL) \
|
||
|
&& (ginsn->flags & GINSN_F_FUNC_MARKER))
|
||
|
|
||
|
/* PS: For ginsn associated with a user-defined symbol location,
|
||
|
GINSN_F_FUNC_MARKER is unset, but GINSN_F_USER_LABEL is set. */
|
||
|
#define GINSN_F_FUNC_END_P(ginsn) \
|
||
|
((ginsn != NULL) \
|
||
|
&& (ginsn->type == GINSN_TYPE_SYMBOL) \
|
||
|
&& !(ginsn->flags & GINSN_F_FUNC_MARKER) \
|
||
|
&& !(ginsn->flags & GINSN_F_USER_LABEL))
|
||
|
|
||
|
#define GINSN_F_INSN_REAL_P(ginsn) \
|
||
|
((ginsn != NULL) \
|
||
|
&& (ginsn->flags & GINSN_F_INSN_REAL))
|
||
|
|
||
|
#define GINSN_F_USER_LABEL_P(ginsn) \
|
||
|
((ginsn != NULL) \
|
||
|
&& (ginsn->type == GINSN_TYPE_SYMBOL) \
|
||
|
&& !(ginsn->flags & GINSN_F_FUNC_MARKER) \
|
||
|
&& (ginsn->flags & GINSN_F_USER_LABEL))
|
||
|
|
||
|
typedef struct ginsn ginsnS;
|
||
|
typedef struct scfi_op scfi_opS;
|
||
|
typedef struct scfi_state scfi_stateS;
|
||
|
|
||
|
/* GAS generic instruction.
|
||
|
|
||
|
Generic instructions are used by GAS to abstract out the binary machine
|
||
|
instructions. In other words, ginsn is a target/ABI independent internal
|
||
|
representation for GAS. Note that, depending on the target, there may be
|
||
|
more than one ginsn per binary machine instruction.
|
||
|
|
||
|
ginsns can be used by GAS to perform validations, or even generate
|
||
|
additional information like, sythesizing DWARF CFI for hand-written asm. */
|
||
|
|
||
|
struct ginsn
|
||
|
{
|
||
|
enum ginsn_type type;
|
||
|
/* GAS instructions are simple instructions with GINSN_NUM_SRC_OPNDS number
|
||
|
of source operands and one destination operand at this time. */
|
||
|
struct ginsn_src src[GINSN_NUM_SRC_OPNDS];
|
||
|
struct ginsn_dst dst;
|
||
|
/* Additional information per instruction. */
|
||
|
uint32_t flags;
|
||
|
/* Symbol. For ginsn of type other than GINSN_TYPE_SYMBOL, this identifies
|
||
|
the end of the corresponding machine instruction in the .text segment.
|
||
|
These symbols are created anew by the targets and are not used elsewhere
|
||
|
in GAS. The only exception is some ginsns of type GINSN_TYPE_SYMBOL, when
|
||
|
generated for the user-defined labels. See ginsn_frob_label. */
|
||
|
const symbolS *sym;
|
||
|
/* Identifier (linearly increasing natural number) for each ginsn. Used as
|
||
|
a proxy for program order of ginsns. */
|
||
|
uint64_t id;
|
||
|
/* Location information for user-interfacing messaging. Only ginsns with
|
||
|
GINSN_F_FUNC_BEGIN_P and GINSN_F_FUNC_END_P may present themselves with no
|
||
|
file or line information. */
|
||
|
const char *file;
|
||
|
unsigned int line;
|
||
|
|
||
|
/* Information needed for synthesizing CFI. */
|
||
|
scfi_opS **scfi_ops;
|
||
|
uint32_t num_scfi_ops;
|
||
|
|
||
|
/* Flag to keep track of visited instructions for CFG creation. */
|
||
|
bool visited;
|
||
|
|
||
|
ginsnS *next; /* A linked list. */
|
||
|
};
|
||
|
|
||
|
struct ginsn_src *ginsn_get_src1 (ginsnS *ginsn);
|
||
|
struct ginsn_src *ginsn_get_src2 (ginsnS *ginsn);
|
||
|
struct ginsn_dst *ginsn_get_dst (ginsnS *ginsn);
|
||
|
|
||
|
unsigned int ginsn_get_src_reg (struct ginsn_src *src);
|
||
|
enum ginsn_src_type ginsn_get_src_type (struct ginsn_src *src);
|
||
|
offsetT ginsn_get_src_disp (struct ginsn_src *src);
|
||
|
offsetT ginsn_get_src_imm (struct ginsn_src *src);
|
||
|
|
||
|
unsigned int ginsn_get_dst_reg (struct ginsn_dst *dst);
|
||
|
enum ginsn_dst_type ginsn_get_dst_type (struct ginsn_dst *dst);
|
||
|
offsetT ginsn_get_dst_disp (struct ginsn_dst *dst);
|
||
|
|
||
|
/* Data object for book-keeping information related to GAS generic
|
||
|
instructions. */
|
||
|
struct frch_ginsn_data
|
||
|
{
|
||
|
/* Mode for GINSN creation. */
|
||
|
enum ginsn_gen_mode mode;
|
||
|
/* Head of the list of ginsns. */
|
||
|
ginsnS *gins_rootP;
|
||
|
/* Tail of the list of ginsns. */
|
||
|
ginsnS *gins_lastP;
|
||
|
/* Function symbol. */
|
||
|
const symbolS *func;
|
||
|
/* Start address of the function. */
|
||
|
symbolS *start_addr;
|
||
|
/* User-defined label to ginsn mapping. */
|
||
|
htab_t label_ginsn_map;
|
||
|
/* Is the list of ginsn apt for creating CFG. */
|
||
|
bool gcfg_apt_p;
|
||
|
};
|
||
|
|
||
|
int ginsn_data_begin (const symbolS *func);
|
||
|
int ginsn_data_end (const symbolS *label);
|
||
|
const symbolS *ginsn_data_func_symbol (void);
|
||
|
void ginsn_frob_label (const symbolS *sym);
|
||
|
|
||
|
void frch_ginsn_data_init (const symbolS *func, symbolS *start_addr,
|
||
|
enum ginsn_gen_mode gmode);
|
||
|
void frch_ginsn_data_cleanup (void);
|
||
|
int frch_ginsn_data_append (ginsnS *ginsn);
|
||
|
enum ginsn_gen_mode frch_ginsn_gen_mode (void);
|
||
|
|
||
|
void label_ginsn_map_insert (const symbolS *label, ginsnS *ginsn);
|
||
|
ginsnS *label_ginsn_map_find (const symbolS *label);
|
||
|
|
||
|
ginsnS *ginsn_new_symbol_func_begin (const symbolS *sym);
|
||
|
ginsnS *ginsn_new_symbol_func_end (const symbolS *sym);
|
||
|
ginsnS *ginsn_new_symbol_user_label (const symbolS *sym);
|
||
|
|
||
|
ginsnS *ginsn_new_phantom (const symbolS *sym);
|
||
|
ginsnS *ginsn_new_symbol (const symbolS *sym, bool real_p);
|
||
|
ginsnS *ginsn_new_add (const symbolS *sym, bool real_p,
|
||
|
enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp,
|
||
|
enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp,
|
||
|
enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp);
|
||
|
ginsnS *ginsn_new_and (const symbolS *sym, bool real_p,
|
||
|
enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp,
|
||
|
enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp,
|
||
|
enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp);
|
||
|
ginsnS *ginsn_new_call (const symbolS *sym, bool real_p,
|
||
|
enum ginsn_src_type src_type, unsigned int src_reg,
|
||
|
const symbolS *src_text_sym);
|
||
|
ginsnS *ginsn_new_jump (const symbolS *sym, bool real_p,
|
||
|
enum ginsn_src_type src_type, unsigned int src_reg,
|
||
|
const symbolS *src_ginsn_sym);
|
||
|
ginsnS *ginsn_new_jump_cond (const symbolS *sym, bool real_p,
|
||
|
enum ginsn_src_type src_type, unsigned int src_reg,
|
||
|
const symbolS *src_ginsn_sym);
|
||
|
ginsnS *ginsn_new_mov (const symbolS *sym, bool real_p,
|
||
|
enum ginsn_src_type src_type, unsigned int src_reg, offsetT src_disp,
|
||
|
enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp);
|
||
|
ginsnS *ginsn_new_store (const symbolS *sym, bool real_p,
|
||
|
enum ginsn_src_type src_type, unsigned int src_reg,
|
||
|
enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp);
|
||
|
ginsnS *ginsn_new_load (const symbolS *sym, bool real_p,
|
||
|
enum ginsn_src_type src_type, unsigned int src_reg, offsetT src_disp,
|
||
|
enum ginsn_dst_type dst_type, unsigned int dst_reg);
|
||
|
ginsnS *ginsn_new_sub (const symbolS *sym, bool real_p,
|
||
|
enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp,
|
||
|
enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp,
|
||
|
enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp);
|
||
|
ginsnS *ginsn_new_other (const symbolS *sym, bool real_p,
|
||
|
enum ginsn_src_type src1_type, unsigned int src1_val,
|
||
|
enum ginsn_src_type src2_type, unsigned int src2_val,
|
||
|
enum ginsn_dst_type dst_type, unsigned int dst_reg);
|
||
|
ginsnS *ginsn_new_return (const symbolS *sym, bool real_p);
|
||
|
|
||
|
void ginsn_set_where (ginsnS *ginsn);
|
||
|
|
||
|
bool ginsn_track_reg_p (unsigned int dw2reg, enum ginsn_gen_mode);
|
||
|
|
||
|
int ginsn_link_next (ginsnS *ginsn, ginsnS *next);
|
||
|
|
||
|
enum gcfg_err_code
|
||
|
{
|
||
|
GCFG_OK = 0,
|
||
|
GCFG_JLABEL_NOT_PRESENT = 1, /* Warning-level code. */
|
||
|
};
|
||
|
|
||
|
typedef struct gbb gbbS;
|
||
|
typedef struct gedge gedgeS;
|
||
|
|
||
|
/* GBB - Basic block of generic GAS instructions. */
|
||
|
|
||
|
struct gbb
|
||
|
{
|
||
|
ginsnS *first_ginsn;
|
||
|
ginsnS *last_ginsn;
|
||
|
uint64_t num_ginsns;
|
||
|
|
||
|
/* Identifier (linearly increasing natural number) for each gbb. Added for
|
||
|
debugging purpose only. */
|
||
|
uint64_t id;
|
||
|
|
||
|
bool visited;
|
||
|
|
||
|
uint32_t num_out_gedges;
|
||
|
gedgeS *out_gedges;
|
||
|
|
||
|
/* Members for SCFI purposes. */
|
||
|
/* SCFI state at the entry of basic block. */
|
||
|
scfi_stateS *entry_state;
|
||
|
/* SCFI state at the exit of basic block. */
|
||
|
scfi_stateS *exit_state;
|
||
|
|
||
|
/* A linked list. In order of addition. */
|
||
|
gbbS *next;
|
||
|
};
|
||
|
|
||
|
struct gedge
|
||
|
{
|
||
|
gbbS *dst_bb;
|
||
|
/* A linked list. In order of addition. */
|
||
|
gedgeS *next;
|
||
|
bool visited;
|
||
|
};
|
||
|
|
||
|
/* Control flow graph of generic GAS instructions. */
|
||
|
|
||
|
struct gcfg
|
||
|
{
|
||
|
uint64_t num_gbbs;
|
||
|
gbbS *root_bb;
|
||
|
};
|
||
|
|
||
|
typedef struct gcfg gcfgS;
|
||
|
|
||
|
#define bb_for_each_insn(bb, ginsn) \
|
||
|
for (ginsn = bb->first_ginsn; ginsn; \
|
||
|
ginsn = (ginsn != bb->last_ginsn) ? ginsn->next : NULL)
|
||
|
|
||
|
#define bb_for_each_edge(bb, edge) \
|
||
|
for (edge = (edge == NULL) ? bb->out_gedges : edge; edge; edge = edge->next)
|
||
|
|
||
|
#define cfg_for_each_bb(cfg, bb) \
|
||
|
for (bb = cfg->root_bb; bb; bb = bb->next)
|
||
|
|
||
|
#define bb_get_first_ginsn(bb) \
|
||
|
(bb->first_ginsn)
|
||
|
|
||
|
#define bb_get_last_ginsn(bb) \
|
||
|
(bb->last_ginsn)
|
||
|
|
||
|
gcfgS *gcfg_build (const symbolS *func, int *errp);
|
||
|
void gcfg_cleanup (gcfgS **gcfg);
|
||
|
void gcfg_print (const gcfgS *gcfg, FILE *outfile);
|
||
|
gbbS *gcfg_get_rootbb (gcfgS *gcfg);
|
||
|
void gcfg_get_bbs_in_prog_order (gcfgS *gcfg, gbbS **prog_order_bbs);
|
||
|
|
||
|
#endif /* GINSN_H. */
|