I'm working on an exercise in x86 assembly (using NASM) that has the niche requirement of limiting each instruction to a maximum of 3 bytes.
I'd like to call a label, but the normal way to do this (shown in the code example) always results in an instruction size of 5 bytes. I'm trying to find out if there's a series of instructions, 3 bytes or less each, that can accomplish this.
I've attempted to load the label address into a register and then call that register, but it seems like the address is then interpreted as an absolute address, instead of a relative one.
I looked around to see if there's a way to force call to interpret the address in the register as a relative address, but couldn't find anything. I have thought about simulating a call by pushing a return address to the stack and using jmp rel8
, but am unsure how to get the absolute address of where I want to return to.
Here is the normal way to do what I want:
[BITS 32]
call func ; this results in a 5-byte call rel32 instruction
; series of instructions here that I would like to return to
func:
; some operations here
ret
I have tried things like this:
[BITS 32]
mov eax, func ; 5-byte mov r32, imm32
call eax ; 2-byte call r32
; this fails, seems to interpret func's relative address as an absolute
... ; series of instructions here that I would like to return to
func:
; some operations here
ret
I have a feeling there may be a way to do this using some sort of LEA magic, but I'm relatively new to assembly so I couldn't figure it out.
Any tips are appreciated!
There is no such thing as relative indirect near CALL
. You will have to find some other mechanism to do the call to the label func
. One method I can think of is building the absolute address in a register and doing an absolute indirect call through the register:
It is unclear what the target of your code is. This assumes you are generating a 32-bit Linux program. I use a linker script to compute the individual bytes of the target label. Those bytes will be used by the program to build a return address in EAX and then an indirect near call via EAX will be performed. A couple methods of building the address are presented.
A linker script link.ld
that breaks a label's address into individual bytes:
SECTIONS
{
. = 0x8048000;
func_b0 = func & 0x000000ff;
func_b1 = (func & 0x0000ff00) >> 8;
func_b2 = (func & 0x00ff0000) >> 16;
func_b3 = (func & 0xff000000) >> 24;
}
Assembly code file myprog.asm
:
[BITS 32]
global func
extern func_b0, func_b1, func_b2, func_b3
_start:
; Method 1
mov al, func_b3 ; EAX = ######b3
mov ah, func_b2 ; EAX = ####b2b3
bswap eax ; EAX = b3b2####
mov ah, func_b1 ; EAX = b3b2b1##
mov al, func_b0 ; EAX = b3b2b1b0
call eax
; Method 2
mov ah, func_b3 ; EAX = ####b3##
mov al, func_b2 ; EAX = ####b3b2
shl eax, 16 ; EAX = b3b20000
mov ah, func_b1 ; EAX = b3b2b100
mov al, func_b0 ; EAX = b3b2b1b0
call eax
; series of instructions here that I would like to return to
xor eax, eax
mov ebx, eax ; EBX = 0 return value
inc eax ; EAX = 1 exit system call
int 0x80 ; Do exit system call
func:
; some operations here
ret
Assemble and link with:
nasm -f elf32 -F dwarf myprog.asm -o myprog.o
gcc -m32 -nostartfiles -g -Tlink.ld myprog.o -o myprog
If you run objdump -Mintel -Dx
the information of interest would look something similar to:
00000020 g *ABS* 00000000 func_b0 00000004 g *ABS* 00000000 func_b2 08048020 g .text 00000000 func 00000080 g *ABS* 00000000 func_b1 00000008 g *ABS* 00000000 func_b3 ... 08048000 <_start>: 8048000: b0 08 mov al,0x8 8048002: b4 04 mov ah,0x4 8048004: 0f c8 bswap eax 8048006: b4 80 mov ah,0x80 8048008: b0 20 mov al,0x20 804800a: ff d0 call eax 804800c: b4 08 mov ah,0x8 804800e: b0 04 mov al,0x4 8048010: c1 e0 10 shl eax,0x10 8048013: b4 80 mov ah,0x80 8048015: b0 20 mov al,0x20 8048017: ff d0 call eax 8048019: 31 c0 xor eax,eax 804801b: 89 c3 mov ebx,eax 804801d: 40 inc eax 804801e: cd 80 int 0x80 08048020 <func>: 8048020: c3 ret
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With