Sign in Join
Wiki copytemporary

macOS Static Libraries: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Example FPC static library
22
33> **Tip:** If you use "cdecl", FPC ensures that your function completely adheres to all ABI requirements (naming, parameter passing, etc). If you don't, then you are on your own.
44
55test.pas:
66
77``` pascal
88{$mode objfpc} {$H+}
99
1010// Note this is a unit and not a library
1111unit test;
1212
1313interface
1414
1515uses
1616 // needed for UpperCase
1717 SysUtils;
1818
1919implementation
2020
2121// library subroutine
2222function cvtString(strIn : string) : PChar; Cdecl;
2323begin
2424 cvtString := PChar(UpperCase(strIn));
2525end;
2626
2727end.
2828```
2929
3030Compile:
3131
32-` fpc test.pas`
32+```text
33+ fpc test.pas
34+```
3335
3436which produces an object file named `test.o` which we will now convert into a static library archive named `libtest.a` using the `libtool` command line utility:
3537
36-` libtool -static -o libtest.a test.o`
38+```text
39+ libtool -static -o libtest.a test.o
40+```
3741
3842## Example FPC application to use FPC static library
3943
4044statlibdemo.pas:
4145
4246``` pascal
4347{$mode objfpc} {$H+}
4448
4549// Link in our static library
4650// - note the prefix (lib) and extension (.a) are not necessary
4751{$LinkLib test}
4852
4953program statlibdemo;
5054
5155// Declare our function
5256function cvtString(strIn : string) : PChar; Cdecl; External Name 'cvtString';
5357
5458begin
5559 WriteLn(cvtString('hello world'));
5660end.
5761```
5862
5963Compile:
6064
61-` fpc statlibdemo.pas`
65+```text
66+ fpc statlibdemo.pas
67+```
6268
6369which unexpectedly produces:
6470
6571```text
6672Free Pascal Compiler version 3.3.1 [2020/04/20] for x86_64
6773Copyright (c) 1993-2020 by Florian Klaempfl and others
6874Target OS: Darwin for x86_64
6975Compiling staticlibdemo.pas
7076Assembling (pipe) staticlibdemo.s
7177Linking staticlibdemo
7278Undefined symbols for architecture x86_64:
7379 "_cvtString", referenced from:
7480 _PASCALMAIN in staticlibdemo.o
7581ld: symbol(s) not found for architecture x86_64
7682An error occurred while linking
7783staticlibdemo.pas(11,34) Error: Error while linking
7884staticlibdemo.pas(11,34) Fatal: There were 1 errors compiling module, stopping
7985Fatal: Compilation aborted
8086Error: /usr/local/bin/ppcx64 returned an error exitcode
8187```
8288
8389What happened? Why wasn't our library function `cvtString` found? Let's look for our function in the `test.o` object file by running the `objdump` command line utility:
8490
8591``` asm
8692$ objdump -d test.o
8793test.o: file format Mach-O 64-bit x86-64
8894
8995Disassembly of section __TEXT,__text:
9096_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR:
9197 0: 55 pushq %rbp
9298 1: 48 89 e5 movq %rsp, %rbp
9399 4: 48 8d 64 24 80 leaq -128(%rsp), %rsp
94100 9: 48 89 7d f8 movq %rdi, -8(%rbp)
95101 d: 48 8b 7d f8 movq -8(%rbp), %rdi
96102 11: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x16>
97103 16: 48 c7 45 88 00 00 00 00 movq $0, -120(%rbp)
98104 1e: 48 8d 55 d8 leaq -40(%rbp), %rdx
99105 22: 48 8d 75 98 leaq -104(%rbp), %rsi
100106 26: bf 01 00 00 00 movl $1, %edi
101107 2b: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x30>
102108 30: 48 89 c7 movq %rax, %rdi
103109 33: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x38>
104110 38: 48 63 d0 movslq %eax, %rdx
105111 3b: 48 89 55 90 movq %rdx, -112(%rbp)
106112 3f: 83 f8 00 cmpl $0, %eax
107113 42: 75 22 jne 34 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x66>
108114 44: 48 8b 75 f8 movq -8(%rbp), %rsi
109115 48: 48 8d 7d 88 leaq -120(%rbp), %rdi
110116 4c: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x51>
111117 51: 48 8b 45 88 movq -120(%rbp), %rax
112118 55: 48 83 f8 00 cmpq $0, %rax
113119 59: 75 07 jne 7 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x62>
114120 5b: 48 8b 05 00 00 00 00 movq (%rip), %rax
115121 62: 48 89 45 f0 movq %rax, -16(%rbp)
116122 66: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x6b>
117123 6b: 48 8d 7d f8 leaq -8(%rbp), %rdi
118124 6f: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x74>
119125 74: 48 8d 7d 88 leaq -120(%rbp), %rdi
120126 78: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x7d>
121127 7d: 48 8b 45 90 movq -112(%rbp), %rax
122128 81: 48 83 f8 00 cmpq $0, %rax
123129 85: 74 0f je 15 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x96>
124130 87: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x8c>
125131 8c: 48 c7 45 90 00 00 00 00 movq $0, -112(%rbp)
126132 94: eb d0 jmp -48 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x66>
127133 96: 48 8b 45 f0 movq -16(%rbp), %rax
128134 9a: 48 89 ec movq %rbp, %rsp
129135 9d: 5d popq %rbp
130136 9e: c3 retq
131137```
132138
133139Aha! Our function name has been well and truly mangled. It is obvious we need to use the mangled name `TEST_$$_CVTSTRING$ANSISTRING$$PCHAR` (omit the underscore as the linker will automatically add it) rather than the unmangled `cvtString`. Our example application now looks like:
134140
135141``` pascal
136142{$mode objfpc} {$H+}
137143
138144// Link in our static library
139145// - note the prefix (lib) and extension (.a) are not necessary
140146{$LinkLib test}
141147
142148program statlibdemo;
143149
144150// Declare our function
145151function cvtString(strIn : string) : PChar; Cdecl; External Name 'TEST_$$_CVTSTRING$ANSISTRING$$PCHAR';
146152
147153begin
148154 WriteLn(cvtString('hello world'));
149155end.
150156```
151157
152158Compile:
153159
154-` fpc statlibdemo.pas`
160+```text
161+ fpc statlibdemo.pas
162+```
155163
156164which again dashes our hopes and produces:
157165
158166```text
159167Free Pascal Compiler version 3.3.1 [2020/04/20] for x86_64
160168Copyright (c) 1993-2020 by Florian Klaempfl and others
161169Target OS: Darwin for x86_64
162170Compiling staticlibdemo.pas
163171Assembling (pipe) staticlibdemo.s
164172Linking staticlibdemo
165173Undefined symbols for architecture x86_64:
166174 "_SYSUTILS_$$_UPPERCASE$ANSISTRING$$ANSISTRING", referenced from:
167175 _TEST_$$_CVTSTRING$ANSISTRING$$PCHAR in libtest.a(test.o)
168176ld: symbol(s) not found for architecture x86_64
169177An error occurred while linking
170178staticlibdemo.pas(11,34) Error: Error while linking
171179staticlibdemo.pas(11,34) Fatal: There were 1 errors compiling module, stopping
172180Fatal: Compilation aborted
173181Error: /usr/local/bin/ppcx64 returned an error exitcode
174182```
175183
176184Unfortunately we are missing the object file for the `UpperCase` function which was pulled in from the FPC SysUtils unit. What to do? Simply add the SysUtils unit to the the Uses clause of the application. Our example application now looks like:
177185
178186``` pascal
179187{$mode objfpc} {$H+}
180188
181189// Link in our static library
182190// - note the prefix (lib) and extension (.a) are not necessary
183191{$LinkLib test}
184192
185193program statlibdemo;
186194
187195// Needed for linking in the UpperCase function
188196Uses
189197 SysUtils;
190198
191199// Declare our function
192200function cvtString(strIn : string) : PChar; Cdecl; External Name 'TEST_$$_CVTSTRING$ANSISTRING$$PCHAR';
193201
194202begin
195203 WriteLn(cvtString('hello world'));
196204end.
197205```
198206
199207Third time lucky, compile:
200208
201-` fpc statlibdemo.pas`
209+```text
210+ fpc statlibdemo.pas
211+```
202212
203213Run:
204214
205215``` bash
206216$ ./statlibdemo
207217HELLO WORLD
208218```
209219
210220## Example C static library
211221
212222myfunc1.c
213223
214224``` c
215225#include <stdio.h>
216226
217227void myfunc1()
218228 {
219229 printf("This is my func1!\n");
220230 }
221231```
222232
223233myfunc2.c
224234
225235``` c
226236#include <ctype.h>
227237
228238int myfunc2(int c)
229239 {
230240 c = toupper(c);
231241 return(c);
232242 }
233243```
234244
235245Compile:
236246
237247> **Note:** If you do not specify the minimum macOS version, the C compiler will compile for your current operating system version, but your Free Pascal Compiler will compile for 10.5 (FPC 3.0.4) or 10.8 (FPC 3.2.0 and later) by default.
238248
239-`cc -c -mmacosx-version-min=10.8 myfunc1.c myfunc2.c`
249+```text
250+cc -c -mmacosx-version-min=10.8 myfunc1.c myfunc2.c
251+```
240252
241253which produces object files named myfunc1.o and myfunc2.o which we will now convert into a static library archive named `libmyfuncs.a` using the `libtool` command line utility:
242254
243-`libtool -static -o libmyfuncs.a myfunc1.o myfunc2.o`
255+```text
256+libtool -static -o libmyfuncs.a myfunc1.o myfunc2.o
257+```
244258
245259## Example FPC application to use C static library
246260
247261statlibdemo.pas:
248262
249263``` pascal
250264{$mode objfpc}
251265{$linklib myfuncs}
252266
253267procedure myfunc1; cdecl; external;
254268function myfunc2(c : integer) : integer; cdecl; external;
255269
256270begin
257271 myfunc1;
258272
259273 write('This is my func2 - Before: "b" ');
260274 writeLn('After : ' + '"' + chr(myfunc2(ord('b'))) + '"');
261275end.
262276```
263277
264278Compile:
265279
266-`fpc statlibdemo.pas`
280+```text
281+fpc statlibdemo.pas
282+```
267283
268284Run:
269285
270286``` bash
271287$ ./statlibdemo
272288This is my func1!
273289This is my func2 - Before: "b" After : "B"
274290```
275291
276292If you need to check whether your external routines have been linked into the final application, you can use the `objdump` command line utility:
277293
278294``` asm
279295$ objdump -d staticlibdemo | grep -A13 ^_myfunc
280296_myfunc1:
281297100000830: 55 pushq %rbp
282298100000831: 48 89 e5 movq %rsp, %rbp
283299100000834: 48 83 ec 10 subq $16, %rsp
284300100000838: 48 8d 3d 7f 44 02 00 leaq 148607(%rip), %rdi
28530110000083f: b0 00 movb $0, %al
286302100000841: e8 e6 3d 02 00 callq 146918 <dyld_stub_binder+0x10002462c>
287303100000846: 89 45 fc movl %eax, -4(%rbp)
288304100000849: 48 83 c4 10 addq $16, %rsp
28930510000084d: 5d popq %rbp
29030610000084e: c3 retq
29130710000084f: 90 nop
292308
293309_myfunc2:
294310100000850: 55 pushq %rbp
295311100000851: 48 89 e5 movq %rsp, %rbp
296312100000854: 48 83 ec 10 subq $16, %rsp
297313100000858: 89 7d fc movl %edi, -4(%rbp)
29831410000085b: 8b 7d fc movl -4(%rbp), %edi
29931510000085e: e8 29 3e 02 00 callq 146985 <dyld_stub_binder+0x10002468c>
300316100000863: 89 45 fc movl %eax, -4(%rbp)
301317100000866: 8b 45 fc movl -4(%rbp), %eax
302318100000869: 48 83 c4 10 addq $16, %rsp
30331910000086d: 5d popq %rbp
30432010000086e: c3 retq
30532110000086f: 90 nop
306322```
307323
308324If you don't care for the assembly language, you can also use the `nm` command line utility:
309325
310326``` bash
311327nm staticlibdemo | grep myfunc
3123280000000100000830 T _myfunc1
3133290000000100000850 T _myfunc2
314330```
315331
316332## See also
317333
318334- [[macOS Dynamic Libraries]] for the same FPC library being used as a dynamic library.
319335- [[macOS Frameworks]] - structured directories for dynamic libraries and associated resources.
320336- [[macOS Programming Tips|macOS Libraries]].
321337
322338---
323339*From the Free Pascal wiki, page "macOS Static Libraries", as it stood on 2022-02-04 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history