Linux X86 Nasm - Subroutine: Print a Dword from Eax

Linux x86 NASM - Subroutine: Print a dword from EAX

I'm very rusty at assembler, but it looks like ECX contains the value 48, when it should contain the address of the buffer to be written.

I presume what you intended in print_eax_val is to take the binary value of EAX, add the ASCII offset to digit 0 (which should have been 48, not 47) and then print that single character. To do this, add 48 before storing the value in to_print, put the address of to_print in ECX, and set the length (EDX) to 1, because you're writing only one character.

Now remember that this will work for EAX values between 0x0000 and 0x0009 only. When you go past 9 you will get other ASCII characters.

Explaining how to take an arbitrary binary value of EAX and convert it to a decimal string that can be printed is far beyond the scope of SO.

Printing an integer with x86 32-bit Linux sys_write (NASM)

Your numbers will quickly grow larger than just a single digit. What you ought to be doing is have an integer in num rather than a character, and then convert that integer into a string that you can print with sys_write.

Here's one way of doing the conversion: repeated division by 10, getting the lowest digit first as the remainder:

; Input:
; eax = integer value to convert
; esi = pointer to buffer to store the string in (must have room for at least 10 bytes)
; Output:
; eax = pointer to the first character of the generated string
; ecx = length of the generated string
int_to_string:
add esi,9
mov byte [esi],0 ; String terminator

mov ebx,10
.next_digit:
xor edx,edx ; Clear edx prior to dividing edx:eax by ebx
div ebx ; eax /= 10
add dl,'0' ; Convert the remainder to ASCII
dec esi ; store characters in reverse order
mov [esi],dl
test eax,eax
jnz .next_digit ; Repeat until eax==0

; return a pointer to the first digit (not necessarily the start of the provided buffer)
mov eax,esi
ret

Which you can use like this:

    mov    dword [num],1
...
mov eax,[num] ; function args using our own private calling convention
mov esi,buffer
call int_to_string
; eax now holds the address that you pass to sys_write
...

section .bss
num resd 1
buffer resb 10

Your number-doubling can be simplified to shl dword [num],1. Or better, double it at some point while it's still in a register with add eax,eax.

Printing a string in x86 Assembly on Mac OS X (NASM)

(Most of the credit goes to @Michael Petch's comment; I'm repeating it here so that it is an answer, and also in order to further clarify the reason for the additional four bytes on the stack.)

macOS is based on BSD, and, as per FreeBSD's documentation re system calls, by default the kernel uses the C calling conventions (which means arguments are pushed to the stack, from last to first), but assuming four extra bytes pushed to the stack, as "it is assumed the program will call a function that issues int 80h, rather than issuing int 80h directly".

That is, the kernel is not built for direct int 80h calls, but rather for code that looks like this:

kernel: ; subroutine to make system calls
int 80h
ret

.
.
.

; code that makes a system call
call kernel ; instead of invoking int 80h directly

Notice that call kernel would push the return address (used by the kernel subroutine's ret to return to calling code after the system call) onto the stack, accounting for four additional bytes – that's why it's necessary to manually push four bytes to the stack (any four bytes – their actual value doesn't matter, as it is ignored by the kernel – so one way to achieve this is sub esp, 4) when invoking int 80h directly.

The reason the kernel expects this behaviour – of calling a method which invokes the interrupt instead of invoking it directly – is that when writing code that can be run on multiple platforms it's then only needed to provide a different version of the kernel subroutine, rather than of every place where a system call is invoked (more details and examples in the link above).

Note: all the above is for 32-bit; for 64-bit the calling conventions are different – registers are used to pass the arguments rather than the stack (there's also a call convention for 32-bit which uses registers, but even then it's not the same registers), the syscall instruction is used instead of int 80h, and no extra four bytes (which, on 64-bit systems, would actually be eight bytes) need to be pushed.

Multiplying two values and printing them to the screen (NASM, Linux)

We spoke separately in chat....

Here's a working version to play with.

It has a subtle problem. Can you find it?
Can you explain WHY it does what it does?

; Multiply two numbers, display in ascii/decimal
;
; (because I have a 64bit system, this forces 32bit code)
bits 32
;
section .text
;
; _start is the ONLY label you MUST prepend _
; others might be library functions (ex: _printf,_exit)
; pure assembly only needs _start, if linked with glibc
; typically need _main INSTEAD of _start
;
global _start
;
;
_start:
nop ;placeholder for gdb's debug interrupt
;
call mul ;multiply the values
call convert_values ;do hex to ascii conversion
;
jmp safe_exit ;use jmp as opposed to call since it technically doesn't 'ret'
;
;
; subroutines / functions follow
;
mul:
mov eax, 0x2A ;store 42 in eax
mov edx, 0x2B ;store 43 in edx (42*43=1806)
mul edx ;multiply eax*edx, result in edx:eax
ret
;
; this routine doesn't handle BIG values from 'mul' which extend into edx
; we're learning, don't make things multiply out to more than 4.2 billion-ish
convert_values:
mov edx,0 ;value actually edx:eax, zero edx
mov ecx,0x0A ;divide edx:eax by 10
idiv ecx ;result in eax, remainder in edx
push eax ;save value on stack
mov eax,edx ;put remainder (0-9) in eax
add eax,'0' ;convert value to ascii character
call print_char ;print the latest character
pop eax ;restore value
or eax,eax ;set flags based on eax value
jnz convert_values ;while eax != 0 continue process
;
; nasm doesn't convert \n into LF... just use 10, equivalent
endl:
mov eax, 10 ;store newline character in eax to be printed
call print_char ;print value
ret
;
print_char:
mov [valueToPrint], eax ;store contents of 'eax' in [valueToPrint]
mov eax, 4 ;syswrite
mov ebx, 1 ;stdout
mov ecx, valueToPrint ;machine will take whatever value exists in [ecx] and print
mov edx, 1 ;print only a single byte's worth of data
int 0x80 ;invoke kernel to perfrom instruction
ret
;
safe_exit:
mov eax,1 ;initiate 'exit' syscall
mov ebx,0 ;exit with error code 0
int 0x80 ;invoke kernel to do its bidding
;
; =====================================
section .bss
; this section is not allocated, just reserved.
; Automatically set to zero when program starts
;
; alloc 4 bytes of data in 'valueToPrint'
valueToPrint:
resd 1 ; 1 resd=4 resb (Dword/Byte)
;
;

Spoiler Alert...


It prints the result BACKWARDS!

To fix this, we'll have to redesign how the digits are obtained
and stored before printing.

I'm emailing this directly to you, along with some additional notes.

NASM Subroutine Call Segmentation Fault

Well, you will need debugger, as there are several problems in your code, and it's a bit too large for me to run it in head accurately (like 100% guarding stack/etc), so only few things I see:

In CHAR_CHECK: loop test the length during loop, so you don't overwrite .bss memory when somebody gives you too long string. You can move the length check right under CHAR_LOOP:, when edi is out of bounds, stop looping.

Also add the null character before storing N (swap those two mov lines), as N is stored right after X in memory, so with 31 (?) long input string you will overwrite N to 0 (this particularly is not exploitable, but the copy of long string may be).

jl/jg used in length check, but length is unsigned, so jb/ja would made more sense to me (not a bug, signed test >=1 && <= 30 will fail at the same time as unsigned one, just doesn't feel right if you have programming OCD).

good/bad char test - you can make it a bit shorter by doing only two tests ('0' <= char && char <= '2'), as ['0', '1', '2'] are values [48, 49, 50].

And now more serious stuff follows.

In I/J loop you don't reset J, so logic of your inner loop will be flawed.

push dword [X] I don't think this does what you think it does. The address of string is X, [X] is content of memory (chars of string). (this will make the sufcmp code to segfault early, when it will try to access "address" '0010', which is not legal.

In the swap, for example mov edx, dword [y + edi] ... you increment edi by 1, but Y is defined as array of dwords, so everywhere the indexing should be edi*4.

cmp esi, dword [N-1] ; if i = N-1 uhm, nope, it will compare esi with value at address N-1, so if [N] contains 16 and ahead of it is single zero byte, the cmp will compare esi with value 4096 (memory at N-1 is: 00 10 00 00 00, so [N] == 0x00000010 and [N-1] == 0x00001000).

mov eax, dword [X] ; move address of X to eax - no, lea would do what the comment says. mov will fetch content of at address X.

add eax, [y + esi] - again using +-1 indexing with dword array.

And you forget to call print_string, only new line is called.

You can rewrite that part as:

mov eax,[y + esi*4]   ; eax = Y[i]
lea eax,[X + eax] ; eax = address X + Y[i]

And, as I'm cruel and tired, I kept the my biggest worry for last note.

I don't think this will work at all. Your bubble sort is iterating over original X string (well, it's not, but once you fix the argument issues with correct addresses, it will).

Every time. So you keep shuffling content of Y array according to original string in every iteration.


The most important part of my answer is the first sentence. You absolutely need debugger. If you felt like the language made some sense to you up till now, your source doesn't prove that. Actually I can see a glimpses of understanding, so you are basically right, but you would have to be total prodigy whizz kid to be able to pull this without debugger within reasonable time. I would grade you only as above-average, maybe good, but far away from prodigious premises.

If you still want to go without debugger, change technique. Don't write so much of code without compiling + running it. Do it by much much much smaller steps, and keep displaying all sort of things, to be sure your new 3 lines of code do what they should. For example if you would create empty stub for sufcmp just printing the string from pointer, it would segfault right after trying to access the string.

That would maybe give you better hint, than when almost final app code is segfaulting, so instead of hunting problem on recent 10 lines you have 50+ to reason about.


EDIT: algorithm suggestion:

Unless you really must use bubble sort, I would avoid that, and do the brute-force dumb "count" variant of sort.

i:[0,N): count[i] = countif(j:[0,N): X[j] < X[i])
i:[0,N): j:[0,N): if (i == count[j]) print X[j]

I hope you will be able to decipher it... it means that I would calculate for every suffix how many suffixes are "smaller" lexicographically, ie. full O(N2) loopy loop (which is in reality N^3, because comparing strings is another O(N) ... but who cares with N=30, even N5 would be bearable).

Then to print suffixes in correct order you simply search the count array again and again, first time for 0 smaller-count (that's the smallest one), then for 1, ... etc. Till you print all of them.

Actually you may loop through all suffixes, calculate how many are smaller, and put index of that suffix into sorted[smaller_count], so for printing you will just loop through sorted array from 0 to N-1, no searching involved.

Output Data Register value in NASM

First, thanks for the question-- it motivated me to study up in int 80h, something I wasn't familiar with before.

What does your program do in its current form? Does it print anything? If I'm executing it correctly in my head, I expect it to print the first message and then crash. Due to buffering, it might not even show the first message before the suspected crash.

int 80h/eax=4 maps to the write() function. Run 'man 2 write' to get the full documentation. The function prototype is:

ssize_t write(int fd, const void *buf, size_t count);

So, eax = 4, ebx = fd, ecx = buf, edx = count. If you want to be more pedantic with your code comments, "mov eax,4" means "write a string to a file" while "mov ebx,1" means "specify file #1, which maps to STDOUT (the terminal)".

I expect the first int 80h call to print something. The second int 80h call is suspect. At this point eax and ebx are unchanged. However, edx is also unchanged and that holds the string length of the first string. More problematic is that you are putting the value 5 into ecx. ecx holds a pointer to the string to be written, not a value to be written. So when you put 5 in there and call the interrupt, the int will try to print a string starting at address 0x00000005. This is almost certain to cause a segfault (crash).

Also, if I'm reading the int 80h docs correctly, the push/pop stack ops in your example don't have any relevance.

Printing a number as a string is a bit of a chore in ASM. You need to convert the number into a string and then you can print it using int 80h. Here's a way you can cheat if you just want to print a single digit:

  • in the .data section, declare a new byte called int2char: 'int2char: db 0'
  • after subtracting 5 from ecx, convert the digit to ASCII by adding 48 (this will give you '5' instead of 5)
  • store ecx into int2char
  • move the address of int2char into ecx
  • set edx to 1 since you only want to print 1 char

Converting numbers with more than one digit will be left as an exercise for you once you get this working.

Good luck!



Related Topics



Leave a reply



Submit