mirror of
https://github.com/pkivolowitz/asm_book.git
synced 2026-06-23 15:36:45 +08:00
updated apple silicon chapter to reflect changes in the macros
This commit is contained in:
parent
f584354075
commit
4ec5ca1e45
1 changed files with 38 additions and 17 deletions
|
|
@ -25,7 +25,7 @@ macro as an early form of C++ templated function (kinda but not really).
|
||||||
Here's an example of an assembly language macro:
|
Here's an example of an assembly language macro:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
.macro LD_ADDR xreg, label
|
.macro LLD_ADDR xreg, label
|
||||||
adrp \xreg, \label@PAGE
|
adrp \xreg, \label@PAGE
|
||||||
add \xreg, \xreg, \label@PAGEOFF
|
add \xreg, \xreg, \label@PAGEOFF
|
||||||
.endm
|
.endm
|
||||||
|
|
@ -34,7 +34,7 @@ Here's an example of an assembly language macro:
|
||||||
Here's how it might be used:
|
Here's how it might be used:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
LD_ADDR x0, fmt
|
LLD_ADDR x0, fmt
|
||||||
```
|
```
|
||||||
|
|
||||||
This gets expanded to:
|
This gets expanded to:
|
||||||
|
|
@ -62,6 +62,7 @@ would this be possible since we've seen that addresses are (often) six
|
||||||
bytes long and our instructions are always 4 bytes long? As we describe
|
bytes long and our instructions are always 4 bytes long? As we describe
|
||||||
elsewhere, the above `ldr` instance is actually turned into instructions
|
elsewhere, the above `ldr` instance is actually turned into instructions
|
||||||
to load an address relative to the address of the current instruction.
|
to load an address relative to the address of the current instruction.
|
||||||
|
|
||||||
As long as the data we want is relatively close to the `ldr`, this works
|
As long as the data we want is relatively close to the `ldr`, this works
|
||||||
out to a difference in addresses that is small (and so, can be fit into
|
out to a difference in addresses that is small (and so, can be fit into
|
||||||
a 4 byte instruction).
|
a 4 byte instruction).
|
||||||
|
|
@ -93,6 +94,11 @@ address into `x0` forming a complete address.
|
||||||
In this way, labels can be further away from the current instruction
|
In this way, labels can be further away from the current instruction
|
||||||
than the Linux way.
|
than the Linux way.
|
||||||
|
|
||||||
|
Apple does something similar with global variables, perhaps defined in
|
||||||
|
C or C++ files. Instead of `PAGE` and `PAGEOFF` they use global
|
||||||
|
versions. The macro `GLD_ADDR` is used in this case rather than
|
||||||
|
`LLD_ADDR` which works with "locally" defined addresses.
|
||||||
|
|
||||||
## How does this help bridge Apple and Linux?
|
## How does this help bridge Apple and Linux?
|
||||||
|
|
||||||
[Here](./macros.S) is an assembly language file containing the macros
|
[Here](./macros.S) is an assembly language file containing the macros
|
||||||
|
|
@ -102,7 +108,7 @@ closer together.
|
||||||
Notice it has:
|
Notice it has:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
.macro LD_ADDR xreg, label
|
.macro LLD_ADDR xreg, label
|
||||||
adrp \xreg, \label@PAGE
|
adrp \xreg, \label@PAGE
|
||||||
add \xreg, \xreg, \label@PAGEOFF
|
add \xreg, \xreg, \label@PAGEOFF
|
||||||
.endm
|
.endm
|
||||||
|
|
@ -111,7 +117,7 @@ Notice it has:
|
||||||
but also:
|
but also:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
.macro LD_ADDR xreg, label
|
.macro LLD_ADDR xreg, label
|
||||||
ldr \xreg, =\label
|
ldr \xreg, =\label
|
||||||
.endm
|
.endm
|
||||||
```
|
```
|
||||||
|
|
@ -134,17 +140,17 @@ provided by the standard C pre-processor. I.e.:
|
||||||
C pre-processor. `clang` on Linux will not by default but can if you
|
C pre-processor. `clang` on Linux will not by default but can if you
|
||||||
specify `-x assembler-with-cpp`.
|
specify `-x assembler-with-cpp`.
|
||||||
|
|
||||||
gcc on Mac OS can be based on clang so on Mac OS it inherits `clang`'s
|
`gcc` on Mac OS can be based on `clang` so on Mac OS it inherits
|
||||||
behavior. gcc on Linux does not run assembly language files through
|
`clang`'s behavior. `gcc` on Linux does not run assembly language files
|
||||||
the C pre-processor *if the asm file ends in .s but WILL if the file
|
through the C pre-processor *if the asm file ends in .s but WILL if the
|
||||||
ends in .S* It took the author a long time to find this...
|
file ends in .S*
|
||||||
|
|
||||||
## Differences between Apple and Linux
|
## Differences between Apple and Linux
|
||||||
|
|
||||||
### Loading label addresses
|
### Loading label addresses
|
||||||
|
|
||||||
This was described above. If you use `LD_ADDR` the macros will adapt for
|
This was described above. If you use `LLD_ADDR` the macros will adapt
|
||||||
you.
|
for you.
|
||||||
|
|
||||||
### Function labels
|
### Function labels
|
||||||
|
|
||||||
|
|
@ -181,6 +187,8 @@ use
|
||||||
|
|
||||||
and the macros will adapt.
|
and the macros will adapt.
|
||||||
|
|
||||||
|
You can find documentation on the macros [here](../../macros/README.md).
|
||||||
|
|
||||||
## Variadic functions
|
## Variadic functions
|
||||||
|
|
||||||
Functions like `printf()` are variadic. This means the function can take
|
Functions like `printf()` are variadic. This means the function can take
|
||||||
|
|
@ -203,17 +211,24 @@ use the stack.
|
||||||
Apple will put the first parameter in the zero register and then shifts
|
Apple will put the first parameter in the zero register and then shifts
|
||||||
immediately to putting all other parameters onto the stack.
|
immediately to putting all other parameters onto the stack.
|
||||||
|
|
||||||
Here is how we overcame this difference:
|
We overcome this difference by detecting which environment we are
|
||||||
|
building in using `#if` after having first set up for the Linux version.
|
||||||
|
By setting up for the Linux version, the Apple version involves just
|
||||||
|
pushing registers onto the stack.
|
||||||
|
|
||||||
|
Remember that to print a float or double, they must be copied to `x`
|
||||||
|
registers.
|
||||||
|
|
||||||
|
An example:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
// setting up a two value printf as usual
|
LLD_ADDR x0, fmt // loads the address of fmt
|
||||||
LD_ADDR x0, fmt // loads the address of fmt
|
LLD_PTR x1, ptr // loads **ptr
|
||||||
LD_ADDR x1, ptr // loads **ptr
|
ldr x1, [x1] // turns **ptr into *ptr
|
||||||
ldr x1, [x1] // dereferences **ptr to make *ptr
|
ldr x2, [x1] // dereferences *ptr to get value
|
||||||
ldr x2, [x1] // dereferences *ptr to get value
|
|
||||||
# if defined(__APPLE__)
|
# if defined(__APPLE__)
|
||||||
// if apple, push the second and third argument to stack
|
// if apple, push the second and third argument to stack
|
||||||
stp x1, x2, [sp, -16]!
|
PUSH_P x1, x2
|
||||||
CRT printf
|
CRT printf
|
||||||
add sp, sp, 16
|
add sp, sp, 16
|
||||||
# else
|
# else
|
||||||
|
|
@ -237,6 +252,8 @@ To be Apple compatible, in addition to backing up `x30` also back up
|
||||||
|
|
||||||
`mov x29, sp`
|
`mov x29, sp`
|
||||||
|
|
||||||
|
We will be converting all sample code to do this over time.
|
||||||
|
|
||||||
### More?
|
### More?
|
||||||
|
|
||||||
As we discover more differences, they will be described here.
|
As we discover more differences, they will be described here.
|
||||||
|
|
@ -248,6 +265,10 @@ code. These work the same on both Apple Silicon and Linux. If you want
|
||||||
these, put `START_PROC` after the label introducing a function. Then,
|
these, put `START_PROC` after the label introducing a function. Then,
|
||||||
put `END_PROC` after the last statement of the function.
|
put `END_PROC` after the last statement of the function.
|
||||||
|
|
||||||
|
This helps the debuggers understand where a function begins and ends.
|
||||||
|
|
||||||
|
We will transition all sample code to use this over time.
|
||||||
|
|
||||||
## A useful link
|
## A useful link
|
||||||
|
|
||||||
[Here](https://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html) is an
|
[Here](https://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html) is an
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue