2

Why does this code work?

http://www.int80h.org/strlen/ says that the string address has to be in EDI register for scasb to work, but this assembly function doesn't seem to do this.

Assembly code for mystrlen:

global  mystrlen
mystrlen:
        sub             ecx, ecx
        not             ecx
        sub             al, al
        cld
        repne scasb
        neg             ecx
        dec             ecx
        dec             ecx
        mov             eax, ecx
        ret

C main:

int mystrlen(const char *);
int main()
{
    return (mystrlen("1234"));
}

Compilation:

nasm -f elf64 test.asm
gcc -c main.c
gcc main.o test.o

Output:

./a.out
echo $?
4
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
NanoPish
  • 1,379
  • 1
  • 19
  • 35
  • 6
    The 64 bit sysv calling convention places the first argument into `rdi`. So the caller `main` already did that load for you. You can examine its assembly code and see for yourself. – Jester Mar 07 '17 at 18:24
  • 1
    `cld` is also nonsense/redundant. The calling convention requires the direction flag to be clear at function entry. – R.. GitHub STOP HELPING ICE Mar 07 '17 at 18:34
  • @R.. What does it mean ? I'm really new to asm – NanoPish Mar 07 '17 at 18:42
  • 2
    I believe this works a bit by accident, that source is actually 32b, searching for 4G string length at most (if you would provide it with 5G long string, it would return incorrect answer). Zeroing `ecx` will clear also `rcx`, but then `not ecx` is fail (`dec rcx` would be better) and then the remaining inversion of `ecx` would have to be adjusted to `rcx` too, and return value in `rax`. – Ped7g Mar 07 '17 at 22:56
  • @Ped7g You are right, add this as answer maybe ? – NanoPish Mar 08 '17 at 15:53
  • @NanoP I did already posted working 64b modified version.. and got downvoted (sort of fair, as it is not directly answering your question, but extended info adding to the accepted answer, still I hope that was the reason, as otherwise I have no idea what's there not to like). – Ped7g Mar 08 '17 at 15:56
  • @Ped7g Should I accept yours or the other, as both are true & answering my question ? – NanoPish Mar 08 '17 at 16:00
  • @NanoP I don't care, basically I don't see any reason to change anything, keep it as is. I just added some more explanations to my answer, if somebody else will hit on it, to avoid confusion. You can comment under it, if something is not clear, or you have some improvement on mind, thank you. :) – Ped7g Mar 08 '17 at 16:05
  • @Ped7g Thank you ! – NanoPish Mar 08 '17 at 16:08

2 Answers2

5

The code from the question is 32 bit version of strlen, which works in 64b environment only partially, sort of "by accident" (as most of the SW works in reality, anyway ;) ).

One of the accidental effects of 64b environment is (in System V ABI, which is used by 64b linux OS, other 64b platforms may follow different calling convention, invalidating this!), that the first argument in function call is passed through rdi register, and the scasb is using es:rdi in 64b mode, so this naturally fits together (as the Jester's answer says).

Rest of the 64b environment effects are less good, that code will return wrong value for 4+G long string (I know, highly unlikely to happen in practical usage, but can be tried by synthetic test providing such long string).

Fixed 64b version (also the end of routine exploits rax=0 to do both neg ecx and mov eax,ecx in single instruction):

global  mystrlen
mystrlen:
        xor       ecx,ecx    ; rcx = 0
        dec       rcx        ; rcx = -1 (0xFFFFFFFFFFFFFFFF)
        ; rcx = maximum length to scan
        xor       eax,eax    ; rax = 0 (al = 0 value to scan for)
        repne scasb          ; scan the memory for AL
        sub       rax,rcx    ; rax = 0 - rcx_leftover = scanned bytes + 1
        sub       rax,2      ; fix that into "string length" (-1 for '\0')
        ret
Ped7g
  • 16,236
  • 3
  • 26
  • 63
3

The 64 bit sysv calling convention places the first argument into rdi. So the caller main already did that load for you. You can examine its assembly code and see for yourself.

(Answer provided by Jester)

anatolyg
  • 26,506
  • 9
  • 60
  • 134