3

I'm writing a function in x86-64 to convert a 1-byte value into a hexadecimal string representing the ASCII code for that byte. At the start of my function, I try to use

movb %dil, %r11b

to store the 1-byte value in the lowest byte of register %r11. However, when I examine this in gdb, %r11b is never set. Instead, the higher bytes of %r11 are getting set. This is what I get when using gdb:

Breakpoint 1, 0x00000000004011f0 in byte_as_hex ()
(gdb) print /x $r11b
$1 = 0x0
(gdb) print /x $r11
$2 = 0x246
(gdb) print /x $rdi
$3 = 0x48
(gdb) print /x $dil
$4 = 0x48
(gdb) stepi  /* subq $8, %rsp */
0x00000000004011f4 in byte_as_hex ()
(gdb) print /x $r11b
$5 = 0x0
(gdb) print /x $r11
$6 = 0x246
(gdb) print /x $rdi
$7 = 0x48
(gdb) print /x $dil
$8 = 0x48
(gdb) stepi /* movb %dil, %r11b */
0x00000000004011f7 in byte_as_hex ()
(gdb) print /x $r11b
$9 = 0x0
(gdb) print /x $r11
$10 = 0x248
(gdb) print /x $rdi
$11 = 0x48
(gdb) print /x $dil
$12 = 0x48
(gdb) print /x $r11d
$13 = 0x248
(gdb) print /x $r11w
$14 = 0x248
(gdb) print /x $r11b
$15 = 0x0

I'm very confused because I specifically tried to movb from %dil into %r11b, but I still can't set the byte. Could anyone explain to me why this is this happening? Thanks!

dan0
  • 31
  • 2
  • Can you show the instruction for each of those steps so it's easier to follow what's *supposed* to have happened? That seems like broken / buggy GDB behaviour. – Peter Cordes Sep 29 '20 at 08:11
  • I wonder if GDB would be different with reg names like `%r11l`? There are 2 different conventions for byte-register names: `r11b` and `r11l`. I forget who invented which. It does seem that GDB is recognizing `r11b` as a register name, so that shouldn't be it, but you never know with possible bugs. – Peter Cordes Sep 29 '20 at 08:14
  • The beginning lines of my function are `byte_as_hex: subq $8, %rsp movb %dil, %r11b shr $4, %r11b` The only thing I did before attempting movb was adjust the stack pointer, so I'm not sure what could be going wrong. – dan0 Sep 29 '20 at 09:32
  • Sure, that's fine, but [edit] your question to put in which instruction just executed for each `stepi`, like as a "comment" or extra line in the GDB output. Your instructions are almost certainly executing just fine, and GDB is just not working correctly. (Wouldn't be the first time GDB has bugs. What version of GDB do you have?) – Peter Cordes Sep 29 '20 at 09:38
  • I just checked `r11l` instead of `r11b`, and it turns out that's where the value was being stored. I'm still not quite sure what was going on, but at least I know the value is being stored somewhere. My version of gdb is Fedora 8.3.50.20190824-30.fc31. I'll edit my question right now - this was my first question so I'm still learning how I should respond on here. Thanks for the help! – dan0 Sep 29 '20 at 09:43
  • `r11l` is the same register as `r11b`, the low byte of R11. It's a GDB bug that it treats them differently. See [Why does Apple use R8l for the byte registers instead of R8b?](https://stackoverflow.com/q/43991779) for the different register-naming conventions. Also [What are the names of the new X86\_64 processors registers?](https://stackoverflow.com/q/1753602) – Peter Cordes Sep 29 '20 at 09:46

1 Answers1

1

Multiple problems are at play here:

  1. (Reported as GDB bug.) An undefined convenience variable (a GDB-local variable that starts with $), when printed with an explicit format specifier, is shown as 0 instead of the default void, which is displayed when format is not specified:
$ gdb /bin/true
Reading symbols from /bin/true...
(gdb) p $asdf
$1 = void          <------ undefined, OK
(gdb) p/x $asdf
$2 = 0x0           <------ the problem
(gdb) set $asdf=4345
(gdb) p $asdf
$3 = 4345
(gdb) p/x $asdf
$4 = 0x10f9
(gdb) 
  1. Register values have the same syntax as the values of convenience variables. Thus, when you mistake the name of a register, e.g. use r11b instead of GDB's r11l, you refer to a(n undefined) convenience variable. Moreover, even if you simply use the correct name in incorrect case, like R11L, you bump into this too.
  2. GDB uses its own set of names for x86(_64) registers. Sometimes they differ from the names given e.g. in Intel manuals (e.g. ftag instead of Intel's FTW). In any case, the lowest bytes of the general purpose registers have the following names in GDB:
al
cl
dl
bl
spl
bpl
sil
dil
r8l
...
r15l

There are no aliases for them like e.g. r11b for r11l, so one must use the correct names.

Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • [Why does Apple use R8l for the byte registers instead of R8b?](https://stackoverflow.com/q/43991779) - those names aren't unique to GDB; I think they originated with AMD in AMD64, which predated Intel IA-32e. But yes, Intel's manuals only mention the `r11b` names for r8..r15. See also [What are the names of the new X86\_64 processors registers?](https://stackoverflow.com/q/1753602) – Peter Cordes Oct 06 '20 at 22:30
  • @PeterCordes oh, that makes sense now. I was "lucky" to choose `RSP` instead of the `R8`-`R15` to test the low-byte naming on :) (note that it's the other way around: it's AMD who used the `*B` for the numbered registers, according to your first link) – Ruslan Oct 06 '20 at 22:36
  • Oops, thanks, yeah I always use the `r8b` style myself, forgot which vendor used which in their manuals. – Peter Cordes Oct 06 '20 at 23:11
  • [How do you access low-byte registers for r8-r15 from gdb in x86-64?](https://stackoverflow.com/q/67029741) is a canonical Q&A for that GDB bug in point 1. Apparently future GDB versions should print void instead of 0 even with a format. I think this Q&A is actually a duplicate, although your answer has some more info like upper-case and other register names like `ftag` – Peter Cordes Apr 10 '21 at 01:15