Skip to content

Commit 0c0c6c9

Browse files
committed
Add demo used for CA function interface.
Contains sum example for asm calling C, C calling asm and inline asm on x64, System V ABI. The README provides a small tutorial on the syntax for inline asm. Signed-off-by: Dan Novischi <dnovischi@users.noreply.github.com>
1 parent 548254d commit 0c0c6c9

8 files changed

Lines changed: 315 additions & 0 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
NASM=nasm
2+
GCC=gcc
3+
4+
all: asm_call_c c_call_asm inline_asm
5+
6+
asm_call_c: asm_call_c_main.o asm_call_c_sum.o
7+
$(GCC) asm_call_c_main.o asm_call_c_sum.o -no-pie -o asm_call_c
8+
9+
asm_call_c_main.o: asm_call_c_main.asm
10+
$(NASM) -felf64 asm_call_c_main.asm -o asm_call_c_main.o
11+
12+
asm_call_c_sum.o: asm_call_c_sum.c
13+
$(GCC) -c asm_call_c_sum.c -o asm_call_c_sum.o
14+
15+
c_call_asm: c_call_asm_main.o c_call_asm_sum.o
16+
$(GCC) c_call_asm_main.o c_call_asm_sum.o -no-pie -o c_call_asm
17+
18+
c_call_asm_main.o: c_call_asm_main.c
19+
$(GCC) -c c_call_asm_main.c -o c_call_asm_main.o
20+
21+
c_call_asm_sum.o: c_call_asm_sum.asm
22+
$(NASM) -felf64 c_call_asm_sum.asm -o c_call_asm_sum.o
23+
24+
inline_asm: inline_asm.o
25+
$(GCC) inline_asm.o -o inline_asm
26+
27+
inline_asm.o: inline_asm.c
28+
$(GCC) -masm=intel -c inline_asm.c -o inline_asm.o
29+
30+
clean:
31+
rm -f *.o asm_call_c c_call_asm inline_asm
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# C <-> ASM Interface Examples (x86-64, System V AMD64)
2+
3+
This folder contains 3 examples that mirror the style of `func-cdecl`, focused on calling between C and assembly.
4+
5+
## Files
6+
7+
- `asm_call_c_main.asm` + `asm_call_c_sum.c`
8+
- Assembly `main` defines the array in `.data`, calls a C function that sums the array, then prints the result.
9+
10+
- `c_call_asm_main.c` + `c_call_asm_sum.asm`
11+
- C `main` has a globally initialized array and calls an assembly function to compute the sum.
12+
13+
- `inline_asm.c`
14+
- Single C file that uses GCC inline assembly to sum an array.
15+
16+
## Build and run
17+
18+
```bash
19+
make
20+
./asm_call_c
21+
./c_call_asm
22+
./inline_asm
23+
```
24+
25+
## Clean
26+
27+
```bash
28+
make clean
29+
```
30+
31+
---
32+
33+
## GCC Extended Inline Assembly
34+
35+
### Basic structure
36+
37+
```c
38+
__asm__ volatile (
39+
"asm instructions"
40+
: outputs
41+
: inputs
42+
: clobbers
43+
);
44+
```
45+
46+
Each section is separated by `:`. Any section can be empty (just leave it blank or omit trailing colons).
47+
48+
---
49+
50+
### Why `volatile`?
51+
52+
Without `volatile`, GCC treats the asm block like a pure function: if it thinks the outputs are unused or the block can be hoisted/eliminated/reordered, it will do so.
53+
54+
`volatile` suppresses all of that:
55+
- The block is **always emitted**, even if the output is unused.
56+
- The block is **not moved** relative to other memory operations.
57+
- Multiple identical blocks are **not merged** into one.
58+
59+
Use `volatile` whenever the asm has side effects beyond its declared outputs (e.g. it reads/writes memory, modifies flags, or does I/O). Omit it only for pure computational blocks where GCC optimizing away redundant calls is acceptable.
60+
61+
---
62+
63+
### Outputs
64+
65+
```c
66+
: "=&r" (sum)
67+
```
68+
69+
Each output is `"constraint" (c_variable)`. The variable receives the register value after the block.
70+
71+
| Modifier | Meaning |
72+
|---|---|
73+
| `=` | write-only - value on entry is discarded |
74+
| `+` | read-write - value is read on entry and written on exit |
75+
| `&` | early-clobber - this register is written before all inputs are consumed; GCC must not share it with any input |
76+
77+
---
78+
79+
### Inputs
80+
81+
```c
82+
: "r" (arr), "r" (n)
83+
```
84+
85+
Each input is `"constraint" (c_expression)`. GCC loads the value into the chosen location before the block. Inputs are numbered after outputs: if there is one output (`%0`), inputs start at `%1`, `%2`, ...
86+
87+
---
88+
89+
### Operand constraints
90+
91+
| Constraint | Location |
92+
|---|---|
93+
| `r` | any general-purpose register (GCC chooses) |
94+
| `a` | `rax` / `eax` |
95+
| `b` | `rbx` / `ebx` |
96+
| `c` | `rcx` / `ecx` |
97+
| `d` | `rdx` / `edx` |
98+
| `S` | `rsi` / `esi` |
99+
| `D` | `rdi` / `edi` |
100+
| `m` | memory operand |
101+
| `i` | immediate integer constant |
102+
103+
With `r`, GCC picks the register - use size modifiers in the asm string to get the right-width name:
104+
105+
| Modifier | Register size | Example (`%0``rsi`) |
106+
|---|---|---|
107+
| `%q0` | 64-bit | `rsi` |
108+
| `%k0` | 32-bit | `esi` |
109+
| `%w0` | 16-bit | `si` |
110+
| `%b0` | 8-bit | `sil` |
111+
112+
With specific constraints (`a`, `c`, `S`, ...) you know the register name and can write it directly.
113+
114+
---
115+
116+
### Named operands
117+
118+
Instead of positional `%0`, `%1`, you can use names:
119+
120+
```c
121+
__asm__ volatile (
122+
"add %k[sum], dword ptr [%q[arr] + r8 * 4]"
123+
: [sum] "=&r" (sum)
124+
: [arr] "r" (arr), [n] "r" (n)
125+
: "r8", "cc", "memory"
126+
);
127+
```
128+
129+
---
130+
131+
### Clobbers
132+
133+
Declare every register and resource the asm modifies that is not an output:
134+
135+
| Clobber | Meaning |
136+
|---|---|
137+
| `"rax"`, `"r8"`, ... | asm modifies this register |
138+
| `"cc"` | asm modifies RFLAGS (`add`, `sub`, `cmp`, `test`, `inc`, `dec`, ...) |
139+
| `"memory"` | asm reads/writes memory GCC cannot see; acts as a compiler barrier - GCC flushes cached values and cannot reorder memory ops across the block |
140+
141+
Omitting a clobber is undefined behaviour: GCC may place a live value in that register and your asm will silently corrupt it.
142+
143+
---
144+
145+
### Numeric labels
146+
147+
Use numbers instead of named labels to avoid collisions when the same asm block is inlined multiple times:
148+
149+
```c
150+
"test ecx, ecx\n\t"
151+
"jle 2f\n\t" // jump forward to label 2
152+
"1:\n\t" // loop top
153+
" ...\n\t"
154+
"jl 1b\n\t" // jump backward to label 1
155+
"2:\n\t" // exit
156+
```
157+
158+
`f` = forward, `b` = backward. The same number can appear many times; the direction disambiguates which instance is the target.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
section .data
2+
values: dd 7, -2, 10, 5, 4
3+
values_len: equ ($ - values) / 4
4+
fmt_print: db "asm_call_c sum = %d", 10, 0
5+
6+
section .text
7+
global main
8+
extern sum_array_c
9+
extern printf
10+
11+
main:
12+
push rbp
13+
mov rbp, rsp
14+
15+
lea rdi, [rel values]
16+
mov esi, values_len
17+
call sum_array_c
18+
19+
mov esi, eax
20+
lea rdi, [rel fmt_print]
21+
xor eax, eax
22+
call printf
23+
24+
xor eax, eax
25+
leave
26+
ret
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
#include <stddef.h>
3+
4+
int sum_array_c(const int *arr, int n)
5+
{
6+
int sum = 0;
7+
8+
for (int i = 0; i < n; i++)
9+
sum += arr[i];
10+
11+
return sum;
12+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
#include <stdio.h>
3+
4+
#include "c_call_asm_sum.h"
5+
6+
#ifndef ARRAY_SIZE
7+
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
8+
#endif
9+
10+
int g_values[] = {3, 4, -1, 9, 12, 5};
11+
int g_values_len = (int)ARRAY_SIZE(g_values);
12+
13+
int main(void)
14+
{
15+
int sum = sum_array_asm(g_values, g_values_len);
16+
17+
printf("c_call_asm sum = %d\n", sum);
18+
return 0;
19+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
section .text
2+
global sum_array_asm
3+
4+
; int sum_array_asm(const int *arr, int n)
5+
; arr -> RDI, n -> ESI, return -> EAX
6+
sum_array_asm:
7+
xor eax, eax
8+
xor edx, edx
9+
10+
test esi, esi
11+
jle .done
12+
13+
.loop:
14+
add eax, dword [rdi + rdx * 4]
15+
inc edx
16+
cmp edx, esi
17+
jl .loop
18+
19+
.done:
20+
ret
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* SPDX-License-Identifier: BSD-3-Clause */
2+
3+
#ifndef C_CALL_ASM_SUM_H
4+
#define C_CALL_ASM_SUM_H
5+
6+
int sum_array_asm(const int *arr, int n);
7+
8+
#endif /* C_CALL_ASM_SUM_H */
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
#include <stdio.h>
3+
4+
#ifndef ARRAY_SIZE
5+
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
6+
#endif
7+
8+
static int sum_array_inline(const int *arr, int n)
9+
{
10+
int sum;
11+
12+
/* %0 = sum (output, eax), %1 = arr (pointer), %2 = n (int) */
13+
__asm__ volatile (
14+
"xor %k0, %k0\n\t" /* sum = 0 */
15+
"xor r8d, r8d\n\t" /* index i = 0 */
16+
"test %k2, %k2\n\t" /* if (n <= 0) skip */
17+
"jle 2f\n\t"
18+
"1:\n\t"
19+
"add %k0, dword ptr [%q1 + r8 * 4]\n\t" /* sum += arr[i] */
20+
"inc r8d\n\t" /* i++ */
21+
"cmp r8d, %k2\n\t" /* if (i < n) loop */
22+
"jl 1b\n\t"
23+
"2:\n\t"
24+
: "=&r" (sum)
25+
: "r" (arr), "r" (n)
26+
: "r8", "cc", "memory"
27+
);
28+
29+
return sum;
30+
}
31+
32+
int main(void)
33+
{
34+
int values[] = {8, 1, -3, 20, 4};
35+
int n = (int)ARRAY_SIZE(values);
36+
int sum = sum_array_inline(values, n);
37+
38+
printf("inline_asm sum = %d\n", sum);
39+
40+
return 0;
41+
}

0 commit comments

Comments
 (0)