mirror of https://github.com/yasm/yasm.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1163 lines
34 KiB
1163 lines
34 KiB
/* |
|
* x86 bytecode utility functions |
|
* |
|
* Copyright (C) 2001 Peter Johnson |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' |
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE |
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
* POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
#include <util.h> |
|
/*@unused@*/ RCSID("$Id$"); |
|
|
|
#define YASM_LIB_INTERNAL |
|
#define YASM_BC_INTERNAL |
|
#define YASM_EXPR_INTERNAL |
|
#include <libyasm.h> |
|
|
|
#include "x86arch.h" |
|
|
|
|
|
/* Effective address type */ |
|
|
|
typedef struct x86_effaddr { |
|
yasm_effaddr ea; /* base structure */ |
|
|
|
/* PC-relative portions are for AMD64 only (RIP addressing) */ |
|
/*@null@*/ /*@dependent@*/ yasm_symrec *origin; /* pcrel origin */ |
|
|
|
/* How the spare (register) bits in Mod/RM are handled: |
|
* Even if valid_modrm=0, the spare bits are still valid (don't overwrite!) |
|
* They're set in bytecode_create_insn(). |
|
*/ |
|
unsigned char modrm; |
|
unsigned char valid_modrm; /* 1 if Mod/RM byte currently valid, 0 if not */ |
|
unsigned char need_modrm; /* 1 if Mod/RM byte needed, 0 if not */ |
|
|
|
unsigned char sib; |
|
unsigned char valid_sib; /* 1 if SIB byte currently valid, 0 if not */ |
|
unsigned char need_sib; /* 1 if SIB byte needed, 0 if not, |
|
0xff if unknown */ |
|
|
|
unsigned char pcrel; /* 1 if PC-relative transformation needed */ |
|
} x86_effaddr; |
|
|
|
/* Effective address callback function prototypes */ |
|
|
|
static void x86_ea_destroy(yasm_effaddr *ea); |
|
static void x86_ea_print(const yasm_effaddr *ea, FILE *f, int indent_level); |
|
|
|
/* Bytecode callback function prototypes */ |
|
|
|
static void x86_bc_insn_destroy(void *contents); |
|
static void x86_bc_insn_print(const void *contents, FILE *f, |
|
int indent_level); |
|
static yasm_bc_resolve_flags x86_bc_insn_resolve |
|
(yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist); |
|
static int x86_bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp, |
|
void *d, yasm_output_expr_func output_expr, |
|
/*@null@*/ yasm_output_reloc_func output_reloc); |
|
|
|
static void x86_bc_jmp_destroy(void *contents); |
|
static void x86_bc_jmp_print(const void *contents, FILE *f, int indent_level); |
|
static yasm_bc_resolve_flags x86_bc_jmp_resolve |
|
(yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist); |
|
static int x86_bc_jmp_tobytes(yasm_bytecode *bc, unsigned char **bufp, |
|
void *d, yasm_output_expr_func output_expr, |
|
/*@null@*/ yasm_output_reloc_func output_reloc); |
|
|
|
static void x86_bc_jmpfar_destroy(void *contents); |
|
static void x86_bc_jmpfar_print(const void *contents, FILE *f, |
|
int indent_level); |
|
static yasm_bc_resolve_flags x86_bc_jmpfar_resolve |
|
(yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist); |
|
static int x86_bc_jmpfar_tobytes |
|
(yasm_bytecode *bc, unsigned char **bufp, void *d, |
|
yasm_output_expr_func output_expr, |
|
/*@null@*/ yasm_output_reloc_func output_reloc); |
|
|
|
/* Effective address callback structures */ |
|
|
|
static const yasm_effaddr_callback x86_ea_callback = { |
|
x86_ea_destroy, |
|
x86_ea_print |
|
}; |
|
|
|
/* Bytecode callback structures */ |
|
|
|
static const yasm_bytecode_callback x86_bc_callback_insn = { |
|
x86_bc_insn_destroy, |
|
x86_bc_insn_print, |
|
yasm_bc_finalize_common, |
|
x86_bc_insn_resolve, |
|
x86_bc_insn_tobytes |
|
}; |
|
|
|
static const yasm_bytecode_callback x86_bc_callback_jmp = { |
|
x86_bc_jmp_destroy, |
|
x86_bc_jmp_print, |
|
yasm_bc_finalize_common, |
|
x86_bc_jmp_resolve, |
|
x86_bc_jmp_tobytes |
|
}; |
|
|
|
static const yasm_bytecode_callback x86_bc_callback_jmpfar = { |
|
x86_bc_jmpfar_destroy, |
|
x86_bc_jmpfar_print, |
|
yasm_bc_finalize_common, |
|
x86_bc_jmpfar_resolve, |
|
x86_bc_jmpfar_tobytes |
|
}; |
|
|
|
int |
|
yasm_x86__set_rex_from_reg(unsigned char *rex, unsigned char *low3, |
|
unsigned long reg, unsigned int bits, |
|
x86_rex_bit_pos rexbit) |
|
{ |
|
*low3 = (unsigned char)(reg&7); |
|
|
|
if (bits == 64) { |
|
x86_expritem_reg_size size = (x86_expritem_reg_size)(reg & ~0xFUL); |
|
|
|
if (size == X86_REG8X || (reg & 0xF) >= 8) { |
|
if (*rex == 0xff) |
|
return 1; |
|
*rex |= 0x40 | (((reg & 8) >> 3) << rexbit); |
|
} else if (size == X86_REG8 && (reg & 7) >= 4) { |
|
/* AH/BH/CH/DH, so no REX allowed */ |
|
if (*rex != 0 && *rex != 0xff) |
|
return 1; |
|
*rex = 0xff; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
void |
|
yasm_x86__bc_transform_insn(yasm_bytecode *bc, x86_insn *insn) |
|
{ |
|
yasm_bc_transform(bc, &x86_bc_callback_insn, insn); |
|
} |
|
|
|
void |
|
yasm_x86__bc_transform_jmp(yasm_bytecode *bc, x86_jmp *jmp) |
|
{ |
|
yasm_bc_transform(bc, &x86_bc_callback_jmp, jmp); |
|
} |
|
|
|
void |
|
yasm_x86__bc_transform_jmpfar(yasm_bytecode *bc, x86_jmpfar *jmpfar) |
|
{ |
|
yasm_bc_transform(bc, &x86_bc_callback_jmpfar, jmpfar); |
|
} |
|
|
|
void |
|
yasm_x86__ea_init(yasm_effaddr *ea, unsigned int spare, yasm_symrec *origin) |
|
{ |
|
x86_effaddr *x86_ea = (x86_effaddr *)ea; |
|
x86_ea->origin = origin; |
|
x86_ea->modrm &= 0xC7; /* zero spare/reg bits */ |
|
x86_ea->modrm |= (spare << 3) & 0x38; /* plug in provided bits */ |
|
} |
|
|
|
void |
|
yasm_x86__ea_set_disponly(yasm_effaddr *ea) |
|
{ |
|
x86_effaddr *x86_ea = (x86_effaddr *)ea; |
|
|
|
x86_ea->valid_modrm = 0; |
|
x86_ea->need_modrm = 0; |
|
x86_ea->valid_sib = 0; |
|
x86_ea->need_sib = 0; |
|
x86_ea->pcrel = 0; |
|
} |
|
|
|
yasm_effaddr * |
|
yasm_x86__ea_create_reg(unsigned long reg, unsigned char *rex, |
|
unsigned int bits) |
|
{ |
|
x86_effaddr *x86_ea; |
|
unsigned char rm; |
|
|
|
if (yasm_x86__set_rex_from_reg(rex, &rm, reg, bits, X86_REX_B)) |
|
return NULL; |
|
|
|
x86_ea = yasm_xmalloc(sizeof(x86_effaddr)); |
|
|
|
x86_ea->ea.callback = &x86_ea_callback; |
|
x86_ea->ea.disp = (yasm_expr *)NULL; |
|
x86_ea->ea.len = 0; |
|
x86_ea->ea.nosplit = 0; |
|
x86_ea->ea.strong = 0; |
|
x86_ea->ea.segreg = 0; |
|
x86_ea->modrm = 0xC0 | rm; /* Mod=11, R/M=Reg, Reg=0 */ |
|
x86_ea->valid_modrm = 1; |
|
x86_ea->need_modrm = 1; |
|
x86_ea->sib = 0; |
|
x86_ea->valid_sib = 0; |
|
x86_ea->need_sib = 0; |
|
x86_ea->pcrel = 0; |
|
|
|
return (yasm_effaddr *)x86_ea; |
|
} |
|
|
|
yasm_effaddr * |
|
yasm_x86__ea_create_expr(yasm_arch *arch, yasm_expr *e) |
|
{ |
|
yasm_arch_x86 *arch_x86 = (yasm_arch_x86 *)arch; |
|
x86_effaddr *x86_ea; |
|
|
|
x86_ea = yasm_xmalloc(sizeof(x86_effaddr)); |
|
|
|
x86_ea->ea.callback = &x86_ea_callback; |
|
if (arch_x86->parser == X86_PARSER_GAS) { |
|
/* Need to change foo+rip into foo wrt rip. |
|
* Note this assumes a particular ordering coming from the parser |
|
* to work (it's not very smart)! |
|
*/ |
|
if (e->op == YASM_EXPR_ADD && e->terms[0].type == YASM_EXPR_REG |
|
&& e->terms[0].data.reg == X86_RIP) { |
|
/* replace register with 0 */ |
|
e->terms[0].type = YASM_EXPR_INT; |
|
e->terms[0].data.intn = yasm_intnum_create_uint(0); |
|
/* build new wrt expression */ |
|
e = yasm_expr_create(YASM_EXPR_WRT, yasm_expr_expr(e), |
|
yasm_expr_reg(X86_RIP), e->line); |
|
} |
|
} |
|
x86_ea->ea.disp = e; |
|
x86_ea->ea.len = 0; |
|
x86_ea->ea.nosplit = 0; |
|
x86_ea->ea.strong = 0; |
|
x86_ea->ea.segreg = 0; |
|
x86_ea->modrm = 0; |
|
x86_ea->valid_modrm = 0; |
|
x86_ea->need_modrm = 1; |
|
x86_ea->sib = 0; |
|
x86_ea->valid_sib = 0; |
|
/* We won't know whether we need an SIB until we know more about expr and |
|
* the BITS/address override setting. |
|
*/ |
|
x86_ea->need_sib = 0xff; |
|
x86_ea->pcrel = 0; |
|
|
|
return (yasm_effaddr *)x86_ea; |
|
} |
|
|
|
/*@-compmempass@*/ |
|
yasm_effaddr * |
|
yasm_x86__ea_create_imm(yasm_expr *imm, unsigned int im_len) |
|
{ |
|
x86_effaddr *x86_ea; |
|
|
|
x86_ea = yasm_xmalloc(sizeof(x86_effaddr)); |
|
|
|
x86_ea->ea.callback = &x86_ea_callback; |
|
x86_ea->ea.disp = imm; |
|
x86_ea->ea.len = (unsigned char)im_len; |
|
x86_ea->ea.nosplit = 0; |
|
x86_ea->ea.strong = 0; |
|
x86_ea->ea.segreg = 0; |
|
x86_ea->modrm = 0; |
|
x86_ea->valid_modrm = 0; |
|
x86_ea->need_modrm = 0; |
|
x86_ea->sib = 0; |
|
x86_ea->valid_sib = 0; |
|
x86_ea->need_sib = 0; |
|
x86_ea->pcrel = 0; |
|
|
|
return (yasm_effaddr *)x86_ea; |
|
} |
|
/*@=compmempass@*/ |
|
|
|
void |
|
yasm_x86__bc_apply_prefixes(x86_common *common, unsigned char *rex, |
|
int num_prefixes, unsigned long **prefixes, |
|
unsigned long line) |
|
{ |
|
int i; |
|
int first = 1; |
|
|
|
for (i=0; i<num_prefixes; i++) { |
|
switch ((x86_parse_insn_prefix)prefixes[i][0]) { |
|
case X86_LOCKREP: |
|
if (common->lockrep_pre != 0) |
|
yasm__warning(YASM_WARN_GENERAL, line, |
|
N_("multiple LOCK or REP prefixes, using leftmost")); |
|
common->lockrep_pre = (unsigned char)prefixes[i][1]; |
|
break; |
|
case X86_ADDRSIZE: |
|
common->addrsize = (unsigned char)prefixes[i][1]; |
|
break; |
|
case X86_OPERSIZE: |
|
common->opersize = (unsigned char)prefixes[i][1]; |
|
break; |
|
case X86_SEGREG: |
|
/* This is a hack.. we should really be putting this in the |
|
* the effective address! |
|
*/ |
|
common->lockrep_pre = (unsigned char)prefixes[i][1]; |
|
break; |
|
case X86_REX: |
|
if (!rex) |
|
yasm__warning(YASM_WARN_GENERAL, line, |
|
N_("ignoring REX prefix on jump")); |
|
else if (*rex == 0xff) |
|
yasm__warning(YASM_WARN_GENERAL, line, |
|
N_("REX prefix not allowed on this instruction, ignoring")); |
|
else { |
|
if (*rex != 0) { |
|
if (first) |
|
yasm__warning(YASM_WARN_GENERAL, line, |
|
N_("overriding generated REX prefix")); |
|
else |
|
yasm__warning(YASM_WARN_GENERAL, line, |
|
N_("multiple REX prefixes, using leftmost")); |
|
} |
|
/* Here we assume that we can't get this prefix in non |
|
* 64 bit mode due to checks in parse_check_prefix(). |
|
*/ |
|
common->mode_bits = 64; |
|
*rex = (unsigned char)prefixes[i][1]; |
|
} |
|
first = 0; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
static void |
|
x86_bc_insn_destroy(void *contents) |
|
{ |
|
x86_insn *insn = (x86_insn *)contents; |
|
if (insn->ea) |
|
yasm_ea_destroy((yasm_effaddr *)insn->ea); |
|
if (insn->imm) { |
|
yasm_expr_destroy(insn->imm->val); |
|
yasm_xfree(insn->imm); |
|
} |
|
yasm_xfree(contents); |
|
} |
|
|
|
static void |
|
x86_bc_jmp_destroy(void *contents) |
|
{ |
|
x86_jmp *jmp = (x86_jmp *)contents; |
|
yasm_expr_destroy(jmp->target); |
|
yasm_xfree(contents); |
|
} |
|
|
|
static void |
|
x86_bc_jmpfar_destroy(void *contents) |
|
{ |
|
x86_jmpfar *jmpfar = (x86_jmpfar *)contents; |
|
yasm_expr_destroy(jmpfar->segment); |
|
yasm_expr_destroy(jmpfar->offset); |
|
yasm_xfree(contents); |
|
} |
|
|
|
static void |
|
x86_ea_destroy(yasm_effaddr *ea) |
|
{ |
|
} |
|
|
|
static void |
|
x86_ea_print(const yasm_effaddr *ea, FILE *f, int indent_level) |
|
{ |
|
const x86_effaddr *x86_ea = (const x86_effaddr *)ea; |
|
fprintf(f, "%*sSegmentOv=%02x PCRel=%u\n", indent_level, "", |
|
(unsigned int)x86_ea->ea.segreg, (unsigned int)x86_ea->pcrel); |
|
fprintf(f, "%*sModRM=%03o ValidRM=%u NeedRM=%u\n", indent_level, "", |
|
(unsigned int)x86_ea->modrm, (unsigned int)x86_ea->valid_modrm, |
|
(unsigned int)x86_ea->need_modrm); |
|
fprintf(f, "%*sSIB=%03o ValidSIB=%u NeedSIB=%u\n", indent_level, "", |
|
(unsigned int)x86_ea->sib, (unsigned int)x86_ea->valid_sib, |
|
(unsigned int)x86_ea->need_sib); |
|
} |
|
|
|
static void |
|
x86_common_print(const x86_common *common, FILE *f, int indent_level) |
|
{ |
|
fprintf(f, "%*sAddrSize=%u OperSize=%u LockRepPre=%02x BITS=%u\n", |
|
indent_level, "", |
|
(unsigned int)common->addrsize, |
|
(unsigned int)common->opersize, |
|
(unsigned int)common->lockrep_pre, |
|
(unsigned int)common->mode_bits); |
|
} |
|
|
|
static void |
|
x86_opcode_print(const x86_opcode *opcode, FILE *f, int indent_level) |
|
{ |
|
fprintf(f, "%*sOpcode: %02x %02x %02x OpLen=%u\n", indent_level, "", |
|
(unsigned int)opcode->opcode[0], |
|
(unsigned int)opcode->opcode[1], |
|
(unsigned int)opcode->opcode[2], |
|
(unsigned int)opcode->len); |
|
} |
|
|
|
static void |
|
x86_bc_insn_print(const void *contents, FILE *f, int indent_level) |
|
{ |
|
const x86_insn *insn = (const x86_insn *)contents; |
|
|
|
fprintf(f, "%*s_Instruction_\n", indent_level, ""); |
|
fprintf(f, "%*sEffective Address:", indent_level, ""); |
|
if (insn->ea) { |
|
fprintf(f, "\n"); |
|
yasm_ea_print((yasm_effaddr *)insn->ea, f, indent_level+1); |
|
} else |
|
fprintf(f, " (nil)\n"); |
|
fprintf(f, "%*sImmediate Value:", indent_level, ""); |
|
if (!insn->imm) |
|
fprintf(f, " (nil)\n"); |
|
else { |
|
indent_level++; |
|
fprintf(f, "\n%*sVal=", indent_level, ""); |
|
if (insn->imm->val) |
|
yasm_expr_print(insn->imm->val, f); |
|
else |
|
fprintf(f, "(nil-SHOULDN'T HAPPEN)"); |
|
fprintf(f, "\n"); |
|
fprintf(f, "%*sLen=%u, Sign=%u\n", indent_level, "", |
|
(unsigned int)insn->imm->len, |
|
(unsigned int)insn->imm->sign); |
|
indent_level--; |
|
} |
|
x86_opcode_print(&insn->opcode, f, indent_level); |
|
x86_common_print(&insn->common, f, indent_level); |
|
fprintf(f, "%*sSpPre=%02x REX=%03o PostOp=%u\n", indent_level, "", |
|
(unsigned int)insn->special_prefix, |
|
(unsigned int)insn->rex, |
|
(unsigned int)insn->postop); |
|
} |
|
|
|
static void |
|
x86_bc_jmp_print(const void *contents, FILE *f, int indent_level) |
|
{ |
|
const x86_jmp *jmp = (const x86_jmp *)contents; |
|
|
|
fprintf(f, "%*s_Jump_\n", indent_level, ""); |
|
fprintf(f, "%*sTarget=", indent_level, ""); |
|
yasm_expr_print(jmp->target, f); |
|
fprintf(f, "%*sOrigin=\n", indent_level, ""); |
|
yasm_symrec_print(jmp->origin, f, indent_level+1); |
|
fprintf(f, "\n%*sShort Form:\n", indent_level, ""); |
|
if (jmp->shortop.len == 0) |
|
fprintf(f, "%*sNone\n", indent_level+1, ""); |
|
else |
|
x86_opcode_print(&jmp->shortop, f, indent_level+1); |
|
fprintf(f, "%*sNear Form:\n", indent_level, ""); |
|
if (jmp->nearop.len == 0) |
|
fprintf(f, "%*sNone\n", indent_level+1, ""); |
|
else |
|
x86_opcode_print(&jmp->nearop, f, indent_level+1); |
|
fprintf(f, "%*sOpSel=", indent_level, ""); |
|
switch (jmp->op_sel) { |
|
case JMP_NONE: |
|
fprintf(f, "None"); |
|
break; |
|
case JMP_SHORT: |
|
fprintf(f, "Short"); |
|
break; |
|
case JMP_NEAR: |
|
fprintf(f, "Near"); |
|
break; |
|
case JMP_SHORT_FORCED: |
|
fprintf(f, "Forced Short"); |
|
break; |
|
case JMP_NEAR_FORCED: |
|
fprintf(f, "Forced Near"); |
|
break; |
|
default: |
|
fprintf(f, "UNKNOWN!!"); |
|
break; |
|
} |
|
x86_common_print(&jmp->common, f, indent_level); |
|
} |
|
|
|
static void |
|
x86_bc_jmpfar_print(const void *contents, FILE *f, int indent_level) |
|
{ |
|
const x86_jmpfar *jmpfar = (const x86_jmpfar *)contents; |
|
|
|
fprintf(f, "%*s_Far_Jump_\n", indent_level, ""); |
|
fprintf(f, "%*sSegment=", indent_level, ""); |
|
yasm_expr_print(jmpfar->segment, f); |
|
fprintf(f, "\n%*sOffset=", indent_level, ""); |
|
yasm_expr_print(jmpfar->offset, f); |
|
x86_opcode_print(&jmpfar->opcode, f, indent_level); |
|
x86_common_print(&jmpfar->common, f, indent_level); |
|
} |
|
|
|
static unsigned int |
|
x86_common_resolve(const x86_common *common) |
|
{ |
|
unsigned int len = 0; |
|
|
|
if (common->addrsize != 0 && common->addrsize != common->mode_bits) |
|
len++; |
|
if (common->opersize != 0 && |
|
((common->mode_bits != 64 && common->opersize != common->mode_bits) || |
|
(common->mode_bits == 64 && common->opersize == 16))) |
|
len++; |
|
if (common->lockrep_pre != 0) |
|
len++; |
|
|
|
return len; |
|
} |
|
|
|
static yasm_bc_resolve_flags |
|
x86_bc_insn_resolve(yasm_bytecode *bc, int save, |
|
yasm_calc_bc_dist_func calc_bc_dist) |
|
{ |
|
x86_insn *insn = (x86_insn *)bc->contents; |
|
/*@null@*/ yasm_expr *temp; |
|
x86_effaddr *x86_ea = (x86_effaddr *)insn->ea; |
|
yasm_effaddr *ea = &x86_ea->ea; |
|
yasm_immval *imm = insn->imm; |
|
yasm_bc_resolve_flags retval = YASM_BC_RESOLVE_MIN_LEN; |
|
|
|
if (ea) { |
|
/* Create temp copy of disp, etc. */ |
|
x86_effaddr eat = *x86_ea; /* structure copy */ |
|
unsigned char displen = ea->len; |
|
|
|
if (ea->disp) { |
|
temp = yasm_expr_copy(ea->disp); |
|
assert(temp != NULL); |
|
|
|
/* Handle shortmov special-casing */ |
|
if (insn->postop == X86_POSTOP_SHORTMOV && |
|
insn->common.mode_bits == 64 && insn->common.addrsize == 32 && |
|
!yasm_expr__contains(temp, YASM_EXPR_REG)) { |
|
yasm_x86__ea_set_disponly((yasm_effaddr *)&eat); |
|
|
|
if (save) { |
|
/* Make the short form permanent. */ |
|
insn->opcode.opcode[0] = insn->opcode.opcode[1]; |
|
} |
|
} |
|
|
|
/* Check validity of effective address and calc R/M bits of |
|
* Mod/RM byte and SIB byte. We won't know the Mod field |
|
* of the Mod/RM byte until we know more about the |
|
* displacement. |
|
*/ |
|
switch (yasm_x86__expr_checkea(&temp, &insn->common.addrsize, |
|
insn->common.mode_bits, ea->nosplit, |
|
insn->postop == X86_POSTOP_ADDRESS16, &displen, &eat.modrm, |
|
&eat.valid_modrm, &eat.need_modrm, &eat.sib, |
|
&eat.valid_sib, &eat.need_sib, &eat.pcrel, &insn->rex, |
|
calc_bc_dist)) { |
|
case 1: |
|
yasm_expr_destroy(temp); |
|
/* failed, don't bother checking rest of insn */ |
|
return YASM_BC_RESOLVE_UNKNOWN_LEN|YASM_BC_RESOLVE_ERROR; |
|
case 2: |
|
yasm_expr_destroy(temp); |
|
/* failed, don't bother checking rest of insn */ |
|
return YASM_BC_RESOLVE_UNKNOWN_LEN; |
|
default: |
|
yasm_expr_destroy(temp); |
|
/* okay */ |
|
break; |
|
} |
|
|
|
if (displen != 1) { |
|
/* Fits into a word/dword, or unknown. */ |
|
retval = YASM_BC_RESOLVE_NONE; /* may not be smallest size */ |
|
|
|
/* Handle unknown case, make displen word-sized */ |
|
if (displen == 0xff) |
|
displen = (insn->common.addrsize == 16) ? 2U : 4U; |
|
} |
|
|
|
/* Handle address16 postop case */ |
|
if (insn->postop == X86_POSTOP_ADDRESS16) |
|
insn->common.addrsize = 0; |
|
|
|
/* If we had forced ea->len but had to override, save it now */ |
|
if (ea->len != 0 && ea->len != displen) |
|
ea->len = displen; |
|
|
|
if (save) { |
|
*x86_ea = eat; /* structure copy */ |
|
ea->len = displen; |
|
if (displen == 0 && ea->disp) { |
|
yasm_expr_destroy(ea->disp); |
|
ea->disp = NULL; |
|
} |
|
} |
|
} |
|
|
|
/* Compute length of ea and add to total */ |
|
bc->len += eat.need_modrm + (eat.need_sib ? 1:0) + displen; |
|
bc->len += (eat.ea.segreg != 0) ? 1 : 0; |
|
} |
|
|
|
if (imm) { |
|
const yasm_intnum *num; |
|
unsigned int immlen = imm->len; |
|
|
|
if (imm->val) { |
|
temp = yasm_expr_copy(imm->val); |
|
assert(temp != NULL); |
|
|
|
/* TODO: check imm->len vs. sized len from expr? */ |
|
|
|
num = yasm_expr_get_intnum(&temp, calc_bc_dist); |
|
|
|
switch (insn->postop) { |
|
case X86_POSTOP_SIGNEXT_IMM8: |
|
/* Handle signext_imm8 postop special-casing */ |
|
if (num) { |
|
long val = yasm_intnum_get_int(num); |
|
if (val >= -128 && val <= 127) { |
|
/* We can use the sign-extended byte form: shorten |
|
* the immediate length to 1. |
|
*/ |
|
immlen = 1; |
|
if (save) { |
|
/* Make the byte form permanent. */ |
|
insn->opcode.opcode[0] = insn->opcode.opcode[1]; |
|
imm->len = 1; |
|
if (insn->opcode.opcode[2] != 0) { |
|
insn->opcode.opcode[1] = insn->opcode.opcode[2]; |
|
insn->opcode.len++; |
|
} |
|
} else if (insn->opcode.opcode[2] != 0) |
|
bc->len++; |
|
} |
|
} |
|
/* Not really necessary, but saves confusion over it. */ |
|
if (save) |
|
insn->postop = X86_POSTOP_NONE; |
|
break; |
|
|
|
case X86_POSTOP_SIGNEXT_IMM32: |
|
/* Handle signext_imm32 postop special-casing */ |
|
if (!num || yasm_intnum_check_size(num, 32, 0, 1)) { |
|
bc->len++; /* Due to ModRM byte */ |
|
immlen = 4; |
|
if (save) { |
|
/* Throwaway REX byte */ |
|
unsigned char rex_temp = 0; |
|
|
|
/* Build ModRM EA - CAUTION: this depends on |
|
* opcode 0 being a mov instruction! |
|
*/ |
|
insn->ea = yasm_x86__ea_create_reg( |
|
(unsigned long)insn->opcode.opcode[0]-0xB8, |
|
&rex_temp, 64); |
|
|
|
/* Make the imm32s form permanent. */ |
|
insn->opcode.opcode[0] = insn->opcode.opcode[1]; |
|
imm->len = 4; |
|
} |
|
} |
|
/* Not really necessary, but saves confusion over it. */ |
|
if (save) |
|
insn->postop = X86_POSTOP_NONE; |
|
break; |
|
|
|
case X86_POSTOP_SHIFT: |
|
/* Handle shift postop special-casing */ |
|
if (num && yasm_intnum_get_uint(num) == 1) { |
|
/* We can use the ,1 form: no imm (set to 0 len) */ |
|
immlen = 0; |
|
|
|
if (save) { |
|
/* Make the ,1 form permanent. */ |
|
insn->opcode.opcode[0] = insn->opcode.opcode[1]; |
|
/* Delete imm, as it's not needed. */ |
|
yasm_expr_destroy(imm->val); |
|
yasm_xfree(imm); |
|
insn->imm = (yasm_immval *)NULL; |
|
} |
|
} else |
|
retval = YASM_BC_RESOLVE_NONE; /* could still get ,1 */ |
|
|
|
/* Not really necessary, but saves confusion over it. */ |
|
if (save) |
|
insn->postop = X86_POSTOP_NONE; |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
|
|
yasm_expr_destroy(temp); |
|
} |
|
|
|
bc->len += immlen; |
|
} |
|
|
|
bc->len += insn->opcode.len; |
|
bc->len += x86_common_resolve(&insn->common); |
|
bc->len += (insn->special_prefix != 0) ? 1:0; |
|
if (insn->rex != 0xff && |
|
(insn->rex != 0 || |
|
(insn->common.mode_bits == 64 && insn->common.opersize == 64 && |
|
insn->def_opersize_64 != 64))) |
|
bc->len++; |
|
|
|
return retval; |
|
} |
|
|
|
static yasm_bc_resolve_flags |
|
x86_bc_jmp_resolve(yasm_bytecode *bc, int save, |
|
yasm_calc_bc_dist_func calc_bc_dist) |
|
{ |
|
x86_jmp *jmp = (x86_jmp *)bc->contents; |
|
yasm_bc_resolve_flags retval = YASM_BC_RESOLVE_MIN_LEN; |
|
/*@null@*/ yasm_expr *temp; |
|
/*@dependent@*/ /*@null@*/ const yasm_intnum *num; |
|
long rel; |
|
unsigned char opersize; |
|
x86_jmp_opcode_sel jrtype = JMP_NONE; |
|
|
|
/* As opersize may be 0, figure out its "real" value. */ |
|
opersize = (jmp->common.opersize == 0) ? |
|
jmp->common.mode_bits : jmp->common.opersize; |
|
|
|
/* We only check to see if forced forms are actually legal if we're in |
|
* save mode. Otherwise we assume that they are legal. |
|
*/ |
|
switch (jmp->op_sel) { |
|
case JMP_SHORT_FORCED: |
|
/* 1 byte relative displacement */ |
|
jrtype = JMP_SHORT; |
|
if (save) { |
|
temp = yasm_expr_copy(jmp->target); |
|
temp = yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(temp), |
|
yasm_expr_sym(jmp->origin), bc->line); |
|
num = yasm_expr_get_intnum(&temp, calc_bc_dist); |
|
if (!num) { |
|
yasm__error(bc->line, |
|
N_("short jump target external or out of segment")); |
|
yasm_expr_destroy(temp); |
|
return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN; |
|
} else { |
|
rel = yasm_intnum_get_int(num); |
|
rel -= jmp->shortop.len+1; |
|
yasm_expr_destroy(temp); |
|
/* does a short form exist? */ |
|
if (jmp->shortop.len == 0) { |
|
yasm__error(bc->line, N_("short jump does not exist")); |
|
return YASM_BC_RESOLVE_ERROR | |
|
YASM_BC_RESOLVE_UNKNOWN_LEN; |
|
} |
|
/* short displacement must fit in -128 <= rel <= +127 */ |
|
if (rel < -128 || rel > 127) { |
|
yasm__error(bc->line, N_("short jump out of range")); |
|
return YASM_BC_RESOLVE_ERROR | |
|
YASM_BC_RESOLVE_UNKNOWN_LEN; |
|
} |
|
} |
|
} |
|
break; |
|
case JMP_NEAR_FORCED: |
|
/* 2/4 byte relative displacement (depending on operand size) */ |
|
jrtype = JMP_NEAR; |
|
if (save) { |
|
if (jmp->nearop.len == 0) { |
|
yasm__error(bc->line, N_("near jump does not exist")); |
|
return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN; |
|
} |
|
} |
|
break; |
|
default: |
|
temp = yasm_expr_copy(jmp->target); |
|
|
|
/* Try to find shortest displacement based on difference between |
|
* target expr value and our (this bytecode's) offset. Note this |
|
* requires offset to be set BEFORE calling calc_len in order for |
|
* this test to be valid. |
|
*/ |
|
temp = yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(temp), |
|
yasm_expr_sym(jmp->origin), bc->line); |
|
num = yasm_expr_get_intnum(&temp, calc_bc_dist); |
|
if (num) { |
|
rel = yasm_intnum_get_int(num); |
|
rel -= jmp->shortop.len+1; |
|
/* short displacement must fit within -128 <= rel <= +127 */ |
|
if (jmp->shortop.len != 0 && rel >= -128 && rel <= 127) { |
|
/* It fits into a short displacement. */ |
|
jrtype = JMP_SHORT; |
|
} else if (jmp->nearop.len != 0) { |
|
/* Near for now, but could get shorter in the future if |
|
* there's a short form available. |
|
*/ |
|
jrtype = JMP_NEAR; |
|
if (jmp->shortop.len != 0) |
|
retval = YASM_BC_RESOLVE_NONE; |
|
} else { |
|
/* Doesn't fit into short, and there's no near opcode. |
|
* Error out if saving, otherwise just make it a short |
|
* (in the hopes that a short might make it possible for |
|
* it to actually be within short range). |
|
*/ |
|
if (save) { |
|
yasm__error(bc->line, |
|
N_("short jump out of range (near jump does not exist)")); |
|
return YASM_BC_RESOLVE_ERROR | |
|
YASM_BC_RESOLVE_UNKNOWN_LEN; |
|
} |
|
jrtype = JMP_SHORT; |
|
} |
|
} else { |
|
/* It's unknown. Thus, assume near displacement. If a near |
|
* opcode is not available, use a short opcode instead. |
|
* If we're saving, error if a near opcode is not available. |
|
*/ |
|
if (jmp->nearop.len != 0) { |
|
if (jmp->shortop.len != 0) |
|
retval = YASM_BC_RESOLVE_NONE; |
|
jrtype = JMP_NEAR; |
|
} else { |
|
if (save) { |
|
yasm__error(bc->line, |
|
N_("short jump out of range (near jump does not exist)")); |
|
return YASM_BC_RESOLVE_ERROR | |
|
YASM_BC_RESOLVE_UNKNOWN_LEN; |
|
} |
|
jrtype = JMP_SHORT; |
|
} |
|
} |
|
yasm_expr_destroy(temp); |
|
break; |
|
} |
|
|
|
switch (jrtype) { |
|
case JMP_SHORT: |
|
if (save) |
|
jmp->op_sel = JMP_SHORT; |
|
if (jmp->shortop.len == 0) |
|
return YASM_BC_RESOLVE_UNKNOWN_LEN; /* size not available */ |
|
|
|
bc->len += jmp->shortop.len + 1; |
|
break; |
|
case JMP_NEAR: |
|
if (save) |
|
jmp->op_sel = JMP_NEAR; |
|
if (jmp->nearop.len == 0) |
|
return YASM_BC_RESOLVE_UNKNOWN_LEN; /* size not available */ |
|
|
|
bc->len += jmp->nearop.len; |
|
bc->len += (opersize == 16) ? 2 : 4; |
|
break; |
|
default: |
|
yasm_internal_error(N_("unknown jump type")); |
|
} |
|
bc->len += x86_common_resolve(&jmp->common); |
|
|
|
return retval; |
|
} |
|
|
|
static yasm_bc_resolve_flags |
|
x86_bc_jmpfar_resolve(yasm_bytecode *bc, int save, |
|
yasm_calc_bc_dist_func calc_bc_dist) |
|
{ |
|
x86_jmpfar *jmpfar = (x86_jmpfar *)bc->contents; |
|
unsigned char opersize; |
|
|
|
opersize = (jmpfar->common.opersize == 0) ? |
|
jmpfar->common.mode_bits : jmpfar->common.opersize; |
|
|
|
bc->len += jmpfar->opcode.len; |
|
bc->len += 2; /* segment */ |
|
bc->len += (opersize == 16) ? 2 : 4; |
|
bc->len += x86_common_resolve(&jmpfar->common); |
|
|
|
return YASM_BC_RESOLVE_MIN_LEN; |
|
} |
|
|
|
static void |
|
x86_common_tobytes(const x86_common *common, unsigned char **bufp, |
|
unsigned int segreg) |
|
{ |
|
if (common->lockrep_pre != 0) |
|
YASM_WRITE_8(*bufp, common->lockrep_pre); |
|
if (segreg != 0) |
|
YASM_WRITE_8(*bufp, (unsigned char)segreg); |
|
if (common->opersize != 0 && |
|
((common->mode_bits != 64 && common->opersize != common->mode_bits) || |
|
(common->mode_bits == 64 && common->opersize == 16))) |
|
YASM_WRITE_8(*bufp, 0x66); |
|
if (common->addrsize != 0 && common->addrsize != common->mode_bits) |
|
YASM_WRITE_8(*bufp, 0x67); |
|
} |
|
|
|
static void |
|
x86_opcode_tobytes(const x86_opcode *opcode, unsigned char **bufp) |
|
{ |
|
unsigned int i; |
|
for (i=0; i<opcode->len; i++) |
|
YASM_WRITE_8(*bufp, opcode->opcode[i]); |
|
} |
|
|
|
static int |
|
x86_bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
|
yasm_output_expr_func output_expr, |
|
/*@unused@*/ yasm_output_reloc_func output_reloc) |
|
{ |
|
x86_insn *insn = (x86_insn *)bc->contents; |
|
/*@null@*/ x86_effaddr *x86_ea = (x86_effaddr *)insn->ea; |
|
/*@null@*/ yasm_effaddr *ea = &x86_ea->ea; |
|
yasm_immval *imm = insn->imm; |
|
unsigned int i; |
|
unsigned char *bufp_orig = *bufp; |
|
|
|
/* Prefixes */ |
|
if (insn->special_prefix != 0) |
|
YASM_WRITE_8(*bufp, insn->special_prefix); |
|
x86_common_tobytes(&insn->common, bufp, ea ? (ea->segreg>>8) : 0); |
|
if (insn->rex != 0xff) { |
|
if (insn->common.mode_bits == 64 && insn->common.opersize == 64 && |
|
insn->def_opersize_64 != 64) |
|
insn->rex |= 0x48; |
|
if (insn->rex != 0) { |
|
if (insn->common.mode_bits != 64) |
|
yasm_internal_error( |
|
N_("x86: got a REX prefix in non-64-bit mode")); |
|
YASM_WRITE_8(*bufp, insn->rex); |
|
} |
|
} |
|
|
|
/* Opcode */ |
|
x86_opcode_tobytes(&insn->opcode, bufp); |
|
|
|
/* Effective address: ModR/M (if required), SIB (if required), and |
|
* displacement (if required). |
|
*/ |
|
if (ea) { |
|
if (x86_ea->need_modrm) { |
|
if (!x86_ea->valid_modrm) |
|
yasm_internal_error(N_("invalid Mod/RM in x86 tobytes_insn")); |
|
YASM_WRITE_8(*bufp, x86_ea->modrm); |
|
} |
|
|
|
if (x86_ea->need_sib) { |
|
if (!x86_ea->valid_sib) |
|
yasm_internal_error(N_("invalid SIB in x86 tobytes_insn")); |
|
YASM_WRITE_8(*bufp, x86_ea->sib); |
|
} |
|
|
|
if (ea->disp) { |
|
x86_effaddr eat = *x86_ea; /* structure copy */ |
|
unsigned char displen = ea->len; |
|
unsigned char addrsize = insn->common.addrsize; |
|
|
|
eat.valid_modrm = 0; /* force checkea to actually run */ |
|
|
|
/* Call checkea() to simplify the registers out of the |
|
* displacement. Throw away all of the return values except for |
|
* the modified expr. |
|
*/ |
|
if (yasm_x86__expr_checkea(&ea->disp, &addrsize, |
|
insn->common.mode_bits, ea->nosplit, |
|
insn->postop == X86_POSTOP_ADDRESS16, |
|
&displen, &eat.modrm, &eat.valid_modrm, |
|
&eat.need_modrm, &eat.sib, |
|
&eat.valid_sib, &eat.need_sib, |
|
&eat.pcrel, &insn->rex, |
|
yasm_common_calc_bc_dist)) |
|
yasm_internal_error(N_("checkea failed")); |
|
|
|
if (ea->disp) { |
|
if (eat.pcrel) { |
|
/*@null@*/ yasm_expr *wrt = yasm_expr_extract_wrt(&ea->disp); |
|
ea->disp = |
|
yasm_expr_create(YASM_EXPR_SUB, |
|
yasm_expr_expr(ea->disp), |
|
yasm_expr_sym(eat.origin), bc->line); |
|
if (wrt) { |
|
ea->disp = |
|
yasm_expr_create(YASM_EXPR_WRT, |
|
yasm_expr_expr(ea->disp), |
|
yasm_expr_expr(wrt), bc->line); |
|
} |
|
if (output_expr(&ea->disp, *bufp, ea->len, |
|
(size_t)(ea->len*8), 0, |
|
(unsigned long)(*bufp-bufp_orig), bc, 1, 1, |
|
d)) |
|
return 1; |
|
} else { |
|
if (output_expr(&ea->disp, *bufp, ea->len, |
|
(size_t)(ea->len*8), 0, |
|
(unsigned long)(*bufp-bufp_orig), bc, 0, 1, |
|
d)) |
|
return 1; |
|
} |
|
*bufp += ea->len; |
|
} else { |
|
/* 0 displacement, but we didn't know it before, so we have to |
|
* write out 0 value. |
|
*/ |
|
for (i=0; i<ea->len; i++) |
|
YASM_WRITE_8(*bufp, 0); |
|
} |
|
} |
|
} |
|
|
|
/* Immediate (if required) */ |
|
if (imm && imm->val) { |
|
if (output_expr(&imm->val, *bufp, imm->len, (size_t)(imm->len*8), 0, |
|
(unsigned long)(*bufp-bufp_orig), bc, 0, 1, d)) |
|
return 1; |
|
*bufp += imm->len; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int |
|
x86_bc_jmp_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
|
yasm_output_expr_func output_expr, |
|
/*@unused@*/ yasm_output_reloc_func output_reloc) |
|
{ |
|
x86_jmp *jmp = (x86_jmp *)bc->contents; |
|
unsigned char opersize; |
|
unsigned int i; |
|
unsigned char *bufp_orig = *bufp; |
|
/*@null@*/ yasm_expr *wrt; |
|
|
|
/* Prefixes */ |
|
x86_common_tobytes(&jmp->common, bufp, 0); |
|
|
|
/* As opersize may be 0, figure out its "real" value. */ |
|
opersize = (jmp->common.opersize == 0) ? |
|
jmp->common.mode_bits : jmp->common.opersize; |
|
|
|
/* Check here to see if forced forms are actually legal. */ |
|
switch (jmp->op_sel) { |
|
case JMP_SHORT_FORCED: |
|
case JMP_SHORT: |
|
/* 1 byte relative displacement */ |
|
if (jmp->shortop.len == 0) |
|
yasm_internal_error(N_("short jump does not exist")); |
|
|
|
/* Opcode */ |
|
x86_opcode_tobytes(&jmp->shortop, bufp); |
|
|
|
/* Relative displacement */ |
|
wrt = yasm_expr_extract_wrt(&jmp->target); |
|
jmp->target = |
|
yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(jmp->target), |
|
yasm_expr_sym(jmp->origin), bc->line); |
|
if (wrt) |
|
jmp->target = yasm_expr_create_tree(jmp->target, |
|
YASM_EXPR_WRT, wrt, |
|
bc->line); |
|
if (output_expr(&jmp->target, *bufp, 1, 8, 0, |
|
(unsigned long)(*bufp-bufp_orig), bc, 1, 1, d)) |
|
return 1; |
|
*bufp += 1; |
|
break; |
|
case JMP_NEAR_FORCED: |
|
case JMP_NEAR: |
|
/* 2/4 byte relative displacement (depending on operand size) */ |
|
if (jmp->nearop.len == 0) { |
|
yasm__error(bc->line, N_("near jump does not exist")); |
|
return 1; |
|
} |
|
|
|
/* Opcode */ |
|
x86_opcode_tobytes(&jmp->nearop, bufp); |
|
|
|
/* Relative displacement */ |
|
wrt = yasm_expr_extract_wrt(&jmp->target); |
|
jmp->target = |
|
yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(jmp->target), |
|
yasm_expr_sym(jmp->origin), bc->line); |
|
if (wrt) |
|
jmp->target = yasm_expr_create_tree(jmp->target, |
|
YASM_EXPR_WRT, wrt, |
|
bc->line); |
|
i = (opersize == 16) ? 2 : 4; |
|
if (output_expr(&jmp->target, *bufp, i, i*8, 0, |
|
(unsigned long)(*bufp-bufp_orig), bc, 1, 1, d)) |
|
return 1; |
|
*bufp += i; |
|
break; |
|
default: |
|
yasm_internal_error(N_("unrecognized relative jump op_sel")); |
|
} |
|
return 0; |
|
} |
|
|
|
static int |
|
x86_bc_jmpfar_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
|
yasm_output_expr_func output_expr, |
|
/*@unused@*/ yasm_output_reloc_func output_reloc) |
|
{ |
|
x86_jmpfar *jmpfar = (x86_jmpfar *)bc->contents; |
|
unsigned int i; |
|
unsigned char *bufp_orig = *bufp; |
|
unsigned char opersize; |
|
|
|
x86_common_tobytes(&jmpfar->common, bufp, 0); |
|
x86_opcode_tobytes(&jmpfar->opcode, bufp); |
|
|
|
/* As opersize may be 0, figure out its "real" value. */ |
|
opersize = (jmpfar->common.opersize == 0) ? |
|
jmpfar->common.mode_bits : jmpfar->common.opersize; |
|
|
|
/* Absolute displacement: segment and offset */ |
|
i = (opersize == 16) ? 2 : 4; |
|
if (output_expr(&jmpfar->offset, *bufp, i, i*8, 0, |
|
(unsigned long)(*bufp-bufp_orig), bc, 0, 1, d)) |
|
return 1; |
|
*bufp += i; |
|
if (output_expr(&jmpfar->segment, *bufp, 2, 2*8, 0, |
|
(unsigned long)(*bufp-bufp_orig), bc, 0, 1, d)) |
|
return 1; |
|
*bufp += 2; |
|
|
|
return 0; |
|
} |
|
|
|
int |
|
yasm_x86__intnum_fixup_rel(yasm_arch *arch, yasm_intnum *intn, size_t valsize, |
|
const yasm_bytecode *bc, unsigned long line) |
|
{ |
|
yasm_intnum *delta; |
|
if (valsize != 8 && valsize != 16 && valsize != 32) |
|
yasm_internal_error( |
|
N_("tried to do PC-relative offset from invalid sized value")); |
|
delta = yasm_intnum_create_uint(bc->len); |
|
yasm_intnum_calc(intn, YASM_EXPR_SUB, delta, line); |
|
yasm_intnum_destroy(delta); |
|
return 0; |
|
} |
|
|
|
int |
|
yasm_x86__intnum_tobytes(yasm_arch *arch, const yasm_intnum *intn, |
|
unsigned char *buf, size_t destsize, size_t valsize, |
|
int shift, const yasm_bytecode *bc, int warn, |
|
unsigned long line) |
|
{ |
|
/* Write value out. */ |
|
yasm_intnum_get_sized(intn, buf, destsize, valsize, shift, 0, warn, |
|
line); |
|
return 0; |
|
}
|
|
|