So this seems like it’s a very simple problem to anyone at all conversant in assembly, but I was hoping someone could explain to me what the difference between the following two pieces of code are, given that one results in a segmentation fault and the other doesn’t, but (to me) they seem like they should be logically equivalent.
Works fine:
char *src1; int esi_out, eax;
__asm__
__volatile__(
"lodsb\n\t;"
: "=&S" (esi_out), "=&a" (eax)
: "0" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x\n", *src1, src1, esi_out, eax);
and prints:
src1 w @ 0x7fffce186959, esi_out: ce18695a, eax: ce186977
So my understanding it that this code should load the value of src1 (which is an address) into ESI, copy that value into EAX, increment the address in ESI by 1 byte, and then upon exiting, output those values into local C variables esi_out and eax. src1 and esi_out look correct, but eax seems like it’s off. What’s going on here?
The second bit of code is where we see a segfault that I can’t quite come to grips with:
__asm__
__volatile__(
"movl %%ebx, %%esi\n\t;"
//"lodsb\n\t;"
: "=&S" (esi_out), "=&b" (ebx), "=&a" (eax)
: "1" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x, ebx: %x\n",
*src1, src1, esi_out, eax, ebx);
With the lodsb command commented out, it produces:
src1 w @ 0x7ffff093b959, esi_out: f093b959, eax: f093b959, ebx: f093b959
And with the lodsb command not commented out, it segfaults. To my way of thinking, loading the ESI value directly, as in the first case above, and loading it into EBX and then movl’ing it into ESI should be equivalent, no?
What am I missing? Why does the value written into EAX look off? I wrote the equivalent program directly into assembly and stepped through it using gdb and it works fine.
Any insight would be greatly appreciated.
From the looks of the output of the
%pin your printf, you’re compiling for 64 bits but your asm code is assuming 32 bits. TryYou should also declare
esi_outandebxas a pointer type (void*orchar*) oruintptr_t.What was happening is that lodsb uses RSI as its source address in 64 bits mode but you were putting only the low 32 bits of the pointer value into RSI so it doesn’t contain a valid address, hence the segfault. As Slagh says, only the low 8 bits of the a register (al) are modified by lodsb. You should either mask off the other bits (eax & 0xff), clear rax (xor %rax,%rax) or declare eax as a
char.You should also find a resource about x86_64 assembly in general if this is a surprise to you.