Hands-on Projects for the Linux Graphics Subsystem
|
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.101And 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 XThe 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 gdbIn 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 777At 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 777or 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 vesaThe 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 textI took an offset value 0x1a40.
I performed then the following addition:
0xc40000 + 0x1a40 = 0xc41a40Calculations 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 0xc41a40Now, 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
With gdb I can accomplish this by entering:
typedef pointer (*ModuleSetupProc)(pointer, pointer, int *, int *);
. . .
typedef struct {
XF86ModuleVersionInfo * vers;
ModuleSetupProc setup;
ModuleTearDownProc teardown;
} XF86ModuleData;
(gdb) info address vesaModuleData
The reply at that time was:
Symbol "vesaModuleData" is at 0xc461a8 in a file compiled without debugging.
(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 0xc461a8By 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:: 0x00c46200 (gdb) 0xc461ac : 0x00c41c00
(gdb) x /100xb 0xc461a8The result is:
0xc461a8At 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.: 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
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 ...