<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ro">
	<id>http://wiki.dcae.pub.ro/index.php?action=history&amp;feed=atom&amp;title=OS_Lab_3_-_Files._Memory_mapping</id>
	<title>OS Lab 3 - Files. Memory mapping - Revizia istoricului</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.dcae.pub.ro/index.php?action=history&amp;feed=atom&amp;title=OS_Lab_3_-_Files._Memory_mapping"/>
	<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Files._Memory_mapping&amp;action=history"/>
	<updated>2026-10-25T19:16:15Z</updated>
	<subtitle>Istoricul versiunilor pentru această pagină din wiki</subtitle>
	<generator>MediaWiki 1.35.14</generator>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Files._Memory_mapping&amp;diff=8427&amp;oldid=prev</id>
		<title>Vserbu: /* The ELF file */</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Files._Memory_mapping&amp;diff=8427&amp;oldid=prev"/>
		<updated>2026-10-23T15:00:40Z</updated>

		<summary type="html">&lt;p&gt;&lt;span dir=&quot;auto&quot;&gt;&lt;span class=&quot;autocomment&quot;&gt;The ELF file&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left diff-editfont-monospace&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;ro&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Versiunea anterioară&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Versiunea de la data 23 octombrie 2026 15:00&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l450&quot; &gt;Linia 450:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Linia 450:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== The ELF file ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== The ELF file ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt;−&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Executable files and shared libraries on Linux use the &amp;#039;&amp;#039;&amp;#039;ELF&amp;#039;&amp;#039;&amp;#039; format (&amp;#039;&amp;#039;Executable and Linkable Format&amp;#039;&amp;#039;: [https://man7.org/linux/man-pages/man5/elf.5.html &amp;lt;code&amp;gt;&lt;del class=&quot;diffchange diffchange-inline&quot;&gt;syscall&lt;/del&gt;(&lt;del class=&quot;diffchange diffchange-inline&quot;&gt;2&lt;/del&gt;)&amp;lt;/code&amp;gt;]). It is a binary format, described by the structures in &amp;lt;code&amp;gt;&amp;amp;lt;elf.h&amp;amp;gt;&amp;lt;/code&amp;gt;. We will only look at the parts the kernel needs to start a program.&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt;+&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Executable files and shared libraries on Linux use the &amp;#039;&amp;#039;&amp;#039;ELF&amp;#039;&amp;#039;&amp;#039; format (&amp;#039;&amp;#039;Executable and Linkable Format&amp;#039;&amp;#039;: [https://man7.org/linux/man-pages/man5/elf.5.html &amp;lt;code&amp;gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;elf&lt;/ins&gt;(&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;5&lt;/ins&gt;)&amp;lt;/code&amp;gt;]). It is a binary format, described by the structures in &amp;lt;code&amp;gt;&amp;amp;lt;elf.h&amp;amp;gt;&amp;lt;/code&amp;gt;. We will only look at the parts the kernel needs to start a program.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Files._Memory_mapping&amp;diff=8426&amp;oldid=prev</id>
		<title>Vserbu: /* The ELF file */</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Files._Memory_mapping&amp;diff=8426&amp;oldid=prev"/>
		<updated>2026-10-23T15:00:22Z</updated>

		<summary type="html">&lt;p&gt;&lt;span dir=&quot;auto&quot;&gt;&lt;span class=&quot;autocomment&quot;&gt;The ELF file&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left diff-editfont-monospace&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;ro&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Versiunea anterioară&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Versiunea de la data 23 octombrie 2026 15:00&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l450&quot; &gt;Linia 450:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Linia 450:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== The ELF file ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== The ELF file ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt;−&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Executable files and shared libraries on Linux use the &amp;#039;&amp;#039;&amp;#039;ELF&amp;#039;&amp;#039;&amp;#039; format (&amp;#039;&amp;#039;Executable and Linkable Format&amp;#039;&amp;#039;). It is a binary format, described by the structures in &amp;lt;code&amp;gt;&amp;amp;lt;elf.h&amp;amp;gt;&amp;lt;/code&amp;gt;. We will only look at the parts the kernel needs to start a program.&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt;+&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Executable files and shared libraries on Linux use the &amp;#039;&amp;#039;&amp;#039;ELF&amp;#039;&amp;#039;&amp;#039; format (&amp;#039;&amp;#039;Executable and Linkable Format&amp;#039;&amp;#039;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;: [https://man7.org/linux/man-pages/man5/elf.5.html &amp;lt;code&amp;gt;syscall(2)&amp;lt;/code&amp;gt;]&lt;/ins&gt;). It is a binary format, described by the structures in &amp;lt;code&amp;gt;&amp;amp;lt;elf.h&amp;amp;gt;&amp;lt;/code&amp;gt;. We will only look at the parts the kernel needs to start a program.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Files._Memory_mapping&amp;diff=8425&amp;oldid=prev</id>
		<title>Vserbu: Pagină nouă: In the last lab, we saw how a user-space program asks the kernel for services through &#039;&#039;&#039;system calls&#039;&#039;&#039;, and what the kernel places...</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Files._Memory_mapping&amp;diff=8425&amp;oldid=prev"/>
		<updated>2026-10-23T07:26:45Z</updated>

		<summary type="html">&lt;p&gt;Pagină nouă: In &lt;a href=&quot;/index.php/OS_Lab_2_-_System_calls._The_initial_stack&quot; title=&quot;OS Lab 2 - System calls. The initial stack&quot;&gt;the last lab&lt;/a&gt;, we saw how a user-space program asks the kernel for services through &amp;#039;&amp;#039;&amp;#039;system calls&amp;#039;&amp;#039;&amp;#039;, and what the kernel places...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pagină nouă&lt;/b&gt;&lt;/p&gt;&lt;div&gt;In [[OS_Lab_2_-_System_calls._The_initial_stack|the last lab]], we saw how a user-space program asks the kernel for services through &amp;#039;&amp;#039;&amp;#039;system calls&amp;#039;&amp;#039;&amp;#039;, and what the kernel places on the stack before &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; runs. In this lab, we will use system calls to work with &amp;#039;&amp;#039;&amp;#039;files&amp;#039;&amp;#039;&amp;#039;: open them, map them into memory, read and write them, and list the contents of directories. As an application, we will look inside the executable file of our own program.&lt;br /&gt;
&lt;br /&gt;
== Learning objectives ==&lt;br /&gt;
&lt;br /&gt;
By the end of this lab you should be able to:&lt;br /&gt;
&lt;br /&gt;
# explain what a file descriptor is, and open and close files with [https://man7.org/linux/man-pages/man2/open.2.html &amp;lt;code&amp;gt;open(2)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man2/close.2.html &amp;lt;code&amp;gt;close(2)&amp;lt;/code&amp;gt;];&lt;br /&gt;
# map a file into memory with [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;], and allocate memory with anonymous mappings;&lt;br /&gt;
# read and write files with [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;], and move the file offset with [https://man7.org/linux/man-pages/man2/lseek.2.html &amp;lt;code&amp;gt;lseek(2)&amp;lt;/code&amp;gt;];&lt;br /&gt;
# list the entries of a directory with [https://man7.org/linux/man-pages/man3/readdir.3.html &amp;lt;code&amp;gt;readdir(3)&amp;lt;/code&amp;gt;] and relate them to inode numbers;&lt;br /&gt;
# describe the layout of an ELF executable and find a program header, such as &amp;lt;code&amp;gt;PT_INTERP&amp;lt;/code&amp;gt;, in it.&lt;br /&gt;
&lt;br /&gt;
== System call wrappers ==&lt;br /&gt;
&lt;br /&gt;
In the last lab, we called every system call through the generic [https://man7.org/linux/man-pages/man2/syscall.2.html &amp;lt;code&amp;gt;syscall(2)&amp;lt;/code&amp;gt;] function, to see the system call numbers at work. In practice, this is very rarely done: for each system call, the GNU C library (&amp;#039;&amp;#039;glibc&amp;#039;&amp;#039;) provides a small C function with the same name and the same arguments, which only calls the syscall function with the appropriate number.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! System call !! glibc wrapper&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;syscall(SYS_write, fd, buf, count)&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;write(fd, buf, count)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;syscall(SYS_read, fd, buf, count)&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;read(fd, buf, count)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;syscall(SYS_close, fd)&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;close(fd)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;syscall(SYS_mmap, addr, length, prot, flags, fd, offset)&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;mmap(addr, length, prot, flags, fd, offset)&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The wrappers handle errors in the same way as [https://man7.org/linux/man-pages/man2/syscall.2.html &amp;lt;code&amp;gt;syscall(2)&amp;lt;/code&amp;gt;]: on failure they return &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; (or another special value, as we will see for [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;]) and set &amp;lt;code&amp;gt;errno&amp;lt;/code&amp;gt;. From now on, we will use the wrappers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; Not every function in glibc is a thin wrapper. [https://man7.org/linux/man-pages/man3/printf.3.html &amp;lt;code&amp;gt;printf(3)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man3/puts.3.html &amp;lt;code&amp;gt;puts(3)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man3/fopen.3.html &amp;lt;code&amp;gt;fopen(3)&amp;lt;/code&amp;gt;] belong to the C standard I/O library (&amp;#039;&amp;#039;stdio&amp;#039;&amp;#039;), which keeps its own buffers in user space and calls [https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;] only from time to time. In the labs, we will aim to talk to the kernel directly, and will use only the thin wrappers.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Files ==&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;&amp;#039;file&amp;#039;&amp;#039;&amp;#039; is an array of bytes, identified by an &amp;#039;&amp;#039;&amp;#039;inode&amp;#039;&amp;#039;&amp;#039;, and found by a name in a &amp;#039;&amp;#039;&amp;#039;directory&amp;#039;&amp;#039;&amp;#039;. The same system calls are used for regular files, for devices (such as the terminal) and for other kinds of files, which we will meet in future lessons.&lt;br /&gt;
&lt;br /&gt;
=== open(2) and close(2) ===&lt;br /&gt;
&lt;br /&gt;
A program cannot use a file by its name directly. It first asks the kernel to &amp;#039;&amp;#039;&amp;#039;open&amp;#039;&amp;#039;&amp;#039; it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
int open(const char *pathname, int flags, ... /* mode_t mode */);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The kernel finds the file, checks the permissions, and records the open file in a table of the process. It returns a small integer, the &amp;#039;&amp;#039;&amp;#039;file descriptor&amp;#039;&amp;#039;&amp;#039;, which identifies the open file in all the following system calls. When the program is done with it, it releases the file descriptor with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
int close(int fd);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Every process starts with three file descriptors already open, inherited from the program that started it (usually the shell):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! File descriptor !! Constant !! Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;STDIN_FILENO&amp;lt;/code&amp;gt; || Standard input&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;STDOUT_FILENO&amp;lt;/code&amp;gt; || Standard output&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;STDERR_FILENO&amp;lt;/code&amp;gt; || Standard error&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_3_-_fd_table.png|center|thumb|700px|Each process has a table of file descriptors. Each entry points to an open file in the kernel, which records the current offset and the access mode, and points to the file itself.]]&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; argument must contain exactly one of the access modes &amp;lt;code&amp;gt;O_RDONLY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;O_WRONLY&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;O_RDWR&amp;lt;/code&amp;gt;, optionally combined (with &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;) with other flags:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Flag !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;O_RDONLY&amp;lt;/code&amp;gt; || Open for reading only&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;O_WRONLY&amp;lt;/code&amp;gt; || Open for writing only&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;O_RDWR&amp;lt;/code&amp;gt; || Open for reading and writing&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;O_CREAT&amp;lt;/code&amp;gt; || Create the file if it does not exist. Requires the third argument, &amp;lt;code&amp;gt;mode&amp;lt;/code&amp;gt;, with the permissions of the new file (for example &amp;lt;code&amp;gt;0644&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;O_EXCL&amp;lt;/code&amp;gt; || Together with &amp;lt;code&amp;gt;O_CREAT&amp;lt;/code&amp;gt;: fail with &amp;lt;code&amp;gt;EEXIST&amp;lt;/code&amp;gt; if the file already exists&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;O_TRUNC&amp;lt;/code&amp;gt; || If the file exists, cut its size to 0&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;O_APPEND&amp;lt;/code&amp;gt; || Every write goes to the end of the file&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For example, the shell runs &amp;lt;code&amp;gt;echo hi &amp;gt; notes.txt&amp;lt;/code&amp;gt; by opening the file with &amp;lt;code&amp;gt;open(&amp;quot;notes.txt&amp;quot;, O_WRONLY | O_CREAT | O_TRUNC, 0644)&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;echo hi &amp;gt;&amp;gt; notes.txt&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;O_APPEND&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;O_TRUNC&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(errno));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        const char usage[] = &amp;quot;Usage: app FILE\n&amp;quot;;&lt;br /&gt;
        write(STDERR_FILENO, usage, strlen(usage));&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    int fd = open(argv[1], O_RDONLY);&lt;br /&gt;
    if (fd == -1) {&lt;br /&gt;
        print_error(argv[1]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;Opened %s as file descriptor %d\n&amp;quot;, argv[1], fd);&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
&lt;br /&gt;
    close(fd);&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app notes.txt&lt;br /&gt;
Opened notes.txt as file descriptor 3&lt;br /&gt;
$ ./build/debug/app nope&lt;br /&gt;
nope: No such file or directory&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== mmap(2) ===&lt;br /&gt;
&lt;br /&gt;
There are two ways for a program to get to the data in a file. The first is the &amp;#039;&amp;#039;&amp;#039;transparent&amp;#039;&amp;#039;&amp;#039; one: the kernel makes the file appear in the virtual memory of the process, and the program uses it like any other memory. This is done with [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;sys/mman.h&amp;gt;&lt;br /&gt;
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);&lt;br /&gt;
int munmap(void *addr, size_t length);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;addr&amp;lt;/code&amp;gt; || A hint for where to place the mapping. Use &amp;lt;code&amp;gt;nullptr&amp;lt;/code&amp;gt;, and let the kernel choose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;length&amp;lt;/code&amp;gt; || The number of bytes to map. The kernel maps whole pages, so it is rounded up to a multiple of the page size&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;prot&amp;lt;/code&amp;gt; || What the program may do with the memory: &amp;lt;code&amp;gt;PROT_READ&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PROT_WRITE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PROT_EXEC&amp;lt;/code&amp;gt;, combined with &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;PROT_NONE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAP_PRIVATE&amp;lt;/code&amp;gt;: writes to the memory stay in this process and never reach the file. &amp;lt;code&amp;gt;MAP_SHARED&amp;lt;/code&amp;gt;: writes go to the file, and other processes mapping it see them&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;fd&amp;lt;/code&amp;gt; || The open file to map. It must have been opened for reading&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;offset&amp;lt;/code&amp;gt; || Where in the file the mapping starts. Must be a multiple of the page size&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
On success, [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;] returns the address of the first mapped byte. On failure, it returns &amp;lt;code&amp;gt;MAP_FAILED&amp;lt;/code&amp;gt; (which is &amp;lt;code&amp;gt;(void *) -1&amp;lt;/code&amp;gt;, &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;nullptr&amp;lt;/code&amp;gt;) and sets &amp;lt;code&amp;gt;errno&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_3_-_mmap_file.png|center|thumb|650px|[https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;] makes the file appear in virtual memory: &amp;lt;code&amp;gt;p[i]&amp;lt;/code&amp;gt; is the byte at offset &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; in the file. The pages are read from the disk only when the program first touches them.]]&lt;br /&gt;
&lt;br /&gt;
To map the whole file, we need to know its size. We will look at [https://man7.org/linux/man-pages/man2/lseek.2.html &amp;lt;code&amp;gt;lseek(2)&amp;lt;/code&amp;gt;] in detail below, together with [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;]. For now, it is enough to know that &amp;lt;code&amp;gt;lseek(fd, 0, SEEK_END)&amp;lt;/code&amp;gt; returns the size of the file.&lt;br /&gt;
&lt;br /&gt;
The following program prints a file, by mapping it into memory and writing the memory to the standard output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/mman.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(errno));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        const char usage[] = &amp;quot;Usage: app FILE\n&amp;quot;;&lt;br /&gt;
        write(STDERR_FILENO, usage, strlen(usage));&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    int fd = open(argv[1], O_RDONLY);&lt;br /&gt;
    if (fd == -1) {&lt;br /&gt;
        print_error(argv[1]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The offset of the end of the file is its size.&lt;br /&gt;
    off_t size = lseek(fd, 0, SEEK_END);&lt;br /&gt;
    if (size == -1) {&lt;br /&gt;
        print_error(&amp;quot;lseek&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    char *p = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);&lt;br /&gt;
    if (p == MAP_FAILED) {&lt;br /&gt;
        print_error(&amp;quot;mmap&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The mapping stays valid after the file descriptor is closed.&lt;br /&gt;
    close(fd);&lt;br /&gt;
&lt;br /&gt;
    // The whole file is now an array of bytes in memory.&lt;br /&gt;
    write(STDOUT_FILENO, p, size);&lt;br /&gt;
&lt;br /&gt;
    munmap(p, size);&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; A mapping cannot be empty: [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;] of an empty file fails with &amp;lt;code&amp;gt;EINVAL&amp;lt;/code&amp;gt; (&amp;#039;&amp;#039;Invalid argument&amp;#039;&amp;#039;).&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The size of a file can be changed with [https://man7.org/linux/man-pages/man2/truncate.2.html &amp;lt;code&amp;gt;truncate(2)&amp;lt;/code&amp;gt;], given its path, or [https://man7.org/linux/man-pages/man2/ftruncate.2.html &amp;lt;code&amp;gt;ftruncate(2)&amp;lt;/code&amp;gt;], given a file descriptor open for writing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
int truncate(const char *path, off_t length);&lt;br /&gt;
int ftruncate(int fd, off_t length);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mapping a file is especially convenient for files with a binary structure: a pointer into the mapping can be cast to a pointer to a C &amp;lt;code&amp;gt;struct&amp;lt;/code&amp;gt;, and the fields read directly, as we will do with the ELF file below.&lt;br /&gt;
&lt;br /&gt;
=== Anonymous mmap(2) ===&lt;br /&gt;
&lt;br /&gt;
With the &amp;lt;code&amp;gt;MAP_ANONYMOUS&amp;lt;/code&amp;gt; flag, [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;] maps no file at all. The &amp;lt;code&amp;gt;fd&amp;lt;/code&amp;gt; argument must be &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;offset&amp;lt;/code&amp;gt; must be 0, and the kernel gives the process new memory, filled with zeros:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/mman.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    size_t size = 1024 * 1024; // 1 MiB&lt;br /&gt;
&lt;br /&gt;
    char *buf = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);&lt;br /&gt;
    if (buf == MAP_FAILED) {&lt;br /&gt;
        char msg[256];&lt;br /&gt;
        snprintf(msg, sizeof msg, &amp;quot;mmap: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
        write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // The new memory is filled with zeros.&lt;br /&gt;
    snprintf(buf, size, &amp;quot;Got %zu bytes at %p, the last one is %d\n&amp;quot;, size, (void *) buf, buf[size - 1]);&lt;br /&gt;
    write(STDOUT_FILENO, buf, strlen(buf));&lt;br /&gt;
&lt;br /&gt;
    munmap(buf, size);&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
Got 1048576 bytes at 0xffff98360000, the last one is 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is how the program gets memory from the kernel: [https://man7.org/linux/man-pages/man3/malloc.3.html &amp;lt;code&amp;gt;malloc(3)&amp;lt;/code&amp;gt;] itself uses anonymous mappings for large allocations (by default, 128 KiB and more), and carves the smaller ones out of bigger regions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The kernel only reserves the virtual addresses at first. A physical page is given to the process the first time it touches each page, so a mapping of 1 GiB that is never used costs almost nothing.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== read(2), write(2) and lseek(2) ===&lt;br /&gt;
&lt;br /&gt;
The second way to get to the data in a file is the &amp;#039;&amp;#039;&amp;#039;explicit&amp;#039;&amp;#039;&amp;#039; one: the program provides a buffer in its own memory, and asks the kernel to copy data from the file into it, or from it into the file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;] copies at most &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; bytes from the file into &amp;lt;code&amp;gt;buf&amp;lt;/code&amp;gt;, and returns the number of bytes it copied:&lt;br /&gt;
&lt;br /&gt;
* a positive number, which may be &amp;#039;&amp;#039;&amp;#039;less&amp;#039;&amp;#039;&amp;#039; than &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;: for a regular file, only near its end; for a terminal or a pipe, whenever less data is available;&lt;br /&gt;
* &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;, at the end of the file;&lt;br /&gt;
* &amp;lt;code&amp;gt;-1&amp;lt;/code&amp;gt;, on error.&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;] works the same way in the other direction. It may also write fewer bytes than asked (for example, when the disk is full), in which case the program must call it again for the rest.&lt;br /&gt;
&lt;br /&gt;
Each open file has a &amp;#039;&amp;#039;&amp;#039;current offset&amp;#039;&amp;#039;&amp;#039;: the position in the file where the next [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;] or [https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;] starts. It begins at 0 when the file is opened, and every [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;] or [https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;] moves it forward by the number of bytes it copied. The program can also move it explicitly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
off_t lseek(int fd, off_t offset, int whence);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! &amp;lt;code&amp;gt;whence&amp;lt;/code&amp;gt; !! The new offset is&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SEEK_SET&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;offset&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SEEK_CUR&amp;lt;/code&amp;gt; || the current offset + &amp;lt;code&amp;gt;offset&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SEEK_END&amp;lt;/code&amp;gt; || the size of the file + &amp;lt;code&amp;gt;offset&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man2/lseek.2.html &amp;lt;code&amp;gt;lseek(2)&amp;lt;/code&amp;gt;] returns the new offset. This is why &amp;lt;code&amp;gt;lseek(fd, 0, SEEK_END)&amp;lt;/code&amp;gt;, which we used in the [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;] example above, returns the size of the file.&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_3_-_file_offset.png|center|thumb|525px|The current offset of an open file: [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;] moves it forward by the number of bytes it read, and [https://man7.org/linux/man-pages/man2/lseek.2.html &amp;lt;code&amp;gt;lseek(2)&amp;lt;/code&amp;gt;] sets it directly.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The offset belongs to the open file, not to the file itself (see the file descriptor table above). Two [https://man7.org/linux/man-pages/man2/open.2.html &amp;lt;code&amp;gt;open(2)&amp;lt;/code&amp;gt;] calls on the same file give two independent offsets.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following program prints a file, like the [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;] example above, this time copying it in pieces of up to 4 KiB, until [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;] reports the end of the file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(errno));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv)&lt;br /&gt;
{&lt;br /&gt;
    if (argc != 2) {&lt;br /&gt;
        const char usage[] = &amp;quot;Usage: app FILE\n&amp;quot;;&lt;br /&gt;
        write(STDERR_FILENO, usage, strlen(usage));&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    int fd = open(argv[1], O_RDONLY);&lt;br /&gt;
    if (fd == -1) {&lt;br /&gt;
        print_error(argv[1]);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    char buf[4096];&lt;br /&gt;
    ssize_t n;&lt;br /&gt;
    while ((n = read(fd, buf, sizeof buf)) &amp;gt; 0) {&lt;br /&gt;
        if (write(STDOUT_FILENO, buf, n) == -1) {&lt;br /&gt;
            print_error(&amp;quot;write&amp;quot;);&lt;br /&gt;
            return EXIT_FAILURE;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (n == -1) {&lt;br /&gt;
        print_error(&amp;quot;read&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    close(fd);&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unlike the [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;] version, this one also works for an empty file. Like the [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;] version, it fails on a directory: &amp;lt;code&amp;gt;read: Is a directory&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== readdir(3) ===&lt;br /&gt;
&lt;br /&gt;
As we saw in the course, a &amp;#039;&amp;#039;&amp;#039;directory&amp;#039;&amp;#039;&amp;#039; is a special file which maps names to &amp;#039;&amp;#039;&amp;#039;inode numbers&amp;#039;&amp;#039;&amp;#039;. Its entries are not read with [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;], but with the [https://man7.org/linux/man-pages/man2/getdents64.2.html &amp;lt;code&amp;gt;getdents64(2)&amp;lt;/code&amp;gt;] system call, which fills a buffer with as many entries as fit. &amp;#039;&amp;#039;glibc&amp;#039;&amp;#039; provides the following interface on top of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;dirent.h&amp;gt;&lt;br /&gt;
DIR *opendir(const char *name);&lt;br /&gt;
struct dirent *readdir(DIR *dirp);&lt;br /&gt;
int closedir(DIR *dirp);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man3/opendir.3.html &amp;lt;code&amp;gt;opendir(3)&amp;lt;/code&amp;gt;] opens the directory and returns a &amp;lt;code&amp;gt;DIR&amp;lt;/code&amp;gt; structure, which holds the file descriptor and a buffer of entries. Each call to [https://man7.org/linux/man-pages/man3/readdir.3.html &amp;lt;code&amp;gt;readdir(3)&amp;lt;/code&amp;gt;] returns the next entry, calling [https://man7.org/linux/man-pages/man2/getdents64.2.html &amp;lt;code&amp;gt;getdents64(2)&amp;lt;/code&amp;gt;] again when the buffer runs out, and returns &amp;lt;code&amp;gt;nullptr&amp;lt;/code&amp;gt; after the last one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;dirent.h&amp;gt; (simplified)&lt;br /&gt;
struct dirent {&lt;br /&gt;
    ino_t d_ino;             // The inode number&lt;br /&gt;
    off_t d_off;             // Used internally&lt;br /&gt;
    unsigned short d_reclen; // Used internally&lt;br /&gt;
    unsigned char d_type;    // The type of the file&lt;br /&gt;
    char d_name[256];        // The name, &amp;#039;\0&amp;#039;-terminated&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_3_-_dirent.png|center|thumb|600px|A directory is a list of (name, inode number) entries. Each call to [https://man7.org/linux/man-pages/man3/readdir.3.html &amp;lt;code&amp;gt;readdir(3)&amp;lt;/code&amp;gt;] returns the next one, in the order the file system keeps them.]]&lt;br /&gt;
&lt;br /&gt;
The entries come in the order the file system stores them, which is not alphabetical, and include &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; (the directory itself) and &amp;lt;code&amp;gt;..&amp;lt;/code&amp;gt; (its parent). The &amp;lt;code&amp;gt;d_type&amp;lt;/code&amp;gt; field holds one of &amp;lt;code&amp;gt;DT_REG&amp;lt;/code&amp;gt; (regular file), &amp;lt;code&amp;gt;DT_DIR&amp;lt;/code&amp;gt; (directory), &amp;lt;code&amp;gt;DT_LNK&amp;lt;/code&amp;gt; (symbolic link), &amp;lt;code&amp;gt;DT_FIFO&amp;lt;/code&amp;gt; (pipe), &amp;lt;code&amp;gt;DT_CHR&amp;lt;/code&amp;gt; (character device), &amp;lt;code&amp;gt;DT_BLK&amp;lt;/code&amp;gt; (block device), &amp;lt;code&amp;gt;DT_SOCK&amp;lt;/code&amp;gt; (socket), or &amp;lt;code&amp;gt;DT_UNKNOWN&amp;lt;/code&amp;gt; if the file system does not provide it.&lt;br /&gt;
&lt;br /&gt;
The following program prints the names in the current directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;dirent.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(errno));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DIR *dir = opendir(&amp;quot;.&amp;quot;);&lt;br /&gt;
    if (dir == nullptr) {&lt;br /&gt;
        print_error(&amp;quot;.&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    struct dirent *entry;&lt;br /&gt;
    errno = 0;&lt;br /&gt;
    while ((entry = readdir(dir)) != nullptr) {&lt;br /&gt;
        write(STDOUT_FILENO, entry-&amp;gt;d_name, strlen(entry-&amp;gt;d_name));&lt;br /&gt;
        write(STDOUT_FILENO, &amp;quot;\n&amp;quot;, 1);&lt;br /&gt;
    }&lt;br /&gt;
    // nullptr means either &amp;quot;no more entries&amp;quot; or an error: only an error sets errno.&lt;br /&gt;
    if (errno != 0) {&lt;br /&gt;
        print_error(&amp;quot;readdir&amp;quot;);&lt;br /&gt;
        return EXIT_FAILURE;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    closedir(dir);&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; sorts the names before printing them. &amp;lt;code&amp;gt;ls -f&amp;lt;/code&amp;gt; prints them unsorted, in the same order as the program above.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To check inode numbers from the shell, use &amp;lt;code&amp;gt;ls -i&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ls -i notes.txt&lt;br /&gt;
645260 notes.txt&lt;br /&gt;
$ stat -c &amp;#039;%i %n&amp;#039; notes.txt&lt;br /&gt;
645260 notes.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The ELF file ==&lt;br /&gt;
&lt;br /&gt;
Executable files and shared libraries on Linux use the &amp;#039;&amp;#039;&amp;#039;ELF&amp;#039;&amp;#039;&amp;#039; format (&amp;#039;&amp;#039;Executable and Linkable Format&amp;#039;&amp;#039;). It is a binary format, described by the structures in &amp;lt;code&amp;gt;&amp;amp;lt;elf.h&amp;amp;gt;&amp;lt;/code&amp;gt;. We will only look at the parts the kernel needs to start a program.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ readelf -h build/debug/app    # the ELF header&lt;br /&gt;
$ readelf -l build/debug/app    # the program headers&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The ELF header ===&lt;br /&gt;
&lt;br /&gt;
Every ELF file starts with the &amp;#039;&amp;#039;&amp;#039;ELF header&amp;#039;&amp;#039;&amp;#039;, at offset 0:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;elf.h&amp;gt; (simplified)&lt;br /&gt;
typedef struct {&lt;br /&gt;
    unsigned char e_ident[16]; // &amp;quot;\x7f&amp;quot; &amp;quot;ELF&amp;quot;, then the class, byte order, ...&lt;br /&gt;
    uint16_t e_type;           // Executable, shared library, ...&lt;br /&gt;
    uint16_t e_machine;        // The architecture: x86-64, AArch64, RISC-V, ...&lt;br /&gt;
    uint32_t e_version;&lt;br /&gt;
    uint64_t e_entry;          // The entry point (see the last lab)&lt;br /&gt;
    uint64_t e_phoff;          // File offset of the program header table&lt;br /&gt;
    uint64_t e_shoff;          // File offset of the section header table&lt;br /&gt;
    uint32_t e_flags;&lt;br /&gt;
    uint16_t e_ehsize;         // Size of this header&lt;br /&gt;
    uint16_t e_phentsize;      // Size of one program header&lt;br /&gt;
    uint16_t e_phnum;          // Number of program headers&lt;br /&gt;
    uint16_t e_shentsize;&lt;br /&gt;
    uint16_t e_shnum;&lt;br /&gt;
    uint16_t e_shstrndx;&lt;br /&gt;
} Elf64_Ehdr;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first 4 bytes are always &amp;lt;code&amp;gt;0x7f&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;#039;E&amp;#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;#039;L&amp;#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;#039;F&amp;#039;&amp;lt;/code&amp;gt;. The next byte, &amp;lt;code&amp;gt;e_ident[EI_CLASS]&amp;lt;/code&amp;gt;, is &amp;lt;code&amp;gt;ELFCLASS64&amp;lt;/code&amp;gt; for 64-bit programs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ readelf -h build/debug/app&lt;br /&gt;
ELF Header:&lt;br /&gt;
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00&lt;br /&gt;
  Class:                             ELF64&lt;br /&gt;
  ...&lt;br /&gt;
  Machine:                           AArch64&lt;br /&gt;
  ...&lt;br /&gt;
  Start of program headers:          64 (bytes into file)&lt;br /&gt;
  ...&lt;br /&gt;
  Size of program headers:           56 (bytes)&lt;br /&gt;
  Number of program headers:         10&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The program headers ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;program header table&amp;#039;&amp;#039;&amp;#039; is an array of &amp;lt;code&amp;gt;e_phnum&amp;lt;/code&amp;gt; entries, each &amp;lt;code&amp;gt;e_phentsize&amp;lt;/code&amp;gt; bytes long, starting at offset &amp;lt;code&amp;gt;e_phoff&amp;lt;/code&amp;gt; in the file. Each entry describes a &amp;#039;&amp;#039;&amp;#039;segment&amp;#039;&amp;#039;&amp;#039;: a range of bytes in the file and what the kernel should do with it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;elf.h&amp;gt;&lt;br /&gt;
typedef struct {&lt;br /&gt;
    uint32_t p_type;   // The kind of segment: PT_LOAD, PT_INTERP, ...&lt;br /&gt;
    uint32_t p_flags;  // Permissions: PF_R, PF_W, PF_X&lt;br /&gt;
    uint64_t p_offset; // Where the segment starts in the file&lt;br /&gt;
    uint64_t p_vaddr;  // Where it goes in virtual memory&lt;br /&gt;
    uint64_t p_paddr;  // Unused&lt;br /&gt;
    uint64_t p_filesz; // Its size in the file&lt;br /&gt;
    uint64_t p_memsz;  // Its size in memory (larger for .bss)&lt;br /&gt;
    uint64_t p_align;&lt;br /&gt;
} Elf64_Phdr;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Common segment types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! &amp;lt;code&amp;gt;p_type&amp;lt;/code&amp;gt; !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PT_LOAD&amp;lt;/code&amp;gt; || 1 || A range of the file to map into memory (&amp;lt;code&amp;gt;.text&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.rodata&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.data&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.bss&amp;lt;/code&amp;gt;, ...)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PT_DYNAMIC&amp;lt;/code&amp;gt; || 2 || Information for the dynamic loader: which shared libraries to load, ...&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PT_INTERP&amp;lt;/code&amp;gt; || 3 || The path of the program interpreter (the dynamic loader)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PT_NOTE&amp;lt;/code&amp;gt; || 4 || Auxiliary information (for example, a unique build ID)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PT_PHDR&amp;lt;/code&amp;gt; || 6 || The program header table itself&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== PT_INTERP ====&lt;br /&gt;
&lt;br /&gt;
As we saw in the [[OS_Lab_2_-_System_calls._The_initial_stack|last lab]], most programs are &amp;#039;&amp;#039;&amp;#039;dynamically linked&amp;#039;&amp;#039;&amp;#039;: the code of the libraries they use (such as &amp;#039;&amp;#039;glibc&amp;#039;&amp;#039;) is not in the executable file, and must be loaded when the program starts. When the kernel finds a &amp;lt;code&amp;gt;PT_INTERP&amp;lt;/code&amp;gt; segment in a program, it also loads the program named there, the &amp;#039;&amp;#039;&amp;#039;dynamic loader&amp;#039;&amp;#039;&amp;#039;, and starts it instead of the program. The loader then loads the libraries and jumps to the program&amp;#039;s entry point.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;PT_INTERP&amp;lt;/code&amp;gt; segment is a &amp;lt;code&amp;gt;&amp;#039;\0&amp;#039;&amp;lt;/code&amp;gt;-terminated string of &amp;lt;code&amp;gt;p_filesz&amp;lt;/code&amp;gt; bytes (the &amp;lt;code&amp;gt;&amp;#039;\0&amp;#039;&amp;lt;/code&amp;gt; included), at offset &amp;lt;code&amp;gt;p_offset&amp;lt;/code&amp;gt; in the file:&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_3_-_elf_interp.png|center|thumb|600px|Finding &amp;lt;code&amp;gt;PT_INTERP&amp;lt;/code&amp;gt;: the ELF header gives the position of the program header table, and the &amp;lt;code&amp;gt;PT_INTERP&amp;lt;/code&amp;gt; entry gives the position of the string.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ readelf -l build/debug/app&lt;br /&gt;
...&lt;br /&gt;
Program Headers:&lt;br /&gt;
  Type           Offset             VirtAddr           PhysAddr&lt;br /&gt;
                 FileSiz            MemSiz              Flags  Align&lt;br /&gt;
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040&lt;br /&gt;
                 0x0000000000000230 0x0000000000000230  R      0x8&lt;br /&gt;
  INTERP         0x0000000000000294 0x0000000000000294 0x0000000000000294&lt;br /&gt;
                 0x000000000000001b 0x000000000000001b  R      0x1&lt;br /&gt;
      [Requesting program interpreter: /lib/ld-linux-aarch64.so.1]&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On x86-64, the dynamic loader is &amp;lt;code&amp;gt;/lib64/ld-linux-x86-64.so.2&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; Statically linked programs contain all the library code they need, and have no &amp;lt;code&amp;gt;PT_INTERP&amp;lt;/code&amp;gt; segment: the kernel jumps directly to their entry point.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Exercises ==&lt;br /&gt;
&lt;br /&gt;
Write each program in its own copy of the project template. As a reminder from [[OS_Lab_1_-_Linux_setup._Bash_primer._C_primer#getting-the-template|the first lab]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/oslab &amp;amp;&amp;amp; cd ~/oslab&lt;br /&gt;
curl -OL https://wiki.dcae.pub.ro/images/9/93/Os_lab_skeleton.zip&lt;br /&gt;
unzip -d lab03 Os_lab_skeleton.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use a new directory name instead of &amp;lt;code&amp;gt;interp_mmap&amp;lt;/code&amp;gt; for each exercise.&lt;br /&gt;
&lt;br /&gt;
For this lab:&lt;br /&gt;
&lt;br /&gt;
* Use the &amp;#039;&amp;#039;glibc&amp;#039;&amp;#039; wrappers for the system calls ([https://man7.org/linux/man-pages/man2/open.2.html &amp;lt;code&amp;gt;open(2)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man2/lseek.2.html &amp;lt;code&amp;gt;lseek(2)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;], ...), instead of [https://man7.org/linux/man-pages/man2/syscall.2.html &amp;lt;code&amp;gt;syscall(2)&amp;lt;/code&amp;gt;].&lt;br /&gt;
* Do not use the standard I/O library: no &amp;lt;code&amp;gt;FILE&amp;lt;/code&amp;gt;, [https://man7.org/linux/man-pages/man3/printf.3.html &amp;lt;code&amp;gt;printf(3)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man3/puts.3.html &amp;lt;code&amp;gt;puts(3)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man3/fopen.3.html &amp;lt;code&amp;gt;fopen(3)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man3/fread.3.html &amp;lt;code&amp;gt;fread(3)&amp;lt;/code&amp;gt;] and so on. [https://man7.org/linux/man-pages/man3/snprintf.3.html &amp;lt;code&amp;gt;snprintf(3)&amp;lt;/code&amp;gt;] is still fine, and recommended, for building a string that you then [https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;].&lt;br /&gt;
* [https://man7.org/linux/man-pages/man3/opendir.3.html &amp;lt;code&amp;gt;opendir(3)&amp;lt;/code&amp;gt;], [https://man7.org/linux/man-pages/man3/readdir.3.html &amp;lt;code&amp;gt;readdir(3)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man3/closedir.3.html &amp;lt;code&amp;gt;closedir(3)&amp;lt;/code&amp;gt;] should be used for exercise C.&lt;br /&gt;
&lt;br /&gt;
=== A. PT_INTERP with mmap(2) ===&lt;br /&gt;
&lt;br /&gt;
Print the path of the dynamic loader of your own program. Open the executable file, using &amp;lt;code&amp;gt;argv[0]&amp;lt;/code&amp;gt; as its path, map the whole file into memory with [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;], then walk the program headers to find the &amp;lt;code&amp;gt;PT_INTERP&amp;lt;/code&amp;gt; segment, and print its string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
/lib/ld-linux-aarch64.so.1&lt;br /&gt;
$ readelf -l build/debug/app | grep interpreter&lt;br /&gt;
      [Requesting program interpreter: /lib/ld-linux-aarch64.so.1]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Your output will be different on x86-64.&lt;br /&gt;
&lt;br /&gt;
* Cast the start of the mapping to &amp;lt;code&amp;gt;const Elf64_Ehdr *&amp;lt;/code&amp;gt;. The program headers are an array of &amp;lt;code&amp;gt;Elf64_Phdr&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;e_phoff&amp;lt;/code&amp;gt; bytes from the start.&lt;br /&gt;
* Check that the file starts with &amp;lt;code&amp;gt;[0x7f, &amp;#039;E&amp;#039;, &amp;#039;L&amp;#039;, &amp;#039;F&amp;#039;]&amp;lt;/code&amp;gt; before looking further.&lt;br /&gt;
* If there is no &amp;lt;code&amp;gt;PT_INTERP&amp;lt;/code&amp;gt; segment, say so on standard error and exit with status 1.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Extra:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;argv[0]&amp;lt;/code&amp;gt; is not always trustworthy as the location of the executable. Where does the kernel offer us this information direcly?&lt;br /&gt;
&lt;br /&gt;
=== B. PT_INTERP with read(2) and lseek(2) ===&lt;br /&gt;
&lt;br /&gt;
Write the same program again, without [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;]-ing the file: read the ELF header with [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;], then use [https://man7.org/linux/man-pages/man2/lseek.2.html &amp;lt;code&amp;gt;lseek(2)&amp;lt;/code&amp;gt;] to move to the program headers and read them, and finally move to the &amp;lt;code&amp;gt;PT_INTERP&amp;lt;/code&amp;gt; segment and read the string. The two programs must print the same thing.&lt;br /&gt;
&lt;br /&gt;
* The size of the program header table is only known at run time (&amp;lt;code&amp;gt;e_phnum * e_phentsize&amp;lt;/code&amp;gt; bytes). Allocate the buffer for it with an anonymous [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;], and [https://man7.org/linux/man-pages/man2/munmap.2.html &amp;lt;code&amp;gt;munmap(2)&amp;lt;/code&amp;gt;] it when you are done.&lt;br /&gt;
* [https://man7.org/linux/man-pages/man2/read.2.html &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt;] may return fewer bytes than you asked for. Check that you got everything you need: for a regular file, getting less means the file is shorter than its headers say.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Extra:&amp;#039;&amp;#039;&amp;#039; compare the system calls the two versions make, with &amp;lt;code&amp;gt;strace ./build/debug/app&amp;lt;/code&amp;gt; (install it with &amp;lt;code&amp;gt;sudo apt install strace&amp;lt;/code&amp;gt;). The ones at the start belong to the dynamic loader and to &amp;#039;&amp;#039;glibc&amp;#039;&amp;#039;; look for &amp;lt;code&amp;gt;openat&amp;lt;/code&amp;gt; and what comes after it.&lt;br /&gt;
&lt;br /&gt;
=== C. Inode numbers with readdir(3) ===&lt;br /&gt;
&lt;br /&gt;
Write a command which takes the name of a file in the current working directory and prints its inode number. Find it by reading the directory with [https://man7.org/linux/man-pages/man3/readdir.3.html &amp;lt;code&amp;gt;readdir(3)&amp;lt;/code&amp;gt;], not with [https://man7.org/linux/man-pages/man2/stat.2.html &amp;lt;code&amp;gt;stat(2)&amp;lt;/code&amp;gt;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo hi &amp;gt; notes.txt&lt;br /&gt;
$ ./build/debug/app notes.txt&lt;br /&gt;
645260&lt;br /&gt;
$ ls -i notes.txt&lt;br /&gt;
645260 notes.txt&lt;br /&gt;
$ ./build/debug/app nope&lt;br /&gt;
nope: No such file or directory&lt;br /&gt;
$ echo $?&lt;br /&gt;
1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Some things to look at:&lt;br /&gt;
&lt;br /&gt;
* Make a hard link with &amp;lt;code&amp;gt;ln notes.txt other.txt&amp;lt;/code&amp;gt; and a symbolic link with &amp;lt;code&amp;gt;ln -s notes.txt link.txt&amp;lt;/code&amp;gt;. What inode numbers do they have, and why?&lt;br /&gt;
* What are the inode numbers of &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;..&amp;lt;/code&amp;gt;?&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Extra:&amp;#039;&amp;#039;&amp;#039; also print the type of the file, from &amp;lt;code&amp;gt;d_type&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;regular file&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;directory&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;symbolic link&amp;lt;/code&amp;gt;, ...&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
</feed>