Where Linux sets its kernel and user-space segment selector values? - linux

I have verified that in Linux the following values are assigned to the kernel and user-space segments respectively:
__KERNEL_CS = 0x60
__KERNEL_DS = 0x68
__USER_CS = 0x73
__USER_DS = 0x7b
After examining the Linux source, I wasn't able to identify where Linux sets the above values into these segments. Actually, I've found the following assigments (/arch/x86/include/asm/segment.h):
#define __KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8) /* 2*8 = 0x10 */
#define __KERNEL_DS (GDT_ENTRY_KERNEL_DS * 8) /* 3*8 = 0x18 */
#define __USER_DS (GDT_ENTRY_DEFAULT_USER_DS* 8 + 3) /* (5*8)+3 = 0x2B */
#define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS* 8 + 3) /* (6*8)+3 = 0x33 */
Any help towards the understanding of this will be appreciated.

$ egrep -srl '(KERNEL|USER)_[CD]S' arch/x86/kernel
I'd look at entry_32.S.

Related

How to evaluate the flags field of mtd_info_user?

The MTD driver placed inside the Linux kernel source has a definition as below.
struct mtd_info_user {
__u8 type;
__u32 flags;
__u32 size; /* Total size of the MTD */
__u32 erasesize;
__u32 writesize;
__u32 oobsize; /* Amount of OOB data per block (e.g. 16) */
__u64 padding; /* Old obsolete field; do not use */
};
I'm trying to understand what the flags field stands for. My ultimate purpose is to find a way to check if the external MTD device is healthy. I thought that the flags field can represent the actual status of the device.
Inside the mtdchar_open(..) function from mtdchar.c source code, there is a comparison as below.
/* You can't open it RW if it's not a writeable device */
if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
ret = -EACCES;
goto out1;
}
So, I guess the flags field can be evaluated by using the macros from the mtd-abi.h header.
#define MTD_ABSENT 0
#define MTD_RAM 1
#define MTD_ROM 2
#define MTD_NORFLASH 3
#define MTD_NANDFLASH 4 /* SLC NAND */
#define MTD_DATAFLASH 6
#define MTD_UBIVOLUME 7
#define MTD_MLCNANDFLASH 8 /* MLC NAND (including TLC) */
#define MTD_WRITEABLE 0x400 /* Device is writeable */
#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */
#define MTD_NO_ERASE 0x1000 /* No erase necessary */
#define MTD_POWERUP_LOCK 0x2000 /* Always locked after reset */
The problem is about why some of those definitions are declared like normal integers, e.g. MTD_ABSENT.

ELF executable file: many zero bytes

Introduction
I'm compiling a simple assembly code (Intel syntax, x86, Linux) printing "Hello World!". Here it is:
SECTION .rodata
msg: db 'Hello world!', 0xA
msg_len: equ $ - msg
SECTION .text
global _start
_start:
mov eax, 4 ; `write` system call
mov ebx, 1 ; to stdout
mov ecx, msg
mov edx, msg_len
int 0x80
mov eax, 1 ; `exit` system call
xor ebx, ebx ; exit code 0
int 0x80
I compile it with the following commands:
nasm -f elf32 -o hello_world.o hello_world.s
ld -m elf_i386 -o hello_world hello_world.o
The code works perfectly fine, but what I'm concerned about are the file sizes:
-rwxrwxr-x 1 nikolay nikolay 8704 Apr 27 15:20 hello_world
-rw-rw-r-- 1 nikolay nikolay 243 Apr 26 22:16 hello_world.s
-rw-rw-r-- 1 nikolay nikolay 640 Apr 27 15:20 hello_world.o
Problem
The object file is slightly bigger than the source code is, but it seems reasonable because there should be some metadata or something in the ELF files, which the source code doesn't contain, right? But the executable file is more than 10 times bigger than even the object file!
Moreover, there are some zero bytes along the object file, but I wouldn't say there are too many of them. However, there are lots of zeros in the executable (see screenshots of both in the Additional info section).
Investigation
I have tried reading some articles about ELF, including the Wikipedia and the manual pages. I didn't read all of them very carefully, so I might have missed something, but what I found helpful was the dumpelf utility (from the pax-utils package, installable via apt), using which I dumped my elf files and found something which is likely to be the cause of these zero streams:
In all the three headers of the executable there is the p_align field set:
.p_align = 4096 , /* (min mem alignment in bytes) */
This should mean that each of the sections is supposed to be padded with zero bytes so that its length is a multiple of 4096. And because each of the following sections has relatively small size, there are lots of zero bytes to be added, and that's where those zeros come from.
Question(s)
So, I'm wondering:
Am I right? Are these zero bytes added to make the sections long enough?
I have also noticed that the first three sections ('', '.rodata', '.text') begin at 0, 4096 and 8192 respectively, but the following ones ('.symtab', '.strtab', '.shstrtab') seem to be not aligned anymore: they begin at 8208, 8368 and 8422... Why? What is happening here?
What do we need this alignment for? In the programming headers, there are p_vaddr and p_paddr fields which are set to the addresses where the first three sections begin, so what is the reason to align sections if we already know the exact addresses of sections from headers? Does it have something to do with memory pages (which are of size 4KiB on my machine)?
When do I want/need to, and how do I change the alignment value? Looks like there should be a linker argument to change this value. I have found the --nmagic argument in the ld's manual, which disables the alignment completely (and, hooray!, the executable file no has the same size as the object file), but I guess the alignment exists on purpose, so maybe I just need to decrease the value so that it better fits my case?
I'd really appreciate answers to any of these questions or any other details on anything here if you know something I have missed. Please, also, tell me if I was wrong anywhere. Thank you in advance!
Additional info
A dump of my object file (with xxd hello_world.o | grep -E '0000|$' --color=always | less -R):
Part of a dump of my executable file (with the command similar to above):
A new section begins at the address 0x1000
Output of dumpelf hello_world.o:
#include <elf.h>
/*
* ELF dump of 'hello_world.o'
* 640 (0x280) bytes
*/
Elf32_Dyn dumpedelf_dyn_0[];
struct {
Elf32_Ehdr ehdr;
Elf32_Phdr phdrs[0];
Elf32_Shdr shdrs[7];
Elf32_Dyn *dyns;
} dumpedelf_0 = {
.ehdr = {
.e_ident = { /* (EI_NIDENT bytes) */
/* [0] EI_MAG: */ 0x7F,'E','L','F',
/* [4] EI_CLASS: */ 1 , /* (ELFCLASS32) */
/* [5] EI_DATA: */ 1 , /* (ELFDATA2LSB) */
/* [6] EI_VERSION: */ 1 , /* (EV_CURRENT) */
/* [7] EI_OSABI: */ 0 , /* (ELFOSABI_NONE) */
/* [8] EI_ABIVERSION: */ 0 ,
/* [9-15] EI_PAD: */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
},
.e_type = 1 , /* (ET_REL) */
.e_machine = 3 , /* (EM_386) */
.e_version = 1 , /* (EV_CURRENT) */
.e_entry = 0x0 , /* (start address at runtime) */
.e_phoff = 0 , /* (bytes into file) */
.e_shoff = 64 , /* (bytes into file) */
.e_flags = 0x0 ,
.e_ehsize = 52 , /* (bytes) */
.e_phentsize = 0 , /* (bytes) */
.e_phnum = 0 , /* (program headers) */
.e_shentsize = 40 , /* (bytes) */
.e_shnum = 7 , /* (section headers) */
.e_shstrndx = 3
},
.phdrs = {
/* no program headers ! */ },
.shdrs = {
/* Section Header #0 '' 0x40 */
{
.sh_name = 0 ,
.sh_type = 0 , /* [SHT_NULL] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 0 , /* (bytes) */
.sh_size = 0 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 0 ,
.sh_entsize = 0
},
/* Section Header #1 '.rodata' 0x68 */
{
.sh_name = 1 ,
.sh_type = 1 , /* [SHT_PROGBITS] */
.sh_flags = 2 ,
.sh_addr = 0x0 ,
.sh_offset = 352 , /* (bytes) */
.sh_size = 13 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 4 ,
.sh_entsize = 0
},
/* Section Header #2 '.text' 0x90 */
{
.sh_name = 9 ,
.sh_type = 1 , /* [SHT_PROGBITS] */
.sh_flags = 6 ,
.sh_addr = 0x0 ,
.sh_offset = 368 , /* (bytes) */
.sh_size = 31 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 16 ,
.sh_entsize = 0
},
/* Section Header #3 '.shstrtab' 0xB8 */
{
.sh_name = 15 ,
.sh_type = 3 , /* [SHT_STRTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 400 , /* (bytes) */
.sh_size = 51 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 1 ,
.sh_entsize = 0
},
/* Section Header #4 '.symtab' 0xE0 */
{
.sh_name = 25 ,
.sh_type = 2 , /* [SHT_SYMTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 464 , /* (bytes) */
.sh_size = 112 , /* (bytes) */
.sh_link = 5 ,
.sh_info = 6 ,
.sh_addralign = 4 ,
.sh_entsize = 16
},
/* Section Header #5 '.strtab' 0x108 */
{
.sh_name = 33 ,
.sh_type = 3 , /* [SHT_STRTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 576 , /* (bytes) */
.sh_size = 37 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 1 ,
.sh_entsize = 0
},
/* Section Header #6 '.rel.text' 0x130 */
{
.sh_name = 41 ,
.sh_type = 9 , /* [SHT_REL] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 624 , /* (bytes) */
.sh_size = 8 , /* (bytes) */
.sh_link = 4 ,
.sh_info = 2 ,
.sh_addralign = 4 ,
.sh_entsize = 8
},
},
.dyns = dumpedelf_dyn_0,
};
Elf32_Dyn dumpedelf_dyn_0[] = {
/* no dynamic tags ! */ };
Output of dumpelf hello_world:
#include <elf.h>
/*
* ELF dump of 'hello_world'
* 8704 (0x2200) bytes
*/
Elf32_Dyn dumpedelf_dyn_0[];
struct {
Elf32_Ehdr ehdr;
Elf32_Phdr phdrs[3];
Elf32_Shdr shdrs[6];
Elf32_Dyn *dyns;
} dumpedelf_0 = {
.ehdr = {
.e_ident = { /* (EI_NIDENT bytes) */
/* [0] EI_MAG: */ 0x7F,'E','L','F',
/* [4] EI_CLASS: */ 1 , /* (ELFCLASS32) */
/* [5] EI_DATA: */ 1 , /* (ELFDATA2LSB) */
/* [6] EI_VERSION: */ 1 , /* (EV_CURRENT) */
/* [7] EI_OSABI: */ 0 , /* (ELFOSABI_NONE) */
/* [8] EI_ABIVERSION: */ 0 ,
/* [9-15] EI_PAD: */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
},
.e_type = 2 , /* (ET_EXEC) */
.e_machine = 3 , /* (EM_386) */
.e_version = 1 , /* (EV_CURRENT) */
.e_entry = 0x8049000 , /* (start address at runtime) */
.e_phoff = 52 , /* (bytes into file) */
.e_shoff = 8464 , /* (bytes into file) */
.e_flags = 0x0 ,
.e_ehsize = 52 , /* (bytes) */
.e_phentsize = 32 , /* (bytes) */
.e_phnum = 3 , /* (program headers) */
.e_shentsize = 40 , /* (bytes) */
.e_shnum = 6 , /* (section headers) */
.e_shstrndx = 5
},
.phdrs = {
/* Program Header #0 0x34 */
{
.p_type = 1 , /* [PT_LOAD] */
.p_offset = 0 , /* (bytes into file) */
.p_vaddr = 0x8048000 , /* (virtual addr at runtime) */
.p_paddr = 0x8048000 , /* (physical addr at runtime) */
.p_filesz = 148 , /* (bytes in file) */
.p_memsz = 148 , /* (bytes in mem at runtime) */
.p_flags = 0x4 , /* PF_R */
.p_align = 4096 , /* (min mem alignment in bytes) */
},
/* Program Header #1 0x54 */
{
.p_type = 1 , /* [PT_LOAD] */
.p_offset = 4096 , /* (bytes into file) */
.p_vaddr = 0x8049000 , /* (virtual addr at runtime) */
.p_paddr = 0x8049000 , /* (physical addr at runtime) */
.p_filesz = 31 , /* (bytes in file) */
.p_memsz = 31 , /* (bytes in mem at runtime) */
.p_flags = 0x5 , /* PF_R | PF_X */
.p_align = 4096 , /* (min mem alignment in bytes) */
},
/* Program Header #2 0x74 */
{
.p_type = 1 , /* [PT_LOAD] */
.p_offset = 8192 , /* (bytes into file) */
.p_vaddr = 0x804A000 , /* (virtual addr at runtime) */
.p_paddr = 0x804A000 , /* (physical addr at runtime) */
.p_filesz = 13 , /* (bytes in file) */
.p_memsz = 13 , /* (bytes in mem at runtime) */
.p_flags = 0x4 , /* PF_R */
.p_align = 4096 , /* (min mem alignment in bytes) */
},
},
.shdrs = {
/* Section Header #0 '' 0x2110 */
{
.sh_name = 0 ,
.sh_type = 0 , /* [SHT_NULL] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 0 , /* (bytes) */
.sh_size = 0 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 0 ,
.sh_entsize = 0
},
/* Section Header #1 '.text' 0x2138 */
{
.sh_name = 27 ,
.sh_type = 1 , /* [SHT_PROGBITS] */
.sh_flags = 6 ,
.sh_addr = 0x8049000 ,
.sh_offset = 4096 , /* (bytes) */
.sh_size = 31 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 16 ,
.sh_entsize = 0
},
/* Section Header #2 '.rodata' 0x2160 */
{
.sh_name = 33 ,
.sh_type = 1 , /* [SHT_PROGBITS] */
.sh_flags = 2 ,
.sh_addr = 0x804A000 ,
.sh_offset = 8192 , /* (bytes) */
.sh_size = 13 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 4 ,
.sh_entsize = 0
},
/* Section Header #3 '.symtab' 0x2188 */
{
.sh_name = 1 ,
.sh_type = 2 , /* [SHT_SYMTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 8208 , /* (bytes) */
.sh_size = 160 , /* (bytes) */
.sh_link = 4 ,
.sh_info = 6 ,
.sh_addralign = 4 ,
.sh_entsize = 16
},
/* Section Header #4 '.strtab' 0x21B0 */
{
.sh_name = 9 ,
.sh_type = 3 , /* [SHT_STRTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 8368 , /* (bytes) */
.sh_size = 54 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 1 ,
.sh_entsize = 0
},
/* Section Header #5 '.shstrtab' 0x21D8 */
{
.sh_name = 17 ,
.sh_type = 3 , /* [SHT_STRTAB] */
.sh_flags = 0 ,
.sh_addr = 0x0 ,
.sh_offset = 8422 , /* (bytes) */
.sh_size = 41 , /* (bytes) */
.sh_link = 0 ,
.sh_info = 0 ,
.sh_addralign = 1 ,
.sh_entsize = 0
},
},
.dyns = dumpedelf_dyn_0,
};
Elf32_Dyn dumpedelf_dyn_0[] = {
/* no dynamic tags ! */ };
The alignment is 4096 bytes, which is the page size on this architecture. This is not a coincidence, as the man page says about nmagic: "Turn off page alignment of sections".
By the size of the normal (non-nmagic) binary you can guess the linker laid out three pages, presumably with different access (code = not writable, data = not executable, rodata = read only), these rights can only be set per-page. The disk layout matches the layout in RAM when it is running.
This is important for demand paging. When the program starts, the entire executable file is basically mmaped and pages are loaded from disk as needed through page faults. Also pages can be shared between its other running instances (this is more important for dynamic libraries) and can be evicted from RAM when needed due to memory pressure.
The nmagic executable still is loaded into three pages when run, but as those no longer match what is on disk, it is not demand paged. I wouldn't recommend using the option on anything larger.
Note: if you make a longer-running executable (add reading of input perhaps), you can examine the memory layout details of the running process by looking at /proc/[pid]/maps and smaps.

Raise command line argument string/size limit (ARG_MAX) Mac OSX

I have a node script I pass a large json stream into as command line arg. It's worth noting I am running Mac OSX High Sierra
The script is unable to run because of this error:
-bash: /usr/local/bin/node: Argument list too long
Upon reading, this is caused my a limit in the ARG_MAX or MAX_ARG_STRLEN
according to https://www.in-ulm.de/~mascheck/various/argmax/
I attempted to raise these limits in the config file here /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/syslimits.h
Here is a section of the file where I have raised the ARG_MAX size from (256 * 1024) and I have added the MAX_ARG_STRLEN config.
#define ARG_MAX (512 * 1024) /* max bytes for an exec function */
#define MAX_ARG_STRLEN (PAGE_SIZE * 64)
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define CHILD_MAX 266 /* max simultaneous processes */
#define GID_MAX 2147483647U /* max value for a gid_t (2^31-2) */
#endif /* (_POSIX_C_SOURCE && !_DARWIN_C_SOURCE) */
#define LINK_MAX 32767 /* max file link count */
#define MAX_CANON 1024 /* max bytes in term canon input line */
#define MAX_INPUT 1024 /* max bytes in terminal input */
#define NAME_MAX 255 /* max bytes in a file name */
#define NGROUPS_MAX 16 /* max supplemental group id's */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define UID_MAX 2147483647U /* max value for a uid_t (2^31-2) */
#define OPEN_MAX 10240 /* max open files per process - todo, make a config option? */
#endif /* (_POSIX_C_SOURCE && !_DARWIN_C_SOURCE) */
#define PATH_MAX 1024 /* max bytes in pathname */
#define PIPE_BUF 512 /* max bytes for atomic pipe writes */
#define BC_BASE_MAX 99 /* max ibase/obase values in bc(1) */
#define BC_DIM_MAX 2048 /* max array elements in bc(1) */
#define BC_SCALE_MAX 99 /* max scale value in bc(1) */
#define BC_STRING_MAX 1000 /* max const string length in bc(1) */
#define CHARCLASS_NAME_MAX 14 /* max character class name size */
#define COLL_WEIGHTS_MAX 2 /* max weights for order keyword */
#define EQUIV_CLASS_MAX 2
#define EXPR_NEST_MAX 32 /* max expressions nested in expr(1) */
#define LINE_MAX 2048 /* max bytes in an input line */
#define RE_DUP_MAX 255 /* max RE's in interval notation */
#if __DARWIN_UNIX03
#define NZERO 20 /* default priority [XSI] */
/* = ((PRIO_MAX - PRIO_MIN) / 2) + 1 */
/* range: 0 - 39 [(2 * NZERO) - 1] */
/* 0 is not actually used */
#else /* !__DARWIN_UNIX03 */
#define NZERO 0 /* default priority */
/* range: -20 - 20 */
/* (PRIO_MIN - PRIO_MAX) */
#endif /* __DARWIN_UNIX03 */
#endif /* !_ANSI_SOURCE */
#endif /* !_SYS_SYSLIMITS_H_ */
After these changes, my file which contains 330,000 characters is still failing. The file is 300kb upon running the node script.
Your expertise is appreciated! I am unsure if this is caused by an overflow in the string size or the arg size (what is the difference?) Either way I have not found a way to make it work
These values cannot be changed in Mac OS X kernel.

User can define the virtual address ignoring API?

It was strange to me when I saw this:
#define HI3518_IOCH1_PHYS 0x10000000 /* 0x1000_0000 ~ 0x1020_0000 */
#define HI3518_IOCH2_PHYS 0x20000000 /* 0x2000_0000 ~ 0x2020_0000 */
#define HI3518_IOCH1_SIZE 0x200000
#define HI3518_IOCH2_SIZE 0x700000
#define HI3518_IOCH1_VIRT 0xFE000000
#define HI3518_IOCH2_VIRT (HI3518_IOCH1_VIRT + HI3518_IOCH1_SIZE)
This is located at arch/arm/mach-hi3518/include/mach/io.h
The last 2 line puzzle me, is that mean user can determine the virtual address themselves, don't need to use APIs?
Thanks in advance!

Linux kernel 0.0.1 vs 2.6.36, "ctype.h", whats the difference?

How efficient is linux kernel 2.6.36 from 0.0.1
0.0.1:
I understand, in this version, Linus has assigned the value of
c(input variable to a function) to a temp variable and operate on
that temp variable inside the function and return the temp variable.
This happens in tolower/toupper functions.
2.6.36:
Here in tolower/toupper he changed it.
ctype is const.
New function ismask is written.
What I want to know?:
Why these changes has been made?, what good it brought home?
I have listed out the ctype.h files in the kernel versions of 0.0.1 and 2.6.36, respectively.
**** ctype.h, linux-0.01 *****
#ifndef _CTYPE_H
#define _CTYPE_H
#define _U 0x01 /* upper */
#define _L 0x02 /* lower */
#define _D 0x04 /* digit */
#define _C 0x08 /* cntrl */
#define _P 0x10 /* punct */
#define _S 0x20 /* white space (space/lf/tab) */
#define _X 0x40 /* hex digit */
#define _SP 0x80 /* hard space (0x20) */
extern unsigned char _ctype[];
extern char _ctmp;
#define isalnum(c) ((_ctype+1)[c]&(_U|_L|_D))
#define isalpha(c) ((_ctype+1)[c]&(_U|_L))
#define iscntrl(c) ((_ctype+1)[c]&(_C))
#define isdigit(c) ((_ctype+1)[c]&(_D))
#define isgraph(c) ((_ctype+1)[c]&(_P|_U|_L|_D))
#define islower(c) ((_ctype+1)[c]&(_L))
#define isprint(c) ((_ctype+1)[c]&(_P|_U|_L|_D|_SP))
#define ispunct(c) ((_ctype+1)[c]&(_P))
#define isspace(c) ((_ctype+1)[c]&(_S))
#define isupper(c) ((_ctype+1)[c]&(_U))
#define isxdigit(c) ((_ctype+1)[c]&(_D|_X))
#define isascii(c) (((unsigned) c)<=0x7f)
#define toascii(c) (((unsigned) c)&0x7f)
#define tolower(c) (_ctmp=c,isupper(_ctmp)?_ctmp+('a'+'A'):_ctmp)
#define toupper(c) (_ctmp=c,islower(_ctmp)?_ctmp+('A'-'a'):_ctmp)
#endif
#
****** ctype.h, linux-2.6.36 *******
#ifndef _LINUX_CTYPE_H
#define _LINUX_CTYPE_H
/*
* NOTE! This ctype does not handle EOF like the standard C
* library is required to.
*/
#define _U 0x01 /* upper */
#define _L 0x02 /* lower */
#define _D 0x04 /* digit */
#define _C 0x08 /* cntrl */
#define _P 0x10 /* punct */
#define _S 0x20 /* white space (space/lf/tab) */
#define _X 0x40 /* hex digit */
#define _SP 0x80 /* hard space (0x20) */
extern const unsigned char _ctype[];
#define __ismask(x) (_ctype[(int)(unsigned char)(x)])
#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0)
#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0)
#define iscntrl(c) ((__ismask(c)&(_C)) != 0)
#define isdigit(c) ((__ismask(c)&(_D)) != 0)
#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0)
#define islower(c) ((__ismask(c)&(_L)) != 0)
#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0)
#define ispunct(c) ((__ismask(c)&(_P)) != 0)
/* Note: isspace() must return false for %NUL-terminator */
#define isspace(c) ((__ismask(c)&(_S)) != 0)
#define isupper(c) ((__ismask(c)&(_U)) != 0)
#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0)
#define isascii(c) (((unsigned char)(c))<=0x7f)
#define toascii(c) (((unsigned char)(c))&0x7f)
static inline unsigned char __tolower(unsigned char c)
{
if (isupper(c))
c -= 'A'-'a';
return c;
}
static inline unsigned char __toupper(unsigned char c)
{
if (islower(c))
c -= 'a'-'A';
return c;
}
#define tolower(c) __tolower(c)
#define toupper(c) __toupper(c)
#endif
Why these changes has been made?, what good it brought home?
For one, you don't have to define a ctemp variable in code calling isupper any longer, thereby reducing lines of code. It also obviously avoids leaking implementation details.

Resources