Hi, I'm using nasm and i wanted to know which is the recommended way of copying a string to a variable located in the .bss section, one char at a time.
Posted on 2007-09-22 12:46:39 by tronic

Hi, I'm using nasm and i wanted to know which is the recommended way of copying a string to a variable located in the .bss section, one char at a time.


By stating "one char at a time" I assume that you are working with variable-length strings... so using a basic loop with address pointers and register-based mov's OR by utilizing instructions like movsb would be a good idea. The first method should be obvious and may be faster in certain situations, here is the movsb example...



src_string DB 'Testing...'


cld ;Instruct ESI/EDI to Increment per rep
mov ecx,10 ;Length of String
mov esi,src_string ;Pointer to Source String
mov edi,dest_string ;Pointer to Destination String
repz movsb


dest_string RESB 512


HtH.
Posted on 2007-09-22 14:10:14 by SpooK
Where is the source string coming from? Do you know it's length in advance? It it a zero-terminated string or some other fancy format? Why do you want to copy it to .bss instead of using it where it's already located? Does the string have a static compile-time length, or dynamic at runtime? How large will the string generally be? Are you aiming for code speed or size? - et cetera.
Posted on 2007-09-22 17:38:16 by f0dder
Thanks for the code but it's not working as it is.

I'm reading from a file using the read(2) Linux syscall, one char at a time.

The current char read is in each time so I do:
mov esi, ecx
mov edi, dest
movsb


I don't know the string's length, but not more than 30 chars.
I'm using printf to print the string.
So when I'm pushing dest inside the loop, underneath the movsb instruction it works.

But not after the loop.
It just prints a \n, which is the last char of the string.

Why?
Posted on 2007-09-22 18:38:04 by tronic

Thanks for the code but it's not working as it is.

I'm reading from a file using the read(2) Linux syscall, one char at a time.

The current char read is in each time so I do:
mov esi, ecx
mov edi, dest
movsb


I don't know the string's length, but not more than 30 chars.
I'm using printf to print the string.
So when I'm pushing dest inside the loop, underneath the movsb instruction it works.

But not after the loop.
It just prints a \n, which is the last char of the string.

Why?


It seems you are resetting edi every time by giving it the base of the "dest" address... thus the first character will always be the last one copied to it. Avoid it by using something like this...


mov edi,dest
.read_one:
;perform the INT 0x80 read SYSCALL
mov al,BYTE
mov BYTE,al
inc edi
;keep looping to .read_one until done...
;printf "dest"


However, you are better off passing the destination pointer directly to read, and avoid trying to do a seemingly useless string copy. My Linux SYSCALL knowledge is a little rusty so bare with me...



;EAX, ECX and EDX are *not* guaranteed to be preserved as per the x86 ABI...

;Setup Pointers for Read Operation
mov edi,dest
mov ebx,DWORD ;Pointer to File Handle established by "open"

;Peform the Read Operation
READ_LOOP:
mov eax,3 ;"read" function
mov ecx,edi ;Pointer to Destination Address
mov edx,1 ;Number of Bytes to read
int 0x80 ;Perform the SYSCALL
inc edi ;Increment the string pointer
;keep looping until whatever...

;printf using the address of "dest"


file RESD 1
dest RESB 30


There are plenty of improvements that can be had, so let us know exactly what you are trying to do and we can give you better answers.
Posted on 2007-09-22 19:55:45 by SpooK
Thank you very much SpooK.
It worked.

I realized it 5 minutes before your post but I was lost on how I would do it.
I don't need an improvement.
Posted on 2007-09-22 20:23:49 by tronic
Oh and something else, is there a quick way to chop off the last char of the dest string? I don't want the last "\n" char.
Posted on 2007-09-22 20:32:05 by tronic

Oh and something else, is there a quick way to chop off the last char of the dest string? I don't want the last "\n" char.



mov eax,dst-1
TRIM:
inc eax
cmp BYTE,0
je DONE
cmp BYTE,0x0A
jne TRIM
mov BYTE,0
DONE:
Posted on 2007-09-22 20:35:37 by SpooK
Thanks!
Posted on 2007-09-22 20:47:12 by tronic

Thanks!


NP :)

BTW, this assumes a NULL-terminated string... so always make sure your destination buffer is 1 more BYTE  larger than your maximum assumed string size just for safety... BSS will make sure that last BYTE is initialized as zero.

You can also substitute the NULL-check with a loop that "knows" the maximum length of the destination buffer ;)
Posted on 2007-09-22 20:48:32 by SpooK
Yes I understand it.

Do I have to use cld each time I copy strings from one place to another or just one time?
If I don't use it, is it the default (the DF flag to be 0)?
Posted on 2007-09-23 09:06:46 by tronic

Yes I understand it.

Do I have to use cld each time I copy strings from one place to another or just one time?
If I don't use it, is it the default (the DF flag to be 0)?


Depends, are you still trying to use MOVSB to only store 1 byte at a time? If you are, this is extremely inefficient. MOVS* only becomes efficient at larger runs of data movement. If you are dead-set on going with this "one byte at a time" method with MOVSB, or not letting read store directly to the buffer itself, then I would keep CLD in there just to be absolutely safe.

Using less instructions does not always equate to faster or more efficient code ;)
Posted on 2007-09-23 12:22:49 by SpooK