<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: junyaU</title>
    <description>The latest articles on Forem by junyaU (@junyaa).</description>
    <link>https://forem.com/junyaa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1086605%2Fe5f9cf1a-9932-4a52-91f8-e8d140e991ec.jpeg</url>
      <title>Forem: junyaU</title>
      <link>https://forem.com/junyaa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/junyaa"/>
    <language>en</language>
    <item>
      <title>x64 Virtual Address Translation</title>
      <dc:creator>junyaU</dc:creator>
      <pubDate>Sun, 15 Dec 2024 11:21:31 +0000</pubDate>
      <link>https://forem.com/junyaa/x64-virtual-address-translation-3bmb</link>
      <guid>https://forem.com/junyaa/x64-virtual-address-translation-3bmb</guid>
      <description>&lt;h1&gt;
  
  
  Introduction to Paging
&lt;/h1&gt;

&lt;p&gt;Paging is a memory management mechanism that maps virtual address space to physical address space. This mechanism divides virtual address space into fixed-size units called 'pages' and physical address space into 'frames', then maps pages to frames. The page size can be set to either 4KiB, 2MiB, or 1GiB. In this explanation, we'll use the 4KiB page size, which is the most commonly used configuration.&lt;/p&gt;

&lt;p&gt;Pages can be mapped to frames independently, so they don't need to be contiguous in physical address space. This mapping is managed by page tables, which are structured in a hierarchical manner in x64 systems.&lt;/p&gt;

&lt;h1&gt;
  
  
  x64 Paging Structure
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20so2c92k18amdz9qo4v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20so2c92k18amdz9qo4v.png" alt="x64 Paging Structure" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A page table is a data structure that stores the necessary information for mapping pages to frames.&lt;/p&gt;

&lt;p&gt;In x64 4-Level Paging, four levels of page tables are used as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PML4&lt;/strong&gt; (Page Map Level 4)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDP&lt;/strong&gt; (Page Directory Pointer)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PD&lt;/strong&gt; (Page Directory)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PT&lt;/strong&gt; (Page Table)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each table consists of 512 entries, with each entry being 8 bytes in size. Each entry stores the base address of the next-level table and its attributes. As an exception, PT entries store the base address of the corresponding frame, which becomes the actual physical address.&lt;/p&gt;

&lt;p&gt;The base address of the PML4 Table is obtained from the CR3 register. All page tables are placed in physical address space, and each table occupies 4KiB (512 entries × 8 bytes).&lt;/p&gt;

&lt;p&gt;Each process requires its own page tables since each process has a unique virtual address space. The OS switches page tables by loading the base address of the process's page table into the CR3 register before execution.&lt;/p&gt;

&lt;p&gt;The entries to be referenced in each table are determined by interpreting the structure of the virtual address during address translation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Virtual Address Interpretation
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8p97gq3mj2r2yz1j8j4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8p97gq3mj2r2yz1j8j4.png" alt="Virtual Address Interpretation" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The conversion from virtual to physical address requires proper interpretation of the virtual address. Let's use &lt;strong&gt;0xFFFF80001E141F3C&lt;/strong&gt; as an example virtual address to explain this process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Canonical Address
&lt;/h2&gt;

&lt;p&gt;In the x64 architecture, bits 63:48 of the virtual address are treated as sign extension bits. These bits must be filled with the value of bit 47.&lt;/p&gt;

&lt;p&gt;Virtual addresses that satisfy this constraint are called 'canonical addresses', and the CPU raises an exception if an address not following this format is used.&lt;/p&gt;

&lt;p&gt;Canonical addresses are classified into two ranges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Negative canonical addresses (bits 63:48 are all 1s)

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;0xFFFF800000000000 ~ 0xFFFFFFFFFFFFFFFF&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Known as kernel space, used by the operating system kernel&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Positive canonical addresses (bits 63:48 are all 0s)

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;0x0000000000000000 ~ 0x00007FFFFFFFFFFF&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Known as user space, used by user processes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Our example address &lt;strong&gt;0xFFFF80001E141F3C&lt;/strong&gt; is a negative canonical address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Page Table Index and Page Offset
&lt;/h2&gt;

&lt;p&gt;Bits 47:12 of the virtual address are divided into four 9-bit segments, each used as an index into a different page table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bits 47:39&lt;/strong&gt; - PML4 Table index&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bits 38:30&lt;/strong&gt; - PDP Table index&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bits 29:21&lt;/strong&gt; - PD Table index&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bits 20:12&lt;/strong&gt; - Page Table index&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bits 11:0 are used as the page offset, which is added to the frame address to obtain the final physical address.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp1zng1zrwh6egscrgjmf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp1zng1zrwh6egscrgjmf.png" alt="result" width="800" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's interpret our example address &lt;strong&gt;0xFFFF80001E141F3C&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bits 47:39&lt;/strong&gt; - 0x1E (30) → references PML4 Table[30]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bits 38:30&lt;/strong&gt; - 0x14 (20) → references PDP Table[20]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bits 29:21&lt;/strong&gt; - 0x1 (1) → references PD Table[1]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bits 20:12&lt;/strong&gt; - 0xF (15) → references Page Table[15]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bits 11:0&lt;/strong&gt; - 0x3C (60) → offset of 60 bytes from the frame address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the frame address pointed to by the final Page Table entry is 0x200000, the physical address becomes &lt;strong&gt;0x200000 + 0x3C = 0x20003C&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>x64</category>
      <category>memory</category>
      <category>kernel</category>
    </item>
    <item>
      <title>Why Both return and exit() Work in main()</title>
      <dc:creator>junyaU</dc:creator>
      <pubDate>Wed, 06 Nov 2024 14:45:36 +0000</pubDate>
      <link>https://forem.com/junyaa/why-both-return-and-exit-work-in-main-2j4k</link>
      <guid>https://forem.com/junyaa/why-both-return-and-exit-work-in-main-2j4k</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In C programming, there are two ways to terminate a program from the main function: using return and using exit().&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, World!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// Method 1: Normal termination&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, World!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// Method 2：Normal termination&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why can both methods terminate the program correctly, even though they appear completely different?&lt;br&gt;
In this article, we'll unravel this mystery by understanding how C programs actually start and terminate.&lt;br&gt;
Note that this article focuses on the implementation in GNU/Linux environments, specifically using glibc.&lt;/p&gt;
&lt;h1&gt;
  
  
  How exit() Works
&lt;/h1&gt;

&lt;p&gt;First, let's examine how the exit function works to understand the program termination mechanism.&lt;br&gt;
The exit function is a standard library function that properly terminates a program.&lt;br&gt;
Internally, the _exit function, which is called by exit, is implemented in glibc as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;_exit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;INLINE_SYSCALL&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exit_group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cp"&gt;#ifdef ABORT_INSTRUCTION
&lt;/span&gt;      &lt;span class="n"&gt;ABORT_INSTRUCTION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at this implementation, we can see that the _exit function receives an exit status as its argument and calls exit_group (system call number 231).&lt;/p&gt;

&lt;p&gt;This system call performs the following operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sends a program termination notification to the kernel&lt;/li&gt;
&lt;li&gt;The kernel performs cleanup operations:

&lt;ul&gt;
&lt;li&gt;Releases resources used by the process&lt;/li&gt;
&lt;li&gt;Updates the process table&lt;/li&gt;
&lt;li&gt;Performs additional cleanup procedures&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Through these operations, the program terminates properly.&lt;/p&gt;

&lt;p&gt;So, why does returning from main() also properly terminate the program?&lt;/p&gt;

&lt;h1&gt;
  
  
  C Program's Hidden Entry Point
&lt;/h1&gt;

&lt;p&gt;To understand this, we need to know an important fact: C programs don't actually start from main.&lt;/p&gt;

&lt;p&gt;Let's check the default settings of the linker (ld) to see the actual entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ld &lt;span class="nt"&gt;--verbose&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"ENTRY"&lt;/span&gt;
ENTRY&lt;span class="o"&gt;(&lt;/span&gt;_start&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As this output shows, the actual entry point of a C program is the _start function. main is called after _start.&lt;br&gt;
The _start function is implemented in the standard library, and in glibc, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;_start:&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;Initialize&lt;/span&gt; &lt;span class="nv"&gt;stack&lt;/span&gt; &lt;span class="nv"&gt;pointer&lt;/span&gt;
    &lt;span class="nf"&gt;xorl&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;ebp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;ebp&lt;/span&gt;
    &lt;span class="nf"&gt;popq&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;rsi&lt;/span&gt;        &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nv"&gt;Get&lt;/span&gt; &lt;span class="nv"&gt;argc&lt;/span&gt;
    &lt;span class="nf"&gt;movq&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;rsp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;rdx&lt;/span&gt;  &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nv"&gt;Get&lt;/span&gt; &lt;span class="nv"&gt;argv&lt;/span&gt;

    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;Setup&lt;/span&gt; &lt;span class="nv"&gt;arguments&lt;/span&gt; &lt;span class="nv"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt;
    &lt;span class="nf"&gt;pushq&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;rsi&lt;/span&gt;       &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nv"&gt;Push&lt;/span&gt; &lt;span class="nv"&gt;argc&lt;/span&gt;
    &lt;span class="nf"&gt;pushq&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nb"&gt;rdx&lt;/span&gt;       &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nv"&gt;Push&lt;/span&gt; &lt;span class="nv"&gt;argv&lt;/span&gt;

    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nf"&gt;Call&lt;/span&gt; &lt;span class="nv"&gt;__libc_start_main&lt;/span&gt;
    &lt;span class="nf"&gt;call&lt;/span&gt; &lt;span class="nv"&gt;__libc_start_main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The _start function has two main roles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Initializes the stack frame required for program execution&lt;/li&gt;
&lt;li&gt;Sets up command-line arguments (argc, argv) for the main function&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After these initializations are complete, __libc_start_main is called.&lt;br&gt;
This function is responsible for calling the main function.&lt;/p&gt;

&lt;p&gt;Now, let's examine how __libc_start_main works in detail.&lt;/p&gt;
&lt;h1&gt;
  
  
  How __libc_start_main Makes return Work
&lt;/h1&gt;

&lt;p&gt;__libc_start_call_main, which is called by __libc_start_main, is implemented as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;_Noreturn&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="nf"&gt;__libc_start_call_main&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;MAIN_AUXVEC_DECL&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;
&lt;span class="cp"&gt;#ifdef LIBC_START_MAIN_AUXVEC_ARG
&lt;/span&gt;                            &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ElfW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auxv_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;auxvec&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;                        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/* Memory for the cancellation buffer.  */&lt;/span&gt;
  &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;pthread_unwind_buf&lt;/span&gt; &lt;span class="n"&gt;unwind_buf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;not_first_call&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;DIAG_PUSH_NEEDS_COMMENT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#if __GNUC_PREREQ (7, 0)
&lt;/span&gt;  &lt;span class="cm"&gt;/* This call results in a -Wstringop-overflow warning because struct
     pthread_unwind_buf is smaller than jmp_buf.  setjmp and longjmp
     do not use anything beyond the common prefix (they never access
     the saved signal mask), so that is a false positive.  */&lt;/span&gt;
  &lt;span class="n"&gt;DIAG_IGNORE_NEEDS_COMMENT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-Wstringop-overflow="&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;  &lt;span class="n"&gt;not_first_call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;setjmp&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;__jmp_buf_tag&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;unwind_buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel_jmp_buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;DIAG_POP_NEEDS_COMMENT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__glibc_likely&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;not_first_call&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;pthread&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;THREAD_SELF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="cm"&gt;/* Store old info.  */&lt;/span&gt;
      &lt;span class="n"&gt;unwind_buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;priv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;THREAD_GETMEM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cleanup_jmp_buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;unwind_buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;priv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cleanup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;THREAD_GETMEM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="cm"&gt;/* Store the new cleanup handler info.  */&lt;/span&gt;
      &lt;span class="n"&gt;THREAD_SETMEM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cleanup_jmp_buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;unwind_buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="cm"&gt;/* Run the program.  */&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__environ&lt;/span&gt; &lt;span class="n"&gt;MAIN_AUXVEC_PARAM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/* Remove the thread-local data.  */&lt;/span&gt;
      &lt;span class="n"&gt;__nptl_deallocate_tsd&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="cm"&gt;/* One less thread.  Decrement the counter.  If it is zero we
         terminate the entire process.  */&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;atomic_fetch_add_relaxed&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;__nptl_nthreads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="cm"&gt;/* Not much left to do but to exit the thread, not the process.  */&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;INTERNAL_SYSCALL_CALL&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;exit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this implementation, the key parts to focus on are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__environ&lt;/span&gt; &lt;span class="n"&gt;MAIN_AUXVEC_PARAM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the important point is how the main function is executed and its return value is handled:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Executes the main function and stores its return value in result&lt;/li&gt;
&lt;li&gt;Uses the return value from main as an argument for exit&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Through this mechanism:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When using return in main → The return value is passed to __libc_start_main, which then passes it to exit&lt;/li&gt;
&lt;li&gt;When exit() is called directly in main → The program terminates immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In either case, exit is ultimately called, ensuring proper program termination.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;C programs have the following mechanism in place:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The program starts from _start&lt;/li&gt;
&lt;li&gt;_start prepares for main's execution&lt;/li&gt;
&lt;li&gt;main is executed through __libc_start_main&lt;/li&gt;
&lt;li&gt;Receives main's return value and uses it as an argument for exit&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Through this mechanism:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Even when using return in main, the return value is automatically passed to exit&lt;/li&gt;
&lt;li&gt;As a result, both return and exit() terminate the program properly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that this mechanism is not limited to GNU/Linux; similar implementations exist in other operating systems (like Windows and macOS) and different C standard libraries.&lt;/p&gt;

</description>
      <category>c</category>
      <category>development</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
