1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: jan@swi-prolog.org 5 WWW: https://www.swi-prolog.org 6 Copyright (c) 1995-2026, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 SWI-Prolog Solutions b.v. 10 All rights reserved. 11 12 Redistribution and use in source and binary forms, with or without 13 modification, are permitted provided that the following conditions 14 are met: 15 16 1. Redistributions of source code must retain the above copyright 17 notice, this list of conditions and the following disclaimer. 18 19 2. Redistributions in binary form must reproduce the above copyright 20 notice, this list of conditions and the following disclaimer in 21 the documentation and/or other materials provided with the 22 distribution. 23 24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 POSSIBILITY OF SUCH DAMAGE. 36*/ 37 38:- module(shlib, 39 [ load_foreign_library/1, % :LibFile 40 load_foreign_library/2, % :LibFile, +Options 41 unload_foreign_library/1, % +LibFile 42 unload_foreign_library/2, % +LibFile, +UninstallFunc 43 current_foreign_library/2, % ?LibFile, ?Public 44 foreign_library_property/2, % ?File:atom, ?Property 45 reload_foreign_libraries/0, 46 % Directives 47 use_foreign_library/1, % :LibFile 48 use_foreign_library/2 % :LibFile, +Options 49 ]). 50:- if(current_predicate(win_add_dll_directory/2)). 51:- export(win_add_dll_directory/1). 52:- endif. 53 54:- autoload(library(error),[existence_error/2]). 55:- autoload(library(lists),[member/2,reverse/2]). 56 57%:- set_prolog_flag(generate_debug_info, false). 58 59/** <module> Utility library for loading foreign objects (DLLs, shared objects) 60 61This section discusses the functionality of the (autoload) 62library(shlib), providing an interface to manage shared libraries. We 63describe the procedure for using a foreign resource (DLL in Windows and 64shared object in Unix) called =mylib=. 65 66First, one must assemble the resource and make it compatible to 67SWI-Prolog. The details for this vary between platforms. The 68``swipl-ld(1)`` utility can be used to deal with this in a portable 69manner. The typical commandline is: 70 71``` 72swipl-ld -shared -o mylib file.{c,o,cc,C} ... 73``` 74 75Make sure that one of the files provides a global function 76``install_mylib()`` that initialises the module using calls to 77PL_register_foreign(). Below is a simple example file ``mylib.c``, which 78prints a "hello" message. Note that we use SWI-Prolog's Sprintf() rather 79than C standard printf() to print the outout through Prolog's 80`current_output` stream, making the example work in a windowed 81environment. The standard C printf() works in a console environment, but 82this bypasses Prolog's output redirection. Also note the use of the 83standard C ``bool`` type, which is supported in 9.2.x and more actively 84promoted in the 9.3.x development series. 85 86``` 87#include <SWI-Prolog.h> 88#include <SWI-Stream.h> 89#include <stdbool.h> 90 91static foreign_t 92pl_say_hello(term_t to) 93{ char *s; 94 95 if ( PL_get_chars(to, &s, CVT_ALL|REP_UTF8) ) 96 { Sprintf("hello %Us", s); 97 98 return true; 99 } 100 101 return false; 102} 103 104install_t 105install_mylib(void) 106{ PL_register_foreign("say_hello", 1, pl_say_hello, 0); 107} 108``` 109 110Now write a file mylib.pl: 111 112``` 113:- module(mylib, [ say_hello/1 ]). 114:- use_foreign_library(foreign(mylib)). 115``` 116 117The file mylib.pl can be loaded as a normal Prolog file and provides the 118predicate defined in C. The generated ``mylib.so`` (or ``.dll``, etc.) 119must be placed in a directory searched for using the Prolog search path 120`foreign` (see absolute_file_name/3). To load this from the current 121directory, we can use the ``-p alias=dir`` option: 122 123``` 124swipl -p foreign=. mylib.pl 125?- say_hello(world). 126hello world 127true. 128``` 129*/ 130 131:- meta_predicate 132 load_foreign_library(), 133 load_foreign_library(, ). 134 135:- dynamic 136 loading/1, % Lib 137 error/2, % File, Error 138 foreign_predicate/2, % Lib, Pred 139 current_library/5. % Lib, Entry, Path, Module, Handle 140 141:- volatile % Do not store in state 142 loading/1, 143 error/2, 144 foreign_predicate/2, 145 current_library/5. 146 147:- '$notransact'((loading/1, 148 error/2, 149 foreign_predicate/2, 150 current_library/5)). 151 152:- ( current_prolog_flag(open_shared_object, true) 153 -> true 154 ; print_message(warning, shlib(not_supported)) % error? 155 ). 156 157% The flag `res_keep_foreign` prevents deleting temporary files created 158% to load shared objects when set to `true`. This may be needed for 159% debugging purposes. 160 161:- create_prolog_flag(res_keep_foreign, false, 162 [ keep(true) ]). 163 164 165%! use_foreign_library(+FileSpec) is det. 166%! use_foreign_library(+FileSpec, +Options:list) is det. 167% 168% Load and install a foreign library as load_foreign_library/1,2 and 169% register the installation using initialization/2 with the option 170% `now`. This is similar to using: 171% 172% ``` 173% :- initialization(load_foreign_library(foreign(mylib))). 174% ``` 175% 176% but using the initialization/1 wrapper causes the library to be 177% loaded _after_ loading of the file in which it appears is completed, 178% while use_foreign_library/1 loads the library _immediately_. I.e. 179% the difference is only relevant if the remainder of the file uses 180% functionality of the C-library. 181% 182% As of SWI-Prolog 8.1.22, use_foreign_library/1,2 is in provided as a 183% built-in predicate that, if necessary, loads library(shlib). This 184% implies that these directives can be used without explicitly loading 185% library(shlib) or relying on demand loading. 186 187 188 /******************************* 189 * DISPATCHING * 190 *******************************/ 191 192%! find_library(+LibSpec, -Lib, -Delete) is det. 193% 194% Find a foreign library from LibSpec. If LibSpec is available as 195% a resource, the content of the resource is copied to a temporary 196% file and Delete is unified with =true=. 197 198find_library(Spec, TmpFile, true) :- 199 '$rc_handle'(Zipper), 200 term_to_atom(Spec, Name), 201 setup_call_cleanup( 202 zip_lock(Zipper), 203 setup_call_cleanup( 204 open_foreign_in_resources(Zipper, Name, In), 205 setup_call_cleanup( 206 tmp_file_stream(binary, TmpFile, Out), 207 copy_stream_data(In, Out), 208 close(Out)), 209 close(In)), 210 zip_unlock(Zipper)), 211 !. 212find_library(Spec, Lib, Copy) :- 213 absolute_file_name(Spec, Lib0, 214 [ file_type(executable), 215 access(read), 216 file_errors(fail) 217 ]), 218 !, 219 lib_to_file(Lib0, Lib, Copy). 220find_library(Spec, Spec, false) :- 221 atom(Spec), 222 !. % use machines finding schema 223find_library(foreign(Spec), Spec, false) :- 224 atom(Spec), 225 !. % use machines finding schema 226find_library(Spec, _, _) :- 227 throw(error(existence_error(source_sink, Spec), _)). 228 229%! lib_to_file(+Lib0, -Lib, -Copy) is det. 230% 231% If Lib0 is not a regular file we need to copy it to a temporary 232% regular file because dlopen() and Windows LoadLibrary() expect a 233% file name. On some systems this can be avoided. Roughly using two 234% approaches (after discussion with Peter Ludemann): 235% 236% - On FreeBSD there is shm_open() to create an anonymous file in 237% memory and than fdlopen() to link this. 238% - In general, we could redefine the system calls open(), etc. to 239% make dlopen() work on non-files. This is highly non-portably 240% though. 241% - We can mount the resource zip using e.g., `fuse-zip` on Linux. 242% This however fails if we include the resources as a string in 243% the executable. 244% 245% @see https://github.com/fancycode/MemoryModule for Windows 246 247lib_to_file(Res, TmpFile, true) :- 248 sub_atom(Res, 0, _, _, 'res://'), 249 !, 250 setup_call_cleanup( 251 open(Res, read, In, [type(binary)]), 252 setup_call_cleanup( 253 tmp_file_stream(binary, TmpFile, Out), 254 copy_stream_data(In, Out), 255 close(Out)), 256 close(In)). 257lib_to_file(Lib, Lib, false). 258 259 260open_foreign_in_resources(Zipper, ForeignSpecAtom, Stream) :- 261 term_to_atom(foreign(Name), ForeignSpecAtom), 262 zipper_members_(Zipper, Entries), 263 entries_for_name(Entries, Name, Entries1), 264 compatible_architecture_lib(Entries1, Name, CompatibleLib), 265 zipper_goto(Zipper, file(CompatibleLib)), 266 zipper_open_current(Zipper, Stream, 267 [ type(binary), 268 release(true) 269 ]). 270 271%! zipper_members_(+Zipper, -Members) is det. 272% 273% Simplified version of zipper_members/2 from library(zip). We already 274% have a lock on the zipper and by moving this here we avoid 275% dependency on another library. 276% 277% @tbd: should we cache this? 278 279zipper_members_(Zipper, Members) :- 280 zipper_goto(Zipper, first), 281 zip_members__(Zipper, Members). 282 283zip_members__(Zipper, [Name|T]) :- 284 zip_file_info_(Zipper, Name, _Attrs), 285 ( zipper_goto(Zipper, next) 286 -> zip_members__(Zipper, T) 287 ; T = [] 288 ). 289 290 291%! compatible_architecture_lib(+Entries, +Name, -CompatibleLib) is det. 292% 293% Entries is a list of entries in the zip file, which are already 294% filtered to match the shared library identified by `Name`. The 295% filtering is done by entries_for_name/3. 296% 297% CompatibleLib is the name of the entry in the zip file which is 298% compatible with the current architecture. The compatibility is 299% determined according to the description in qsave_program/2 using the 300% qsave:compat_arch/2 hook. 301% 302% The entries are of the form 'shlib(Arch, Name)' 303 304compatible_architecture_lib([], _, _) :- !, fail. 305compatible_architecture_lib(Entries, Name, CompatibleLib) :- 306 current_prolog_flag(arch, HostArch), 307 ( member(shlib(EntryArch, Name), Entries), 308 qsave_compat_arch1(HostArch, EntryArch) 309 -> term_to_atom(shlib(EntryArch, Name), CompatibleLib) 310 ; existence_error(arch_compatible_with(Name), HostArch) 311 ). 312 313qsave_compat_arch1(Arch1, Arch2) :- 314 qsave:compat_arch(Arch1, Arch2), !. 315qsave_compat_arch1(Arch1, Arch2) :- 316 qsave:compat_arch(Arch2, Arch1), !. 317 318%! qsave:compat_arch(Arch1, Arch2) is semidet. 319% 320% User definable hook to establish if Arch1 is compatible with Arch2 321% when running a shared object. It is used in saved states produced by 322% qsave_program/2 to determine which shared object to load at runtime. 323% 324% @see `foreign` option in qsave_program/2 for more information. 325 326:- multifile qsave:compat_arch/2. 327 328qsavecompat_arch(A,A). 329 330entries_for_name([], _, []). 331entries_for_name([H0|T0], Name, [H|T]) :- 332 shlib_atom_to_term(H0, H), 333 match_filespec(Name, H), 334 !, 335 entries_for_name(T0, Name, T). 336entries_for_name([_|T0], Name, T) :- 337 entries_for_name(T0, Name, T). 338 339shlib_atom_to_term(Atom, shlib(Arch, Name)) :- 340 sub_atom(Atom, 0, _, _, 'shlib('), 341 !, 342 term_to_atom(shlib(Arch,Name), Atom). 343shlib_atom_to_term(Atom, Atom). 344 345match_filespec(Name, shlib(_,Name)). 346 347base(Path, Base) :- 348 atomic(Path), 349 !, 350 file_base_name(Path, File), 351 file_name_extension(Base, _Ext, File). 352base(_/Path, Base) :- 353 !, 354 base(Path, Base). 355base(Path, Base) :- 356 Path =.. [_,Arg], 357 base(Arg, Base). 358 359entry(_, Function, Function) :- 360 Function \= default(_), 361 !. 362entry(Spec, default(FuncBase), Function) :- 363 base(Spec, Base), 364 atomic_list_concat([FuncBase, Base], '_', Function). 365entry(_, default(Function), Function). 366 367 /******************************* 368 * (UN)LOADING * 369 *******************************/ 370 371%! load_foreign_library(:FileSpec) is det. 372%! load_foreign_library(:FileSpec, +Options:list) is det. 373% 374% Load a _|shared object|_ or _DLL_. After loading the Entry function 375% is called without arguments. The default entry function is composed 376% from =install_=, followed by the file base-name. E.g., the load-call 377% below calls the function =|install_mylib()|=. If the platform 378% prefixes extern functions with =_=, this prefix is added before 379% calling. Options provided are below. Other options are passed to 380% open_shared_object/3. 381% 382% - install(+Function) 383% Installation function to use. Default is default(install), 384% which derives the function from FileSpec. 385% 386% ``` 387% ... 388% load_foreign_library(foreign(mylib)), 389% ... 390% ``` 391% 392% @arg FileSpec is a specification for absolute_file_name/3. If searching 393% the file fails, the plain name is passed to the OS to try the default 394% method of the OS for locating foreign objects. The default definition 395% of file_search_path/2 searches <prolog home>/lib/<arch> on Unix and 396% <prolog home>/bin on Windows. 397% 398% @see use_foreign_library/1,2 are intended for use in directives. 399 400load_foreign_library(Library) :- 401 load_foreign_library(Library, []). 402 403load_foreign_library(Module:LibFile, InstallOrOptions) :- 404 ( is_list(InstallOrOptions) 405 -> Options = InstallOrOptions 406 ; Options = [install(InstallOrOptions)] 407 ), 408 with_mutex('$foreign', 409 load_foreign_library(LibFile, Module, Options)). 410 411load_foreign_library(LibFile, _Module, _) :- 412 current_library(LibFile, _, _, _, _), 413 !. 414load_foreign_library(LibFile, Module, Options) :- 415 retractall(error(_, _)), 416 find_library(LibFile, Path, Delete), 417 asserta(loading(LibFile)), 418 retractall(foreign_predicate(LibFile, _)), 419 catch(Module:open_shared_object(Path, Handle, Options), E, true), 420 ( nonvar(E) 421 -> delete_foreign_lib(Delete, Path), 422 assert(error(Path, E)), 423 fail 424 ; delete_foreign_lib(Delete, Path) 425 ), 426 !, 427 '$option'(install(DefEntry), Options, default(install)), 428 ( entry(LibFile, DefEntry, Entry), 429 Module:call_shared_object_function(Handle, Entry) 430 -> retractall(loading(LibFile)), 431 assert_shlib(LibFile, Entry, Path, Module, Handle) 432 ; foreign_predicate(LibFile, _) 433 -> retractall(loading(LibFile)), % C++ object installed predicates 434 assert_shlib(LibFile, 'C++', Path, Module, Handle) 435 ; retractall(loading(LibFile)), 436 retractall(foreign_predicate(LibFile, _)), 437 close_shared_object(Handle), 438 findall(Entry, entry(LibFile, DefEntry, Entry), Entries), 439 throw(error(existence_error(foreign_install_function, 440 install(Path, Entries)), 441 _)) 442 ). 443load_foreign_library(LibFile, _, _) :- 444 retractall(loading(LibFile)), 445 ( error(_Path, E) 446 -> retractall(error(_, _)), 447 throw(E) 448 ; throw(error(existence_error(foreign_library, LibFile), _)) 449 ). 450 451delete_foreign_lib(true, Path) :- 452 \+ current_prolog_flag(res_keep_foreign, true), 453 !, 454 catch(delete_file(Path), _, true). 455delete_foreign_lib(_, _). 456 457 458%! unload_foreign_library(+FileSpec) is det. 459%! unload_foreign_library(+FileSpec, +Exit:atom) is det. 460% 461% Unload a _shared object_ or _DLL_. After calling the Exit function, 462% the shared object is removed from the process. The default exit 463% function is composed from `uninstall_`, followed by the file 464% base-name. 465 466unload_foreign_library(LibFile) :- 467 unload_foreign_library(LibFile, default(uninstall)). 468 469unload_foreign_library(LibFile, DefUninstall) :- 470 with_mutex('$foreign', do_unload(LibFile, DefUninstall)). 471 472do_unload(LibFile, DefUninstall) :- 473 current_library(LibFile, _, _, Module, Handle), 474 retractall(current_library(LibFile, _, _, _, _)), 475 ( entry(LibFile, DefUninstall, Uninstall), 476 Module:call_shared_object_function(Handle, Uninstall) 477 -> true 478 ; true 479 ), 480 abolish_foreign(LibFile), 481 close_shared_object(Handle). 482 483abolish_foreign(LibFile) :- 484 ( retract(foreign_predicate(LibFile, Module:Head)), 485 functor(Head, Name, Arity), 486 abolish(Module:Name, Arity), 487 fail 488 ; true 489 ). 490 491system:'$foreign_registered'(M, H) :- 492 ( loading(Lib) 493 -> true 494 ; Lib = '<spontaneous>' 495 ), 496 assert(foreign_predicate(Lib, M:H)). 497 498assert_shlib(File, Entry, Path, Module, Handle) :- 499 retractall(current_library(File, _, _, _, _)), 500 asserta(current_library(File, Entry, Path, Module, Handle)). 501 502 503 /******************************* 504 * ADMINISTRATION * 505 *******************************/ 506 507%! current_foreign_library(?File, ?Public) 508% 509% Query currently loaded shared libraries. 510 511current_foreign_library(File, Public) :- 512 current_library(File, _Entry, _Path, _Module, _Handle), 513 findall(Pred, foreign_predicate(File, Pred), Public). 514 515%! foreign_library_property(?File, ?Property) is nondet. 516% 517% True when Property is a property of the foreign library File. 518% Currently defined properties are: 519% 520% - module(Module) 521% Module context into which the library was loaded 522% - entry(Entry) 523% Name of the _install_ function used. 524% - absolute_file_name(Path) 525% Absolute file name for File when known. 526% - predicate(Pred) 527% Predicate registered by calling the entry function. 528 529foreign_library_property(File, module(Module)) :- 530 current_library(File, _Entry, _Path, Module, _Handle). 531foreign_library_property(File, entry(Entry)) :- 532 current_library(File, Entry, _Path, _Module, _Handle). 533foreign_library_property(File, absolute_file_name(Path)) :- 534 current_library(File, _Entry, Path, _Module, _Handle). 535foreign_library_property(File, predicate(Pred)) :- 536 current_library(File, _Entry, _Path, _Module, _Handle), 537 foreign_predicate(File, Pred). 538 539 540 /******************************* 541 * RELOAD * 542 *******************************/ 543 544%! reload_foreign_libraries 545% 546% Reload all foreign libraries loaded (after restore of a state 547% created using qsave_program/2. 548 549reload_foreign_libraries :- 550 findall(lib(File, Entry, Module), 551 ( retract(current_library(File, Entry, _, Module, _)), 552 File \== - 553 ), 554 Libs), 555 reverse(Libs, Reversed), 556 reload_libraries(Reversed). 557 558reload_libraries([]). 559reload_libraries([lib(File, Entry, Module)|T]) :- 560 ( load_foreign_library(File, Module, [install(Entry)]) 561 -> true 562 ; print_message(error, shlib(File, load_failed)) 563 ), 564 reload_libraries(T). 565 566 567 /******************************* 568 * CLEANUP (WINDOWS ...) * 569 *******************************/ 570 571/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 572Called from Halt() in pl-os.c (if it is defined), *after* all at_halt/1 573hooks have been executed, and after dieIO(), closing and flushing all 574files has been called. 575 576On Unix, this is not very useful, and can only lead to conflicts. 577- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 578 579unload_all_foreign_libraries :- 580 current_prolog_flag(unload_foreign_libraries, true), 581 !, 582 forall(current_library(File, _, _, _, _), 583 unload_foreign(File)). 584unload_all_foreign_libraries. 585 586%! unload_foreign(+File) 587% 588% Unload the given foreign file and all `spontaneous' foreign 589% predicates created afterwards. Handling these spontaneous 590% predicates is a bit hard, as we do not know who created them and 591% on which library they depend. 592 593unload_foreign(File) :- 594 unload_foreign_library(File), 595 ( clause(foreign_predicate(Lib, M:H), true, Ref), 596 ( Lib == '<spontaneous>' 597 -> functor(H, Name, Arity), 598 abolish(M:Name, Arity), 599 erase(Ref), 600 fail 601 ; ! 602 ) 603 -> true 604 ; true 605 ). 606 607 608:- if(current_predicate(win_add_dll_directory/2)). 609 610%! win_add_dll_directory(+AbsDir) is det. 611% 612% Add AbsDir to the directories where dependent DLLs are searched on 613% Windows systems. This call uses the AddDllDirectory() API when 614% provided. On older Windows systems it extends ``%PATH%``. 615% 616% @error existence_error(directory, AbsDir) if the target directory 617% does not exist. 618% @error domain_error(absolute_file_name, AbsDir) if AbsDir is not an 619% absolute file name. 620 621win_add_dll_directory(Dir) :- 622 win_add_dll_directory(Dir, _), 623 !. 624win_add_dll_directory(Dir) :- 625 prolog_to_os_filename(Dir, OSDir), 626 getenv('PATH', Path0), 627 atomic_list_concat([Path0, OSDir], ';', Path), 628 setenv('PATH', Path). 629 630% Environments such as MSYS2 and CONDA install DLLs in some separate 631% directory. We add these directories to the search path for indirect 632% dependencies from ours foreign plugins. 633 634add_dll_directories :- 635 current_prolog_flag(msys2, true), 636 !, 637 env_add_dll_dir('MINGW_PREFIX', '/bin'). 638add_dll_directories :- 639 current_prolog_flag(conda, true), 640 !, 641 env_add_dll_dir('CONDA_PREFIX', '/Library/bin'), 642 ignore(env_add_dll_dir('PREFIX', '/Library/bin')). 643add_dll_directories. 644 645env_add_dll_dir(Var, Postfix) :- 646 getenv(Var, Prefix), 647 atom_concat(Prefix, Postfix, Dir), 648 win_add_dll_directory(Dir). 649 650:- initialization 651 add_dll_directories. 652 653:- endif. 654 655 /******************************* 656 * SEARCH PATH * 657 *******************************/ 658 659:- dynamic 660 user:file_search_path/2. 661:- multifile 662 user:file_search_path/2. 663 664:- if((current_prolog_flag(apple, true), 665 current_prolog_flag(bundle, true))). 666user:file_search_path(foreign, swi('../../PlugIns/swipl')). 667:- elif(current_prolog_flag(apple_universal_binary, true)). 668user:file_search_path(foreign, swi('lib/fat-darwin')). 669:- elif((current_prolog_flag(windows, true), 670 current_prolog_flag(bundle, true))). 671user:file_search_path(foreign, swi(bin)). 672:- else. 673user:file_search_path(foreign, swi(ArchLib)) :- 674 current_prolog_flag(arch, Arch), 675 atom_concat('lib/', Arch, ArchLib). 676:- endif. 677 678 /******************************* 679 * MESSAGES * 680 *******************************/ 681 682:- multifile 683 prolog:message//1, 684 prolog:error_message//1. 685 686prologmessage(shlib(LibFile, load_failed)) --> 687 [ '~w: Failed to load file'-[LibFile] ]. 688prologmessage(shlib(not_supported)) --> 689 [ 'Emulator does not support foreign libraries' ]. 690 691prologerror_message(existence_error(foreign_install_function, 692 install(Lib, List))) --> 693 [ 'No install function in ~q'-[Lib], nl, 694 '\tTried: ~q'-[List] 695 ]