From a4dd0ad63c00f4dee3b86dfd3075d1d61b2b3180 Mon Sep 17 00:00:00 2001 From: sanine Date: Sat, 27 Aug 2022 23:52:56 -0500 Subject: add plibsys --- 3rdparty/plibsys/src/plibraryloader-amiga.c | 583 ++++++++++++++++++++++++++++ 1 file changed, 583 insertions(+) create mode 100644 3rdparty/plibsys/src/plibraryloader-amiga.c (limited to '3rdparty/plibsys/src/plibraryloader-amiga.c') diff --git a/3rdparty/plibsys/src/plibraryloader-amiga.c b/3rdparty/plibsys/src/plibraryloader-amiga.c new file mode 100644 index 0000000..463e350 --- /dev/null +++ b/3rdparty/plibsys/src/plibraryloader-amiga.c @@ -0,0 +1,583 @@ +/* + * The MIT License + * + * Copyright (C) 2017-2018 Alexander Saprykin + * Copyright (C) 2002-2015 by Olaf Barthel + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Path conversion code was taken and adopted from clib2 project: + * https://github.com/adtools/clib2 + * + * This part of code is distributed under the BSD-3-Clause license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Neither the name of Olaf Barthel nor the names of contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "perror.h" +#include "pfile.h" +#include "plibraryloader.h" +#include "pmem.h" +#include "pstring.h" + +#include + +#include +#include + +#if defined (__CLIB2__) +# include +#elif defined (__NEWLIB__) +# include +#endif + +typedef APTR plibrary_handle; + +struct PLibraryLoader_ { + plibrary_handle handle; + Elf32_Error last_error; +}; + +static Elf32_Handle pp_library_loader_elf_root = NULL; + +static void pp_library_loader_clean_handle (plibrary_handle handle); +static pint pp_library_loader_translate_path (const pchar *in, pchar *out, psize out_len); + +/* + * The following patterns must translate properly: + * + * foo/ + * /// + * foo//bar + * foo//bar//baz + * ./foo + * ././foo + * foo/./baz + * foo/./bar/./baz + * foo/./././bar + * foo/. + * /. + * /tmp + * /tmp/foo + * /dev/null + * /dev/null/foo + * /dev/nullX + * /foo + * /foo/ + * /foo/bar + * /foo/bar/baz + * foo/../bar + * foo/bar/../../baz + * foo/bar/.. + * ../foo + * ../../foo + * . + * .. + */ + +static pint +pp_library_loader_translate_path (const pchar *in, pchar *out, psize out_len) +{ + pchar volume_name[MAXPATHLEN]; + psize len, volume_name_len; + pint i, j; + + len = strlen (in); + + if (out_len < MAXPATHLEN || len > MAXPATHLEN) + return -1; + + strcpy (out, in); + + /* Just copy is path is already an Amiga one */ + + if (strchr (in, ':') != NULL) + return 0; + + in = out; + + /* Strip neighbouring slashes: ('foo//bar' -> 'foo/bar'). + * The "//" pattern in a Unix file name is apparently harmless, + * but on AmigaDOS it has a very definite meaning. */ + + if (len > 2) { + pboolean have_double_slash = FALSE; + + for (i = 0; i < len - 1; ++i) { + if (in[i] == '/' && in[i + 1] == '/') { + have_double_slash = TRUE; + break; + } + } + + if (have_double_slash) { + pboolean have_slash; + pchar c; + + have_slash = FALSE; + + for (i = j = 0; i < len; ++i) { + c = in[i]; + + if (c == '/') { + if (!have_slash) + out[j++] = c; + + have_slash = TRUE; + } else { + out[j++] = c; + have_slash = FALSE; + } + } + + len = j; + out[len] = '\0'; + } + } + + /* Strip trailing slashes ('foo/' -> 'foo'). A leading '/' must + * be preserved, though ('///' -> '/'). */ + + if(len > 1) { + psize num_trailing_slashes = 0; + + while ((num_trailing_slashes < len - 1) && (in[len - (num_trailing_slashes + 1)] == '/')) + num_trailing_slashes++; + + if (num_trailing_slashes > 0) { + len -= num_trailing_slashes; + out[len] = '\0'; + } + } + + /* Ditch all leading './' ('./foo' -> 'foo'). */ + + while (len > 2 && out[0] == '.' && out[1] == '/') { + len -= 2; + memmove (out, &out[2], len); + out[len] = '\0'; + } + + /* Ditch all embedded '/./' ('foo/./bar' -> 'foo/bar', 'foo/././bar' -> 'foo/bar'). */ + + if (len > 2) { + pboolean have_slash_dot_slash = FALSE; + + for (i = j = 0; i < len - 2; ++i) { + if(in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '/') { + have_slash_dot_slash = TRUE; + break; + } + } + + if (have_slash_dot_slash) { + for (i = j = 0; i < len; ++i) { + while (i < len - 2 && in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '/') + i += 2; + + if (i < len) + out[j++] = in[i]; + } + + len = j; + out[len] = '\0'; + } + } + + /* Special case: the path name may end with "/." signifying that the + * directory itself is requested ('foo/.' -> 'foo'). */ + + if (len >= 2 && strncmp (&in[len - 2], "/.", 2) == 0) { + /* If it's just '/.' then it's really '/'. */ + if (len == 2) { + strcpy (out, "/"); + len = 1; + } else { + len -= 2; + out[len] = '\0'; + } + } + + /* Check for absolute path. */ + + if (in[0] == '/') { + /* OK, so this is an absolute path. We begin by checking + * for a few special cases, the first being a reference + * to "/tmp". */ + if ((strncmp (in, "/tmp", 4) == 0) && (in[4] == '/' || len == 4)) { + if (in[4] == '/') { + /* Convert "/tmp/foo" to "T:foo". */ + memmove (&out[2], &in[5], len - 5); + memmove (out, "T:", 2); + + len -= 3; + } else { + /* Convert "/tmp" to "T:". */ + strcpy (out, "T:"); + + len = 2; + } + + out[len] = '\0'; + } else if ((strncmp (in, "/dev/null", 9)) == 0 && (len == 9 || in[9] == '/')) { + strcpy (out, "NIL:"); + len = 4; + } else { + psize path_name_start = 0; + volume_name_len = 0; + + /* Find out how long the first component of the absolute path is. */ + for (i = 1; i <= len; ++i) { + if (i == len || in[i] == '/') { + volume_name_len = i - 1; + + /* Is there anything following the path name? */ + if (i < len) + path_name_start = i + 1; + + break; + } + } + + /* Copy the first component and attach a colon. "/foo" becomes "foo:". */ + memmove (out, &in[1], volume_name_len); + out[volume_name_len++] = ':'; + + /* Now add the finishing touches. "/foo/bar" finally + * becomes "foo:bar" and "/foo" becomes "foo:". */ + if (path_name_start > 0) { + memmove (&out[volume_name_len], &in[path_name_start], len - path_name_start); + + len--; + } + + out[len] = '\0'; + } + } + + /* Extract and remove the volume name from the path. We + * are going to need it later. */ + + volume_name_len = 0; + + for (i = 0; i < len; ++i) { + if (in[i] == ':') { + /* Look for extra colon characters embedded in the name + * (as in "foo/bar:baz") which really don't belong here. */ + for (j = 0 ; j < i ; j++) { + if(in[j] == '/') + return -1; + } + + volume_name_len = i + 1; + len -= volume_name_len; + + memmove (volume_name, in, volume_name_len); + memmove (out, &out[volume_name_len], len); + + out[len] = '\0'; + + break; + } + } + + /* Look for extra colon characters embedded in the name + * (as in "foo:bar:baz") which really don't belong here. */ + + for (i = 0; i < len; ++i) { + if (in[i] == ':') + return -1; + } + + /* Now parse the path name and replace all embedded '..' with + * the AmigaDOS counterparts ('foo/../bar' -> 'foo//bar'). */ + + if (len > 3) { + pboolean have_slash_dot_dot_slash = FALSE; + + for (i = j = 0; i < len - 3; ++i) { + if (in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '.' && in[i + 3] == '/') { + have_slash_dot_dot_slash = TRUE; + break; + } + } + + if (have_slash_dot_dot_slash) { + pboolean have_before = FALSE; + + for (i = j = 0; i < len; ++i) { + if(i < len - 3 && in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '.' && in[i + 3] == '/') { + out[j++] = in[i]; + + if (have_before) + out[j++] = '/'; + + i += 2; + + have_before = TRUE; + } else { + have_before = FALSE; + out[j++] = in[i]; + } + } + + len = j; + out[len] = '\0'; + } + } + + /* Translate a trailing '/..' to '//' */ + + if (len >= 3 && strncmp (&in[len - 3], "/..", 3) == 0) { + len -= 2; + out[len++] = '/'; + out[len] = '\0'; + } + + /* Translate a leading '../' ('../foo' -> '/foo') */ + + if (len >= 3 && strncmp (in, "../", 3) == 0) { + memmove (out, &in[2], len - 2); + + len -= 2; + out[len] = '\0'; + } + + /* Translate the '..' ('..' -> '/') */ + + if (len == 2 && strncmp (in, "..", 2) == 0) { + strcpy (out, "/"); + + len = 1; + } + + /* Translate the '.' ('.' -> '') */ + + if (len == 1 && in[0] == '.') { + strcpy (out, ""); + + len = 0; + } + + /* Now put it all together again. */ + + if(volume_name_len > 0) { + memmove (&out[volume_name_len], in, len); + memmove (out, volume_name, volume_name_len); + + len += volume_name_len; + out[len] = '\0'; + } + + return 0; +} + +static void +pp_library_loader_clean_handle (plibrary_handle handle) +{ + IElf->DLClose (pp_library_loader_elf_root, handle); +} + +P_LIB_API PLibraryLoader * +p_library_loader_new (const pchar *path) +{ + PLibraryLoader *loader = NULL; + plibrary_handle handle = NULL; + pchar path_buffer[MAXPATHLEN]; + + if (!p_file_is_exists (path)) + return NULL; + + if (pp_library_loader_elf_root == NULL) { + P_ERROR ("PLibraryLoader::p_library_loader_new: shared library subsystem is not initialized"); + return NULL; + } + + if (strlen (path) >= MAXPATHLEN) { + P_ERROR ("PLibraryLoader::p_library_loader_new: too long file path or name"); + return NULL; + } + + if (pp_library_loader_translate_path (path, path_buffer, MAXPATHLEN) != 0) { + P_ERROR ("PLibraryLoader::p_library_loader_new: failed to convert to UNIX path"); + return NULL; + } + + path = path_buffer; + + handle = IElf->DLOpen (pp_library_loader_elf_root, (CONST_STRPTR) path, ELF32_RTLD_LOCAL); + + if (P_UNLIKELY (handle == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: DLOpen() failed"); + return NULL; + } + + if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory"); + pp_library_loader_clean_handle (handle); + return NULL; + } + + loader->handle = handle; + loader->last_error = ELF32_NO_ERROR; + + return loader; +} + +P_LIB_API PFuncAddr +p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym) +{ + APTR func_addr = NULL; + + if (P_UNLIKELY (loader == NULL || sym == NULL || loader->handle == NULL)) + return NULL; + + if (pp_library_loader_elf_root == NULL) { + P_ERROR ("PLibraryLoader::p_library_loader_new: shared library subsystem is not initialized"); + return NULL; + } + + loader->last_error = IElf->DLSym (pp_library_loader_elf_root, + loader->handle, + (CONST_STRPTR) sym, + &func_addr); + + return (PFuncAddr) func_addr; +} + +P_LIB_API void +p_library_loader_free (PLibraryLoader *loader) +{ + if (P_UNLIKELY (loader == NULL)) + return; + + pp_library_loader_clean_handle (loader->handle); + + p_free (loader); +} + +P_LIB_API pchar * +p_library_loader_get_last_error (PLibraryLoader *loader) +{ + if (P_UNLIKELY (loader == NULL)) + return NULL; + + switch (loader->last_error) { + case ELF32_NO_ERROR: + return NULL; + case ELF32_OUT_OF_MEMORY: + return p_strdup ("Out of memory"); + case ELF32_INVALID_HANDLE: + return p_strdup ("Invalid resource handler"); + case ELF32_NO_MORE_RELOCS: + return p_strdup ("No more relocations left"); + case ELF32_SECTION_NOT_LOADED: + return p_strdup ("Section not loaded"); + case ELF32_UNKNOWN_RELOC: + return p_strdup ("Unknown relocation"); + case ELF32_READ_ERROR: + return p_strdup ("Read error"); + case ELF32_INVALID_SDA_BASE: + return p_strdup ("Invalid SDA base"); + case ELF32_SYMBOL_NOT_FOUND: + return p_strdup ("Symbol not found"); + case ELF32_INVALID_NAME: + return p_strdup ("Invalid procedure name"); + case ELF32_REQUIRED_OBJECT_MISSING: + return p_strdup ("Required object is missing"); + default: + return p_strdup ("Unknown error"); + } +} + +P_LIB_API pboolean +p_library_loader_is_ref_counted (void) +{ + return TRUE; +} + +void +p_library_loader_init (void) +{ + BPTR segment_list; + Elf32_Handle elf_handle; + + if (pp_library_loader_elf_root != NULL) + return; + + segment_list = IDOS->GetProcSegList (NULL, GPSLF_RUN); + + if (P_UNLIKELY (segment_list == 0)) { + P_ERROR ("PLibraryLoader::p_library_loader_init: GetProcSegList() failed"); + return; + } + + if (P_UNLIKELY (IDOS->GetSegListInfoTags (segment_list, + GSLI_ElfHandle, + &elf_handle, + TAG_DONE) != 1)) { + P_ERROR ("PLibraryLoader::p_library_loader_init: GetSegListInfoTags() failed"); + return; + } + + if (P_UNLIKELY (elf_handle == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_init: failed to finf proper GSLI_ElfHandle"); + return; + } + + pp_library_loader_elf_root = IElf->OpenElfTags (OET_ElfHandle, elf_handle, TAG_DONE); + + if (P_UNLIKELY (pp_library_loader_elf_root == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_init: OpenElfTags() failed"); + return; + } +} + +void +p_library_loader_shutdown (void) +{ + if (pp_library_loader_elf_root == NULL) + return; + + IElf->CloseElfTags (pp_library_loader_elf_root, CET_ReClose, TRUE, TAG_DONE); + + pp_library_loader_elf_root = NULL; +} -- cgit v1.2.1