Skip to content

Commit 97de020

Browse files
committed
fix: Fix compilation issues with PIE
1 parent 938a8b1 commit 97de020

File tree

6 files changed

+133
-18
lines changed

6 files changed

+133
-18
lines changed

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,30 @@ Arx is a prototype that should replace the current Arx compiler in c++.
1111

1212
If you want more information about ArxLang, please check the original project in
1313
c++: https://github.com/arxlang/arx
14+
15+
## Link Modes
16+
17+
Arx supports explicit executable link modes:
18+
19+
```bash
20+
arx program.x --link-mode auto # default, use toolchain default
21+
arx program.x --link-mode pie # force PIE executable
22+
arx program.x --link-mode no-pie # force non-PIE executable
23+
```
24+
25+
## Troubleshooting (PIE / Colab / Conda)
26+
27+
If you hit an error like:
28+
29+
```text
30+
relocation R_X86_64_32 ... can not be used when making a PIE object
31+
```
32+
33+
use:
34+
35+
```bash
36+
arx program.x --link-mode no-pie
37+
```
38+
39+
This typically happens on environments where the linker defaults to PIE while
40+
objects were not compiled in a PIE-compatible mode.

docs/getting-started.md

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ arx --show-llvm-ir hello.x
7272
arx hello.x --output-file hello
7373
```
7474

75+
### Control executable link mode
76+
77+
```bash
78+
arx examples/print-star.x --link-mode auto
79+
arx examples/print-star.x --link-mode pie
80+
arx examples/print-star.x --link-mode no-pie
81+
```
82+
7583
## Examples
7684

7785
The `examples/` directory contains several sample programs:
@@ -193,16 +201,40 @@ arx run examples/print-star.x
193201
arx [input_files] [options]
194202
```
195203

196-
| Option | Description |
197-
| ---------------- | ------------------------------------------------ |
198-
| `--version` | Show the installed version |
199-
| `--output-file` | Specify the output file path |
200-
| `--lib` | Build source code as a library |
201-
| `--show-ast` | Print the AST for the input source code |
202-
| `--show-tokens` | Print the tokens for the input source |
203-
| `--show-llvm-ir` | Print the LLVM IR for the input source |
204-
| `--run` | Build and execute the compiled binary |
205-
| `--shell` | Open Arx in a shell prompt (not yet implemented) |
204+
| Option | Description |
205+
| ---------------- | ------------------------------------------------- |
206+
| `--version` | Show the installed version |
207+
| `--output-file` | Specify the output file path |
208+
| `--lib` | Build source code as a library |
209+
| `--show-ast` | Print the AST for the input source code |
210+
| `--show-tokens` | Print the tokens for the input source |
211+
| `--show-llvm-ir` | Print the LLVM IR for the input source |
212+
| `--run` | Build and execute the compiled binary |
213+
| `--shell` | Open Arx in a shell prompt (not yet implemented) |
214+
| `--link-mode` | Set executable link mode: `auto`, `pie`, `no-pie` |
215+
216+
## Troubleshooting
217+
218+
### PIE linker error in Colab or Conda
219+
220+
If the build fails with an error similar to:
221+
222+
```text
223+
relocation R_X86_64_32 against `.rodata' can not be used when making a PIE object
224+
```
225+
226+
run the compile step with:
227+
228+
```bash
229+
arx average.x --link-mode no-pie
230+
```
231+
232+
If you need a manual fallback:
233+
234+
```bash
235+
arx --lib average.x --output-file average.o
236+
clang -no-pie average.o -o average
237+
```
206238

207239
## Language Basics
208240

src/arx/cli.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ def get_args() -> argparse.ArgumentParser:
108108
action="store_true",
109109
help="Build and run the compiled binary.",
110110
)
111+
parser.add_argument(
112+
"--link-mode",
113+
type=str,
114+
choices=("auto", "pie", "no-pie"),
115+
default="auto",
116+
help=(
117+
"Set executable link mode: auto (toolchain default), "
118+
"pie, or no-pie."
119+
),
120+
)
111121

112122
return parser
113123

src/arx/codegen.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import os
88
import tempfile
99

10-
from typing import Any, Callable
10+
from typing import Any, Callable, Literal
1111

1212
import astx
1313
import xh
@@ -37,6 +37,8 @@ class LLVMLiteIR(BaseLLVMLiteIR):
3737
type: ArxLLVMLiteIRVisitor
3838
"""
3939

40+
LINK_MODES = {"auto", "pie", "no-pie"}
41+
4042
def __init__(self) -> None:
4143
"""
4244
title: Initialize LLVMIR.
@@ -45,7 +47,11 @@ def __init__(self) -> None:
4547
self.translator: ArxLLVMLiteIRVisitor = ArxLLVMLiteIRVisitor()
4648

4749
def build(
48-
self, node: astx.AST, output_file: str, link: bool = True
50+
self,
51+
node: astx.AST,
52+
output_file: str,
53+
link: bool = True,
54+
link_mode: Literal["auto", "pie", "no-pie"] = "auto",
4955
) -> None:
5056
"""
5157
title: >-
@@ -57,6 +63,8 @@ def build(
5763
type: str
5864
link:
5965
type: bool
66+
link_mode:
67+
type: Literal[auto, pie, no-pie]
6068
"""
6169
self.translator = ArxLLVMLiteIRVisitor()
6270
result = self.translator.translate(node)
@@ -78,11 +86,18 @@ def build(
7886
file_handler.write(result_object)
7987
return
8088

89+
if link_mode not in self.LINK_MODES:
90+
raise ValueError(
91+
"Invalid link mode. Expected one of: auto, pie, no-pie."
92+
)
93+
8194
# fix xh typing
8295
clang: Callable[..., Any] = xh.clang
83-
clang(
84-
file_path_o,
85-
"-o",
86-
self.output_file,
87-
)
96+
clang_args = [file_path_o]
97+
if link_mode == "pie":
98+
clang_args.append("-pie")
99+
elif link_mode == "no-pie":
100+
clang_args.append("-no-pie")
101+
clang_args.extend(["-o", self.output_file])
102+
clang(*clang_args)
88103
os.chmod(self.output_file, 0o755)

src/arx/main.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from dataclasses import dataclass, field
99
from pathlib import Path
10-
from typing import Any
10+
from typing import Any, Literal, cast
1111

1212
import astx
1313

@@ -40,11 +40,14 @@ class ArxMain:
4040
type: str
4141
is_lib:
4242
type: bool
43+
link_mode:
44+
type: Literal[auto, pie, no-pie]
4345
"""
4446

4547
input_files: list[str] = field(default_factory=list)
4648
output_file: str = ""
4749
is_lib: bool = False
50+
link_mode: Literal["auto", "pie", "no-pie"] = "auto"
4851

4952
def _format_ast_fallback(self, node: object) -> str:
5053
lines: list[str] = []
@@ -181,6 +184,15 @@ def run(self, **kwargs: Any) -> None:
181184
output_file = kwargs.get("output_file")
182185
self.output_file = output_file.strip() if output_file else ""
183186
self.is_lib = kwargs.get("is_lib", False)
187+
link_mode = str(kwargs.get("link_mode", "auto")).strip().lower()
188+
if link_mode not in {"auto", "pie", "no-pie"}:
189+
raise ValueError(
190+
"Invalid link mode. Expected one of: auto, pie, no-pie."
191+
)
192+
self.link_mode = cast(
193+
Literal["auto", "pie", "no-pie"],
194+
link_mode,
195+
)
184196

185197
if kwargs.get("show_ast"):
186198
return self.show_ast()
@@ -278,5 +290,6 @@ def compile(self, show_llvm_ir: bool = False) -> bool:
278290
tree_ast,
279291
output_file=self.output_file,
280292
link=emits_executable,
293+
link_mode=self.link_mode,
281294
)
282295
return emits_executable

tests/test_app_paths.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,16 @@ def test_cli_get_args_parsing() -> None:
158158
"out.o",
159159
"--lib",
160160
"--show-tokens",
161+
"--link-mode",
162+
"no-pie",
161163
]
162164
)
163165

164166
assert args.input_files == ["examples/sum.x"]
165167
assert args.output_file == "out.o"
166168
assert args.is_lib is True
167169
assert args.show_tokens is True
170+
assert args.link_mode == "no-pie"
168171
assert args.run is False
169172

170173

@@ -194,6 +197,7 @@ def parse_args(self) -> Namespace:
194197
version=True,
195198
output_file="",
196199
is_lib=False,
200+
link_mode="auto",
197201
show_ast=False,
198202
show_tokens=False,
199203
show_llvm_ir=False,
@@ -231,6 +235,7 @@ def parse_args(self) -> Namespace:
231235
version=False,
232236
output_file="out.o",
233237
is_lib=True,
238+
link_mode="auto",
234239
show_ast=False,
235240
show_tokens=True,
236241
show_llvm_ir=False,
@@ -270,6 +275,7 @@ def parse_args(self) -> Namespace:
270275
version=False,
271276
output_file="",
272277
is_lib=True,
278+
link_mode="auto",
273279
show_ast=False,
274280
show_tokens=False,
275281
show_llvm_ir=False,
@@ -508,23 +514,27 @@ class DummyIRBuild:
508514
built_tree: object | None = None
509515
built_out: str | None = None
510516
built_link: bool | None = None
517+
built_link_mode: str | None = None
511518

512519
def build(
513520
self,
514521
tree: object,
515522
output_file: str = "",
516523
link: bool = True,
524+
link_mode: str = "auto",
517525
) -> None:
518526
DummyIRBuild.built_tree = tree
519527
DummyIRBuild.built_out = output_file
520528
DummyIRBuild.built_link = link
529+
DummyIRBuild.built_link_mode = link_mode
521530

522531
monkeypatch.setattr(main_module, "LLVMLiteIR", DummyIRBuild)
523532
monkeypatch.setattr(app, "_get_astx", fake_get_astx_tree)
524533
app.compile()
525534
assert DummyIRBuild.built_tree is not None
526535
assert DummyIRBuild.built_out == "out.o"
527536
assert DummyIRBuild.built_link is False
537+
assert DummyIRBuild.built_link_mode == "auto"
528538

529539

530540
def test_arxmain_compile_default_output_name(
@@ -544,16 +554,19 @@ def fake_get_astx_tree() -> object:
544554
class DummyIRBuild:
545555
built_out: str | None = None
546556
built_link: bool | None = None
557+
built_link_mode: str | None = None
547558

548559
def build(
549560
self,
550561
tree: object,
551562
output_file: str = "",
552563
link: bool = True,
564+
link_mode: str = "auto",
553565
) -> None:
554566
del tree
555567
DummyIRBuild.built_out = output_file
556568
DummyIRBuild.built_link = link
569+
DummyIRBuild.built_link_mode = link_mode
557570

558571
monkeypatch.setattr(app, "_get_astx", fake_get_astx_tree)
559572
monkeypatch.setattr(main_module, "LLVMLiteIR", DummyIRBuild)
@@ -562,6 +575,7 @@ def build(
562575

563576
assert DummyIRBuild.built_out == "print-star"
564577
assert DummyIRBuild.built_link is False
578+
assert DummyIRBuild.built_link_mode == "auto"
565579
assert app.output_file == "print-star"
566580

567581

@@ -581,15 +595,18 @@ def fake_get_astx_tree() -> object:
581595

582596
class DummyIRBuild:
583597
built_link: bool | None = None
598+
built_link_mode: str | None = None
584599

585600
def build(
586601
self,
587602
tree: object,
588603
output_file: str = "",
589604
link: bool = True,
605+
link_mode: str = "auto",
590606
) -> None:
591607
del tree, output_file
592608
DummyIRBuild.built_link = link
609+
DummyIRBuild.built_link_mode = link_mode
593610

594611
def fake_has_main_entry(node: object) -> bool:
595612
del node
@@ -602,6 +619,7 @@ def fake_has_main_entry(node: object) -> bool:
602619
app.compile()
603620

604621
assert DummyIRBuild.built_link is True
622+
assert DummyIRBuild.built_link_mode == "auto"
605623

606624

607625
def test_arxmain_run_requires_executable_for_run_flag(

0 commit comments

Comments
 (0)