Linking a pxx object into another program

--emit-obj (or an output path ending in .o) writes a relocatable ELF object instead of a linked executable, so pxx-compiled code can be linked into a build driven by someone else's toolchain.

pascal26 --emit-obj mylib.pas mylib.o
gcc -no-pie main.c mylib.o -o prog

Targets: x86-64, i386, riscv32 and xtensa. arm32 and aarch64 have no object writer. This page describes the x86-64 and i386 objects, which behave identically — same export surface, same -no-pie contract, and gcc -m32 -no-pie for i386. The two ESP targets are a different kind of object: they export app_main for an IDF image and are covered under Targets.

What the object exports

The routines declared with the C convention, and nothing else. In Pascal that is cdecl; in C source it is every function of the translation unit.

function pxx_sum(n: Integer): Integer; cdecl;
var i, s: Integer;
begin
  s := 0;
  for i := 1 to n do s := s + i;
  pxx_sum := s;
end;

Everything else the compilation defines — your other routines and the whole RTL it pulled in — is written as a local symbol: a debugger can see it, the linker cannot, so an object can never collide with its host program over a name like WriteLn. Calls out to external routines become ordinary undefined symbols under their link names, which the linker resolves as it would for any other object.

If nothing carries the C convention the object would define no linkable symbol, and pxx refuses to write it rather than hand you a file whose failure surfaces later at your link step.

-no-pie is required. The backend reaches globals through absolute address operands, so the object carries R_X86_64_64/R_X86_64_32S relocations (or R_386_32 on i386). A linker can satisfy those only in a non-PIE link, and today's toolchains default to PIE, so a plain gcc main.c mylib.o fails with relocation R_X86_64_32S against .bss can not be used when making a PIE object. Add -no-pie.

No initialisation runs. Linking an object into a foreign program does not run the Pascal main body, unit initialisation, or anything else the program would have done on startup — the host program's main is the entry point, and it never calls yours. An exported routine must therefore not depend on a global having been assigned at startup; give it what it needs through its parameters, or export an explicit cdecl init routine for the host to call first.

What works today

Measured, not inferred: a gcc-built main linking a pxx object and calling into it — Pascal and C sources, integer and floating-point signatures, string work through the pxx heap, and pxx calling back out to a shared library (sqrt from libm) resolved by the system linker.

Not yet: --shared for compiled sources (it serves the .asm frontend only), a Pascal library unit, and object output for arm32 and aarch64.

One known defect, worth knowing before you link an i386 object: a pxx routine clobbers EBX on i386 and does not restore it, so a C caller keeping a live value there can crash after your function has returned the right answer. x86-64 is unaffected.