Thursday, July 9, 2009

Determining if your kernel and hardware is 32bit or 64bit on Unix environments

HP UNIX

This technote explains how to establish if an HP-UX® 11.x kernel is 32-bit or 64-bit capable.

Run getconf KERNEL_BITS on the system in question. The output, either "32" or "64", corresponds to 32-bit or 64-bit kernels, respectively.

# getconf KERNEL_BITS
64

Check the vmunix file for the following entries:

# file /stand/vmunix
/stand/vmunix: PA-RISC1.1 executable ---> 32-bit
/stand/vmunix: ELF-64 executable object file ---> 64-bit

This will tell you if your currently running kernel is 64 bits or 32 bits.It returns the number of bits used by the kernel for pointer and long data types.

# getconf KERNEL_BITS
64

Returns which kernel is supported on the hardware.

# getconf HW_32_64_CAPABLE
1

This will show you if the CPU’s are capable of running 32, 64, or 32/64 bit kernels.

# getconf HW_CPU_SUPP_BITS
64

SOLARIS

The easiest way to determine which version is running on your system is to use the isainfo command. This new command prints information about the application environments supported on the system.

The following is an example of the isainfo command executed on an UltraSPARC™ system running the 64-bit operating system:

% isainfo -v
64-bit sparcv9 applications
32-bit sparc applications

One useful option of the isainfo(1) command is the -n option, which prints the native instruction set of the running platform:

% isainfo -n
sparcv9

The -b option prints the number of bits in the address space ( cpu’s bit size capabilities ) of the corresponding native applications environment :

% isainfo -b
64

% echo "Welcome to "`isainfo -b`"-bit Solaris"
Welcome to 64-bit Solaris

A related command, isalist(1), that is more suited for use in shell scripts, can be used to print the complete list of supported instruction sets on the platform. Some of the instruction set architectures listed by isalist are highly platform specific, while isainfo(1) describes only the attributes of the most portable application environments on the system. Both commands are built on the SI_ISALIST suboption of the sysinfo(2) system call. See isalist(5) for further details.

The following is an example of the isalist command executed on an UltraSPARC system running the 64-bit operating system:

% isalist
sparcv9+vis sparcv9 sparcv8plus+vis sparcv8plus sparcv8
sparcv8-fsmuld sparcv7 sparc


AIX

For AIX, we will use the bootinfo command . The below command's shows if the hardware is 32 bit or 64 capable.

# bootinfo -y
64

# getconf HARDWARE_BITMODE
64

# prtconf -c
CPU Type: 64-bit

Below commands show the running kernel’s bit size :

# bootinfo -K
64

# prtconf -k
Kernel Type: 64-bit

# getconf KERNEL_BITMODE
64

LINUX

For linux, we will look at the cpuinfo from /proc. Here, we are mainly interested in the “flags” for the CPU’s:

# cat /proc/cpuinfo | grep -i flags
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc arch_perfmon pebs bts rep_good pni dtes64 monitor ds_cpl est tm2 ssse3 cx16 xtpr pdcm sse4_1 lahf_lm

We are interested in three values in the output, as they indicate the bit size capabilities of the CPU:

16 Bit = rm (Real Mode)
32 Bit = tm (Transparent Mode)
64 Bit = lm (Long Mode)

This doesn’t necessarily mean the Motherboard is capable of 64 bit.

To determine the bit size of your running kernel, you can also use getconf, similar to HPUX, to find this info:

# getconf LONG_BIT
64

This shows that my kernel is running 64 bit.

Tuesday, April 14, 2009

Create shared library on Unix

We will see how we can build dynamic libraries on different Unix flavours.

Apple Mac OS X

$ gcc -arch x86_64 -fno-common -c source.c
$ gcc -arch x86_64 -fno-common -c code.c
$ gcc -dynamiclib -flat_namespace -undefined suppress -install_name /usr/local/lib/libfoo.2.dylib
-o libfoo.2.4.5.dylib source.o code.o

-dynamiclib
When passed this option, GCC will produce a dynamic library instead of an executable when linking,
using the Darwin libtool command.

-arch arch
Compile for the specified target architecture arch. The allowable values are i386,x86_64,ppc and ppc64.

-flat_namespace
Use a single level address space for name resolution and done for al Unixes.

-undefined suppress
Supress undefined symbols.It will get resolved later from dependent libraries.

GNU Linux

gcc -m64 -fPIC -g -c -Wall a.c
gcc -m64 -fPIC -g -c -Wall b.c
gcc -m64 -shared -Wl,-soname,libmystuff.so.1 -o libmystuff.so.1.0.1 a.o b.o -lc

-fpic/-fPIC
Generate position-independent code ( PIC ) suitable for use in a shared library.

-shared
Produce a shared object which can then be linked with other objects to form an executable.

-m32/-m64
Generate code for 32-bit or 64-bit environments

HP HP-UX

cc +DD64 -Aa -c +Z length.c volume.c mass.c ( 64-bit )
ld -b -o libunits.sl length.o volume.o mass.o

-Amode
Specify the compilation standard to be used by the compiler.
a
Compile under ANSI mode

+z,+Z
Both of these options cause the compiler to generate position independent code (PIC) in 32/64-bit respectively.

+DD64
Recommended option for compiling in 64-bit mode on either Itanium-based or PA-RISC 2.0 architecture. The macros __LP64__ and (on PA platforms) _PA_RISC2_0 are #defined.

+DD32
Compiles in 32-bit mode and on PA systems creates code compatible with PA-RISC 1.1 architectures. (Same as +DA1.1 and +DAportable.)

+DA2.0W
Compiles in 64-bit mode for the PA-RISC 2.0 architecture. The macros __LP64__ and _PA_RISC2_0 are #defined.

+DA2.0N
Compiles in 32-bit mode (narrow mode) for the PA-RISC 2.0 architecture. The macro _PA_RISC2_0 is #defined. +DA options are not supported on Itanium-based platforms.

SUN SOLARIS

cc -xarch=v9 -Kpic -c a.c
cc -xarch=v9 -Kpic -c b.c
ld -G -o outputfile.so a.o b.o

-Kpic/-KPIC
Generate position-independent code for use in shared libs.

-G
Produce a shared object rather than a dynamically linked executable.

-xarch=v9
Specifies compiling for a 64-bit Solaris OS on SPARC platform.

-xarch=amd64
Specifies compilation for the 64-bit AMD instruction set.The C compiler from studio 10 onwards predefines __amd64 and __x86_64 when you specify -xarch=amd64.

Links:
Using static and shared libraries across platforms

Shared Libraries (HP-UX)

Thursday, January 15, 2009

FD Passing with Unix Domain Sockets

Unix domain sockets are two-way local inter-process communication mechanism through the socket interfaces.The protocol family is AF_UNIX/AF_LOCAL/PF_UNIX/PF_LOCAL.It supports both SOCK_STREAM & SOCK_DATA mode of communication.

SOCK_STREAM unix domain sockets can also be used to pass ancillary/control information including the passing of open file descriptors from one process to another.Any valid descriptor can be passed.File descriptors are transferred between separate processes across a UNIX domain socket using the sendmsg() and recvmsg() functions.Both of these system calls pass a struct msghdr to minimize the number of directly supplied arguments.

The structure hs the below form :
struct msghdr {
void *msg_name; /* optional address */
socklen_t msg_namelen; /* size of address */
struct iovec *msg_iov; /* scatter/gather array */
int msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data, see below */
socklen_t msg_controllen; /* ancillary data buffer len */
int msg_flags; /* flags on received message */
};

msg_name -> destination address ( specified for un-connected sockets )
msg_namelen -> length of the address specified in msg_name

msg_iov -> scatter/gather buffer address
msg_iovlen -> Number of scatter/gather ( struct iov ) elements specified

msg_control -> pointer to ancillary/control header & data
msg_controllen -> total length of the control header & data's.

msg_flags -> flags on received message

The control message header declared as below :
struct cmsghdr {
u_int cmsg_len; /* data byte count, including hdr */
int cmsg_level; /* originating protocol */
int cmsg_type; /* protocol-specific type */
/* followed by u_char cmsg_data[]; */
};

cmsg_len -> No. of bytes ( header + data )
cmsg_level -> Originating protocol
cmsg_type -> Protocol specific type

As shown in this definition, normally there is no member with the name cmsg_data[]. Instead, the data portion is accessed using the CMSG_xxx() macros, as described shortly.Nevertheless, it is common to refer to the cmsg_data[] member.

When ancillary data is sent or received, any number of ancillary data objects can be specified by the msg_control and msg_controllen members of the msghdr structure, because each object is preceded by a cmsghdr structure defining the object's length (the cmsg_len member).

CMSG_LEN
unsigned int CMSG_LEN(unsigned int length);

Given the length of an ancillary data object, CMSG_LEN() returns the value to store in the cmsg_len member of the cmsghdr structure, taking into account any padding
needed to satisfy alignment requirements.

One possible implementation could be:
#define CMSG_LEN(length) ( ALIGN(sizeof(struct cmsghdr)) + length )

CMSG_SPACE
unsigned int CMSG_SPACE(unsigned int length);

Given the length of an ancillary data object, CMSG_SPACE() returns the space required by the object and its cmsghdr structure, including any padding needed to satisfy alignment requirements.This macro can be used, for example, to allocate space dynamically for the ancillary data.This macro should not be used to initialize the cmsg_len member of a cmsghdr structure,instead use the CMSG_LEN() macro.

One possible implementation could be:
#define CMSG_SPACE(length) ( ALIGN(sizeof(struct cmsghdr)) + \
ALIGN(length) )

Note the difference between CMSG_SPACE() and CMSG_LEN(), shown also in the figure in Section 4.2: the former accounts for any required padding at the end of the ancillary data object and the latter is the actual length to store in the cmsg_len member of the ancillary data object.

CMSG_FIRSTHDR
struct cmsghdr *CMSG_FIRSTHDR(const struct msghdr *mhdr);

CMSG_FIRSTHDR() returns a pointer to the first cmsghdr structure in the msghdr structure pointed to by mhdr.The macro returns NULL if there is no ancillary data pointed to the by msghdr structure (that is, if either msg_control is NULL or if msg_controllen is less than the size of a cmsghdr structure).

We provide a server and client source examples to show how descriptor passing works.

server.c

#define UDS "domain_socket"

int send_connection(int fd,int sockfd)
{
struct msghdr msg; /* message header */
struct iovec iov; /* scatter/gather buffer */
char b='b';
int rc;
/* Control Message header */
union
{
struct cmsghdr cm; /* For alignment */
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;

msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);

/* Populate the control info */
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(sizeof (int));
cmptr->cmsg_type = SCM_RIGHTS;
cmptr->cmsg_level = SOL_SOCKET;
*((int *) CMSG_DATA(cmptr)) = fd; /* fd being passed here */

msg.msg_name = (caddr_t) NULL;
msg.msg_namelen = 0;

iov.iov_base = &b;
iov.iov_len = 1;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

msg.msg_flags = 0;

rc = sendmsg(sockfd,&msg,0);
if(rc == -1 ){
perror("sendmsg");
exit(-5);
}
close(sockfd);
}

int listener(char *path)
{
struct sockaddr_un unsock = {0};
struct sockaddr_un remote = {0};
int sockfd;
socklen_t len;

sockfd = socket(AF_UNIX,SOCK_STREAM,0); /* AF_UNIX for local domain sockets */
if(sockfd == -1){
perror("socket");
exit(-1);
}

unlink(UDS);
bzero(&unsock,sizeof(unsock));
unsock.sun_family = AF_UNIX;
strcpy(unsock.sun_path,UDS);
unsock.sun_len=SUN_LEN(&unsock);

/* Binding to a pathname creates the reference file in the file system */
if(bind(sockfd ,(struct sockaddr *)&unsock,SUN_LEN(&unsock)) == -1){
perror("bind");
exit(-1);
}

if (listen(sockfd, 5) == -1) {
perror("listen");
exit(1);
}
len = SUN_LEN(&unsock);
getsockname(sockfd,(struct sockaddr *)&unsock,&len);
printf("bound name = %s, returned len = %d\n", unsock.sun_path, len);

for(;;){
socklen_t len = sizeof(struct sockaddr_un);
int fd,sendfd;

fd = accept(sockfd ,(struct sockaddr *)&remote,&len);
if(fd == -1 ){
perror("accept");
exit(-2);
}
printf("Accepted a connection\n");

/* Open the file . This returned fd of the file is passed to the client */
sendfd = open(path,O_RDONLY|O_CREAT,0755);
if(sendfd == -1 ){
perror("open");
exit(-3);
}
send_connection(sendfd,fd);
close(sendfd);
}
}

int main()
{
listener("./test.txt");
return 0;
}

client.c

#define UDS "domain_socket"

int receive_fd(int fd)
{
struct msghdr msg;
struct iovec iov;
char buf[1];
int rv;

union
{
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;

iov.iov_base=buf;
iov.iov_len=1;

msg.msg_name=NULL;
msg.msg_namelen=0;
msg.msg_iov=&iov;
msg.msg_iovlen=1;

msg.msg_control=control_un.control;
msg.msg_controllen=sizeof(control_un.control);

rv = recvmsg(fd,&msg,0);
if(rv == -1){
perror("recvmsg");
exit(-1);
}
else if(rv > 0){
cmptr = CMSG_FIRSTHDR(&msg);
if(cmptr->cmsg_type != SCM_RIGHTS){
printf("Unknown control info\n");
exit(-3);
}
return *((int *)CMSG_DATA(cmptr));
}
else
return -1;
}

int sock_dgram()
{
int s, t, len;
struct sockaddr_un remote;
char str[100];

if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}

printf("Trying to connect...\n");

remote.sun_family = AF_UNIX;
strcpy(remote.sun_path, UDS);
remote.sun_len=SUN_LEN(&remote);
len = strlen(remote.sun_path) + sizeof(remote.sun_len) + sizeof(remote.sun_family);
if (connect(s, (struct sockaddr *)&remote, len) == -1) {
perror("connect");
exit(1);
}
printf("Connected ..\n");
return s;
}

void reader(int fd)
{
char ch;
while(read(fd,&ch,1))
write(1,&ch,1);
}
int main()
{
int fd,passfd;

fd = sock_dgram();
passfd = receive_fd(fd);
if(passfd != -1)
reader(passfd);

return 0;
}