macOS Static Libraries: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: macOS Static Libraries
| Old | New | Line | |
|---|---|---|---|
| 1 | + | ## Example FPC static library | |
| 2 | + | ||
| 3 | + | > **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. | |
| 4 | + | ||
| 5 | + | test.pas: | |
| 6 | + | ||
| 7 | + | ``` pascal | |
| 8 | + | {$mode objfpc} {$H+} | |
| 9 | + | ||
| 10 | + | // Note this is a unit and not a library | |
| 11 | + | unit test; | |
| 12 | + | ||
| 13 | + | interface | |
| 14 | + | ||
| 15 | + | uses | |
| 16 | + | // needed for UpperCase | |
| 17 | + | SysUtils; | |
| 18 | + | ||
| 19 | + | implementation | |
| 20 | + | ||
| 21 | + | // library subroutine | |
| 22 | + | function cvtString(strIn : string) : PChar; Cdecl; | |
| 23 | + | begin | |
| 24 | + | cvtString := PChar(UpperCase(strIn)); | |
| 25 | + | end; | |
| 26 | + | ||
| 27 | + | end. | |
| 28 | + | ``` | |
| 29 | + | ||
| 30 | + | Compile: | |
| 31 | + | ||
| 32 | + | ` fpc test.pas` | |
| 33 | + | ||
| 34 | + | which 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: | |
| 35 | + | ||
| 36 | + | ` libtool -static -o libtest.a test.o` | |
| 37 | + | ||
| 38 | + | ## Example FPC application to use FPC static library | |
| 39 | + | ||
| 40 | + | statlibdemo.pas: | |
| 41 | + | ||
| 42 | + | ``` pascal | |
| 43 | + | {$mode objfpc} {$H+} | |
| 44 | + | ||
| 45 | + | // Link in our static library | |
| 46 | + | // - note the prefix (lib) and extension (.a) are not necessary | |
| 47 | + | {$LinkLib test} | |
| 48 | + | ||
| 49 | + | program statlibdemo; | |
| 50 | + | ||
| 51 | + | // Declare our function | |
| 52 | + | function cvtString(strIn : string) : PChar; Cdecl; External Name 'cvtString'; | |
| 53 | + | ||
| 54 | + | begin | |
| 55 | + | WriteLn(cvtString('hello world')); | |
| 56 | + | end. | |
| 57 | + | ``` | |
| 58 | + | ||
| 59 | + | Compile: | |
| 60 | + | ||
| 61 | + | ` fpc statlibdemo.pas` | |
| 62 | + | ||
| 63 | + | which unexpectedly produces: | |
| 64 | + | ||
| 65 | + | ```text | |
| 66 | + | Free Pascal Compiler version 3.3.1 [2020/04/20] for x86_64 | |
| 67 | + | Copyright (c) 1993-2020 by Florian Klaempfl and others | |
| 68 | + | Target OS: Darwin for x86_64 | |
| 69 | + | Compiling staticlibdemo.pas | |
| 70 | + | Assembling (pipe) staticlibdemo.s | |
| 71 | + | Linking staticlibdemo | |
| 72 | + | Undefined symbols for architecture x86_64: | |
| 73 | + | "_cvtString", referenced from: | |
| 74 | + | _PASCALMAIN in staticlibdemo.o | |
| 75 | + | ld: symbol(s) not found for architecture x86_64 | |
| 76 | + | An error occurred while linking | |
| 77 | + | staticlibdemo.pas(11,34) Error: Error while linking | |
| 78 | + | staticlibdemo.pas(11,34) Fatal: There were 1 errors compiling module, stopping | |
| 79 | + | Fatal: Compilation aborted | |
| 80 | + | Error: /usr/local/bin/ppcx64 returned an error exitcode | |
| 81 | + | ``` | |
| 82 | + | ||
| 83 | + | What 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: | |
| 84 | + | ||
| 85 | + | ``` asm | |
| 86 | + | $ objdump -d test.o | |
| 87 | + | test.o: file format Mach-O 64-bit x86-64 | |
| 88 | + | ||
| 89 | + | Disassembly of section __TEXT,__text: | |
| 90 | + | _TEST_$$_CVTSTRING$ANSISTRING$$PCHAR: | |
| 91 | + | 0: 55 pushq %rbp | |
| 92 | + | 1: 48 89 e5 movq %rsp, %rbp | |
| 93 | + | 4: 48 8d 64 24 80 leaq -128(%rsp), %rsp | |
| 94 | + | 9: 48 89 7d f8 movq %rdi, -8(%rbp) | |
| 95 | + | d: 48 8b 7d f8 movq -8(%rbp), %rdi | |
| 96 | + | 11: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x16> | |
| 97 | + | 16: 48 c7 45 88 00 00 00 00 movq $0, -120(%rbp) | |
| 98 | + | 1e: 48 8d 55 d8 leaq -40(%rbp), %rdx | |
| 99 | + | 22: 48 8d 75 98 leaq -104(%rbp), %rsi | |
| 100 | + | 26: bf 01 00 00 00 movl $1, %edi | |
| 101 | + | 2b: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x30> | |
| 102 | + | 30: 48 89 c7 movq %rax, %rdi | |
| 103 | + | 33: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x38> | |
| 104 | + | 38: 48 63 d0 movslq %eax, %rdx | |
| 105 | + | 3b: 48 89 55 90 movq %rdx, -112(%rbp) | |
| 106 | + | 3f: 83 f8 00 cmpl $0, %eax | |
| 107 | + | 42: 75 22 jne 34 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x66> | |
| 108 | + | 44: 48 8b 75 f8 movq -8(%rbp), %rsi | |
| 109 | + | 48: 48 8d 7d 88 leaq -120(%rbp), %rdi | |
| 110 | + | 4c: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x51> | |
| 111 | + | 51: 48 8b 45 88 movq -120(%rbp), %rax | |
| 112 | + | 55: 48 83 f8 00 cmpq $0, %rax | |
| 113 | + | 59: 75 07 jne 7 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x62> | |
| 114 | + | 5b: 48 8b 05 00 00 00 00 movq (%rip), %rax | |
| 115 | + | 62: 48 89 45 f0 movq %rax, -16(%rbp) | |
| 116 | + | 66: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x6b> | |
| 117 | + | 6b: 48 8d 7d f8 leaq -8(%rbp), %rdi | |
| 118 | + | 6f: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x74> | |
| 119 | + | 74: 48 8d 7d 88 leaq -120(%rbp), %rdi | |
| 120 | + | 78: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x7d> | |
| 121 | + | 7d: 48 8b 45 90 movq -112(%rbp), %rax | |
| 122 | + | 81: 48 83 f8 00 cmpq $0, %rax | |
| 123 | + | 85: 74 0f je 15 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x96> | |
| 124 | + | 87: e8 00 00 00 00 callq 0 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x8c> | |
| 125 | + | 8c: 48 c7 45 90 00 00 00 00 movq $0, -112(%rbp) | |
| 126 | + | 94: eb d0 jmp -48 <_TEST_$$_CVTSTRING$ANSISTRING$$PCHAR+0x66> | |
| 127 | + | 96: 48 8b 45 f0 movq -16(%rbp), %rax | |
| 128 | + | 9a: 48 89 ec movq %rbp, %rsp | |
| 129 | + | 9d: 5d popq %rbp | |
| 130 | + | 9e: c3 retq | |
| 131 | + | ``` | |
| 132 | + | ||
| 133 | + | Aha! 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: | |
| 134 | + | ||
| 135 | + | ``` pascal | |
| 136 | + | {$mode objfpc} {$H+} | |
| 137 | + | ||
| 138 | + | // Link in our static library | |
| 139 | + | // - note the prefix (lib) and extension (.a) are not necessary | |
| 140 | + | {$LinkLib test} | |
| 141 | + | ||
| 142 | + | program statlibdemo; | |
| 143 | + | ||
| 144 | + | // Declare our function | |
| 145 | + | function cvtString(strIn : string) : PChar; Cdecl; External Name 'TEST_$$_CVTSTRING$ANSISTRING$$PCHAR'; | |
| 146 | + | ||
| 147 | + | begin | |
| 148 | + | WriteLn(cvtString('hello world')); | |
| 149 | + | end. | |
| 150 | + | ``` | |
| 151 | + | ||
| 152 | + | Compile: | |
| 153 | + | ||
| 154 | + | ` fpc statlibdemo.pas` | |
| 155 | + | ||
| 156 | + | which again dashes our hopes and produces: | |
| 157 | + | ||
| 158 | + | ```text | |
| 159 | + | Free Pascal Compiler version 3.3.1 [2020/04/20] for x86_64 | |
| 160 | + | Copyright (c) 1993-2020 by Florian Klaempfl and others | |
| 161 | + | Target OS: Darwin for x86_64 | |
| 162 | + | Compiling staticlibdemo.pas | |
| 163 | + | Assembling (pipe) staticlibdemo.s | |
| 164 | + | Linking staticlibdemo | |
| 165 | + | Undefined symbols for architecture x86_64: | |
| 166 | + | "_SYSUTILS_$$_UPPERCASE$ANSISTRING$$ANSISTRING", referenced from: | |
| 167 | + | _TEST_$$_CVTSTRING$ANSISTRING$$PCHAR in libtest.a(test.o) | |
| 168 | + | ld: symbol(s) not found for architecture x86_64 | |
| 169 | + | An error occurred while linking | |
| 170 | + | staticlibdemo.pas(11,34) Error: Error while linking | |
| 171 | + | staticlibdemo.pas(11,34) Fatal: There were 1 errors compiling module, stopping | |
| 172 | + | Fatal: Compilation aborted | |
| 173 | + | Error: /usr/local/bin/ppcx64 returned an error exitcode | |
| 174 | + | ``` | |
| 175 | + | ||
| 176 | + | Unfortunately 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: | |
| 177 | + | ||
| 178 | + | ``` pascal | |
| 179 | + | {$mode objfpc} {$H+} | |
| 180 | + | ||
| 181 | + | // Link in our static library | |
| 182 | + | // - note the prefix (lib) and extension (.a) are not necessary | |
| 183 | + | {$LinkLib test} | |
| 184 | + | ||
| 185 | + | program statlibdemo; | |
| 186 | + | ||
| 187 | + | // Needed for linking in the UpperCase function | |
| 188 | + | Uses | |
| 189 | + | SysUtils; | |
| 190 | + | ||
| 191 | + | // Declare our function | |
| 192 | + | function cvtString(strIn : string) : PChar; Cdecl; External Name 'TEST_$$_CVTSTRING$ANSISTRING$$PCHAR'; | |
| 193 | + | ||
| 194 | + | begin | |
| 195 | + | WriteLn(cvtString('hello world')); | |
| 196 | + | end. | |
| 197 | + | ``` | |
| 198 | + | ||
| 199 | + | Third time lucky, compile: | |
| 200 | + | ||
| 201 | + | ` fpc statlibdemo.pas` | |
| 202 | + | ||
| 203 | + | Run: | |
| 204 | + | ||
| 205 | + | ``` bash | |
| 206 | + | $ ./statlibdemo | |
| 207 | + | HELLO WORLD | |
| 208 | + | ``` | |
| 209 | + | ||
| 210 | + | ## Example C static library | |
| 211 | + | ||
| 212 | + | myfunc1.c | |
| 213 | + | ||
| 214 | + | ``` c | |
| 215 | + | #include <stdio.h> | |
| 216 | + | ||
| 217 | + | void myfunc1() | |
| 218 | + | { | |
| 219 | + | printf("This is my func1!\n"); | |
| 220 | + | } | |
| 221 | + | ``` | |
| 222 | + | ||
| 223 | + | myfunc2.c | |
| 224 | + | ||
| 225 | + | ``` c | |
| 226 | + | #include <ctype.h> | |
| 227 | + | ||
| 228 | + | int myfunc2(int c) | |
| 229 | + | { | |
| 230 | + | c = toupper(c); | |
| 231 | + | return(c); | |
| 232 | + | } | |
| 233 | + | ``` | |
| 234 | + | ||
| 235 | + | Compile: | |
| 236 | + | ||
| 237 | + | > **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. | |
| 238 | + | ||
| 239 | + | `cc -c -mmacosx-version-min=10.8 myfunc1.c myfunc2.c` | |
| 240 | + | ||
| 241 | + | which 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: | |
| 242 | + | ||
| 243 | + | `libtool -static -o libmyfuncs.a myfunc1.o myfunc2.o` | |
| 244 | + | ||
| 245 | + | ## Example FPC application to use C static library | |
| 246 | + | ||
| 247 | + | statlibdemo.pas: | |
| 248 | + | ||
| 249 | + | ``` pascal | |
| 250 | + | {$mode objfpc} | |
| 251 | + | {$linklib myfuncs} | |
| 252 | + | ||
| 253 | + | procedure myfunc1; cdecl; external; | |
| 254 | + | function myfunc2(c : integer) : integer; cdecl; external; | |
| 255 | + | ||
| 256 | + | begin | |
| 257 | + | myfunc1; | |
| 258 | + | ||
| 259 | + | write('This is my func2 - Before: "b" '); | |
| 260 | + | writeLn('After : ' + '"' + chr(myfunc2(ord('b'))) + '"'); | |
| 261 | + | end. | |
| 262 | + | ``` | |
| 263 | + | ||
| 264 | + | Compile: | |
| 265 | + | ||
| 266 | + | `fpc statlibdemo.pas` | |
| 267 | + | ||
| 268 | + | Run: | |
| 269 | + | ||
| 270 | + | ``` bash | |
| 271 | + | $ ./statlibdemo | |
| 272 | + | This is my func1! | |
| 273 | + | This is my func2 - Before: "b" After : "B" | |
| 274 | + | ``` | |
| 275 | + | ||
| 276 | + | If you need to check whether your external routines have been linked into the final application, you can use the `objdump` command line utility: | |
| 277 | + | ||
| 278 | + | ``` asm | |
| 279 | + | $ objdump -d staticlibdemo | grep -A13 ^_myfunc | |
| 280 | + | _myfunc1: | |
| 281 | + | 100000830: 55 pushq %rbp | |
| 282 | + | 100000831: 48 89 e5 movq %rsp, %rbp | |
| 283 | + | 100000834: 48 83 ec 10 subq $16, %rsp | |
| 284 | + | 100000838: 48 8d 3d 7f 44 02 00 leaq 148607(%rip), %rdi | |
| 285 | + | 10000083f: b0 00 movb $0, %al | |
| 286 | + | 100000841: e8 e6 3d 02 00 callq 146918 <dyld_stub_binder+0x10002462c> | |
| 287 | + | 100000846: 89 45 fc movl %eax, -4(%rbp) | |
| 288 | + | 100000849: 48 83 c4 10 addq $16, %rsp | |
| 289 | + | 10000084d: 5d popq %rbp | |
| 290 | + | 10000084e: c3 retq | |
| 291 | + | 10000084f: 90 nop | |
| 292 | + | ||
| 293 | + | _myfunc2: | |
| 294 | + | 100000850: 55 pushq %rbp | |
| 295 | + | 100000851: 48 89 e5 movq %rsp, %rbp | |
| 296 | + | 100000854: 48 83 ec 10 subq $16, %rsp | |
| 297 | + | 100000858: 89 7d fc movl %edi, -4(%rbp) | |
| 298 | + | 10000085b: 8b 7d fc movl -4(%rbp), %edi | |
| 299 | + | 10000085e: e8 29 3e 02 00 callq 146985 <dyld_stub_binder+0x10002468c> | |
| 300 | + | 100000863: 89 45 fc movl %eax, -4(%rbp) | |
| 301 | + | 100000866: 8b 45 fc movl -4(%rbp), %eax | |
| 302 | + | 100000869: 48 83 c4 10 addq $16, %rsp | |
| 303 | + | 10000086d: 5d popq %rbp | |
| 304 | + | 10000086e: c3 retq | |
| 305 | + | 10000086f: 90 nop | |
| 306 | + | ``` | |
| 307 | + | ||
| 308 | + | If you don't care for the assembly language, you can also use the `nm` command line utility: | |
| 309 | + | ||
| 310 | + | ``` bash | |
| 311 | + | nm staticlibdemo | grep myfunc | |
| 312 | + | 0000000100000830 T _myfunc1 | |
| 313 | + | 0000000100000850 T _myfunc2 | |
| 314 | + | ``` | |
| 315 | + | ||
| 316 | + | ## See also | |
| 317 | + | ||
| 318 | + | - [[macOS Dynamic Libraries]] for the same FPC library being used as a dynamic library. | |
| 319 | + | - [[macOS Frameworks]] - structured directories for dynamic libraries and associated resources. | |
| 320 | + | - [[macOS Programming Tips|macOS Libraries]]. | |
| 321 | + | ||
| 322 | + | --- | |
| 323 | + | *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).* |
