diff options
author | Peter Johnson <peter@tortall.net> | 2006-09-29 07:18:45 +0000 |
---|---|---|
committer | Peter Johnson <peter@tortall.net> | 2006-09-29 07:18:45 +0000 |
commit | 3e8d2c2e7a162cc7a8d20f07e60f6f009914e795 (patch) | |
tree | b2ebe82cdff8684829f809d3fd73633eedf29e88 /libyasm/value.c | |
parent | 096e2eb79a83c8da2d9e7f4841c2cb853be5befc (diff) | |
download | yasm-3e8d2c2e7a162cc7a8d20f07e60f6f009914e795.tar.gz |
- Fix much brokenness in absolute value handling, particularly in regards to
PC-relative relocations (jumps and calls).
- Allow SEG:OFF to be used as just an offset portion (like NASM does).
- Labels in absolute sections that are declared global are given the correct
absolute value in the symbol table.
One difference from NASM:
label equ 0040h:001eh
jmp label
in NASM means the same as:
jmp 001eh (a near jump)
but yasm will treat this the same as:
jmp 0040h:001eh (a far jump)
I'm still not completely happy with this implementation, but it's workable
and fixes all the bugs I've found so far in absolute handling.
svn path=/trunk/yasm/; revision=1634
Diffstat (limited to 'libyasm/value.c')
-rw-r--r-- | libyasm/value.c | 40 |
1 files changed, 38 insertions, 2 deletions
diff --git a/libyasm/value.c b/libyasm/value.c index 50b5482c..6ef16855 100644 --- a/libyasm/value.c +++ b/libyasm/value.c @@ -98,6 +98,22 @@ yasm_value_delete(yasm_value *value) value->rel = NULL; } +void +yasm_value_set_curpos_rel(yasm_value *value, yasm_bytecode *bc, + unsigned int ip_rel) +{ + value->curpos_rel = 1; + value->ip_rel = ip_rel; + /* In order for us to correctly output curpos-relative values, we must + * have a relative portion of the value. If one doesn't exist, point + * to a custom absolute symbol. + */ + if (!value->rel) { + value->rel = yasm_symtab_abs_sym(yasm_object_get_symtab( + yasm_section_get_object(yasm_bc_get_section(bc)))); + } +} + static int value_finalize_scan(yasm_value *value, yasm_expr *e, int ssym_not_ok) { @@ -569,9 +585,22 @@ yasm_value_output_basic(yasm_value *value, /*@out@*/ unsigned char *buf, return -1; } - /* Handle integer expressions */ + /* Handle normal integer expressions */ intn = yasm_expr_get_intnum(&value->abs, 1); + if (!intn) { + /* Second try before erroring: yasm_expr_get_intnum doesn't handle + * SEG:OFF, so try simplifying out any to just the OFF portion, + * then getting the intnum again. + */ + yasm_expr *seg = yasm_expr_extract_deep_segoff(&value->abs); + if (seg) + yasm_expr_destroy(seg); + intn = yasm_expr_get_intnum(&value->abs, 1); + } + + if (!intn) { + /* Still don't have an integer! */ yasm_error_set(YASM_ERROR_TOO_COMPLEX, N_("expression too complex")); return -1; @@ -617,7 +646,14 @@ yasm_value_output_basic(yasm_value *value, /*@out@*/ unsigned char *buf, bc, warn)) retval = -1; yasm_intnum_destroy(outval); - } else if (intn) { + return retval; + } + + if (value->seg_of || value->rshift || value->curpos_rel || value->ip_rel + || value->section_rel) + return 0; /* We can't handle this with just an absolute */ + + if (intn) { /* Output just absolute portion */ if (yasm_arch_intnum_tobytes(arch, intn, buf, destsize, valsize, 0, bc, warn)) |