View Single Post
Old 30 December 2013, 12:39   #23
FrodeSolheim
FS-UAE Developer

FrodeSolheim's Avatar
 
Join Date: Dec 2011
Location: Førde, Norway
Age: 36
Posts: 3,368
Quote:
Originally Posted by Der Wanderer View Post
It would be a good idea if the .dll or .so is NOT aware of being used by UAE.
Means, there should be no glue code on the x86 side. Why? Simply because you may want to use existing libraries that you don't want to contaminate with Amiga specific stuff or you don't even have the source code.
In my opinion, this is a very bad idea for several reasons including:
  • The Amiga library would need to know a lot about the native library, and account for differences between native architectures (CPU and OS). This includes dealing with different calling conventions (cdecl, stdcall, etc), different data type sizes and pointer size, different struct alignment and padding (struct member offsets may be different due to both differing data type sizes and struct alignment). You would also have to byte-swap integers based on whether the native platform is little-endian or big-ending.
  • The native library might give you a pointer which you are expected to use, but the native memory isn't available from the Amiga side (without proxy functions to access it).

It should be much more convenient (and better performance-wise) to handle the Amiga<->native differences on the native side (and it will be compiled into the native library, so no dynamic translation based on native architecture is needed).

If you want to use an existing native library you simply write a wrapper library instead. If you create it yourself you can do as Thellier and include both a "real function" and a wrapper function in the same library, so it can easily be used by both UAE and non-UAE program.

Quote:
Originally Posted by Toni Wilen View Post
I can do the Amiga side library, including some skeleton code that does the important Amiga-side bits.
That is probably a good idea

Quote:
Originally Posted by Toni Wilen View Post
There is only something that MUST be changed = no more open the external library with his full name (soft3d.dll) but the name (soft3d) so allowing to have the same 68k code that will open soft3d.so soft3d.elf
This is already accounted for - as you can see there is no extension in the example. This situation is a little bit more complex than just adding file type extension - we need CPU architecture as part of the name, and for some extension also the OS (or just always include it for consistency), so it would be something like this:
  • soft3d-x86(-windows).dll, soft3d-x86-64(-windows).dll
  • soft3d-x86(-macosx).dylib, soft3d-x86-64(-macosx).dylib soft3d-ppc(-macosx).dylib
  • soft3d-x86-linux.so, etc

I write a custom ELF loader a couple of years ago which can load (position independent) self-contained code from an .so file with an API similar to LoadLibrary/dlopen and friends. So for native code which does not interface with other system libraries, I can include support for this as well and allow the native interface to load one of soft3d-x86.so, soft3d-x86-64.so, soft3d-ppc.so (etc) on all platforms - operating system-specific variants not needed.

Quote:
Originally Posted by Toni Wilen View Post
I don't see how it can work when native libraries are linked based on export name, not order of export. How to map native call to jump vector offset? Library would need to have some extra information.
Yes, it only works because the Amiga library looks up the names in a specific order. If some kind of automation is desired here I guess it must be one of:
  • The Amiga code provides a string list to an UAE library function which resolves the names in order (just what Telliers code does, but the code is moved to the UAE library).
  • Alternatively, the library can export a function (uni_get_function_names) which the native interface the native interface can resolve in order - provided that names are always appended to the list only (to avoid breaking older clients). The Amiga side would still need to have #defines or something for the offsets, so perhaps not a lot better than just let the Amiga side provide the function names of interest, in order, as well...

Regarding library version number (mentioned by Tellier), I included a version number in the UAE_DEFINE_LIBRARY macro, for example:
Code:
UNI_DEFINE_LIBRARY(1, init);
The open library call from the Amiga side takes a minimum library version, and the call fails with UNI_ERROR_LIBRARY_TOO_OLD if the requirement is not met.

Here is more recent example native library which compiles -but I have not tested it because I haven't get any Amiga-side program for it yet (anyone volunteer to write one?):

Code:
#include <string.h>
#include <uae/uni.h>

static const char *message = "";

uni_u32 init() {
    message = "Hello, World!";
    return 0;
}

UNI_DEFINE_LIBRARY(1, init);

UNIAPI void UNICALL hello_world(struct uni *uni) {
    char *data = (char *) UNI_PTR_PARAM(0);
    strcpy(data, message);
}

#pragma pack(2)
struct numbers {
    uni_char c;
    uni_short s;
    uni_long l;
};
#pragma pack()

UNIAPI void UNICALL add_numbers(struct uni *uni) {
    struct numbers *numbers = (struct numbers *) UNI_PTR_PARAM(0);
    uni_long *result = (uni_long *) UNI_PTR_PARAM(1);
    *result = numbers->c + numbers->s + numbers->l;
}
As a side note (and used in the example) it looks like #pragma pack(2) can be used with both MSVC and GCC to safely share C many struct definitions between the Amiga C code and the native C code! (Though not part of the native interface layer itself, please let me know if you see any problems with this )

EDIT: uae_char and uae_short isn't really necessary - just included for consistency. But uae_long is useful in shared structs since int can be either 16- or 32-bit on the Amiga depending on the compiler, and on the native side, longs can be either 32- or 64-bit.

Last edited by FrodeSolheim; 30 December 2013 at 12:44.
FrodeSolheim is offline  
 
Page generated in 0.06229 seconds with 9 queries