diff options
Diffstat (limited to 'asm.sh')
| -rw-r--r-- | asm.sh | 1133 |
1 files changed, 1133 insertions, 0 deletions
@@ -0,0 +1,1133 @@ +# Copyright (C) 2026 Sebastian G. Kirmayer <gloria@gloria-mundi.eu> +# +# This file is part of sysf-i386. +# +# sysf-i386 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 of the License, or (at your option) +# any later version. +# +# sysf-i386 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 sysf-i386. If not, see <https://www.gnu.org/licenses/>. +# + +LC_ALL=POSIX + +_asm_cc() { + _asm_ccval=0 + for _asm_ccs in o no "b c nae" "nb nc ae" "e z" "ne nz" "be na" "nbe a" \ + s ns "p pe" "np po" "l nge" "nl ge" "le ng" "nle g" + do + for _asm_cc in $_asm_ccs + do + eval "$1$_asm_cc() { _asm_$1 $_asm_ccval \"\$@\" ; }" + done + : $(( _asm_ccval += 1 )) + done +} + +addl() { + _asm_src "$1" + _asm_dst "$2" + case $_asm_src/$_asm_dst in + imm/reg|imm/mem) + if [ $(( -0x80 <= _asm_src_imm && _asm_src_imm < 0x80 )) != 0 ] + then + byte 0x83 + _asm_modrm 0 _asm_dst + byte $_asm_src_imm + elif [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ] + then + byte 0x05 + long $_asm_src_imm + else + byte 0x81 + _asm_modrm 0 _asm_dst + long $_asm_src_imm + fi + ;; + reg/reg|reg/mem) + byte 0x01 + _asm_modrm $_asm_src_reg _asm_dst + ;; + reg/reg|mem/reg) + byte 0x03 + _asm_modrm $_asm_dst_reg _asm_src + ;; + *) + _asm_panic "addl $_asm_src $_asm_dst: unsupported" + esac +} + +andl() { + _asm_src "$1" + _asm_dst "$2" + case $_asm_src/$_asm_dst/$_asm_dst_reg in + imm/reg/0) + byte 0x25 + long $_asm_src_imm + ;; + imm/reg/*|imm/mem/*) + byte 0x81 + _asm_modrm 4 _asm_dst + long $_asm_src_imm + ;; + reg/reg/*|reg/mem/*) + byte 0x21 + _asm_modrm $_asm_src_reg _asm_dst + ;; + reg/reg/*|mem/reg/*) + byte 0x23 + _asm_modrm $_asm_dst_reg _asm_src + ;; + *) + _asm_panic "andl $_asm_src $_asm_dst: unsupported" + esac +} + +cmpb() { + _asm_src8 "$1" + _asm_dst8 "$2" + case $_asm_src/$_asm_dst/$_asm_dst_reg in + imm/reg/0) + byte 0x3C + byte $_asm_src_imm + ;; + imm/reg/*|imm/mem/*) + byte 0x80 + _asm_modrm 7 _asm_dst + byte $_asm_src_imm + ;; + reg/reg|reg/mem) + byte 0x38 + _asm_modrm $_asm_src_reg _asm_dst + ;; + reg/reg|mem/reg) + byte 0x3A + _asm_modrm $_asm_dst_reg _asm_src + ;; + *) + _asm_panic "cmpb $_asm_src $_asm_dst: unsupported" + esac +} + +cmpl() { + _asm_src "$1" + _asm_dst "$2" + case $_asm_src/$_asm_dst in + imm/reg|imm/mem) + if [ $(( -0x80 <= _asm_src_imm && _asm_src_imm < 0x80 )) != 0 ] + then + byte 0x83 + _asm_modrm 7 _asm_dst + byte $_asm_src_imm + elif [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ] + then + byte 0x3D + long $_asm_src_imm + else + byte 0x81 + _asm_modrm 7 _asm_dst + long $_asm_src_imm + fi + ;; + reg/reg|reg/mem) + byte 0x39 + _asm_modrm $_asm_src_reg _asm_dst + ;; + reg/reg|mem/reg) + byte 0x3B + _asm_modrm $_asm_dst_reg _asm_src + ;; + *) + _asm_panic "cmpl $_asm_src $_asm_dst: unsupported" + esac +} + +cmpsb() { + byte 0xA6 +} + +call() { + _asm_dst "$1" + case $_asm_dst in + reg|mem) + byte 0xFF + _asm_modrm 2 _asm_dst + return + ;; + imm) + ;; + *) + _asm_panic "call $_asm_dst: unsupported" + esac + + _asm_jmp_dist=$(( _asm_dst_imm - _asm_offset )) + byte 0xE8 + long $((_asm_jmp_dist-5)) +} + +_asm_cc cmov +_asm_cmov() { + _asm_src "$2" + _asm_dst "$3" + case $_asm_src/$_asm_dst in + reg/reg|mem/reg) + byte 0x0F + byte $(( 0x40 | $1 )) + _asm_modrm $_asm_dst_reg _asm_src + ;; + *) + _asm_panic "cmov $_asm_src $_asm_dst: unsupported" + esac +} + +decl() { + _asm_dst "$1" + case $_asm_dst in + reg) + byte $(( 0x48 | _asm_dst_reg )) + ;; + reg|mem) + byte 0xFF + _asm_modrm 1 _asm_dst + ;; + *) + _asm_panic "decl $_asm_dst: unsupported" + esac +} + +divl() { + _asm_src "$1" + case $_asm_src in + reg|mem) + byte 0xF7 + _asm_modrm 6 _asm_src + ;; + *) + _asm_panic "divl $_asm_src: unsupported" + esac +} + +incl() { + _asm_dst "$1" + case $_asm_dst in + reg) + byte $(( 0x40 | _asm_dst_reg )) + ;; + reg|mem) + byte 0xFF + _asm_modrm 0 _asm_dst + ;; + *) + _asm_panic "incl $_asm_dst: unsupported" + esac +} + +incb() { + _asm_dst8 "$1" + case $_asm_dst in + reg|mem) + byte 0xFE + _asm_modrm 0 _asm_dst + ;; + *) + _asm_panic "incb $_asm_dst: unsupported" + esac +} + +int() { + byte 0xCD + byte "$1" +} + +int3() { + byte 0xCC +} + +_asm_cc j +_asm_j() { + _asm_eval "$2" + _asm_jmp_dist=$(( _asm_value - _asm_offset )) + if [ $(( -0x80 <= _asm_jmp_dist-2 && _asm_jmp_dist-2 < 0x80 )) != 0 ] + then + byte $(( 0x70 | $1 )) + byte $((_asm_jmp_dist-2)) + else + byte 0x0F + byte $(( 0x80 | $1 )) + long $((_asm_jmp_dist-6)) + fi +} + +jmp() { + _asm_dst "$1" + case $_asm_dst in + reg|mem) + byte 0xFF + _asm_modrm 4 _asm_dst + return + ;; + imm) + ;; + *) + _asm_panic "jmp $_asm_dst: unsupported" + esac + + _asm_jmp_dist=$(( _asm_dst_imm - _asm_offset )) + if [ $(( -0x80 <= _asm_jmp_dist-2 && _asm_jmp_dist-2 < 0x80 )) != 0 ] + then + byte 0xEB + byte $((_asm_jmp_dist-2)) + else + byte 0xE9 + long $((_asm_jmp_dist-5)) + fi +} + +leal() { + _asm_src "$1" + _asm_dst "$2" + case $_asm_src/$_asm_dst in + mem/reg) + byte 0x8D + _asm_modrm $_asm_dst_reg _asm_src + ;; + *) + _asm_panic "leal $_asm_src $_asm_dst: unsupported" + esac +} + +movb() { + _asm_src8 "$1" + _asm_dst8 "$2" + case $_asm_src/$_asm_dst in + reg/reg|reg/mem) + byte 0x88 + _asm_modrm $_asm_src_reg _asm_dst + ;; + reg/reg|mem/reg) + byte 0x8A + _asm_modrm $_asm_dst_reg _asm_src + ;; + imm/reg) + byte $(( 0xB0 | _asm_dst_reg )) + byte $_asm_src_imm + ;; + imm/reg|imm/mem) + byte 0xC6 + _asm_modrm 0 _asm_dst + byte $_asm_src_imm + ;; + *) + _asm_panic "movb $_asm_src $_asm_dst: unsupported" + esac +} + +movl() { + _asm_src "$1" + _asm_dst "$2" + case $_asm_src/$_asm_dst in + reg/reg|reg/mem) + byte 0x89 + _asm_modrm $_asm_src_reg _asm_dst + ;; + reg/reg|mem/reg) + byte 0x8B + _asm_modrm $_asm_dst_reg _asm_src + ;; + imm/reg) + byte $(( 0xB8 | _asm_dst_reg )) + long $_asm_src_imm + ;; + imm/reg|imm/mem) + byte 0xC7 + _asm_modrm 0 _asm_dst + long $_asm_src_imm + ;; + *) + _asm_panic "movl $_asm_src $_asm_dst: unsupported" + esac +} + +mull() { + _asm_src "$1" + case $_asm_src in + reg|mem) + byte 0xF7 + _asm_modrm 4 _asm_src + ;; + *) + _asm_panic "mull $_asm_src: unsupported" + esac +} + +negl() { + _asm_dst "$1" + case $_asm_dst in + reg|mem) + byte 0xF7 + _asm_modrm 3 _asm_dst + ;; + *) + _asm_panic "negl $_asm_dst: unsupported" + esac +} + +nop() { + byte 0x90 +} + +notl() { + _asm_dst "$1" + case $_asm_dst in + reg|mem) + byte 0xF7 + _asm_modrm 2 _asm_dst + ;; + *) + _asm_panic "notl $_asm_dst: unsupported" + esac +} + +orl() { + _asm_src "$1" + _asm_dst "$2" + case $_asm_src/$_asm_dst in + imm/reg|imm/mem) + if [ $(( -0x80 <= _asm_src_imm && _asm_src_imm < 0x80 )) != 0 ] + then + byte 0x83 + _asm_modrm 1 _asm_dst + byte $_asm_src_imm + elif [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ] + then + byte 0x0D + long $_asm_src_imm + else + byte 0x81 + _asm_modrm 1 _asm_dst + long $_asm_src_imm + fi + ;; + reg/reg|reg/mem) + byte 0x09 + _asm_modrm $_asm_src_reg _asm_dst + ;; + reg/reg|mem/reg) + byte 0x0B + _asm_modrm $_asm_dst_reg _asm_src + ;; + *) + _asm_panic "orl $_asm_src $_asm_dst: unsupported" + esac +} + +orb() { + _asm_src8 "$1" + _asm_dst8 "$2" + case $_asm_src/$_asm_dst in + imm/reg|imm/mem) + if [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ] + then + byte 0x0C + long $_asm_src_imm + else + byte 0x80 + _asm_modrm 1 _asm_dst + long $_asm_src_imm + fi + ;; + reg/reg|reg/mem) + byte 0x08 + _asm_modrm $_asm_src_reg _asm_dst + ;; + reg/reg|mem/reg) + byte 0x0A + _asm_modrm $_asm_dst_reg _asm_src + ;; + *) + _asm_panic "orb $_asm_src $_asm_dst: unsupported" + esac +} + +popl() { + _asm_dst "$1" + case $_asm_dst in + reg) + byte $((0x58 | _asm_dst_reg)) + ;; + reg|mem) + byte 0x8F + _asm_modrm 0 _asm_dst + ;; + *) + _asm_panic "popl $_asm_src: unsupported" + esac +} + +pushl() { + _asm_src "$1" + case $_asm_src in + reg) + byte $((0x50 | _asm_src_reg)) + ;; + reg|mem) + byte 0xFF + _asm_modrm 6 _asm_src + ;; + imm) + byte 0x68 + long $_asm_src_imm + ;; + *) + _asm_panic "pushl $_asm_src: unsupported" + esac +} + +repnz() { + byte 0xF2 + "$@" +} + +repz() { + byte 0xF3 + "$@" +} + +ret() { + if [ $# -eq 0 ] + then + byte 0xC3 + else + byte 0xC2 + word "$1" + fi +} + +scasb() { + byte 0xAE +} + +shll() { + _asm_src8 "$1" + _asm_dst "$2" + case $_asm_src/$_asm_src_reg/$_asm_dst in + imm/*/reg|imm/*/mem) + if [ $_asm_src_imm = 1 ] ; then + byte 0xD1 + _asm_modrm 4 _asm_dst + else + byte 0xC1 + _asm_modrm 4 _asm_dst + byte $_asm_src_imm + fi + ;; + reg/1/reg|reg/1/mem) + byte 0xD3 + _asm_modrm 4 _asm_dst + ;; + *) + _asm_panic "shll $_asm_src $_asm_dst: unsupported" + esac +} + +shrl() { + _asm_src8 "$1" + _asm_dst "$2" + case $_asm_src/$_asm_src_reg/$_asm_dst in + imm/*/reg|imm/*/mem) + if [ $_asm_src_imm = 1 ] ; then + byte 0xD1 + _asm_modrm 5 _asm_dst + else + byte 0xC1 + _asm_modrm 5 _asm_dst + byte $_asm_src_imm + fi + ;; + reg/1/reg|reg/1/mem) + byte 0xD3 + _asm_modrm 5 _asm_dst + ;; + *) + _asm_panic "shrl $_asm_src $_asm_dst: unsupported" + esac +} + +subl() { + _asm_src "$1" + _asm_dst "$2" + case $_asm_src/$_asm_dst in + imm/reg|imm/mem) + if [ $(( -0x80 <= _asm_src_imm && _asm_src_imm < 0x80 )) != 0 ] + then + byte 0x83 + _asm_modrm 5 _asm_dst + byte $_asm_src_imm + elif [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ] + then + byte 0x2D + long $_asm_src_imm + else + byte 0x81 + _asm_modrm 5 _asm_dst + long $_asm_src_imm + fi + ;; + reg/reg|reg/mem) + byte 0x29 + _asm_modrm $_asm_src_reg _asm_dst + ;; + reg/reg|mem/reg) + byte 0x2B + _asm_modrm $_asm_dst_reg _asm_src + ;; + *) + _asm_panic "subl $_asm_src $_asm_dst: unsupported" + esac +} + +test() { + _asm_panic "test requires width suffix" +} + +testl() { + _asm_src "$1" + _asm_dst "$2" + case $_asm_src/$_asm_dst/$_asm_dst_reg in + imm/reg/0) + byte 0xA9 + long $_asm_src_imm + ;; + imm/reg/*|imm/mem/*) + byte 0xF7 + _asm_modrm 0 _asm_dst + long $_asm_src_imm + ;; + reg/reg/*|reg/mem/*) + byte 0x85 + _asm_modrm $_asm_src_reg _asm_dst + ;; + *) + _asm_panic "testl $_asm_src $_asm_dst: unsupported" + esac +} + +ud2() { + byte 0x0F + byte 0x0B +} + +xchgl() { + _asm_src "$1" + _asm_dst "$2" + case $_asm_src/$_asm_dst/$_asm_src_reg/$_asm_dst_reg in + reg/reg/0/*) + byte $((0x90 + _asm_dst_reg)) + ;; + reg/reg/*/0) + byte $((0x90 + _asm_src_reg)) + ;; + reg/reg/*|mem/reg/*) + byte 0x87 + _asm_modrm $_asm_dst_reg _asm_src + ;; + reg/reg/*|reg/mem/*) + byte 0x87 + _asm_modrm $_asm_src_reg _asm_dst + ;; + *) + _asm_panic "xchgl $_asm_src $_asm_dst: unsupported" + esac +} + +xorl() { + _asm_src "$1" + _asm_dst "$2" + case $_asm_src/$_asm_dst in + imm/reg|imm/mem) + if [ $(( -0x80 <= _asm_src_imm && _asm_src_imm < 0x80 )) != 0 ] + then + byte 0x83 + _asm_modrm 6 _asm_dst + byte $_asm_src_imm + elif [ $_asm_dst = reg ] && [ $_asm_dst_reg = 0 ] + then + byte 0x35 + long $_asm_src_imm + else + byte 0x81 + _asm_modrm 6 _asm_dst + long $_asm_src_imm + fi + ;; + reg/reg|reg/mem) + byte 0x31 + _asm_modrm $_asm_src_reg _asm_dst + ;; + reg/reg|mem/reg) + byte 0x33 + _asm_modrm $_asm_dst_reg _asm_src + ;; + *) + _asm_panic "xorl $_asm_src $_asm_dst: unsupported" + esac +} + +byte() { + : $(( _asm_offset += 1 )) + _asm_eval "$1" + _asm_printf "\\$(printf %03o $(( _asm_value & 0xFF )))" +} + +word() { + _asm_eval "$1" + _asm_word=$_asm_value + byte $(( _asm_word )) + byte $(( _asm_word >> 8 )) +} + +long() { + _asm_eval "$1" + _asm_long=$_asm_value + word $(( _asm_long )) + word $(( _asm_long >> 16 )) +} + +align() { + _asm_align=${1-4} + skip $(( (_asm_align - (_asm_offset % _asm_align)) % _asm_align )) +} + +skip() { + _asm_eval "$1" + : $(( _asm_offset += _asm_value )) + if [ $_asm_active != 0 ] ; then + while [ $_asm_value -gt 0 ] ; do + printf '\0' + : $(( _asm_value -= 1 )) + done + fi +} + +ascii() { + : $(( _asm_offset += ${#1} )) + _asm_printf '%s' "$1" +} + +_asm_modrm() { + eval "_asm_modrm_rm=\$$2" + case $_asm_modrm_rm in + reg) + byte $(( 0xC0 | $1 << 3 | $2_reg )) + return + ;; + mem) + ;; + *) + _asm_panic "modrm $_asm_modrm_rm: unsupported" + esac + + for part in base index scale displ ; do + eval "_asm_modrm_$part=\$$2_mem_$part" + done + + if [ -z "$_asm_modrm_displ" ] && [ "$_asm_modrm_base" != 5 ] ; then + _asm_modrm_displsize=0 + elif [ $(( -128 <= _asm_modrm_displ && _asm_modrm_displ < 128 )) != 0 ] + then + _asm_modrm_displsize=1 + else + _asm_modrm_displsize=2 + fi + + if [ -z "$_asm_modrm_index" ] && [ "$_asm_modrm_base" != 4 ] ; then + if [ -z "$_asm_modrm_base" ] ; then + byte $(( $1 << 3 | 5 )) + _asm_modrm_displsize=2 + else + byte $(( _asm_modrm_displsize << 6 | $1 << 3 | _asm_modrm_base )) + fi + else + if [ -z "$_asm_modrm_base" ] ; then + byte $(( $1 << 3 | 4 )) + _asm_modrm_displsize=2 + _asm_modrm_base=5 + else + byte $(( _asm_modrm_displsize << 6 | $1 << 3 | 4 )) + fi + [ "$_asm_modrm_index" ] || _asm_modrm_index=4 + byte $(( _asm_modrm_scale << 6 | _asm_modrm_index << 3 | _asm_modrm_base )) + fi + case $_asm_modrm_displsize in + 0) + ;; + 1) + byte $((_asm_modrm_displ)) + ;; + 2) + long $((_asm_modrm_displ)) + ;; + esac +} + +_asm_src8() { + _asm_op8 "$1" "_asm_src$2" +} + +_asm_src() { + _asm_op "$1" "_asm_src$2" +} + +_asm_dst8() { + _asm_op8 "$1" _asm_dst +} + +_asm_dst() { + _asm_op "$1" _asm_dst +} + +_asm_op8() { + case "$1" in + %*) + eval "$2=reg" + _asm_register8 "${1#%}" "$2_reg" + ;; + @*) + eval "$2=mem" + _asm_memory "${1#@}" "$2_mem" + ;; + *) + eval "$2=imm" + _asm_eval "$1" + eval "$2_imm=\$_asm_value" + esac +} + +_asm_op() { + case "$1" in + %*) + eval "$2=reg" + _asm_register "${1#%}" "$2_reg" + ;; + @*) + eval "$2=mem" + _asm_memory "${1#@}" "$2_mem" + ;; + *) + eval "$2=imm" + _asm_eval "$1" + eval "$2_imm=\$_asm_value" + esac +} + +_asm_register8() { + case "$1" in + al) eval "$2=0" ;; + cl) eval "$2=1" ;; + dl) eval "$2=2" ;; + bl) eval "$2=3" ;; + ah) eval "$2=4" ;; + ch) eval "$2=5" ;; + dh) eval "$2=6" ;; + bh) eval "$2=7" ;; + *) _asm_panic "unknown register %$1" + esac +} + +_asm_register() { + case "$1" in + eax) eval "$2=0" ;; + ecx) eval "$2=1" ;; + edx) eval "$2=2" ;; + ebx) eval "$2=3" ;; + esp) eval "$2=4" ;; + ebp) eval "$2=5" ;; + esi) eval "$2=6" ;; + edi) eval "$2=7" ;; + *) _asm_panic "unknown register %$1" + esac +} + +_asm_memory() { + _asm_memory_base= + _asm_memory_index= + _asm_memory_scale= + _asm_memory_displ= + _asm_memory_arg="$1+" + while [ "$_asm_memory_arg" ] ; do + _asm_memory_head="${_asm_memory_arg%%+*}" + _asm_memory_arg="${_asm_memory_arg#*+}" + case "$_asm_memory_head" in + %*) + if [ -z "$_asm_memory_base" ] ; then + _asm_register "${_asm_memory_head#%}" _asm_memory_base + elif [ -z "$_asm_memory_index" ] ; then + _asm_register "${_asm_memory_head#%}" _asm_memory_index + _asm_memory_scale=0 + else + _asm_panic "more than two registers in address calculation" + fi + ;; + [1248]%*) + if [ -z "$_asm_memory_index" ] ; then + _asm_register "${_asm_memory_head#[1248]%}" _asm_memory_index + _asm_memory_scale1=0 + _asm_memory_scale2=1 + _asm_memory_scale4=2 + _asm_memory_scale8=3 + : $(( _asm_memory_scale = _asm_memory_scale${_asm_memory_head%%\%*} )) + else + _asm_panic "two index registers in address calculation" + fi + ;; + *%*) + _asm_panic "illegal scale ${_asm_memory_head%%\%}" + ;; + *) + _asm_eval "$_asm_memory_head" + if [ -z "$_asm_memory_displ" ] ; then + _asm_memory_displ="$_asm_value" + else + _asm_panic "two displacements in address calculation" + fi + esac + done + [ "$_asm_memory_index" != 4 ] || _asm_panic "cannot use %esp as index" + for part in base index scale displ ; do + eval "$2_$part=\"\$_asm_memory_$part\"" + done +} + +_asm_eval() { + _asm_dropminus="${1#-}" + case "$_asm_dropminus" in + 0x*) + case "${_asm_dropminus#0x}" in + *[!0-9A-Fa-f]*) + ;; + *) + : $(( _asm_value = $1 )) + return + esac + ;; + 0*) + case "$_asm_dropminus" in + *[!0-7]*) + ;; + *) + : $(( _asm_value = $1 )) + return + esac + ;; + *[!0-9]*) + ;; + *) + : $(( _asm_value = $1 )) + return + esac + case "$1" in + [!a-zA-Z_]*|*[!a-zA-Z0-9_]*) + ;; + *) + eval "_asm_value=\$_asm_label_$1" + if [ "$_asm_value$_asm_pass0" = "" ] ; then + _asm_panic "label $1 not declared" + fi + : $(( _asm_value = _asm_value )) + return + esac + case "$1" in + *^|*.) + _asm_eval_L="${1%[.^]}" + _asm_L $_asm_eval_L + : $(( _asm_eval_L_idx = _asm_L_${_asm_eval_L}_idx )) + case "$1" in + *.) : $(( _asm_eval_L_idx += 1 )) + esac + eval "_asm_value=\$_asm_L_${_asm_eval_L}_${_asm_eval_L_idx}" + if [ "$_asm_value$_asm_pass0" = "" ] ; then + _asm_panic "label $1 not declared" + fi + : $(( _asm_value = _asm_value )) + return + esac + _asm_panic "failed to parse value $1" +} + +_asm_panic() { + printf '%s\n' "$@" >&2 + exit 1 +} + +_asm_printf() { + if [ $_asm_active != 0 ] ; then printf "$@" ; fi +} + +_asm_start_elf=0 +_asm_mmap_min_addr=0x10000 +_asm_flags_text=0x1 # x +_asm_flags_rodata=0x4 # r +_asm_flags_data=0x6 # rw + +_asm_section() { + : $(( _asm_offset_$_asm_section = _asm_offset )) + _asm_section="$1" + : $(( _asm_offset = _asm_offset_$_asm_section )) + : $(( _asm_active = _asm_active_$_asm_section )) +} + +text() { _asm_section text ; } +rodata() { _asm_section rodata ; } +data() { _asm_section data ; } +bss() { _asm_section bss ; } + +_asm_set() { + : $(( _asm_set_new = ($2) )) + : $(( _asm_modified |= $1 != _asm_set_new )) + : $(( $1 = _asm_set_new )) +} + +label() { + eval "_asm_label_oldval=\$_asm_label_$1" + if [ "$_asm_pass0" ] && [ "$_asm_label_oldval" ] ; then + _asm_panic "redefinition of label $1" + fi + if [ $# -eq 1 ] + then + _asm_set _asm_label_$1 _asm_offset + else + _asm_eval "$2" + _asm_set _asm_label_$1 '_asm_offset - _asm_value' + fi +} + +_asm_L() { + [ $(( _asm_generation == _asm_L_$1_gen )) != 0 ] || { + : $(( _asm_L_$1_gen = _asm_generation )) + : $(( _asm_L_$1_idx = 0 )) + } +} + +L() { + _asm_L $1 + _asm_set _asm_L_$1_$((_asm_L_$1_idx += 1)) _asm_offset +} + +_asm_elf() { + _asm_section elf + byte 0x7F # magic number + ascii 'ELF' + byte 1 # 32-bit format + byte 1 # little endian + byte 1 # ELF version + byte 3 # Linux + skip 8 # padding + word 2 # executable + word 3 # x86 + long 1 # ELF version + long _start # entry point + long _asm_phoff # program header offset + long 0 # section header offset + long 0 # flags + word _asm_elfhdr_end # ELF header size + word 32 # program header entry size + word 4 # program header entry count + word 0 # section header entry size + word 0 # section header entry count + word 0 # section name string table index + label _asm_elfhdr_end + label _asm_phoff + for _asm_local_section in text rodata data ; do + : $(( _asm_local_section_length = + _asm_end_$_asm_local_section - _asm_start_$_asm_local_section )) + long 1 # PT_LOAD + long $(( _asm_foffset_$_asm_local_section )) # file offset + long $(( _asm_start_$_asm_local_section )) # virtual address + long 0 # physical address + long $_asm_local_section_length # size in file + if [ $_asm_local_section = data ] + then + long $(( _asm_end_bss - _asm_start_data )) + else + long $_asm_local_section_length # size in memory + fi + long $(( _asm_flags_$_asm_local_section )) # permission flags + long 0x1000 # alignment + done + # The PT_GNU_STACK segment is needed to avoid the kernel's + # read-implies-exec personality. + long 0x6474e551 # PT_GNU_STACK + long 0 # file offset + long 0 # virtual address + long 0 # physical address + long 0 # size in file + long 0 # size in memory + long 0x6 # read/write + long 0x1000 # alignment +} + +_asm_run() { + for _asm_local_section in elf text rodata data bss ; do + : $(( _asm_offset_$_asm_local_section = + _asm_start_$_asm_local_section )) + : $(( _asm_active_$_asm_local_section = 0 )) + done + for _asm_local_section in "$@" ; do + : $(( _asm_active_$_asm_local_section = 1 )) + done + _asm_section=text + : $(( _asm_offset = _asm_offset_$_asm_section )) + : $(( _asm_active = _asm_active_$_asm_section )) + + _asm_modified=0 + : $(( _asm_generation += 1 )) + _asm_elf + text + prog + + : $(( _asm_offset_$_asm_section = _asm_offset )) + + _asm_set _asm_end_elf _asm_offset_elf + : $(( _asm_offset = _asm_end_elf + _asm_mmap_min_addr )) + for _asm_local_section in text rodata data bss ; do + : $(( _asm_offset2 = _asm_offset + + (_asm_offset_$_asm_local_section - _asm_start_$_asm_local_section) )) + _asm_set _asm_start_$_asm_local_section _asm_offset + _asm_set _asm_end_$_asm_local_section _asm_offset2 + _asm_offset="$_asm_offset2" + [ "$_asm_local_section" = data ] || \ + : $(( _asm_offset += 0x1000 )) + done + + _asm_foffset=0 + for _asm_local_section in elf text rodata data ; do + _asm_set _asm_foffset_$_asm_local_section _asm_foffset + : $(( _asm_foffset = _asm_foffset + + (_asm_end_$_asm_local_section - _asm_start_$_asm_local_section) )) + done +} + + +_asm_pass0=y +_asm_run +_asm_pass0= +_asm_limit=20 +while [ $_asm_modified -eq 1 ] ; do + _asm_run + : $(( _asm_limit -= 1 )) + if [ $_asm_limit -eq 0 ] ; then + _asm_panic "pass limit exceeded, failed to converge" + fi +done + +_asm_file_offset=0 +for _asm_top_section in elf text rodata data ; do + _asm_run $_asm_top_section +done |
