Sunday, February 17, 2008

Let arm-linux support setlocale

Author: XianJim lee

Most of the ARM-linux BSP(Board support package) provided by the CPU manufacture don't support setlocate. Marvell PXA3xx linux BSP is an example, I ask FAE for help, but they told me that it is beyond their support scope. So, I have to do it myself.

After reading the source code of setlocale in glibc-2.5, I knew setlocale need the data file /usr/lib/locale/locale-archive, but it doesn't exist in my board. I should put one at here, but where can I get the file? From the PC? Oh, it is about 60M, too large for mobile device.

Does the toolchain provide a tiny one? I checked the toolchain, no, there is no such file in the toolchain directory. I studied the crosstool and glibc-2.5, I found it is easy to make a customized one. The following text shows the process step by step:

1. Unpack the glibc tarball in the sources directory.

  # tar zxvf glibc-2.5.tar.gz


2.Edit glibc-2.5/localedata/SUPPORTED to customize your own locale-archive.
 # vim glibc-2.5/localedata/SUPPORTED
(Delete all the unused locales.)


3.Edit glibc-2.5/localedata/Makefile to support cross-compile.
 # vim glibc-2.5/localedata/Makefile
(Redefine the macro LOCALEDEF, use host localedef instead.)


4.Edit crosstool.sh to build and install locale-archive.
 # vim crosstool.sh
(Uncomment the line make localedata/install-locales install_root=${SYSROOT},
and change it to make localedata/install-locales install root=${TARGET},
or your system locale-archive will be overwritten.)


5. Tar the modified file back the tarball.
 # tar czvf glibc-2.5.tar.gz glibc-2.5.)


6.Build it by run build.sh. Wait until it is done,
copy locale-archive to your board.
 NOTE: You should copy the files in lib/gconv too, 
or the function iconv will still fail..)


Friday, February 15, 2008

Debug shared library with gdbserver

Author: XianJim lee

It is hard to debug in embedded system, especially the bug can't be reproduced in simulator. Don't be surprise, if it takes several days to find out why the system failed--just because there is no suitable debugger!

You are lucky, if you develop programs in Linux embedded environment. GDB server is powerful tool for you, actually, it helps me solved many difficult problems. It is a pity that it doesn't support shared library, you can't insert break points in the shared library code.

There is a command named add-shared-symbol-files, but it doesn't work. The mechanism of inserting break points is very simple: generally, the debugger insert a piece of special instruction at the address, when the CPU execute that instruction, an exception will be throw, then the debugger take over the control of execution.

Why doesn't it work? The most possible answer is that the symbol does not match the according address. After reading the help information of the command add-symbol-file, I knew that I should specify an address for it. But what address should I specify? We should know where the code of the shared library locates in the memory. You will say, that is simple, we can consult the /proc/$PID/maps. Yes, you are right, but not enough. The following example shows a complete demonstration of debugging shared library.

1. Let's create a shared library.


foo.c
int foo(int a, int b)
{
int s = a + b;
printf("%d\n", s);
return s;
}

2. Then create an executable file that calls the shared library.

main.c
#include
extern int foo(int a, int b);
int main(int argc, char* argv[])
{
int s = foo(10, 20);
return s;
}

3. Of course we need a Makefile.

Makefile
all: so main
so:
gcc -g foo.c -shared -o libfoo.so
main:
gcc -g main.c -L./ -lfoo -o test
clean:
rm -f test *.so

4. Make and prepare for running.
# make
# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./

5. Run the gdbserver
# gdbserver localhost:2000 ./test

6. Connect to gdbserver and run to function main.
#gdb
(gdb) symbol-file test
(gdb) target remote localhost:2000
(gdb) b main
(gdb) c

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.

Well, it works, but it is still complex. If you get better solution, let me know please, thank you in advance.