Monday, August 13, 2012

Getting the core file under Linux Environment

Once again after long time, my techBlog is getting something...

When i started working on QNX environment, host environment was Linux. After few days of time, thought to do some experiements on target to know any different behavior of QNX, compare to Linux/Unix. Hence wrote small application programs. But got a question, How to compile for target..?Host machines Linux was installed with toolChain and got to know about 'qcc' command which is nothing but gcc for qnx. When i compiled using qcc command, executable getting created but it was not executing on target. When i looked at the file info,

a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

But the file info of the files which were getting executed on target were

a.out: ELF 32-bit LSB executable, Hitachi SH, version 1 (SYSV), dynamically linked (uses hared libs), not stripped

This is specific to Target, SH based MC. Then i understood that qcc was creating binaries for generic target (default x86), not for specific target. Then looked into makefiles which were with full of MACROS, no direct commands. When i saw the output of makefiles, i got that some configuration files is being used to cross compile which is a argument for qcc.

qcc -V3.3.5,gcc_ntoshle q.c

Above command could create a.out file for our target and i could execute and the file on actual target. After this, i could test something on target. After this i have tested some system calls related programs and etc. I was happy when i have done what i wanted... !!!!

Core file creation under Linux environment:

For segmentation fault kind of issues, .core file was getting created on target with the process name. If process/exe file name is test, test.core file was getting created on target which we use to debug the issue. From that core file, we could backtrace (execution stack) to understand the last line of code executed, using target version of gdb.

But i knew that QNX behavior is almost same as Linux/Unix because of POSIX compliance. Means, i was expected the same kind of core file creation on linux paltform also which was not happening. After discussion with my lecturer and googling, i came to know about 'ulimit'.

[pramoda@server ~]$ ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
pending signals (-i) 1024
max locked memory (kbytes, -l) 32
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 8190
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

When i change ulimit value from 0 to unlimited, (ulimit -c unlimited) then for segFault erros, i could get core files in linux also. The filename will be like 'core.[PID]'. If the application file executed and if the PID of the same is 102302, then the core file will be core.102302.

To create the debug file, which is nothing buf object file, execute cc test.c -o test-debug command.

Using gdb, we can look into backtrace to get the execution stack.
[pramod@testServer ~]$ gdb test-debug core.14611
GNU gdb Red Hat Linux (6.1post-1.20040607.62rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu"...(no debugging symbols found)...Using host libthread_db library "/lib64/tls/libthread_db.so.1".
warning: core file may not match specified executable file.
 
Core was generated by `./a.out'.
Program terminated with signal 6, Aborted.
Reading symbols from /lib64/tls/libc.so.6...(no debugging symbols found)...done.

Loaded symbols for /lib64/tls/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
#0 0x00000031c992e4dd in raise () from /lib64/tls/libc.so.6
(gdb) bt

#0 0x00000031c992e4dd in raise () from /lib64/tls/libc.so.6
#1 0x00000031c992fc8e in abort () from /lib64/tls/libc.so.6
#2 0x00000031c9962b41 in __libc_message () from /lib64/tls/libc.so.6
#3 0x00000031c996846f in _int_free () from /lib64/tls/libc.so.6
#4 0x00000031c9968a06 in free () from /lib64/tls/libc.so.6
#5 0x000000000040057a in main ()

Most of the time, we get libc is the triggerer because of printf, sprintf kind of library calls. We may get more info like line number of our source file, function names etc which is depending on the workspace.

I am trying to get more info about ulimit and core files. Let me see...

Monday, January 30, 2012

Linux Device Drivers : Kernel APIs used for Character Device Drivers

As i have planned, i went to 4-day workshop on Linux Device Drivers by Anil Kumar Pugalia. It was fine and made us to put some individual effort to understand more. Hence i will start posting them in my blog, not in order.

First to start with, i am keeping some info about Character Device Drivers. Here are Kernel APIs to be used for Character device drivers.

1. alloc_chrdev_region() : Allocates a range of char device numbers. The major number will be chosen dynamically, and returned in dev along with the first minor number.

alloc_chrdev_region (dev_t* dev, unsigned baseMinor, unsigned count, const char* name);

dev : output param for first assigned number
baseMinor: first of the requested range of minor numbers
count: number of minor numbers required
name: name of the associated device/driver

2. cdev_init() : Initialize the cdev structure, remembering fops, making it ready to add to the system with cdev_add

cdev_add(struct cdev* cdev, const struct file_operations* fops);

cdev : the structure to initialize
fops : the file operations for this device.

3. cdev_add() : Add a char device driver to the system, making it live immediately.

cdev_add(struct cdev *p, dev_t dev, unsigned count);

p : the cdev structure for this device
dev: the first device number for which this device is responsible
count: the number of consecutive minor numbers corresponding to this device

4. class_create() : creates a struct class structure (sysfs entry and generates uevents).

class_create(struct module * owner, const char *name);

owner: THIS MODULE
name: pointer to a string for the name of this class

5. device_create() : creates a device and register it with sysfs. This will be used by character device classes. A struct device will be created in sysfs, registered to the specified class.

device_create(struct class *class, struct device *parent, dev_t dev, const char* fmt, ...);
class: output of class_create()
parent: pointer to the parent struct device (NULL for the time being)
dev: dev-t for the device to be added (output of alloc_chrdev_region())
fmt: string for the device name
...

6. unregister_chrdev_region() :

unregister_chrdev_region(dev, minor_count);

7. cdev_del():

cdev_del(&c_dev);


/*** * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
if ((ret = alloc_chrdev_region(&dev, 0, 1, "final_driver")) < 0)
{
return ret;
}
cdev_init(&c_dev, &driver_fops);
if ((ret = cdev_add(&c_dev, dev, 0)) < 0)
{
unregister_chrdev_region(dev, 0);
return ret;
}
if (IS_ERR(cl = class_create(THIS_MODULE, "char")))
{
cdev_del(&c_dev);
unregister_chrdev_region(dev, 0);
return PTR_ERR(cl);
}
if (IS_ERR(dev_ret = device_create(cl, NULL, dev, NULL, "mychar%d", 0)))
{
class_destroy(cl);
cdev_del(&c_dev);
unregister_chrdev_region(dev, 0);
return PTR_ERR(dev_ret);
}
/*** * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */




Memory Allocation Related:

1. ioremap() : For mapping the physical address to virtual kernel space address.

void* ioremap(unsigned long phys_addr, unsigned long size);

phy_addr: begin of physical address range
size: size of the physical address range

ioremap builds new page tables. It doesn’t actually allocate memory. The return value of ioremap is a special address that can be used to access the specified physical address range. The return value form ioremap cannot be dereferenced directly on all platforms. Instead functions like readb, writeb etc will be used. ioremap allocates uncacheable pages.

2. ioread8(), ioread16(), ioread32(): The return address of ioremap cannot be dereferenced directly. Hence these functions are provided.

ioread8(void * address);

address: return value of opremap()

3. iowrite8(), iowrite16(), iowrite32(): To write and data into the given I/O memory returned by ioremap();

iowrite(U8 value , void *address);
iowrite16(U16 value , void *address);
iowrite32(U32 value , void *address);

4. copy_to_user() and copy_from_user() :To copy to or from user space to kernel space

copy_to_user(ubuf, kbuf, len);
copy_from_user(kbuf, ubuf, len);

5. iounmap() : Virtual address obtained by ioremap() should be released by calling iounmap()

iounmap(void * address);


About ioctl() :

This is to support unusual functionalities other than read, write, seek kind of operations with the device. Most devices can perform beyond the simple data transfers like for example eject the media, lock the door, change baud rate, change the page selection tray etc. To support these, ioctl() will be used to implement the handlers.

int (*ioctl)(struct inode *inode, struct file *filep, unsigned long cmd, unsigned long arg);


ioctl in user space:

int ioctl(int fd, unsigned cmd, . . .);

A system call can't actually have a variable number of arguments. The dots in the prototype represent not a variable number of arguments but a single optional argument. The dots are simply there to prevent type checking during compilation. The actual nature of the third argument depends on the specific control command being issued. Some commands take no arguments, some take an integer value, and some take a pointer.

ioctl() commands calculation:

The ioctl command numbers should be unique across the system in order to prevent errors caused by issuing the right command to the wrong drvice. To help programmers create unique ioctl command codes, these codes have been split up into several bitfields.