Site hosted by Angelfire.com: Build your free website today!

Hands-on Projects for the Linux Graphics Subsystem

New book from Christos Karayiannis

Available in Amazon Kindle format at:

amazon.com   amazon.co.uk   amazon.de   amazon.es   amazon.fr   amazon.it

Quiz: which is the address of the 'Setup' function?

At section 3.2.2.5 we saw that the address of the 'Setup' routine, for instance mgaSetup() has to be located in the video driver module in order to call it in order to furthrer initialise the driver.

The combination of dlopen() and dlsym() for the newer versions or the ELF specific routines for the older versions handle this, however it would be an interesting question to locate exaclty the address of the 'Setup' function of the driver module in the virtual address space of the X server. As I said dlsym() can find this address, however since this is the entry point to the module it intrigues me to inspect the memory areas of the X SErver virtual address space and find it by myself. The following hands-on experiment could shed some light on the module's addresses space.

I followed the advice from the X Server Debugging page from xorg:

You'll really want to have a second machine around. It's very difficult to debug the X server from within itself; when it stops and returns control to the debugger, you won't be able to send events to the xterm running your debugger. ssh is your friend here.

The good old telnet is also sufficient here. I had to install the telnet server for my Ubuntu laptop and then I used a nearby pc to connect to the laptop. The IP address of the laptop is 192.168.1.101 and therefore the telnet command becomes:

telnet 192.168.1.101
And then to log in with my usual login account. At this stage I had to found out the process id (pid) of the X server by running the following command in the Ubuntu xterm or remotely from my telnet shell in my pc:
ps -xa | grep X
The process id from the ps output at that moment was 777. Next from the telnet shell I run the GNU Debugger in root mode:
sudo gdb
In the gdb session that started I run the attach command, which enters an already running process (called simply a process) to the debugging phase:
(gdb) attach 777
At this point the X server and therefore the graphical environment of my laptop freezes as expected since the debugger takes control of the X process and my remote telnet shell is the only internface to the X server and the Operating System in general. Control is returned back to the X server when I finish the debugging process with the detach gdb command:
(gdb) dettach 777
or by simply clossing the telnet connection.

Then I followed the last steps from this Linux Mobile Programmer page:

7. Now, It is time to check where library is loaded.
# ps -efgrep ./test
(You can get the PID from the output, here is 7186)
# cat /proc/ 7186/maps
It will output something like:


007b1000-007cc000 r-xp 00000000 08:02 2737838 /lib/ld-2.6.so
007cc000-007cd000 r--p 0001a000 08:02 2737838 /lib/ld-2.6.so
007cd000-007ce000 rw-p 0001b000 08:02 2737838 /lib/ld-2.6.so
08048000-08049000 r-xp 00000000 08:02 1759415 /root/writting/gdbserver/test
08049000-0804a000 rw-p 00000000 08:02 1759415 /root/writting/gdbserver/test
4d940000-4da8e000 r-xp 00000000 08:02 2738392 /lib/libc-2.6.so
4da8e000-4da90000 r--p 0014e000 08:02 2738392 /lib/libc-2.6.so
4da90000-4da91000 rw-p 00150000 08:02 2738392 /lib/libc-2.6.so
4da91000-4da94000 rw-p 4da91000 00:00 0
b7efc000-b7efd000 rw-p b7efc000 00:00 0
b7f11000-b7f12000 r-xp 00000000 08:02 1759414 /root/writting/gdbserver/libfoo.so
b7f12000-b7f13000 rw-p 00000000 08:02 1759414 /root/writting/gdbserver/libfoo.so
b7f13000-b7f14000 rw-p b7f13000 00:00 0
bff04000-bff19000 rw-p bffeb000 00:00 0 [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]


This means the code segment of libfoo.so is loaded at 0xb7f11000.

8. With the help of objdump, we can get the offset.
# objdump -h libfoo.so grep text
It will output something like:


 .text 00000154 000002f0 000002f0 000002f0 2**4


So, the offset is 0x000002f0

9. Add the loaded address and offset, we can get the real address.
ADDR=0xb7f11000+0x000002f0=0xb7f112f0

10. Now, we can load the symbol file into gdb.
(gdb) add-symbol-file libfoo.so 0xb7f112f0
add symbol table from file "libfoo.so" at
.text_addr = 0xb7f112f0
(y or n) y
Reading symbols from /root/writting/gdbserver/libfoo.so...done.

11. Done, debug it as normal case.

Since my video driver is vesa_drv.so and the pid of the X server at that moment 777 I run the following command remotely (or locally from the laptop before using the 'attach' command):

sudo cat /proc/777/maps | grep vesa
The resulting address was at 0xc40000.

By running objdump for the vesa_drv.so as:

 
sudo objdump -h /usr/lib/xorg/modules/drivers/vesa_drv.so | grep text
I took an offset value 0x1a40.

I performed then the following addition:

0xc40000 + 0x1a40 = 0xc41a40
Calculations with hex numbers like the previous one is a matter of seconds when I combine the common calculator from my pc with a 'Base Tranlator' program which converts numbers to and from the hexadecimal system.

Then a used the add-symbol-file command from the gdb as described previously:

(gdb) add-symbol-file /usr/lib/xorg/modules/drivers/vesa_drv.so 0xc41a40
Now, except the X server symbols, symbols from the vesa_drv.so module can be also used.

I use this ability to obtain the virtual address of the vesaModuleData, which as seen in section 3.2.2.5 is the ModuleData struct that includes the address of the Setup() routine. In my case this would be vesaSetup(). Recall some definitions from xf86Module.h:

typedef pointer (*ModuleSetupProc)(pointer, pointer, int *, int *);
. . .
typedef struct {
    XF86ModuleVersionInfo *	vers;
    ModuleSetupProc		setup;
    ModuleTearDownProc		teardown;
} XF86ModuleData;

With gdb I can accomplish this by entering:

(gdb)  info address vesaModuleData 
The reply at that time was:
Symbol "vesaModuleData" is at 0xc461a8 in a file compiled without debugging.

The virtual address of struct vesaModuleData is thus obtained. From this we acquire the address of vesaSetup(), which is the second member of the struct. The first is a pointer to struct XF86ModuleVersionInfo and occupies in my 32-bit Linux system four bytes. Therefore I have to look for the setup routine after the first four bytes of the struct. To examine the memory at the start of vesaModuleData I use the x command in the gdb:
(gdb) x 0xc461a8

By pressing enter after the first output in gdb I take the second which is the one I look for

(gdb) x 0xc461a8
0xc461a8 :      0x00c46200
(gdb)
0xc461ac :    0x00c41c00    
By giving arguments to the x gdb command as seen bellow I can examine bigger chunks of memory, for instance the first 100 bytes after the start of vesaModuleData:
(gdb) x /100xb 0xc461a8
The result is:
 
0xc461a8 :      0x00    0x62    0xc4    0x00    0x00    0x1c
0xc4    0x00
0xc461b0 :    0x00    0x00    0x00    0x00    0x00    0x00
0x00    0x00
0xc461b8:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0xc461c0:       0xf0    0x1e    0xc4    0x00    0x00    0x00    0x00    0x00
0xc461c8:       0x20    0x1b    0xc4    0x00    0xc0    0x1b    0xc4    0x00
0xc461d0:       0xf0    0x1b    0xc4    0x00    0x00    0x00    0x00    0x00
0xc461d8:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0xc461e0:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0xc461e8:       0x92    0x48    0xc4    0x00    0xff    0xff    0xff    0xff
0xc461f0:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0xc461f8:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0xc46200:       0x92    0x48    0xc4    0x00    0x81    0x49    0xc4    0x00
0xc46208:       0xc5    0xfd    0x23    0xef 
At the previous gdb command four bytes after the start of vesaModuleData we find 0x00c41c00 or in the second case the same as 0x00 0x1c 0xc4 0x00. Since we my Intel system has Little Endianes I have to reconstruct the four bytes as 0x00c41c00, which is the address of the setup routine.

The following diagram reminds us how in a Little-Endian system address 0x00c41c00 is stored in the memory, starting at position a, as 0x00 0x1c 0xc4 0x00:

                                  0x  00  c4  1c  00    Memory  
                                       |   |   |   |    ...
                                       |   |   |    ->  a
                                       |   |    - - ->  a+1  
                                       |    - - - - ->  a+2 
                                        - - - - - - ->  a+3
                                                        ...