Linux Tips & Tricks · October 28, 2021

System calls of cd command

[Linux] See System Calls Of An Internal Command

If you need to check what system calls is used by an internal command, this post might help you. “strace” is a program used for trace system calls and signals. And to be honest, I’ve no idea how it works…

This guide isn’t about using “strace” command. It’s just shows you a trick to work with strace and shell builtins.

How to install strace?

If you get “strace: command not found” you can install it with “yum install strace” or “apt-get install strace“:

[root@localhost ~]# strace
-bash: strace: command not found
[root@localhost ~]# yum install strace
Loaded plugins: fastestmirror
Determining fastest mirrors
 * base: repo.boun.edu.tr
 * extras: repo.boun.edu.tr
 * updates: repo.boun.edu.tr
[...............................................................................................]
Total download size: 902 k
Installed size: 1.6 M
Is this ok [y/d/N]: y
Downloading packages:
strace-4.24-6.el7.x86_64.rpm                                                                                                                   | 902 kB  00:00:00     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : strace-4.24-6.el7.x86_64                                                                                                                           1/1 
  Verifying  : strace-4.24-6.el7.x86_64                                                                                                                           1/1 

Installed:
  strace.x86_64 0:4.24-6.el7                                                                                                                                          

Complete!

How to see system calls of a program?

If you simply run “strace <command>“, strace will run the command until it exits. In the meantime, strace intercepts and records the system calls which are called by a process and the signals which are received by a process. (From the man page, obviously.)

System calls used by ls program

Let’s try to find out which ones used by “ls“:

[root@localhost ~]# strace ls
execve("/usr/bin/ls", ["ls"], 0x7ffdbc971430 /* 33 vars */) = 0
brk(NULL)                               = 0x1e1f000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7feebd11b000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=18629, ...}) = 0
mmap(NULL, 18629, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7feebd116000
close(3)                                = 0
open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220j\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=155744, ...}) = 0
mmap(NULL, 2255216, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7feebccd4000
mprotect(0x7feebccf8000, 2093056, PROT_NONE) = 0
mmap(0x7feebcef7000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x23000) = 0x7feebcef7000
mmap(0x7feebcef9000, 6512, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7feebcef9000
close(3)                                = 0
open("/lib64/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\26\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=20048, ...}) = 0
mmap(NULL, 2114112, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7feebcacf000
mprotect(0x7feebcad3000, 2093056, PROT_NONE) = 0
mmap(0x7feebccd2000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7feebccd2000
close(3)                                = 0
open("/lib64/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\37\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=37064, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7feebd115000
mmap(NULL, 2130560, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7feebc8c6000
mprotect(0x7feebc8cd000, 2097152, PROT_NONE) = 0
mmap(0x7feebcacd000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x7000) = 0x7feebcacd000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`&\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2156352, ...}) = 0
mmap(NULL, 3985920, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7feebc4f8000
mprotect(0x7feebc6bc000, 2093056, PROT_NONE) = 0
mmap(0x7feebc8bb000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7feebc8bb000
mmap(0x7feebc8c1000, 16896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7feebc8c1000
close(3)                                = 0
open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360\25\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=402384, ...}) = 0
mmap(NULL, 2494984, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7feebc296000
mprotect(0x7feebc2f6000, 2097152, PROT_NONE) = 0
mmap(0x7feebc4f6000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x60000) = 0x7feebc4f6000
close(3)                                = 0
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\16\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=19248, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7feebd114000
mmap(NULL, 2109744, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7feebc092000
mprotect(0x7feebc094000, 2097152, PROT_NONE) = 0
mmap(0x7feebc294000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7feebc294000
close(3)                                = 0
open("/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\23\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=19896, ...}) = 0
mmap(NULL, 2113904, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7feebbe8d000
mprotect(0x7feebbe91000, 2093056, PROT_NONE) = 0
mmap(0x7feebc090000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7feebc090000
close(3)                                = 0
open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200m\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=142144, ...}) = 0
mmap(NULL, 2208904, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7feebbc71000
mprotect(0x7feebbc88000, 2093056, PROT_NONE) = 0
mmap(0x7feebbe87000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16000) = 0x7feebbe87000
mmap(0x7feebbe89000, 13448, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7feebbe89000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7feebd113000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7feebd111000
arch_prctl(ARCH_SET_FS, 0x7feebd111840) = 0
mprotect(0x7feebc8bb000, 16384, PROT_READ) = 0
mprotect(0x7feebbe87000, 4096, PROT_READ) = 0
mprotect(0x7feebc090000, 4096, PROT_READ) = 0
mprotect(0x7feebc294000, 4096, PROT_READ) = 0
mprotect(0x7feebc4f6000, 4096, PROT_READ) = 0
mprotect(0x7feebcacd000, 4096, PROT_READ) = 0
mprotect(0x7feebccd2000, 4096, PROT_READ) = 0
mprotect(0x7feebcef7000, 4096, PROT_READ) = 0
mprotect(0x61a000, 4096, PROT_READ)     = 0
mprotect(0x7feebd11c000, 4096, PROT_READ) = 0
munmap(0x7feebd116000, 18629)           = 0
set_tid_address(0x7feebd111b10)         = 1403
set_robust_list(0x7feebd111b20, 24)     = 0
rt_sigaction(SIGRTMIN, {sa_handler=0x7feebbc77860, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7feebbc80630}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {sa_handler=0x7feebbc778f0, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7feebbc80630}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
statfs("/sys/fs/selinux", {f_type=SELINUX_MAGIC, f_bsize=4096, f_blocks=0, f_bfree=0, f_bavail=0, f_files=0, f_ffree=0, f_fsid={val=[0, 0]}, f_namelen=255, f_frsize=4096, f_flags=ST_VALID|ST_RELATIME}) = 0
statfs("/sys/fs/selinux", {f_type=SELINUX_MAGIC, f_bsize=4096, f_blocks=0, f_bfree=0, f_bavail=0, f_files=0, f_ffree=0, f_fsid={val=[0, 0]}, f_namelen=255, f_frsize=4096, f_flags=ST_VALID|ST_RELATIME}) = 0
stat("/sys/fs/selinux", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
brk(NULL)                               = 0x1e1f000
brk(0x1e40000)                          = 0x1e40000
access("/etc/selinux/config", F_OK)     = 0
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106176928, ...}) = 0
mmap(NULL, 106176928, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7feeb572e000
close(3)                                = 0
ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, TIOCGWINSZ, {ws_row=43, ws_col=166, ws_xpixel=0, ws_ypixel=0}) = 0
openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
getdents(3, /* 11 entries */, 32768)    = 384
getdents(3, /* 0 entries */, 32768)     = 0
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7feebd11a000
write(1, "anaconda-ks.cfg  wget-1.14-18.el"..., 49anaconda-ks.cfg  wget-1.14-18.el7_6.1.x86_64.rpm
) = 49
close(1)                                = 0
munmap(0x7feebd11a000, 4096)            = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

We see lots of “mmap“. You should check this to learn more about it.

A few “open” and they seem to open some libraries, right? These our beloved C libraries.

And we have a write near at the end. That call has written our “ls” output.

Pretty cool.

How to see system calls of an internal command?

Allright. Let’s see what system calls used by, say, “cd” command:

ali@zion:~$ strace cd
strace: Can't stat 'cd': No such file or directory

So what’s the problem here? The thing is that “cd” isn’t a program. It’s a shell function. Therefore it’s executed by your shell. It’s not an external program. It’s already implemented in your shell.

In my CentOS 7, “cd” refers to “/usr/bin/cd”. This is a program. Therefore, it’s an external command and strace should work with it.

But in my Ubuntu 20.04, “cd” refers to nothing:

ali@zion:~$ which cd
ali@zion:~$ type cd
cd is a shell builtin
ali@zion:~$ 

As you can see, “which cd” returned nothing and “type cd” gives you the reason.

Just like said in the man page of strace, it returns system calls of a “process“. But “cd” is not a process. It’s a process’ function. And that process is my shell. And my shell is already running. It’s not executed by “strace”.

To see the list for “cd” command, we must start it as a new process. But how? A simple shell script can help us in this situation. As you know, shell scripts run in a subshell. Well, if you don’t know, you can start from here.

Let’s create a shell script called “my_cd.sh”:

ali@zion:~$ vi my_cd.sh
ali@zion:~$ cat my_cd.sh 
#!/bin/bash

cd "$@"
ali@zion:~$ chmod u+x my_cd.sh 

This is a simple shell script that runs “cd” command with a given parameter. Later on, we gave execute permission to user.

If we run this script, it’ll work as a new process. Therefore, “strace” can intercept:

ali@zion:~$ strace ./my_cd.sh /boot
execve("./my_cd.sh", ["./my_cd.sh", "/boot"], 0x7fff7f756358 /* 61 vars */) = 0
brk(NULL)                               = 0x563d1c28b000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffc230bec90) = -1 EINVAL (Geçersiz bağımsız değişken)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (Böyle bir dosya ya da dizin yok)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=120840, ...}) = 0
mmap(NULL, 120840, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa27988d000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libtinfo.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\346\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=192032, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa27988b000
mmap(NULL, 194944, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa27985b000
mmap(0x7fa279869000, 61440, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xe000) = 0x7fa279869000
mmap(0x7fa279878000, 57344, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1d000) = 0x7fa279878000
mmap(0x7fa279886000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2a000) = 0x7fa279886000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \22\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=18816, ...}) = 0
mmap(NULL, 20752, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa279855000
mmap(0x7fa279856000, 8192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7fa279856000
mmap(0x7fa279858000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7fa279858000
mmap(0x7fa279859000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7fa279859000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360q\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2029224, ...}) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
mmap(NULL, 2036952, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa279663000
mprotect(0x7fa279688000, 1847296, PROT_NONE) = 0
mmap(0x7fa279688000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7fa279688000
mmap(0x7fa279800000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0x7fa279800000
mmap(0x7fa27984b000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7fa27984b000
mmap(0x7fa279851000, 13528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa279851000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa279660000
arch_prctl(ARCH_SET_FS, 0x7fa279660740) = 0
mprotect(0x7fa27984b000, 12288, PROT_READ) = 0
mprotect(0x7fa279859000, 4096, PROT_READ) = 0
mprotect(0x7fa279886000, 16384, PROT_READ) = 0
mprotect(0x563d1ba8f000, 16384, PROT_READ) = 0
mprotect(0x7fa2798d8000, 4096, PROT_READ) = 0
munmap(0x7fa27988d000, 120840)          = 0
openat(AT_FDCWD, "/dev/tty", O_RDWR|O_NONBLOCK) = 3
close(3)                                = 0
brk(NULL)                               = 0x563d1c28b000
brk(0x563d1c2ac000)                     = 0x563d1c2ac000
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=17343632, ...}) = 0
mmap(NULL, 17343632, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa2785d5000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=27002, ...}) = 0
mmap(NULL, 27002, PROT_READ, MAP_SHARED, 3, 0) = 0x7fa2798a4000
close(3)                                = 0
getuid()                                = 1000
getgid()                                = 1000
geteuid()                               = 1000
getegid()                               = 1000
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
ioctl(-1, TIOCGPGRP, 0x7ffc230beae4)    = -1 EBADF (Dosya betimleyici hatalı)
sysinfo({uptime=15311, loads=[72096, 50656, 45632], totalram=16572739584, freeram=7434588160, sharedram=954138624, bufferram=173338624, totalswap=1027600384, freeswap=1025765376, procs=1268, totalhigh=0, freehigh=0, mem_unit=1}) = 0
rt_sigaction(SIGCHLD, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGCHLD, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fa2796a9210}, 8) = 0
rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGQUIT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, 8) = 0
rt_sigaction(SIGTSTP, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTSTP, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, 8) = 0
rt_sigaction(SIGTTIN, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTTIN, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, 8) = 0
rt_sigaction(SIGTTOU, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTTOU, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigaction(SIGQUIT, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fa2796a9210}, 8) = 0
uname({sysname="Linux", nodename="zion", ...}) = 0
stat("/home/ali", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/home/ali", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/home/ali", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
getpid()                                = 12207
getppid()                               = 12204
getpid()                                = 12207
getpgrp()                               = 12204
ioctl(2, TIOCGPGRP, [12204])            = 0
rt_sigaction(SIGCHLD, {sa_handler=0x563d1b9d5aa0, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fa2796a9210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fa2796a9210}, 8) = 0
prlimit64(0, RLIMIT_NPROC, NULL, {rlim_cur=62972, rlim_max=62972}) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
openat(AT_FDCWD, "./my_cd.sh", O_RDONLY) = 3
stat("./my_cd.sh", {st_mode=S_IFREG|0764, st_size=21, ...}) = 0
ioctl(3, TCGETS, 0x7ffc230bea70)        = -1 ENOTTY (Aygıt için G/Ç kontrol işlemi uygun değil)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/bash\n\ncd \"$@\"\n", 80) = 21
lseek(3, 0, SEEK_SET)                   = 0
prlimit64(0, RLIMIT_NOFILE, NULL, {rlim_cur=1024, rlim_max=1024*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Dosya betimleyici hatalı)
dup2(3, 255)                            = 255
close(3)                                = 0
fcntl(255, F_SETFD, FD_CLOEXEC)         = 0
fcntl(255, F_GETFL)                     = 0x8000 (flags O_RDONLY|O_LARGEFILE)
fstat(255, {st_mode=S_IFREG|0764, st_size=21, ...}) = 0
lseek(255, 0, SEEK_CUR)                 = 0
read(255, "#!/bin/bash\n\ncd \"$@\"\n", 21) = 21
stat("/boot", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
chdir("/boot")                          = 0
read(255, "", 21)                       = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
exit_group(0)                           = ?
+++ exited with 0 +++
ali@zion:~$ pwd
/home/ali

Now we can easily see the system calls of “cd” command. But did we actually changed our working directory? Yes and no. We’ve changed it to “/boot” in our subshell that runs our script. Later on, our script finished it’s job and exited. And then we’ve got back into our parent shell. “cd” action done in subshell but now that subshell is dead. Therefore, we’re still in our home.

System calls of cd command
System calls of cd command