Sign in Join
Wiki copytemporary

shared library/ko: what changed

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

OldNewLine
11공유 라이브러리는 공유 및 다양한 프로그램에서 사용할 수있는 컴파일 된 코드의 일부이며, 기능과 다른 프로그램에서 호출 할 수있는 절차를 제공합니다. (즉 실행에 연결되고 그것의 일부가된다) 또는 실행 가능한 정적 라이브러리 다르다. 공유 라이브러리는 명시 적으로 언급 된 경우를 제외하고는 두 리눅스 .so를 및 Windows 인 DLL, 같은 또는 "DLL", "유닉스 공유 라이브러리를"포함하는 것을 의미한다.
22
33이 페이지 공유 라이브러리는 FPC와 함께 동작하는 방법을 문서화하려고합니다.즉 : 공유 라이브러리는 어떻게 작동하는지, 작동뿐만 아니라 델파이에서 공유라이브러리를 사용하는 방법을.
44
55우리는 물론 합리적인 (요구 사항에 의해 제한 여러 플랫폼에서 작동하는)로 호환되도록 노력할 것입니다 그렇기 때문에, 델파이가 지원하는 폼의 간단한 스케치부터 시작합니다.
66
77이 문서에서는 Kylix의 세부 사항을 표시하지 않습니다; 그들은 여전히​​ 관련된 경우 카일릭스의 세부 정보를 추가 주시기 바랍니다.
88
99## 델파이
1010
1111Delphi to my knowledge knows three (or four) ways of dynamic linking.
1212
13131. create a standalone shared library. (compiling a library unit without "runtime packages" selected).
1414 - This means the RTL will be linked into the shared library
1515 - This also means the memory manager will be its own island. Using automated types in functions that communicate with it is not possible.
1616 - Use of Classes is not possible: both program and shared library have their own copies of the VMT, which breaks e.g. *is* and *as* operators
17172. create a standalone shared library. (compiling a library unit without "runtime packages" selected), but while using unit **sharemem**)
1818 - Comparable with previous case but the memory manager is switched to COM compatible. This means other components/programs that also switch their memory manager to **sharemem** can call functions that use automated types, because it doesn't matter who returns the block to the COM memmanagement system.
1919 - Note that AFAIK the COM memory manager is quite slow. This road is probably not desired unless you really want to mess with COM, or your componentization is more important than speed.
20203. [[packages|Library packages]] These are shared libraries for which all dependancies on the Pascal level are known (which units they contain and depend on), and can be treated as parts of the main program that reside in a DLL.
2121 - The RTL is in a separate package (DLL), and both the main program and package use it. Therefore there is only one copy of any unit including system.
2222 - This also means there is only one memory manager, at least for the main program and the packages it uses. IOW shared libs that are not a package can still use an own RTL and memory manager.
2323 - A package can only depend on units in its own package or in other packages. (separate compilation requirement)
2424 - Because no units are duplicated, there is only one copy of each VMT, making classes use transparent.
2525 - Probably packages can also switch to sharemem, making it compatible with other systems using sharemem. Some way must be found to initialise sharemem as early as possible though (does Delphi do this? Possible test for this is to pass an ansistring created in a init section of a unit in a package to a different sharemem using shared lib that is not a package) **to be determined**
2626
2727Besides these, Delphi can also generate DLLs that are ActiveX components. **to be described/determined**
2828
2929Topics about library packages are mostly moved from this page to the separate [[packages]] article, to avoid confusing the discussion.
3030
3131## Linker namespaces
3232
3333One of the big problems with porting dynamic libraries from Windows (e.g. DLLs) to Linux and FreeBSD, is the fact that Windows and OS X have a linker namespace per module (as in, per shared lib or binary), with export tables carefully governing exported symbols, and Linux and FreeBSD only have one single linker namespace.
3434
3535Specially in multi-language projects this is a problem.
3636
3737I got some feedback on this from a FreeBSD hacker, who recommended to look into ELF visibility attribute, and/or API versioning:
3838
3939- <http://gcc.gnu.org/wiki/Visibility>
4040- <http://people.freebsd.org/~deischen/symver/freebsd_versioning.txt>
4141
4242Note that while the last URL is about FreeBSD, it mentions Linux doing the same.
4343
4444## Sharemem implementation details
4545
4646As said, sharemem switches the memory manager to a global one. Under Windows this is AFAIK the COM manager. On *nix a similar memmanager doesn't always exist (Gnome and KDE component architectures might have something similar), so this is not guaranteed.
4747
4848One could probably simply have all programs use cmem, which could be a "level 0" implementation for everything that runs in this process.
4949
5050I explicitely mention this because it might be necessary to impose an own initialisation order (independant of OS shared lib initialisation) to allow the main program to initialise units right after the RTL (system unit) initializations, but before other libs, e.g. a different memory manager.
5151
5252## VMT duplication
5353
5454The basic problem of VMT duplication is mostly support for the *is* parameter and similar methods of *TObject* and *TClass* like *inheritsfrom*. This is only solved for packages, IOW for the other two types named above, the *is* operator doesn't work across libraries/binaries.
5555
5656In earlier discussions about packages, there was some confusion about this topic. People assumed that packages would somehow tap into the RTL VMTs in the main binary. I'm however pretty sure this is not the case, at least not in Delphi, since if you use packages, the RTL always is a package, too. Also, every dependency of a unit in a package must be in the same package or in a package it has a dependency on. This means a unit (and thus, the VMTs declared in it) only exists once in the greater program (main program + its packages)
5757
5858## Initialization and finalization sections and RTL
5959
6060(The initialization order of units in packages is moved to the packages page).
6161
6262The problem with libraries is usually that libraries are initialized as a whole. This can lead to problems with plugin units modifying RTL behaviour (cwstrings, cmem, FV drivers etc)
6363
6464For stand-alone libraries with internal copies of RTL to function, the RTL needs to be initialized, and the initialization section of the library needs to be called as well.
6565
6666Roughly there are two options here:
6767
6868- Use (e.g. on ELF platforms) the ABI .init and .fini sections or similar constructs in other binary formats.
6969- Leave the above initializers mostly empty sections, and enforce an own order using a set of own initializers and finalizers. The ELF init and fini sections merely register the real initializers.
7070
7171Mixed forms are also possible, e.g. initialize standalone shared libraries via the initializer sections, but with packages use an own definition.
7272
7373## 공유 Exe 메모리 매니저
7474
7575Lars says: regarding a single memory manager for BPL style packages: what about using the executable and exporting its memory manager? I have had problems trying to use CMEM but have successfully shared the same memory manager using SetMemoryManager/GetMemoryManager tricks.
7676
7777See <http://www.freepascal.org/contrib/delete.php3?ID=543> for an example of sharing memory between exe and dll without using CMEM. Powtils CGI library also uses this trick for dynpwu.pas so that ansistrings can be used without any sharemem or cmem unit. The memory manager is exported from the executable or a single library and shared with all the other DLL/EXE's that are connecting to the single module.
7878
7979Marco's answer: First, library packages have more features than just shared memory managers. Unique VMTs (try to use the *is* operator in your example on a class created in the other lib/exe) and no need to handcraft proper initialization. One just groups units into libs, and the compiler does the rest. No special code required. So what must be done for (library) packages is different (and non competing) to the more general shared library case. And that is what this page is about.
8080
8181From this general "shared library" case, a subdivision can be separated that has a shared memory manager.
8282
8383Note that there can be a bunch of memory managers used for this, and multiple units that work like sharemem. Nearly any memory manager applies. However it is logical to reserve the "sharemem" unit name for the memory manager that is the most compatible for inter-process work on the given platform (COM on Windows, cmem on Unix). So, I'm talking about the default case here, which doesn't mean that other people can't roll their own.
8484
8585Also, in-compiler (RTL) support must be as universal as possible, and also work for libraries that are not *loadlibrary*-ed, but autoloaded at binary startup, and not require handcoding e.g. initialization. Also it mustn't stand in the way of handcoded solutions (like the one you reference), in other words it must be overridable, since otherwise in-compiler (RTL) support would stand in the way of what people build on top of FPC.
8686
8787It would be useful though, if somebody investigate if it is possible to call exported symbols from the binary from an autoloaded library (for the Tier 1 targets, Linux/FreeBSD - Windows - OS X). This because then a library could maybe indeed plug in the mother binary.
8888
8989In short the first objective is to simply have a default scheme that works like Delphi's sharemem to put in the "sharemem" unit.
9090
9191Marco, added later after some new insights
9292
9393Moreover a difference is also if the units in the dll are accessed directly, or only over a separately (additionally) defined interface.
9494
9595Lars says: Well another idea: instead of exporting the memory manager from the exe I was thinking about making a fpsharemem.dll that exports one single common FPC memory manager on each platform for all the programs and DLLs to use, instead of using CMEM. i.e. all units put fpsharemem.dll in their uses clause. In fact, that's actually what the demo does but instead of a separate fpsharemem.dll it is just inside that single DLL included with the demo and all the other code. i.e. why use CMEM if we can use freepascal memory manager? The freepascal mem manager is available on all platforms isn't it, as long as there is a FreePascal DLL that is created for each platform?
9696
9797I guess to answer my own questions it must have to do with COM/IPC (inter process communication) issues/compatibilities. Well anyway, still a very interesting discussion. Doing it by hand the way I did was just a demo to show the concept in action and to prove to people that ansistrings and automated types can be used in regular old DLLs.
9898
9999Marco: If you have to make one (thing? memory manager?) mandatory, make it cmem, because otherwise you might not be able to interface with some shared libs. If you can somehow make it user selectable it would be better. But keep in mind that it is already hard to pack two precompiled sets of units into a release (increasing to 200MB installed size or so), let alone more.
100100
101101Roughly this scheme is what sharemem does, communication over COM/IPC to make sure that a second instance of a library doesn't instantiate its state twice. It avoids the DLL with the memmanager itself by using COM as memory manager. (Something that wouldn't work on Unix, since there is no such global memory manager).
102102
103103I've thought about this in the past too, but once you start adding a few more things (like *is* operator support, and something end-user supportable for people that can't write a header to a DLL), you end up with effectively packages again. It is really not that much more, runtime, just a few tables to govern proper unit initialization.
104104
105105## 델파이에서 변환
106106
107107### dllproc
108108
109109델파이는 윈도우즈 DLL의 DLLProc hook의 DLL entry point 접근이 가능하며 아래와 같다:
110110
111111- DLL_PROCESS_ATTACH
112112- DLL_PROCESS_DETACH
113113- DLL_THREAD_ATTACH
114114- DLL_THREAD_DETACH
115115
116116프리파스칼에서는 이기능들을 사용할 수 없고, 윈도우즈상에서 아래 3가지만 조작할 수 있다:
117117
118118- Dll_Process_Detach_Hook
119119- Dll_Thread_Attach_Hook
120120- Dll_Thread_Detach_Hook
121121
122122## FPC에서 공유라이브러리 사용
123123
124124See the FPC documentation (to do: insert link). On Windows, the bitness of the program you are writing (e.g. 64 bit application) must match the bitness of the DLL you are using.
125125
126126Debugging FPC libraries called by an FPC program: if you are using Lazarus:
127127
128128- Open your library project
129129- Go to Run/Run Parameters, enter the full path of the application that uses your library in Launching Application
130130- Check the "Use launching application".
131131- Set the breakpoints as usual and Run the program.
132132
133133### Initialization order
134134
135135Warning: you should not initialize resources like a db engine that use LoadLibrary to load external libraries in `initialization` sections or finalize them in `finalization` (maybe only as last chance) because this is not supported by the underlying operating system. See [Issue \#26801](https://bugs.freepascal.org/view.php?id=26801).
136136
137137## 다른언어 에서 FPC 라이브러리 사용
138138
139139The possibility to create a shared library in FPC and being able to use it in other languages (typically but not limited to C/C++) is one of the key goals of having the idea of shared libraries.
140140
141141Official documentation: FPC programmer's guide, chapter 12.
142142
143143In general
144144
145145- keep you library procedural; do not rely on OOP (Object-Oriented Programming)
146146- use simple types in the interface procedures, **avoid** usage of
147147 - ANSI / Unicode strings (non OLE-bases). ShortStrings are ok.
148148 - explicit dynamic strings (open arrays are ok)
149149 - classes
150150- OOP implementation is not portable due to compiler dependent implementation (except for Objective C). Even worse, objects might not be compatible between different versions of the same compiler (i.e. TObject for FPC 2.4.x is not compatible to TObject for 2.6.x)
151151
152152More specific information about which types can be exported:
153153
154154- var / const / out parameters
155155 - var - a variable is always passed by reference
156156 - const - might be passed as reference (depending on the size of the variable and target CPU platform (ABI))
157157 - out - a variable is always passed by reference
158158 - if a parameter is passed by reference, it's typically a pointer reference in C
159159- Records - yes, they are compatible with most of the languages. Do keep in mind the "packing" of the record. The best approach is to use "packed record" (the data will be allocated in the most compact way).
160160- ShortStrings (fixed size strings) - yes - as a simple array of 256-characters (bytes), the first byte in the array is the number of characters
161161- Strings (dynamic strings: AnsiString) - incompatible - do not export; Consider using PChar instead (see example below)
162162- WideStrings (Windows ONLY - based on OLE) - could be used. Other languages would need to support OLEStrings as well (and handle them through Windows OLE utilities)
163163- WideStrings (non Windows), Unicode strings - incompatible - consider using PWideChar instead;
164164- Classes - incompatible. Consider using wrapper functions'
165165- Objects (old ones) - could be done, but you'll need to export all methods as separate functions/procedures
166166- Open Arrays - yes, but a single "open array" parameter is in fact 2 parameters - a pointer to the array and the size of the array (in elements). Example:
167167
168-`// this declaration cannot be converted to C, as C doesn't have "open arrays"`\
169-`function Calculation(const params: array of TSomeData; a, b : integer): Integer;`
168+``` pascal
169+// this declaration cannot be converted to C, as C doesn't have "open arrays"
170+function Calculation(const params: array of TSomeData; a, b : integer): Integer;
171+```
170172
171173could be called as
172174
173-`// this declaration is easily converted to C`\
174-`function _Calculation(params: PSomeData; paramscount: Integer; a, b : integer): Integer;`
175+``` pascal
176+// this declaration is easily converted to C
177+function _Calculation(params: PSomeData; paramscount: Integer; a, b : integer): Integer;
178+```
175179
176180- StaticArrays - yes - *const* might, if *var* will, be passed as a reference, rather than an array itslef;
177181- DynamicArrays - **dynamic arrays are different from open arrays parameter** - not portable. consider using arrays or create wrappers;
178182- Interfaces COM/CORBA - yes.
179183- Procedure types - yes - but keep in mind the calling convention, since a callback function call would need to use it as well.
180184
181185### Returning string results as PChars
182186
183187This example illustrates the use of PChar to return string-type results:
184188
185189``` pascal
186190function InLibStringFunction: PChar; cdecl;
187191const
188192 MyString: AnsiString = 'Hello World';
189193begin
190194 result := PChar(MyString);
191195end;
192196```
193197
194198The DLL's memory manager will allocate memory for the PChar. This means that your application must not free that memory. It needs to be freed by the DLL. This is not a problem if "MyString" does not change through the complete lifetime of your application.
195199
196200If you want your application to be able to free the PChars then you can allow your main application to free the memory via the library.
197201
198202So in that case use this kind of function:
199203
200204``` pascal
201205function InLibStringFunction: PChar; cdecl;
202206const
203207 MyString: AnsiString = 'Hello World';
204208begin
205209 Result := GetMem(Length(MyString) + 1);
206210 Move(@MyString[1], Result, Length(MyString));
207211 Result[Length(MyString)] := 0;
208212end;
209213```
210214
211215And, for freeing the allocated memory, you may add this procedure in the library, which you can then call from your main application :
212216
213217``` pascal
214218procedure InLibFreeString(aStr: PChar);
215219begin
216220 FreeMem(aStr);
217221end;
218222```
219223
220224It is a good idea to store the pointer values of the allocated PChar results (e.g. TList or specialize TFPGList\<PChar\>) so that you can use this inside your exported FreeString function to check whether this is indeed a pointer that you need to free.
221225
222226### Exposing objects using a wrapper
223227
224228If you need to expose an object, you will need to create wrapper functions. These wrapper functions would accept a pointer parameter, cast it to the object and call the method. Sanity checks are advised.
225229
226230This is also known as "flattening" the object (to a procedural API/interface).
227231
228232Keep in mind that you will need to wrap/flatten:
229233
230234- object methods
231235- object properties
232236- object fields (Note: BigChimp: what is an object field? Do you mean property?)
233237
234238``` pascal
235239library libtest;
236240
237241uses
238242 Classes, ctypes, Math, SysUtils;
239243
240244type
241245TMyClass = class(TThread)
242246 public
243247 enabled1: boolean;
244248 int1, int2: shortint;
245249 function function1(): integer; cdecl;
246250end;
247251
248252function TMyClass.function1(): integer; cdecl;
249253begin
250254 if enabled1 then
251255 result := int1 + int2 else result := -1;
252256end;
253257
254258exports
255259 TMyClass.function1();
256260end.
257261```
258262
259263Chould be "wrapped" like this:
260264
261265``` pascal
262266library libtest;
263267
264268uses
265269 Classes, ctypes, Math, SysUtils;
266270
267271type
268272TMyClass = class(TThread)
269273 public
270274 enabled1: boolean;
271275 int1, int2: shortint;
272276 function function1(): integer; cdecl;
273277end;
274278
275279function TMyClass.function1(): integer; cdecl;
276280begin
277281 if enabled1 then
278282 result := int1 + int2
279283 else
280284 result := -1;
281285end;
282286
283287function MyClassCreate:Pointer; cdecl;
284288begin
285289 Result:=TMyClass.Create;
286290end;
287291
288292function MyClassFree(obj:Pointer); cdecl;
289293begin
290294 TMyClass(Obj).free;
291295end;
292296
293297function MyClassSetEnabled(obj:Pointer; aenabled: Boolean); cdecl;
294298begin
295299 TMyClass(Obj).enabled:=aenabled;
296300end;
297301
298302function MyClassGetEnabled(obj:Pointer): Boolean; cdecl;
299303begin
300304 Result:=TMyClass(Obj).enabled;
301305end;
302306
303307function MyClassSetData(obj:Pointer; i1, i2: shortint); cdecl;
304308begin
305309 TMyClass(Obj).int1:=i1;
306310 TMyClass(Obj).int2:=i2;
307311end;
308312
309313function MyClassGetData(obj:Pointer; var i1, i2: shortint); cdecl;
310314begin
311315 i1:=TMyClass(Obj).int1;
312316 i2:=TMyClass(Obj).int2;
313317end;
314318
315319function MyClassFunction1(obj:Pointer): Integer; cdecl;
316320begin
317321 Result:=TMyClass(Obj).function1
318322end;
319323
320324exports
321325 TMyClassCreate, MyClassFree, MyClassSetEnabled, MyClassGetEnabled
322326 ,MyClassSetData, MyClassGetData, MyClassFunction1;
323327end.
324328```
325329
326330By the way, the same "wrapper"/flattening approach is taken when a C++ library (with classes) needs to be accessed from FPC (or any other language)
327331
328332### .NET
329333
330334More information [[Using Pascal Libraries with .NET and Mono|here]]
331335
332336### Java
333337
334338More information [[Using Pascal Libraries with Java]]
335339
336340## 같이보기
337341
338342- [[Lazarus/FPC Libraries]]
339343
340344## 참조
341345
342346- FPC programmer's guide, chapter 12: FPC documentation on programming shared libraries
343347- <http://www.iecc.com/linker/>
344348- <ftp://ftp.freepascal.org/pub/fpc/docs-pdf/CinFreePascal.pdf>
345349
346350---
347351*From the Free Pascal wiki, page "shared library/ko", as it stood on 2020-02-26 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