How to use a logical address with an FS or GS base in gdb?
Asked Answered
S

3

18

gdb provides functionality to read or write to a specific linear address, for example:

(gdb) x/1wx 0x080483e4
0x80483e4 <main>:       0x83e58955
(gdb) 

but how do you specify a logical address ? I came accross the following instruction:

   0x0804841a <+6>:     mov    %gs:0x14,%eax

how can i read the memory at "%gs:0x14" in gdb, or translate this logical address to a linear address that i could use in x command ?

note: i know that i could simply read %eax after this instruction, but that is not my concern

Stephanus answered 27/4, 2012 at 16:6 Comment(2)
possible duplicate of how to resolve segment:offset adres in GDB. That doesn't have a good answer though.Designate
Arguably a better answer is https://mcmap.net/q/336196/-how-are-the-fs-gs-registers-used-in-linux-amd64 that covers obtaining the base address of the TLS segment behind %gs: or %fs: (depending on arch) but seemingly not possible in GDB directly.Candace
I
6

how can i read the memory at "%gs:0x14" in gdb

You can't: there is no way for GDB to know how the segment to which %gs refers to has been set up.

or translate this logical address to a linear address that i could use in x command

Again, you can't do this in general. However, you appear to be on 32-bit x86 Linux, and there you can do that -- the %gs is set up to point to the thread descriptor via set_thread_area system call.

You can do catch syscall set_thread_area in GDB, and examine the parameters (each thread will have one such call). The code to actually do that is here. Once you know how %gs has been set up, just add 0x14 to the base_addr, and you are done.

Intelligentsia answered 28/4, 2012 at 17:56 Comment(7)
Nice answer. Unfortunately, my program seems to not call set_thread_area(2). You can see the source code here: pastebin.com/us5sbzVg (compilation options provided in source code).Stephanus
@Stephanus Your program only has one thread. The program does call set_thread_area (you should be able to see that under strace). I was able to catch that call with GDB; not sure why you claim that set_thread_area is not called.Intelligentsia
my mistake. I used another computer after the OP. It was an x86-64... You're anwser works perfectly well on 32bit x86. I guess things are different on x86-64.Stephanus
@Stephanus "I guess things are different on x86-64" -- indeed they are. There is no set_thread_area system call on x86_64, and it doesn't use %gs to access thread locals.Intelligentsia
I don't buy "there is no way for GDB to know how the segment to which %gs refers to has been set up". GDB is running in user mode and can't read the GDT/LDT, but it could write machine code for something like mov %gs:0x14,%eax into some memory it controls and then jump to it. In that way, it could "read" %gs:0x14.Promiscuous
@AlexD: Or under Linux, could just ask the kernel what the GS base is with arch_prctl(ARCH_GET_GS) in the context of the target process. (BTW, modern OSes don't use the GDT/LDT to set FS/GS base, they use the MSRs or even new instructions like wrfsbase on CPUs where available). IDK if ptrace has a way for the debugger to just directly query the FS/GS bases; I thought I remembered something about GDB being able to query it somehow but it's not part of info reg all.Graniela
@PeterCordes Apparently, info reg all doesn't list all registers. The command maintenance print register-groups shows which register groups each register belongs to. For me, fs_base and gs_base belong to the system group (as well as save and restore, but not all), so info reg system (or i r sy) prints them.Carswell
U
7

As answered in Using GDB to read MSRs, this is possible with gdb 8, using the registers $fs_base and $gs_base.

Unhorse answered 19/6, 2021 at 22:31 Comment(0)
I
6

how can i read the memory at "%gs:0x14" in gdb

You can't: there is no way for GDB to know how the segment to which %gs refers to has been set up.

or translate this logical address to a linear address that i could use in x command

Again, you can't do this in general. However, you appear to be on 32-bit x86 Linux, and there you can do that -- the %gs is set up to point to the thread descriptor via set_thread_area system call.

You can do catch syscall set_thread_area in GDB, and examine the parameters (each thread will have one such call). The code to actually do that is here. Once you know how %gs has been set up, just add 0x14 to the base_addr, and you are done.

Intelligentsia answered 28/4, 2012 at 17:56 Comment(7)
Nice answer. Unfortunately, my program seems to not call set_thread_area(2). You can see the source code here: pastebin.com/us5sbzVg (compilation options provided in source code).Stephanus
@Stephanus Your program only has one thread. The program does call set_thread_area (you should be able to see that under strace). I was able to catch that call with GDB; not sure why you claim that set_thread_area is not called.Intelligentsia
my mistake. I used another computer after the OP. It was an x86-64... You're anwser works perfectly well on 32bit x86. I guess things are different on x86-64.Stephanus
@Stephanus "I guess things are different on x86-64" -- indeed they are. There is no set_thread_area system call on x86_64, and it doesn't use %gs to access thread locals.Intelligentsia
I don't buy "there is no way for GDB to know how the segment to which %gs refers to has been set up". GDB is running in user mode and can't read the GDT/LDT, but it could write machine code for something like mov %gs:0x14,%eax into some memory it controls and then jump to it. In that way, it could "read" %gs:0x14.Promiscuous
@AlexD: Or under Linux, could just ask the kernel what the GS base is with arch_prctl(ARCH_GET_GS) in the context of the target process. (BTW, modern OSes don't use the GDT/LDT to set FS/GS base, they use the MSRs or even new instructions like wrfsbase on CPUs where available). IDK if ptrace has a way for the debugger to just directly query the FS/GS bases; I thought I remembered something about GDB being able to query it somehow but it's not part of info reg all.Graniela
@PeterCordes Apparently, info reg all doesn't list all registers. The command maintenance print register-groups shows which register groups each register belongs to. For me, fs_base and gs_base belong to the system group (as well as save and restore, but not all), so info reg system (or i r sy) prints them.Carswell
A
1

I think the easiest way to do this is to read the content of EAX register as you can see the value of %gs:0x14 is moved to EAX.

In GDB, set a breakpoint at the address right after 0x0804841a with break. For example

break *0x0804841e

Then run the program and you can print the contents of EAX register with

info registers eax
Astigmia answered 31/1, 2021 at 20:12 Comment(1)
Yes, but the question already mentions this method: note: i know that i could simply read %eax after this instruction, but that is not my concern. Some code does things like sub %gs:0x14, %eax / jnz, not loading it into a separate register first. (e.g. GCC's -fstack-protector-strong will do that to check the stack cookie.)Graniela

© 2022 - 2024 — McMap. All rights reserved.