mirror of
https://github.com/pkivolowitz/asm_book.git
synced 2026-06-21 02:26:59 +08:00
more bigger faster
This commit is contained in:
parent
918ce23f57
commit
29b1367730
13 changed files with 339 additions and 345 deletions
15
README.md
15
README.md
|
|
@ -62,12 +62,12 @@ In this book we will use the ARM LINUX conventions. This means:
|
|||
hope to add a chapter detailing the Windows calling convention.
|
||||
|
||||
You'll notice right away that we make use of the C-runtime directly
|
||||
rather than make OS service calls. So, for instance, if we want to
|
||||
call `write()`, we call `write` from the assembly language. This
|
||||
version of the system call `write` is a wrapper function built into
|
||||
the C-runtime which handles the low level details of performing a
|
||||
system call. See the [chapter](./not_written_yet.md) on what actually
|
||||
happens inside these wrapper functions.
|
||||
rather than make OS service calls. So, for instance, if we want to call
|
||||
`write()`, we call `write` from the assembly language. This version of
|
||||
the system call `write` is a wrapper function built into the C-runtime
|
||||
which handles the low level details of performing a system call. See the
|
||||
[here](./more/system_calls/README.md) on what actually happens inside
|
||||
these wrapper functions.
|
||||
|
||||
## A Lot of Names
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ knowledge - how cool is that!
|
|||
| 6 | Functions | |
|
||||
| .... a | [.... Calling and Returning](./section_1/funcs/README.md) | NA |
|
||||
| .... b | [.... Passing Parameters](./section_1/funcs/README2.md) | NA |
|
||||
| .... c | [.... Calling common C runtime functions](./section_1/funcs/README3.md) | NA |
|
||||
| .... c | [.... Example of calling some common C runtime functions](./section_1/funcs/README3.md) | NA |
|
||||
| 7 | [FizzBuzz - a Complete Program](./section_1/fizzbuzz/README.md) | NA |
|
||||
| 8 | Structs | |
|
||||
| .... a | [.... Alignment](./section_1/structs/alignment.md) | NA |
|
||||
|
|
@ -261,6 +261,7 @@ What would a book about assembly language be without bit bashing?
|
|||
| Chapter | Markdown | PDF |
|
||||
| ------- | -------- | --- |
|
||||
| --- | [Determining string literal lengths for C functions](./more/strlen_for_c/README.md) | NA |
|
||||
| --- | [Under the hood: System Calls](./more/system_calls/README.md) | NA |
|
||||
|
||||
## Projects
|
||||
|
||||
|
|
|
|||
178
more/system_calls/README.md
Normal file
178
more/system_calls/README.md
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
# Under the hood: System Calls
|
||||
|
||||
The term "function" is used many places in this book and needs no
|
||||
additional explanation here. The term "system call" has also been used
|
||||
in many places, often with a comment that making a system call through
|
||||
the C runtime is actually just calling an ordinary function acting as
|
||||
a wrapper. An explanation of this has been promised...
|
||||
|
||||
## Wrappers
|
||||
|
||||
"Wrapper" is a term used to describe a function which hides the details
|
||||
of something else, often another function or functions. Hiding details
|
||||
is a form of abstraction and can be a good thing. Broadly speaking,
|
||||
an API (Application Programmer's Interface) is itself another example
|
||||
of wrappers in common use.
|
||||
|
||||
## C runtime as a wrapper
|
||||
|
||||
Many C runtime functions are just wrappers for system calls. For example
|
||||
if you call `open()` from the C runtime, the function will perform a few
|
||||
bookkeeping operations and then make the actual system call.
|
||||
|
||||
## What IS a system call?
|
||||
|
||||
The short answer is a system call is a sort-of function call that is
|
||||
serviced by the operating system itself, within its own private region
|
||||
of memory and with access to internal features and data structures.
|
||||
|
||||
Our programs run in "userland". The technical name for userland on the
|
||||
ARM64 processor is EL0 (Exception Level 0).
|
||||
|
||||
We can operate within the kernel's space only through carefully
|
||||
controlled mechanisms - such as system calls. The technical name for
|
||||
where the kernel (or system) generally operates is called EL1.
|
||||
|
||||
There are two higher Exception Levels (EL2 and EL3) which are beyond
|
||||
the scope of this book.
|
||||
|
||||
## Mechanism of making a system call
|
||||
|
||||
First, like any function call, parameters need to be set up. The first
|
||||
parameter goes in the first register, etc.
|
||||
|
||||
Second, a number associated with the specific system call we wish to
|
||||
make is loaded in a specific register (`w8`).
|
||||
|
||||
Finally, a special instruction `svc` causes a trap which elevates us out
|
||||
of userland into kernel space. Said differently, `svc` causes a
|
||||
transition from EL0 to EL1. There, various checks are done and the
|
||||
actual code for the system call is run.
|
||||
|
||||
A description of returning from a system call is beyond the scope of
|
||||
this book. Hint: just as there's a special instruction that escalates
|
||||
from EL0 to EL1, there is a special instruction that does the reverse.
|
||||
|
||||
## What is the number associated with a particular system call?
|
||||
|
||||
Hard question.
|
||||
|
||||
In a perfect world, each Linux distribution would use the same set of
|
||||
system call numbers. But no.
|
||||
|
||||
[This](https://marcin.juszkiewicz.com.pl/download/tables/syscalls.html)
|
||||
is he most comprehensive list of system call numbers we have seen. It
|
||||
shows system call numbers for many architectures and distributions.
|
||||
|
||||
## Example: calling `getpid()`
|
||||
|
||||
The system call `getpid()` fetches the running process's process ID.
|
||||
Every executing entity has one.
|
||||
|
||||
We present four different versions of the same program:
|
||||
|
||||
1. Written in C++
|
||||
|
||||
2. Written in C
|
||||
|
||||
3. Written using C runtime from assembly language
|
||||
|
||||
4. Calling the system call directly from assembly language
|
||||
|
||||
### Written in C++
|
||||
|
||||
```c++
|
||||
#include <iostream> // 1
|
||||
#include <unistd.h> // 2
|
||||
// 3
|
||||
using std::cout; // 4
|
||||
using std::endl; // 5
|
||||
// 6
|
||||
int main() { // 7
|
||||
cout << "Greetings from: " << getpid() << endl; // 8
|
||||
return 0; // 9
|
||||
} // 10
|
||||
```
|
||||
|
||||
### Written in C
|
||||
|
||||
```c
|
||||
#include <stdio.h> // 1
|
||||
#include <unistd.h> // 2
|
||||
// 3
|
||||
int main() { // 4
|
||||
printf("Greetings from: %d\n", getpid()); // 5
|
||||
return 0; // 6
|
||||
} // 7
|
||||
```
|
||||
|
||||
### Written in assembly language using C runtime
|
||||
|
||||
```text
|
||||
.global main // 1
|
||||
.text // 2
|
||||
.align 2 // 3
|
||||
// 4
|
||||
main: stp x29, x30, [sp, -16]! // 5
|
||||
bl getpid // 6
|
||||
mov w1, w0 // 7
|
||||
ldr x0, =fmt // 8
|
||||
bl printf // 9
|
||||
ldp x29, x30, [sp], 16 // 10
|
||||
mov w0, wzr // 11
|
||||
ret // 12
|
||||
// 13
|
||||
.data // 14
|
||||
fmt: .asciz "Greetings from: %d\n" // 15
|
||||
// 16
|
||||
.end // 17
|
||||
```
|
||||
|
||||
### And finally: calling the system call directly
|
||||
|
||||
```text
|
||||
.global main // 1
|
||||
.text // 2
|
||||
.align 2 // 3
|
||||
// 4
|
||||
main: stp x29, x30, [sp, -16]! // 5
|
||||
mov x8, 172 // getpid on ARM64 // 6
|
||||
svc 0 // trap to EL1 // 7
|
||||
mov w1, w0 // 8
|
||||
ldr x0, =fmt // 9
|
||||
bl printf // 10
|
||||
ldp x29, x30, [sp], 16 // 11
|
||||
mov w0, wzr // 12
|
||||
ret // 13
|
||||
// 14
|
||||
.data // 15
|
||||
fmt: .asciz "Greetings from: %d\n" // 16
|
||||
// 17
|
||||
.end // 18
|
||||
```
|
||||
|
||||
We chose `getpid()` because it doesn't require any parameters. Using
|
||||
the C runtime, we simply `bl` to it. Calling the system call directly
|
||||
is different in that we must first load `x8` with the number that
|
||||
corresponds to `getpid()` for the AARCH64 architecture.
|
||||
|
||||
Consulting [this](https://marcin.juszkiewicz.com.pl/download/tables/syscalls.html)
|
||||
awesome website, we find that the number we want is 172.
|
||||
|
||||

|
||||
|
||||
The constant specific to the system call we want is loaded into `x8`.
|
||||
Recall that `x0` through `x7` are scratch registers.
|
||||
|
||||
Then on line 7, the `svc` with the argument 0 initiates the escalation
|
||||
from EL0 to EL1 where the kernel implements our desired functionality
|
||||
and returns to us.
|
||||
|
||||
### Review
|
||||
|
||||
System calls are functions implemented inside the operating system.
|
||||
|
||||
To get there, at some point perhaps behind a wrapper function, a
|
||||
specific system call number is placed in `x8` with other scratch
|
||||
registers getting the system call's documented parameters and the `svc`
|
||||
instruction is executed.
|
||||
BIN
more/system_calls/getpid/getpid.png
Normal file
BIN
more/system_calls/getpid/getpid.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 372 KiB |
7
more/system_calls/getpid/main.c
Normal file
7
more/system_calls/getpid/main.c
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
printf("Greetings from: %d\n", getpid());
|
||||
return 0;
|
||||
}
|
||||
10
more/system_calls/getpid/main.cpp
Normal file
10
more/system_calls/getpid/main.cpp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
int main() {
|
||||
cout << "Greetings from: " << getpid() << endl;
|
||||
return 0;
|
||||
}
|
||||
17
more/system_calls/getpid/main_asm.s
Normal file
17
more/system_calls/getpid/main_asm.s
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.global main
|
||||
.text
|
||||
.align 2
|
||||
|
||||
main: stp x29, x30, [sp, -16]!
|
||||
bl getpid
|
||||
mov w1, w0
|
||||
ldr x0, =fmt
|
||||
bl printf
|
||||
ldp x29, x30, [sp], 16
|
||||
mov w0, wzr
|
||||
ret
|
||||
|
||||
.data
|
||||
fmt: .asciz "Greetings from: %d\n"
|
||||
|
||||
.end
|
||||
18
more/system_calls/getpid/main_direct.s
Normal file
18
more/system_calls/getpid/main_direct.s
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
.global main
|
||||
.text
|
||||
.align 2
|
||||
|
||||
main: stp x29, x30, [sp, -16]!
|
||||
mov x8, 172 // getpid on ARM64
|
||||
svc 0 // trap to EL1
|
||||
mov w1, w0
|
||||
ldr x0, =fmt
|
||||
bl printf
|
||||
ldp x29, x30, [sp], 16
|
||||
mov w0, wzr
|
||||
ret
|
||||
|
||||
.data
|
||||
fmt: .asciz "Greetings from: %d\n"
|
||||
|
||||
.end
|
||||
|
|
@ -1,9 +1,57 @@
|
|||
# Section 1 / Examples of calling common C runtime functions
|
||||
# Section 1 / Example of calling some common C runtime functions
|
||||
|
||||
This chapter gives examples of calling common C runtime functions from
|
||||
assembly language.
|
||||
This chapter gives an example of calling the most common C I/O functions
|
||||
from assembly language.
|
||||
|
||||
There are, by the way, two broad types of functions within the C
|
||||
runtime. Some are implemented largely in the C runtime itself. Others
|
||||
that exist in the C runtime act as wrappers for functions implemented
|
||||
within the OS itself. These are called "system calls".
|
||||
|
||||
For the purposes of calling functions in the C runtime, there is no
|
||||
practical difference between these two types. Note however, there are
|
||||
ways of calling system calls directly using the `svc` instruction. We
|
||||
will cover this way of making system calls as well. See
|
||||
[here](../../more/system_calls/README.md).
|
||||
|
||||
## Low level file operations
|
||||
|
||||
The following example shows `open()`, `close()`, `read()`, `write()`
|
||||
and `lseek()`.
|
||||
This example [program](./file_ops.s) makes use of `open()`, `close()`,
|
||||
`read()`, `write()` and `lseek()`. These are implemented in the C
|
||||
runtime as wrappers for system calls.
|
||||
|
||||
The program will
|
||||
|
||||
* create a file,
|
||||
|
||||
* write a small amount of text to it,
|
||||
|
||||
* rewind (seek) back to the beginning of the file,
|
||||
|
||||
* read back and print the contents of the file and then
|
||||
|
||||
* close the file.
|
||||
|
||||
A lot of error checking is also implemented (frankly speaking: until we
|
||||
got bored writing the example).
|
||||
|
||||
Doing all of these ballooned the example program to about 200 lines. As
|
||||
such we won't explain the code line by line but, in compensation, the
|
||||
code is liberally commented.
|
||||
|
||||
Here is just a bit:
|
||||
|
||||
```text
|
||||
/* off_t lseek(int fd, off_t offset, int whence);
|
||||
*/
|
||||
seek_zero:
|
||||
stp x29, x30, [sp, -16]!
|
||||
mov w0, fd // file descriptor
|
||||
mov x1, xzr // beginning of file
|
||||
mov w2, wzr // SEEK_SET - absolute offset
|
||||
bl lseek
|
||||
ldp x29, x30, [sp], 16
|
||||
ret
|
||||
```
|
||||
|
||||
Calling this function rewinds the read / write "head" to position 0.
|
||||
|
|
|
|||
|
|
@ -1,313 +0,0 @@
|
|||
.arch armv8-a
|
||||
.file "fo.cpp"
|
||||
.text
|
||||
.section .text._ZStanSt13_Ios_FmtflagsS_,"axG",@progbits,_ZStanSt13_Ios_FmtflagsS_,comdat
|
||||
.align 2
|
||||
.weak _ZStanSt13_Ios_FmtflagsS_
|
||||
.type _ZStanSt13_Ios_FmtflagsS_, %function
|
||||
_ZStanSt13_Ios_FmtflagsS_:
|
||||
.LFB1316:
|
||||
.cfi_startproc
|
||||
sub sp, sp, #16
|
||||
.cfi_def_cfa_offset 16
|
||||
str w0, [sp, 12]
|
||||
str w1, [sp, 8]
|
||||
ldr w1, [sp, 12]
|
||||
ldr w0, [sp, 8]
|
||||
and w0, w1, w0
|
||||
add sp, sp, 16
|
||||
.cfi_def_cfa_offset 0
|
||||
ret
|
||||
.cfi_endproc
|
||||
.LFE1316:
|
||||
.size _ZStanSt13_Ios_FmtflagsS_, .-_ZStanSt13_Ios_FmtflagsS_
|
||||
.section .text._ZStorSt13_Ios_FmtflagsS_,"axG",@progbits,_ZStorSt13_Ios_FmtflagsS_,comdat
|
||||
.align 2
|
||||
.weak _ZStorSt13_Ios_FmtflagsS_
|
||||
.type _ZStorSt13_Ios_FmtflagsS_, %function
|
||||
_ZStorSt13_Ios_FmtflagsS_:
|
||||
.LFB1317:
|
||||
.cfi_startproc
|
||||
sub sp, sp, #16
|
||||
.cfi_def_cfa_offset 16
|
||||
str w0, [sp, 12]
|
||||
str w1, [sp, 8]
|
||||
ldr w1, [sp, 12]
|
||||
ldr w0, [sp, 8]
|
||||
orr w0, w1, w0
|
||||
add sp, sp, 16
|
||||
.cfi_def_cfa_offset 0
|
||||
ret
|
||||
.cfi_endproc
|
||||
.LFE1317:
|
||||
.size _ZStorSt13_Ios_FmtflagsS_, .-_ZStorSt13_Ios_FmtflagsS_
|
||||
.section .text._ZStcoSt13_Ios_Fmtflags,"axG",@progbits,_ZStcoSt13_Ios_Fmtflags,comdat
|
||||
.align 2
|
||||
.weak _ZStcoSt13_Ios_Fmtflags
|
||||
.type _ZStcoSt13_Ios_Fmtflags, %function
|
||||
_ZStcoSt13_Ios_Fmtflags:
|
||||
.LFB1319:
|
||||
.cfi_startproc
|
||||
sub sp, sp, #16
|
||||
.cfi_def_cfa_offset 16
|
||||
str w0, [sp, 12]
|
||||
ldr w0, [sp, 12]
|
||||
mvn w0, w0
|
||||
add sp, sp, 16
|
||||
.cfi_def_cfa_offset 0
|
||||
ret
|
||||
.cfi_endproc
|
||||
.LFE1319:
|
||||
.size _ZStcoSt13_Ios_Fmtflags, .-_ZStcoSt13_Ios_Fmtflags
|
||||
.section .text._ZStoRRSt13_Ios_FmtflagsS_,"axG",@progbits,_ZStoRRSt13_Ios_FmtflagsS_,comdat
|
||||
.align 2
|
||||
.weak _ZStoRRSt13_Ios_FmtflagsS_
|
||||
.type _ZStoRRSt13_Ios_FmtflagsS_, %function
|
||||
_ZStoRRSt13_Ios_FmtflagsS_:
|
||||
.LFB1320:
|
||||
.cfi_startproc
|
||||
stp x29, x30, [sp, -32]!
|
||||
.cfi_def_cfa_offset 32
|
||||
.cfi_offset 29, -32
|
||||
.cfi_offset 30, -24
|
||||
mov x29, sp
|
||||
str x0, [sp, 24]
|
||||
str w1, [sp, 20]
|
||||
ldr x0, [sp, 24]
|
||||
ldr w0, [x0]
|
||||
ldr w1, [sp, 20]
|
||||
bl _ZStorSt13_Ios_FmtflagsS_
|
||||
mov w1, w0
|
||||
ldr x0, [sp, 24]
|
||||
str w1, [x0]
|
||||
ldr x0, [sp, 24]
|
||||
ldp x29, x30, [sp], 32
|
||||
.cfi_restore 30
|
||||
.cfi_restore 29
|
||||
.cfi_def_cfa_offset 0
|
||||
ret
|
||||
.cfi_endproc
|
||||
.LFE1320:
|
||||
.size _ZStoRRSt13_Ios_FmtflagsS_, .-_ZStoRRSt13_Ios_FmtflagsS_
|
||||
.section .text._ZStaNRSt13_Ios_FmtflagsS_,"axG",@progbits,_ZStaNRSt13_Ios_FmtflagsS_,comdat
|
||||
.align 2
|
||||
.weak _ZStaNRSt13_Ios_FmtflagsS_
|
||||
.type _ZStaNRSt13_Ios_FmtflagsS_, %function
|
||||
_ZStaNRSt13_Ios_FmtflagsS_:
|
||||
.LFB1321:
|
||||
.cfi_startproc
|
||||
stp x29, x30, [sp, -32]!
|
||||
.cfi_def_cfa_offset 32
|
||||
.cfi_offset 29, -32
|
||||
.cfi_offset 30, -24
|
||||
mov x29, sp
|
||||
str x0, [sp, 24]
|
||||
str w1, [sp, 20]
|
||||
ldr x0, [sp, 24]
|
||||
ldr w0, [x0]
|
||||
ldr w1, [sp, 20]
|
||||
bl _ZStanSt13_Ios_FmtflagsS_
|
||||
mov w1, w0
|
||||
ldr x0, [sp, 24]
|
||||
str w1, [x0]
|
||||
ldr x0, [sp, 24]
|
||||
ldp x29, x30, [sp], 32
|
||||
.cfi_restore 30
|
||||
.cfi_restore 29
|
||||
.cfi_def_cfa_offset 0
|
||||
ret
|
||||
.cfi_endproc
|
||||
.LFE1321:
|
||||
.size _ZStaNRSt13_Ios_FmtflagsS_, .-_ZStaNRSt13_Ios_FmtflagsS_
|
||||
.section .text._ZNSt8ios_base4setfESt13_Ios_FmtflagsS0_,"axG",@progbits,_ZNSt8ios_base4setfESt13_Ios_FmtflagsS0_,comdat
|
||||
.align 2
|
||||
.weak _ZNSt8ios_base4setfESt13_Ios_FmtflagsS0_
|
||||
.type _ZNSt8ios_base4setfESt13_Ios_FmtflagsS0_, %function
|
||||
_ZNSt8ios_base4setfESt13_Ios_FmtflagsS0_:
|
||||
.LFB1350:
|
||||
.cfi_startproc
|
||||
stp x29, x30, [sp, -48]!
|
||||
.cfi_def_cfa_offset 48
|
||||
.cfi_offset 29, -48
|
||||
.cfi_offset 30, -40
|
||||
mov x29, sp
|
||||
str x0, [sp, 24]
|
||||
str w1, [sp, 20]
|
||||
str w2, [sp, 16]
|
||||
ldr x0, [sp, 24]
|
||||
ldr w0, [x0, 24]
|
||||
str w0, [sp, 44]
|
||||
ldr w0, [sp, 16]
|
||||
bl _ZStcoSt13_Ios_Fmtflags
|
||||
mov w1, w0
|
||||
ldr x0, [sp, 24]
|
||||
add x0, x0, 24
|
||||
bl _ZStaNRSt13_Ios_FmtflagsS_
|
||||
ldr w1, [sp, 16]
|
||||
ldr w0, [sp, 20]
|
||||
bl _ZStanSt13_Ios_FmtflagsS_
|
||||
mov w1, w0
|
||||
ldr x0, [sp, 24]
|
||||
add x0, x0, 24
|
||||
bl _ZStoRRSt13_Ios_FmtflagsS_
|
||||
ldr w0, [sp, 44]
|
||||
ldp x29, x30, [sp], 48
|
||||
.cfi_restore 30
|
||||
.cfi_restore 29
|
||||
.cfi_def_cfa_offset 0
|
||||
ret
|
||||
.cfi_endproc
|
||||
.LFE1350:
|
||||
.size _ZNSt8ios_base4setfESt13_Ios_FmtflagsS0_, .-_ZNSt8ios_base4setfESt13_Ios_FmtflagsS0_
|
||||
.section .text._ZSt3hexRSt8ios_base,"axG",@progbits,_ZSt3hexRSt8ios_base,comdat
|
||||
.align 2
|
||||
.weak _ZSt3hexRSt8ios_base
|
||||
.type _ZSt3hexRSt8ios_base, %function
|
||||
_ZSt3hexRSt8ios_base:
|
||||
.LFB1378:
|
||||
.cfi_startproc
|
||||
stp x29, x30, [sp, -32]!
|
||||
.cfi_def_cfa_offset 32
|
||||
.cfi_offset 29, -32
|
||||
.cfi_offset 30, -24
|
||||
mov x29, sp
|
||||
str x0, [sp, 24]
|
||||
mov w2, 74
|
||||
mov w1, 8
|
||||
ldr x0, [sp, 24]
|
||||
bl _ZNSt8ios_base4setfESt13_Ios_FmtflagsS0_
|
||||
ldr x0, [sp, 24]
|
||||
ldp x29, x30, [sp], 32
|
||||
.cfi_restore 30
|
||||
.cfi_restore 29
|
||||
.cfi_def_cfa_offset 0
|
||||
ret
|
||||
.cfi_endproc
|
||||
.LFE1378:
|
||||
.size _ZSt3hexRSt8ios_base, .-_ZSt3hexRSt8ios_base
|
||||
.local _ZStL8__ioinit
|
||||
.comm _ZStL8__ioinit,1,8
|
||||
.section .rodata
|
||||
.align 3
|
||||
.LC0:
|
||||
.string "test.txt"
|
||||
.text
|
||||
.align 2
|
||||
.global main
|
||||
.type main, %function
|
||||
main:
|
||||
.LFB1729:
|
||||
.cfi_startproc
|
||||
stp x29, x30, [sp, -32]!
|
||||
.cfi_def_cfa_offset 32
|
||||
.cfi_offset 29, -32
|
||||
.cfi_offset 30, -24
|
||||
mov x29, sp
|
||||
mov w2, 438
|
||||
mov w1, 66
|
||||
adrp x0, .LC0
|
||||
add x0, x0, :lo12:.LC0
|
||||
bl open
|
||||
str w0, [sp, 28]
|
||||
ldr w1, [sp, 28]
|
||||
adrp x0, :got:_ZSt4cout
|
||||
ldr x0, [x0, #:got_lo12:_ZSt4cout]
|
||||
bl _ZNSolsEi
|
||||
mov x2, x0
|
||||
adrp x0, :got:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
|
||||
ldr x1, [x0, #:got_lo12:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_]
|
||||
mov x0, x2
|
||||
bl _ZNSolsEPFRSoS_E
|
||||
adrp x0, _ZSt3hexRSt8ios_base
|
||||
add x1, x0, :lo12:_ZSt3hexRSt8ios_base
|
||||
adrp x0, :got:_ZSt4cout
|
||||
ldr x0, [x0, #:got_lo12:_ZSt4cout]
|
||||
bl _ZNSolsEPFRSt8ios_baseS0_E
|
||||
mov w1, 66
|
||||
bl _ZNSolsEi
|
||||
mov x2, x0
|
||||
adrp x0, :got:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
|
||||
ldr x1, [x0, #:got_lo12:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_]
|
||||
mov x0, x2
|
||||
bl _ZNSolsEPFRSoS_E
|
||||
ldr w0, [sp, 28]
|
||||
cmp w0, 0
|
||||
blt .L16
|
||||
ldr w0, [sp, 28]
|
||||
bl close
|
||||
.L16:
|
||||
mov w0, 0
|
||||
ldp x29, x30, [sp], 32
|
||||
.cfi_restore 30
|
||||
.cfi_restore 29
|
||||
.cfi_def_cfa_offset 0
|
||||
ret
|
||||
.cfi_endproc
|
||||
.LFE1729:
|
||||
.size main, .-main
|
||||
.align 2
|
||||
.type _Z41__static_initialization_and_destruction_0ii, %function
|
||||
_Z41__static_initialization_and_destruction_0ii:
|
||||
.LFB2230:
|
||||
.cfi_startproc
|
||||
stp x29, x30, [sp, -32]!
|
||||
.cfi_def_cfa_offset 32
|
||||
.cfi_offset 29, -32
|
||||
.cfi_offset 30, -24
|
||||
mov x29, sp
|
||||
str w0, [sp, 28]
|
||||
str w1, [sp, 24]
|
||||
ldr w0, [sp, 28]
|
||||
cmp w0, 1
|
||||
bne .L20
|
||||
ldr w1, [sp, 24]
|
||||
mov w0, 65535
|
||||
cmp w1, w0
|
||||
bne .L20
|
||||
adrp x0, _ZStL8__ioinit
|
||||
add x0, x0, :lo12:_ZStL8__ioinit
|
||||
bl _ZNSt8ios_base4InitC1Ev
|
||||
adrp x0, __dso_handle
|
||||
add x2, x0, :lo12:__dso_handle
|
||||
adrp x0, _ZStL8__ioinit
|
||||
add x1, x0, :lo12:_ZStL8__ioinit
|
||||
adrp x0, :got:_ZNSt8ios_base4InitD1Ev
|
||||
ldr x0, [x0, #:got_lo12:_ZNSt8ios_base4InitD1Ev]
|
||||
bl __cxa_atexit
|
||||
.L20:
|
||||
nop
|
||||
ldp x29, x30, [sp], 32
|
||||
.cfi_restore 30
|
||||
.cfi_restore 29
|
||||
.cfi_def_cfa_offset 0
|
||||
ret
|
||||
.cfi_endproc
|
||||
.LFE2230:
|
||||
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
|
||||
.align 2
|
||||
.type _GLOBAL__sub_I_main, %function
|
||||
_GLOBAL__sub_I_main:
|
||||
.LFB2231:
|
||||
.cfi_startproc
|
||||
stp x29, x30, [sp, -16]!
|
||||
.cfi_def_cfa_offset 16
|
||||
.cfi_offset 29, -16
|
||||
.cfi_offset 30, -8
|
||||
mov x29, sp
|
||||
mov w1, 65535
|
||||
mov w0, 1
|
||||
bl _Z41__static_initialization_and_destruction_0ii
|
||||
ldp x29, x30, [sp], 16
|
||||
.cfi_restore 30
|
||||
.cfi_restore 29
|
||||
.cfi_def_cfa_offset 0
|
||||
ret
|
||||
.cfi_endproc
|
||||
.LFE2231:
|
||||
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
|
||||
.section .init_array,"aw"
|
||||
.align 3
|
||||
.xword _GLOBAL__sub_I_main
|
||||
.hidden __dso_handle
|
||||
.ident "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0"
|
||||
.section .note.GNU-stack,"",@progbits
|
||||
6
section_1/funcs/seek.c
Normal file
6
section_1/funcs/seek.c
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#include <unistd.h>
|
||||
|
||||
void Foo() {
|
||||
write(0, 0, 0);
|
||||
return;
|
||||
}
|
||||
21
section_1/funcs/seek.s
Normal file
21
section_1/funcs/seek.s
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
.section __TEXT,__text,regular,pure_instructions
|
||||
.build_version macos, 13, 0 sdk_version 13, 1
|
||||
.globl _Foo ; -- Begin function Foo
|
||||
.p2align 2
|
||||
_Foo: ; @Foo
|
||||
.cfi_startproc
|
||||
; %bb.0:
|
||||
stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill
|
||||
mov x29, sp
|
||||
.cfi_def_cfa w29, 16
|
||||
.cfi_offset w30, -8
|
||||
.cfi_offset w29, -16
|
||||
mov w0, #0
|
||||
mov x1, #0
|
||||
mov x2, #0
|
||||
bl _write
|
||||
ldp x29, x30, [sp], #16 ; 16-byte Folded Reload
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.subsections_via_symbols
|
||||
1
section_1/funcs/test.txt
Normal file
1
section_1/funcs/test.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
some data
|
||||
|
|
@ -13,27 +13,24 @@ The TL;DR of endianness is this:
|
|||
|
||||
* suppose you have a pointer to a `short`
|
||||
|
||||
* is the *byte* at the address contained in the pointer the first byte
|
||||
* is the byte at the address contained in the pointer the first byte
|
||||
of the `short` or the second?
|
||||
|
||||
* this extends to `int` and to `long` as well
|
||||
|
||||
That's the essential nugget.
|
||||
This extends to `int` and to `long` as well.
|
||||
|
||||
## Why talk about this?
|
||||
|
||||
Endianness is mostly hidden from you. The value of an `int` is the value
|
||||
you think it is and how the `int` is constructed isn't even on your
|
||||
you think it is and how the `int` is constructed probably isn't on your
|
||||
radar.
|
||||
|
||||
Endianness is a big deal for people who code network applications where
|
||||
early on a standard was required to determine which byte of an `int` is
|
||||
early on, a standard was required to determine which byte of an `int` is
|
||||
the first one transmitted over the wire. Network Byte Order is big
|
||||
endian. Unfortunately, most of todays very popular processors are
|
||||
little endian.
|
||||
endian. Most of todays very popular processors are little endian.
|
||||
|
||||
For us assembly language coders, endianness comes into play when we're
|
||||
using our debuggers. After all, when we examine memory, memory is a
|
||||
For us assembly language coders, endianness especially comes into play
|
||||
when we're using our debuggers. When we examine memory, memory is a
|
||||
linear stream of bytes. Without understanding endianness, you might be
|
||||
confused about what you're seeing.
|
||||
|
||||
|
|
@ -48,8 +45,10 @@ end.
|
|||
|
||||
Please read the entirety of Gulliver's Travels keeping in mind how
|
||||
absolutely nasty Swift's portrayal of 18th century politics can be. You
|
||||
won't be disappointed. And no, the classic cartoon version, Max
|
||||
Fleischer's 1939 masterpiece, doesn't do the book justice.
|
||||
won't be disappointed.
|
||||
|
||||
The classic cartoon version, Max Fleischer's 1939 masterpiece, doesn't
|
||||
do the book justice.
|
||||
|
||||
## How do the terms apply?
|
||||
|
||||
|
|
@ -59,7 +58,7 @@ significant byte lives. Thus the terms...
|
|||
|
||||
If the most significant byte comes first, the architecture is said to
|
||||
be big-endian. If the least significant byte comes first, it is little
|
||||
endian.
|
||||
endian. There's a little more to it than that, but not much.
|
||||
|
||||
**Notice I have not discussed strings.**
|
||||
|
||||
|
|
@ -122,17 +121,17 @@ string Dump(T & i) { // 15
|
|||
} // 24
|
||||
```
|
||||
|
||||
First, you might not be familiar with templated functions. Notice
|
||||
line 14 tells the compiler that the next function is templated and that
|
||||
`T` be take on the value matching the parameter that is actually given
|
||||
to the function.
|
||||
You might not be familiar with templated functions. Notice line 14 tells
|
||||
the compiler that the next function is templated and that `T` will stand
|
||||
in for the type matching the parameter that is actually given to the
|
||||
function.
|
||||
|
||||
Thus, at compile time, the compiler write a different function for each
|
||||
Thus, at compile time, the compiler writes a different function for each
|
||||
of `int`, `short` and `long`. When the compiler gets to the `sizeof()`
|
||||
on line 20, the size of the "right" type is taken.
|
||||
|
||||
Using the templated approach we need write this function only once
|
||||
rather than three times for each of `int`, `short` and `long`.
|
||||
Using the templated approach, we need write this function only once
|
||||
rather than three times (for each of `int`, `short` and `long`).
|
||||
|
||||
## Output on a little endian machine
|
||||
|
||||
|
|
@ -169,6 +168,7 @@ i64: 89abcdef01234567
|
|||
```
|
||||
|
||||
Notice the values for `i16` and `i32` match the right hand column above.
|
||||
|
||||
The value for `i64` is borked in that we specified it in the C code as a
|
||||
`long`. We then tried specifying the `long` as a `long long`. Apparently
|
||||
there is little support for 64 bit numbers on this ancient but
|
||||
|
|
|
|||
Loading…
Reference in a new issue