hrev46378 adds 6 changesets to branch 'master' old head: 905f910e5364ca9b0e0ca98fe9e35fa6cffe42ed new head: 4f5e93857671d231372096457d024926635dcee6 overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=4f5e938+%5E905f910 ---------------------------------------------------------------------------- b83f9b3: HAIKU_DEFINES: Add __HAIKU_PRIMARY_PACKAGING_ARCH * It is set to the primary architecture we're building for. * Remove __HAIKU__. The macro is already defined by the compiler. cf7e2ad: runtime loader: Export get_executable_architecture() function Given a path of an ELF file, it tries to determine its architecture. 60b39cd: Add get_*architecture() API, extend find_path*() API * Add get_architecture(), get_primary_architecture(), get_secondary_architectures(), guess_architecture_for_path() to get the caller's architecture, the primary architecture, all secondary architectures, or the architecture associated with a specified path respectively. * Rename the find_path*() functions to find_path*_etc() and add an optional architecture parameter. Add simplified find_path*() functions. * BPathFinder: Add FindPath[s]() versions with an architecture parameter. 2c2f3ab: findpaths: Add an -a <architecture> option c39c928: Add get_architectures() and C++ versions * get_architectures() returns the primary and the secondary architectures in one array. That turned out to be convenient. * Add C++ versions for get[_secondary]_architectures(), returning a BStringList. 4f5e938: Add setarch and getarch commands [ Ingo Weinhold <ingo_weinhold@xxxxxx> ] ---------------------------------------------------------------------------- 22 files changed, 1285 insertions(+), 71 deletions(-) build/jam/BuildSetup | 4 +- build/jam/images/HaikuImage | 8 +- build/jam/images/HaikuImageBootstrap | 4 +- headers/os/storage/FindDirectory.h | 24 +- headers/os/storage/PathFinder.h | 8 + headers/os/support/Architecture.h | 42 +++ headers/private/runtime_loader/runtime_loader.h | 2 + headers/private/system/architecture_private.h | 26 ++ headers/private/system/find_directory_private.h | 29 +- src/bin/Jamfile | 2 + src/bin/findpaths.cpp | 20 +- src/bin/getarch.cpp | 169 ++++++++++ src/bin/setarch.cpp | 230 ++++++++++++++ src/kits/storage/PathFinder.cpp | 46 ++- src/kits/support/Architecture.cpp | 53 ++++ src/kits/support/Jamfile | 1 + src/system/libroot/os/Architecture.cpp | 129 ++++++++ src/system/libroot/os/Jamfile | 1 + src/system/libroot/os/find_paths.cpp | 315 +++++++++++++++++-- src/system/runtime_loader/export.cpp | 1 + src/system/runtime_loader/runtime_loader.cpp | 240 +++++++++++++- .../runtime_loader/runtime_loader_private.h | 2 + ############################################################################ Commit: b83f9b39401caf09c46d66487c3c2174e0742598 URL: http://cgit.haiku-os.org/haiku/commit/?id=b83f9b3 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat Nov 16 21:18:40 2013 UTC HAIKU_DEFINES: Add __HAIKU_PRIMARY_PACKAGING_ARCH * It is set to the primary architecture we're building for. * Remove __HAIKU__. The macro is already defined by the compiler. ---------------------------------------------------------------------------- diff --git a/build/jam/BuildSetup b/build/jam/BuildSetup index ed49622..858fb07 100644 --- a/build/jam/BuildSetup +++ b/build/jam/BuildSetup @@ -125,7 +125,9 @@ if $(HAIKU_PACKAGING_ARCH) { KernelArchitectureSetup $(HAIKU_PACKAGING_ARCH) ; } -HAIKU_DEFINES = __HAIKU__ ; +# define primary packaging architecture macro +HAIKU_DEFINES = __HAIKU_PRIMARY_PACKAGING_ARCH=\\\"$(HAIKU_PACKAGING_ARCH)\\\" ; + HAIKU_LIBSUPC++ = $(HAIKU_LIBSUPC++_$(HAIKU_PACKAGING_ARCH)) ; HAIKU_LIBSTDC++ = $(HAIKU_LIBSTDC++_$(HAIKU_PACKAGING_ARCH)) ; ############################################################################ Commit: cf7e2ad8129137fa3a25d8831c363ead09e99b2b URL: http://cgit.haiku-os.org/haiku/commit/?id=cf7e2ad Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat Nov 16 21:20:51 2013 UTC runtime loader: Export get_executable_architecture() function Given a path of an ELF file, it tries to determine its architecture. ---------------------------------------------------------------------------- diff --git a/headers/private/runtime_loader/runtime_loader.h b/headers/private/runtime_loader/runtime_loader.h index 4d32841..3772a09 100644 --- a/headers/private/runtime_loader/runtime_loader.h +++ b/headers/private/runtime_loader/runtime_loader.h @@ -41,6 +41,8 @@ struct rld_export { image_id* _imageID, char** _imagePath, char** _symbolName, int32* _type, void** _location); status_t (*test_executable)(const char *path, char *interpreter); + status_t (*get_executable_architecture)(const char *path, + const char** _architecture); status_t (*get_next_image_dependency)(image_id id, uint32 *cookie, const char **_name); diff --git a/src/system/runtime_loader/export.cpp b/src/system/runtime_loader/export.cpp index f8c0b06..bae3006 100644 --- a/src/system/runtime_loader/export.cpp +++ b/src/system/runtime_loader/export.cpp @@ -53,6 +53,7 @@ struct rld_export gRuntimeLoader = { get_nth_symbol, get_nearest_symbol_at_address, test_executable, + get_executable_architecture, get_next_image_dependency, elf_reinit_after_fork, diff --git a/src/system/runtime_loader/runtime_loader.cpp b/src/system/runtime_loader/runtime_loader.cpp index 392deac..2b2251b 100644 --- a/src/system/runtime_loader/runtime_loader.cpp +++ b/src/system/runtime_loader/runtime_loader.cpp @@ -9,17 +9,22 @@ #include "runtime_loader_private.h" -#include <syscalls.h> -#include <user_runtime.h> - -#include <directories.h> - #include <string.h> #include <stdlib.h> #include <sys/stat.h> #include <algorithm> +#include <ByteOrder.h> + +#include <directories.h> +#include <image_defs.h> +#include <syscalls.h> +#include <user_runtime.h> +#include <vm_defs.h> + +#include "elf_symbol_lookup.h" + struct user_space_program_args *gProgramArgs; void *__gCommPageAddress; @@ -385,6 +390,231 @@ out: } +static bool +determine_x86_abi(int fd, const Elf32_Ehdr& elfHeader, bool& _isGcc2) +{ + // Unless we're a little-endian CPU, don't bother. We're not x86, so it + // doesn't matter all that much whether we can determine the correct gcc + // ABI. This saves the code below from having to deal with endianess + // conversion. +#if B_HOST_IS_LENDIAN + + // Since we don't want to load the complete image, we can't use the + // functions that normally determine the Haiku version and ABI. Instead + // we'll load the symbol and string tables and resolve the ABI symbol + // manually. + + // map the file into memory + struct stat st; + if (_kern_read_stat(fd, NULL, true, &st, sizeof(st)) != B_OK) + return false; + + void* fileBaseAddress; + area_id area = _kern_map_file("mapped file", &fileBaseAddress, + B_ANY_ADDRESS, st.st_size, B_READ_AREA, REGION_NO_PRIVATE_MAP, false, + fd, 0); + if (area < 0) + return false; + + struct AreaDeleter { + AreaDeleter(area_id area) + : + fArea(area) + { + } + + ~AreaDeleter() + { + _kern_delete_area(fArea); + } + + private: + area_id fArea; + } areaDeleter(area); + + // get the section headers + if (elfHeader.e_shoff == 0 || elfHeader.e_shentsize < sizeof(Elf32_Shdr)) + return false; + + size_t sectionHeadersSize = elfHeader.e_shentsize * elfHeader.e_shnum; + if (elfHeader.e_shoff + (off_t)sectionHeadersSize > st.st_size) + return false; + + void* sectionHeaders = (uint8*)fileBaseAddress + elfHeader.e_shoff; + + // find the sections we need + uint32* symbolHash = NULL; + uint32 symbolHashSize = 0; + uint32 symbolHashChainSize = 0; + Elf32_Sym* symbolTable = NULL; + uint32 symbolTableSize = 0; + const char* stringTable = NULL; + off_t stringTableSize = 0; + + for (int32 i = 0; i < elfHeader.e_shnum; i++) { + Elf32_Shdr* sectionHeader + = (Elf32_Shdr*)((uint8*)sectionHeaders + i * elfHeader.e_shentsize); + if ((off_t)sectionHeader->sh_offset + (off_t)sectionHeader->sh_size + > st.st_size) { + continue; + } + + void* sectionAddress = (uint8*)fileBaseAddress + + sectionHeader->sh_offset; + + switch (sectionHeader->sh_type) { + case SHT_HASH: + symbolHash = (uint32*)sectionAddress; + if (sectionHeader->sh_size < (off_t)sizeof(symbolHash[0])) + return false; + symbolHashSize = symbolHash[0]; + symbolHashChainSize + = sectionHeader->sh_size / sizeof(symbolHash[0]); + if (symbolHashChainSize < symbolHashSize + 2) + return false; + symbolHashChainSize -= symbolHashSize + 2; + break; + case SHT_DYNSYM: + symbolTable = (Elf32_Sym*)sectionAddress; + symbolTableSize = sectionHeader->sh_size; + break; + case SHT_STRTAB: + // .shstrtab has the same type as .dynstr, but it isn't loaded + // into memory. + if (sectionHeader->sh_addr == 0) + continue; + stringTable = (const char*)sectionAddress; + stringTableSize = (off_t)sectionHeader->sh_size; + break; + default: + continue; + } + } + + if (symbolHash == NULL || symbolTable == NULL || stringTable == NULL) + return false; + uint32 symbolCount + = std::min(symbolTableSize / sizeof(Elf32_Sym), symbolHashChainSize); + if (symbolCount < symbolHashSize) + return false; + + // look up the ABI symbol + const char* name = B_SHARED_OBJECT_HAIKU_ABI_VARIABLE_NAME; + size_t nameLength = strlen(name); + uint32 bucket = elf_hash(name) % symbolHashSize; + + for (uint32 i = symbolHash[bucket + 2]; i < symbolCount && i != STN_UNDEF; + i = symbolHash[2 + symbolHashSize + i]) { + Elf32_Sym* symbol = symbolTable + i; + if (symbol->st_shndx != SHN_UNDEF + && ((symbol->Bind() == STB_GLOBAL) || (symbol->Bind() == STB_WEAK)) + && symbol->Type() == STT_OBJECT + && (off_t)symbol->st_name + (off_t)nameLength < stringTableSize + && strcmp(stringTable + symbol->st_name, name) == 0) { + if (symbol->st_value > 0 && symbol->st_size >= sizeof(uint32) + && symbol->st_shndx < elfHeader.e_shnum) { + Elf32_Shdr* sectionHeader = (Elf32_Shdr*)((uint8*)sectionHeaders + + symbol->st_shndx * elfHeader.e_shentsize); + if (symbol->st_value >= sectionHeader->sh_addr + && symbol->st_value + <= sectionHeader->sh_addr + sectionHeader->sh_size) { + off_t fileOffset = symbol->st_value - sectionHeader->sh_addr + + sectionHeader->sh_offset; + if (fileOffset + sizeof(uint32) <= st.st_size) { + uint32 abi + = *(uint32*)((uint8*)fileBaseAddress + fileOffset); + _isGcc2 = (abi & B_HAIKU_ABI_MAJOR) + == B_HAIKU_ABI_GCC_2; + return true; + } + } + } + + return false; + } + } + + // ABI symbol not found. That means the object pre-dates its introduction + // in Haiku. So this is most likely gcc 2. We don't fall back to reading + // the comment sections to verify. + _isGcc2 = true; + return true; +#else // not little endian + return false; +#endif +} + + +static status_t +get_executable_architecture(int fd, const char** _architecture) +{ + // Read the ELF header. We read the 32 bit header. Generally the e_machine + // field is the last one that interests us and the 64 bit header is still + // identical at that point. + Elf32_Ehdr elfHeader; + ssize_t bytesRead = _kern_read(fd, 0, &elfHeader, sizeof(elfHeader)); + if (bytesRead < 0) + return bytesRead; + if ((size_t)bytesRead != sizeof(elfHeader)) + return B_NOT_AN_EXECUTABLE; + + // check whether this is indeed an ELF file + if (memcmp(elfHeader.e_ident, ELF_MAGIC, 4) != 0) + return B_NOT_AN_EXECUTABLE; + + // check the architecture + uint16 machine = elfHeader.e_machine; + if ((elfHeader.e_ident[EI_DATA] == ELFDATA2LSB) != (B_HOST_IS_LENDIAN != 0)) + machine = (machine >> 8) | (machine << 8); + + const char* architecture = NULL; + switch (machine) { + case EM_386: + case EM_486: + { + bool isGcc2; + if (determine_x86_abi(fd, elfHeader, isGcc2) && isGcc2) + architecture = "x86_gcc2"; + else + architecture = "x86"; + break; + } + case EM_68K: + architecture = "m68k"; + break; + case EM_PPC: + architecture = "ppc"; + break; + case EM_ARM: + architecture = "arm"; + break; + case EM_X86_64: + architecture = "x86_64"; + break; + } + + if (architecture == NULL) + return B_NOT_SUPPORTED; + + *_architecture = architecture; + return B_OK; +} + + +status_t +get_executable_architecture(const char* path, const char** _architecture) +{ + int fd = _kern_open(-1, path, O_RDONLY, 0); + if (fd < 0) + return fd; + + status_t error = get_executable_architecture(fd, _architecture); + + _kern_close(fd); + return error; +} + + /*! This is the main entry point of the runtime loader as specified by its ld-script. diff --git a/src/system/runtime_loader/runtime_loader_private.h b/src/system/runtime_loader/runtime_loader_private.h index 4a0faa3..81dabfe 100644 --- a/src/system/runtime_loader/runtime_loader_private.h +++ b/src/system/runtime_loader/runtime_loader_private.h @@ -58,6 +58,8 @@ int runtime_loader(void* arg, void* commpage); int open_executable(char* name, image_type type, const char* rpath, const char* programPath, const char* abiSpecificSubDir); status_t test_executable(const char* path, char* interpreter); +status_t get_executable_architecture(const char* path, + const char** _architecture); void terminate_program(void); image_id load_program(char const* path, void** entry); ############################################################################ Commit: 60b39cd7416028e61e3d30bb3ba28bd3526e6001 URL: http://cgit.haiku-os.org/haiku/commit/?id=60b39cd Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat Nov 16 21:27:47 2013 UTC Add get_*architecture() API, extend find_path*() API * Add get_architecture(), get_primary_architecture(), get_secondary_architectures(), guess_architecture_for_path() to get the caller's architecture, the primary architecture, all secondary architectures, or the architecture associated with a specified path respectively. * Rename the find_path*() functions to find_path*_etc() and add an optional architecture parameter. Add simplified find_path*() functions. * BPathFinder: Add FindPath[s]() versions with an architecture parameter. ---------------------------------------------------------------------------- diff --git a/headers/os/storage/FindDirectory.h b/headers/os/storage/FindDirectory.h index 761a1ee..5b2fba3 100644 --- a/headers/os/storage/FindDirectory.h +++ b/headers/os/storage/FindDirectory.h @@ -170,16 +170,26 @@ extern "C" { status_t find_directory(directory_which which, dev_t volume, bool createIt, char* pathString, int32 length); -status_t find_path(const void* codePointer, const char* dependency, - path_base_directory baseDirectory, const char* subPath, uint32 flags, - char* pathBuffer, size_t bufferSize); +status_t find_path(const void* codePointer, path_base_directory baseDirectory, + const char* subPath, char* pathBuffer, size_t bufferSize); -status_t find_path_for_path(const char* path, const char* dependency, - path_base_directory baseDirectory, const char* subPath, uint32 flags, - char* pathBuffer, size_t bufferSize); +status_t find_path_etc(const void* codePointer, const char* dependency, + const char* architecture, path_base_directory baseDirectory, + const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize); + +status_t find_path_for_path(const char* path, path_base_directory baseDirectory, + const char* subPath, char* pathBuffer, size_t bufferSize); + +status_t find_path_for_path_etc(const char* path, const char* dependency, + const char* architecture, path_base_directory baseDirectory, + const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize); status_t find_paths(path_base_directory baseDirectory, const char* subPath, - uint32 flags, char*** _paths, size_t* _pathCount); + char*** _paths, size_t* _pathCount); + +status_t find_paths_etc(const char* architecture, + path_base_directory baseDirectory, const char* subPath, uint32 flags, + char*** _paths, size_t* _pathCount); #ifdef __cplusplus diff --git a/headers/os/storage/PathFinder.h b/headers/os/storage/PathFinder.h index a1819fb..80f342a 100644 --- a/headers/os/storage/PathFinder.h +++ b/headers/os/storage/PathFinder.h @@ -31,6 +31,10 @@ public: status_t SetTo(const entry_ref& ref, const char* dependency = NULL); + status_t FindPath(const char* architecture, + path_base_directory baseDirectory, + const char* subPath, uint32 flags, + BPath& path); status_t FindPath(path_base_directory baseDirectory, const char* subPath, uint32 flags, BPath& path); @@ -39,6 +43,10 @@ public: status_t FindPath(path_base_directory baseDirectory, BPath& path); + static status_t FindPaths(const char* architecture, + path_base_directory baseDirectory, + const char* subPath, uint32 flags, + BStringList& paths); static status_t FindPaths(path_base_directory baseDirectory, const char* subPath, uint32 flags, BStringList& paths); diff --git a/headers/os/support/Architecture.h b/headers/os/support/Architecture.h new file mode 100644 index 0000000..7781397 --- /dev/null +++ b/headers/os/support/Architecture.h @@ -0,0 +1,27 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _SUPPORT_ARCHITECTURE_H +#define _SUPPORT_ARCHITECTURE_H + + +#include <sys/cdefs.h> + +#include <sys/types.h> + + +__BEGIN_DECLS + + +const char* get_architecture(); +const char* get_primary_architecture(); +size_t get_secondary_architectures(const char** architectures, + size_t count); +const char* guess_architecture_for_path(const char* path); + + +__END_DECLS + + +#endif /* _SUPPORT_ARCHITECTURE_H */ diff --git a/headers/private/system/architecture_private.h b/headers/private/system/architecture_private.h new file mode 100644 index 0000000..e638375 --- /dev/null +++ b/headers/private/system/architecture_private.h @@ -0,0 +1,25 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx. + * Distributed under the terms of the MIT License. + */ +#ifndef _SYSTEM_ARCHITECTURE_PRIVATE_H +#define _SYSTEM_ARCHITECTURE_PRIVATE_H + + +#include <Architecture.h> + + +__BEGIN_DECLS + + +const char* __get_architecture(); +const char* __get_primary_architecture(); +size_t __get_secondary_architectures(const char** architectures, + size_t count); +const char* __guess_architecture_for_path(const char* path); + + +__END_DECLS + + +#endif /* _SYSTEM_ARCHITECTURE_PRIVATE_H */ diff --git a/headers/private/system/find_directory_private.h b/headers/private/system/find_directory_private.h index 3bdd7bf..5db48f3 100644 --- a/headers/private/system/find_directory_private.h +++ b/headers/private/system/find_directory_private.h @@ -17,16 +17,31 @@ __BEGIN_DECLS status_t __find_directory(directory_which which, dev_t device, bool createIt, char *returnedPath, int32 pathLength); -status_t __find_path(const void* codePointer, const char* dependency, - path_base_directory baseDirectory, const char* subPath, uint32 flags, - char* pathBuffer, size_t bufferSize); +status_t __find_path(const void* codePointer, path_base_directory baseDirectory, + const char* subPath, char* pathBuffer, size_t bufferSize); -status_t __find_path_for_path(const char* path, const char* dependency, - path_base_directory baseDirectory, const char* subPath, uint32 flags, - char* pathBuffer, size_t bufferSize); +status_t __find_path_etc(const void* codePointer, const char* dependency, + const char* architecture, path_base_directory baseDirectory, + const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize); + +status_t __find_path_for_path(const char* path, + path_base_directory baseDirectory, const char* subPath, char* pathBuffer, + size_t bufferSize); + +status_t __find_path_for_path_etc(const char* path, const char* dependency, + const char* architecture, path_base_directory baseDirectory, + const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize); status_t __find_paths(path_base_directory baseDirectory, const char* subPath, - uint32 flags, char*** _paths, size_t* _pathCount); + char*** _paths, size_t* _pathCount); + +status_t __find_paths_etc(const char* architecture, + path_base_directory baseDirectory, const char* subPath, uint32 flags, + char*** _paths, size_t* _pathCount); + +const char* __guess_secondary_architecture_from_path(const char* path, + const char* const* secondaryArchitectures, + size_t secondaryArchitectureCount); __END_DECLS diff --git a/src/kits/storage/PathFinder.cpp b/src/kits/storage/PathFinder.cpp index 5794189..f51eb65 100644 --- a/src/kits/storage/PathFinder.cpp +++ b/src/kits/storage/PathFinder.cpp @@ -55,9 +55,11 @@ BPathFinder::SetTo(const entry_ref& ref, const char* dependency) } + status_t -BPathFinder::FindPath(path_base_directory baseDirectory, const char* subPath, - uint32 flags, BPath& path) +BPathFinder::FindPath(const char* architecture, + path_base_directory baseDirectory, const char* subPath, uint32 flags, + BPath& path) { path.Unset(); @@ -71,11 +73,11 @@ BPathFinder::FindPath(path_base_directory baseDirectory, const char* subPath, status_t error; if (!fPath.IsEmpty()) { - error = find_path_for_path(fPath, dependency, baseDirectory, subPath, - flags, pathBuffer, sizeof(pathBuffer)); + error = find_path_for_path_etc(fPath, dependency, architecture, + baseDirectory, subPath, flags, pathBuffer, sizeof(pathBuffer)); } else { - error = find_path(fCodePointer, dependency, baseDirectory, subPath, - flags, pathBuffer, sizeof(pathBuffer)); + error = find_path_etc(fCodePointer, dependency, architecture, + baseDirectory, subPath, flags, pathBuffer, sizeof(pathBuffer)); } if (error != B_OK) @@ -84,33 +86,41 @@ BPathFinder::FindPath(path_base_directory baseDirectory, const char* subPath, return path.SetTo(pathBuffer); } +status_t +BPathFinder::FindPath(path_base_directory baseDirectory, const char* subPath, + uint32 flags, BPath& path) +{ + return FindPath(NULL, baseDirectory, subPath, flags, path); +} + status_t BPathFinder::FindPath(path_base_directory baseDirectory, const char* subPath, BPath& path) { - return FindPath(baseDirectory, subPath, 0, path); + return FindPath(NULL, baseDirectory, subPath, 0, path); } status_t BPathFinder::FindPath(path_base_directory baseDirectory, BPath& path) { - return FindPath(baseDirectory, NULL, 0, path); + return FindPath(NULL, baseDirectory, NULL, 0, path); } /*static*/ status_t -BPathFinder::FindPaths(path_base_directory baseDirectory, const char* subPath, - uint32 flags, BStringList& paths) +BPathFinder::FindPaths(const char* architecture, + path_base_directory baseDirectory, const char* subPath, uint32 flags, + BStringList& paths) { paths.MakeEmpty(); // get the paths char** pathArray; size_t pathCount; - status_t error = find_paths(baseDirectory, subPath, flags, &pathArray, - &pathCount); + status_t error = find_paths_etc(architecture, baseDirectory, subPath, flags, + &pathArray, &pathCount); if (error != B_OK) return error; @@ -129,16 +139,24 @@ BPathFinder::FindPaths(path_base_directory baseDirectory, const char* subPath, /*static*/ status_t BPathFinder::FindPaths(path_base_directory baseDirectory, const char* subPath, + uint32 flags, BStringList& paths) +{ + return FindPaths(NULL, baseDirectory, subPath, 0, paths); +} + + +/*static*/ status_t +BPathFinder::FindPaths(path_base_directory baseDirectory, const char* subPath, BStringList& paths) { - return FindPaths(baseDirectory, subPath, 0, paths); + return FindPaths(NULL, baseDirectory, subPath, 0, paths); } /*static*/ status_t BPathFinder::FindPaths(path_base_directory baseDirectory, BStringList& paths) { - return FindPaths(baseDirectory, NULL, 0, paths); + return FindPaths(NULL, baseDirectory, NULL, 0, paths); } diff --git a/src/system/libroot/os/Architecture.cpp b/src/system/libroot/os/Architecture.cpp new file mode 100644 index 0000000..9b96256 --- /dev/null +++ b/src/system/libroot/os/Architecture.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include <architecture_private.h> + +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> + +#include <OS.h> + +#include <directories.h> +#include <find_directory_private.h> +#include <runtime_loader.h> + + +static const char* const kArchitecture = B_HAIKU_ABI_NAME; +static const char* const kPrimaryArchitecture = __HAIKU_PRIMARY_PACKAGING_ARCH; + +#ifdef __HAIKU_ARCH_X86 + static const char* const kSiblingArchitectures[] = {"x86_gcc2", "x86"}; +#else + static const char* const kSiblingArchitectures[] = {}; +#endif + +static const size_t kSiblingArchitectureCount + = sizeof(kSiblingArchitectures) / sizeof(const char*); + + +static bool +has_secondary_architecture(const char* architecture) +{ + if (strcmp(architecture, kPrimaryArchitecture) == 0) + return false; + + char path[B_PATH_NAME_LENGTH]; + snprintf(path, sizeof(path), kSystemLibDirectory "/%s/libroot.so", + architecture); + + struct stat st; + return lstat(path, &st) == 0; +} + + +// #pragma mark - + + +const char* +__get_architecture() +{ + return kArchitecture; +} + + +const char* +__get_primary_architecture() +{ + return kPrimaryArchitecture; +} + + +size_t +__get_secondary_architectures(const char** architectures, size_t count) +{ + size_t index = 0; + + // If this is an architecture that could be a primary or secondary + // architecture, check for which architectures a libroot.so is present. + if (kSiblingArchitectureCount > 0) { + for (size_t i = 0; i < kSiblingArchitectureCount; i++) { + const char* architecture = kSiblingArchitectures[i]; + if (!has_secondary_architecture(architecture)) + continue; + + if (index < count) + architectures[index] = architecture; + index++; + } + } + + return index; +} + + +const char* +__guess_architecture_for_path(const char* path) +{ + if (kSiblingArchitectureCount == 0) + return kPrimaryArchitecture; + + // ask the runtime loader + const char* architecture; + if (__gRuntimeLoader->get_executable_architecture(path, &architecture) + == B_OK) { + // verify that it is one of the sibling architectures + for (size_t i = 0; i < kSiblingArchitectureCount; i++) { + if (strcmp(architecture, kSiblingArchitectures[i]) == 0) + return kSiblingArchitectures[i]; + } + } + + // guess from the given path + architecture = __guess_secondary_architecture_from_path(path, + kSiblingArchitectures, kSiblingArchitectureCount); + + return architecture != NULL && has_secondary_architecture(architecture) + ? architecture : kPrimaryArchitecture; +} + + +B_DEFINE_WEAK_ALIAS(__get_architecture, get_architecture); +B_DEFINE_WEAK_ALIAS(__get_primary_architecture, get_primary_architecture); +B_DEFINE_WEAK_ALIAS(__get_secondary_architectures, get_secondary_architectures); +B_DEFINE_WEAK_ALIAS(__guess_architecture_for_path, guess_architecture_for_path); diff --git a/src/system/libroot/os/Jamfile b/src/system/libroot/os/Jamfile index 9e5809a..1b2b30c 100644 --- a/src/system/libroot/os/Jamfile +++ b/src/system/libroot/os/Jamfile @@ -14,6 +14,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { SEARCH_SOURCE += [ FDirName $(SUBDIR) locks ] ; MergeObject <$(architecture)>os_main.o : + Architecture.cpp area.c atomic.c debug.c diff --git a/src/system/libroot/os/find_paths.cpp b/src/system/libroot/os/find_paths.cpp index 384f4cd..f3360a2 100644 --- a/src/system/libroot/os/find_paths.cpp +++ b/src/system/libroot/os/find_paths.cpp @@ -11,8 +11,11 @@ #include <string.h> #include <sys/stat.h> +#include <algorithm> + #include <fs_attr.h> +#include <architecture_private.h> #include <AutoDeleter.h> #include <syscalls.h> @@ -28,7 +31,70 @@ static size_t kHomeInstallationLocationIndex = 1; static size_t kInstallationLocationCount = sizeof(kInstallationLocations) / sizeof(kInstallationLocations[0]); +static const path_base_directory kArchitectureSpecificBaseDirectories[] = { + B_FIND_PATH_ADD_ONS_DIRECTORY, + B_FIND_PATH_BIN_DIRECTORY, + B_FIND_PATH_DEVELOP_LIB_DIRECTORY, + B_FIND_PATH_HEADERS_DIRECTORY, +}; + +static size_t kArchitectureSpecificBaseDirectoryCount = + sizeof(kArchitectureSpecificBaseDirectories) + / sizeof(kArchitectureSpecificBaseDirectories[0]); + + +namespace { + +struct PathBuffer { + PathBuffer(char* buffer, size_t size) + : + fBuffer(buffer), + fSize(size), + fLength(0) + { + if (fSize > 0) + fBuffer[0] = '\0'; + } + + bool Append(const char* toAppend, size_t length) + { + if (fLength < fSize) { + size_t toCopy = std::min(length, fSize - fLength); + if (toCopy > 0) { + memcpy(fBuffer + fLength, toAppend, toCopy); + fBuffer[fLength + toCopy] = '\0'; + } + } + + fLength += length; + return fLength < fSize; + } + + bool Append(const char* toAppend) + { + return Append(toAppend, strlen(toAppend)); + } + + size_t Length() const + { + return fLength; + } + +private: + char* fBuffer; + size_t fSize; + size_t fLength; +}; + +} + + +/*! Returns the installation location relative path for the given base directory + constant and installation location index. A '%' in the returned path must be + replaced by "" for the primary architecture and by "/<arch>" for a secondary + architecture. + */ static const char* get_relative_directory_path(size_t installationLocationIndex, path_base_directory baseDirectory) @@ -37,11 +103,11 @@ get_relative_directory_path(size_t installationLocationIndex, case B_FIND_PATH_INSTALLATION_LOCATION_DIRECTORY: return ""; case B_FIND_PATH_ADD_ONS_DIRECTORY: - return "/add-ons"; + return "/add-ons%"; case B_FIND_PATH_APPS_DIRECTORY: return "/apps"; case B_FIND_PATH_BIN_DIRECTORY: - return "/bin"; + return "/bin%"; case B_FIND_PATH_BOOT_DIRECTORY: return "/boot"; case B_FIND_PATH_CACHE_DIRECTORY: @@ -51,7 +117,7 @@ get_relative_directory_path(size_t installationLocationIndex, case B_FIND_PATH_DEVELOP_DIRECTORY: return "/develop"; case B_FIND_PATH_DEVELOP_LIB_DIRECTORY: - return "/develop/lib"; + return "/develop/lib%"; case B_FIND_PATH_DOCUMENTATION_DIRECTORY: return "/documentation"; case B_FIND_PATH_ETC_DIRECTORY: @@ -59,7 +125,7 @@ get_relative_directory_path(size_t installationLocationIndex, case B_FIND_PATH_FONTS_DIRECTORY: return "/data/fonts"; case B_FIND_PATH_HEADERS_DIRECTORY: - return "/develop/headers"; + return "/develop/headers%"; case B_FIND_PATH_LIB_DIRECTORY: return "/lib"; case B_FIND_PATH_LOG_DIRECTORY: @@ -80,7 +146,7 @@ get_relative_directory_path(size_t installationLocationIndex, case B_FIND_PATH_SPOOL_DIRECTORY: return "/var/spool"; case B_FIND_PATH_TRANSLATORS_DIRECTORY: - return "/add-ons/Translators"; + return "/add-ons%/Translators"; case B_FIND_PATH_VAR_DIRECTORY: return "/var"; @@ -183,6 +249,73 @@ normalize_path(const char* path, char* buffer, size_t bufferSize) } +static status_t +normalize_longest_existing_path_prefix(const char* path, char* buffer, + size_t bufferSize) +{ + if (strlcpy(buffer, path, bufferSize) >= bufferSize) + return B_NAME_TOO_LONG; + + // Until we have an existing path, chop off leaf components. + for (;;) { + struct stat st; + if (lstat(buffer, &st) == 0) + break; + + // Chop off the leaf, but fail, it it's "..", since then we'd actually + // construct a subpath. + char* lastSlash = strrchr(buffer, '/'); + if (lastSlash == NULL || strcmp(lastSlash + 1, "..") == 0) + return B_ENTRY_NOT_FOUND; + + *lastSlash = '\0'; + } + + // normalize the existing prefix path + size_t prefixLength = strlen(buffer); + status_t error = normalize_path(buffer, buffer, bufferSize); + if (error != B_OK) + return error; + + // Re-append the non-existent suffix. Remove duplicate slashes and "." + // components. + const char* bufferEnd = buffer + bufferSize; + char* end = buffer + strlen(buffer); + const char* remainder = path + prefixLength + 1; + while (*remainder != '\0') { + // find component start + if (*remainder == '/') { + remainder++; + continue; + } + + // find component end + const char* componentEnd = strchr(remainder, '/'); + if (componentEnd == NULL) + componentEnd = remainder + strlen(remainder); + + // skip "." components + size_t componentLength = componentEnd - remainder; + if (componentLength == 1 && *remainder == '.') { + remainder++; + continue; + } + + // append the component + if (end + 1 + componentLength >= bufferEnd) + return B_BUFFER_OVERFLOW; + + *end++ = '/'; + memcpy(end, remainder, componentLength); + end += componentLength; + remainder += componentLength; + } + + *end = '\0'; + return B_OK; +} + + static const char* get_installation_location(const char* path, size_t& _index) { @@ -238,21 +371,38 @@ normalize_dependency(const char* dependency, char* buffer, size_t bufferSize) static ssize_t -process_path(const char* installationLocation, const char* relativePath, - const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize) +process_path(const char* installationLocation, const char* architecture, + const char* relativePath, const char* subPath, uint32 flags, + char* pathBuffer, size_t bufferSize) { - size_t totalLength; + // copy the installation location + PathBuffer buffer(pathBuffer, bufferSize); + buffer.Append(installationLocation); + + // append the relative path, expanding the architecture placeholder + if (const char* placeholder = strchr(relativePath, '%')) { + buffer.Append(relativePath, placeholder - relativePath); + + if (architecture != NULL) { + buffer.Append("/", 1); + buffer.Append(architecture); + } + + buffer.Append(placeholder + 1); + } else + buffer.Append(relativePath); + + // append subpath, if given if (subPath != NULL) { - totalLength = snprintf(pathBuffer, bufferSize, "%s%s/%s", - installationLocation, relativePath, subPath); - } else { - totalLength = snprintf(pathBuffer, bufferSize, "%s%s", - installationLocation, relativePath); + buffer.Append("/", 1); + buffer.Append(subPath); } + size_t totalLength = buffer.Length(); if (totalLength >= bufferSize) return B_BUFFER_OVERFLOW; + // handle the flags char* path = pathBuffer; status_t error = B_OK; @@ -283,9 +433,13 @@ process_path(const char* installationLocation, const char* relativePath, status_t internal_path_for_path(char* referencePath, size_t referencePathSize, - const char* dependency, path_base_directory baseDirectory, - const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize) + const char* dependency, const char* architecture, + path_base_directory baseDirectory, const char* subPath, uint32 flags, + char* pathBuffer, size_t bufferSize) { + if (strcmp(architecture, __get_primary_architecture()) == 0) + architecture = NULL; + // normalize status_t error = normalize_path(referencePath, referencePath, referencePathSize); @@ -359,8 +513,8 @@ internal_path_for_path(char* referencePath, size_t referencePathSize, if (relativePath == NULL) return B_BAD_VALUE; - ssize_t pathSize = process_path(installationLocation, relativePath, subPath, - flags, pathBuffer, bufferSize); + ssize_t pathSize = process_path(installationLocation, architecture, + relativePath, subPath, flags, pathBuffer, bufferSize); if (pathSize <= 0) return pathSize == 0 ? B_ENTRY_NOT_FOUND : pathSize; return B_OK; @@ -371,9 +525,18 @@ internal_path_for_path(char* referencePath, size_t referencePathSize, status_t -__find_path(const void* codePointer, const char* dependency, - path_base_directory baseDirectory, const char* subPath, uint32 flags, - char* pathBuffer, size_t bufferSize) +__find_path(const void* codePointer, path_base_directory baseDirectory, + const char* subPath, char* pathBuffer, size_t bufferSize) +{ + return __find_path_etc(codePointer, NULL, NULL, baseDirectory, subPath, 0, + pathBuffer, bufferSize); +} + + +status_t +__find_path_etc(const void* codePointer, const char* dependency, + const char* architecture, path_base_directory baseDirectory, + const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize) { if (pathBuffer == NULL) return B_BAD_VALUE; @@ -384,15 +547,28 @@ __find_path(const void* codePointer, const char* dependency, if (error != B_OK) return error; + if (architecture == NULL) + architecture = __get_architecture(); + return internal_path_for_path(imageInfo.name, sizeof(imageInfo.name), - dependency, baseDirectory, subPath, flags, pathBuffer, bufferSize); + dependency, architecture, baseDirectory, subPath, flags, pathBuffer, + bufferSize); } status_t -__find_path_for_path(const char* path, const char* dependency, - path_base_directory baseDirectory, const char* subPath, uint32 flags, - char* pathBuffer, size_t bufferSize) +__find_path_for_path(const char* path, path_base_directory baseDirectory, + const char* subPath, char* pathBuffer, size_t bufferSize) +{ + return __find_path_for_path_etc(path, NULL, NULL, baseDirectory, subPath, 0, + pathBuffer, bufferSize); +} + + +status_t +__find_path_for_path_etc(const char* path, const char* dependency, + const char* architecture, path_base_directory baseDirectory, + const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize) { char referencePath[B_PATH_NAME_LENGTH]; if (strlcpy(referencePath, path, sizeof(referencePath)) @@ -400,18 +576,42 @@ __find_path_for_path(const char* path, const char* dependency, return B_NAME_TOO_LONG; } + if (architecture == NULL) + architecture = __guess_architecture_for_path(path); + return internal_path_for_path(referencePath, sizeof(referencePath), - dependency, baseDirectory, subPath, flags, pathBuffer, bufferSize); + dependency, architecture, baseDirectory, subPath, flags, pathBuffer, + bufferSize); } status_t __find_paths(path_base_directory baseDirectory, const char* subPath, - uint32 flags, char*** _paths, size_t* _pathCount) + char*** _paths, size_t* _pathCount) +{ + return __find_paths_etc(NULL, baseDirectory, subPath, 0, _paths, + _pathCount); +} + + +status_t +__find_paths_etc(const char* architecture, path_base_directory baseDirectory, + const char* subPath, uint32 flags, char*** _paths, size_t* _pathCount) { if (_paths == NULL || _pathCount == NULL) return B_BAD_VALUE; + // Analyze architecture. If NULL, use the caller's architecture. If the + // effective architecture is the primary one, set architecture to NULL to + // indicate that we don't need to insert an architecture subdirectory + // component. + if (architecture == NULL) + architecture = __get_architecture(); + if (strcmp(architecture, __get_primary_architecture()) == 0) + architecture = NULL; + size_t architectureSize = architecture != NULL + ? strlen(architecture) + 1 : 0; + size_t subPathLength = subPath != NULL ? strlen(subPath) + 1 : 0; // Get the relative paths and compute the total size to allocate. @@ -425,6 +625,8 @@ __find_paths(path_base_directory baseDirectory, const char* subPath, totalSize += strlen(kInstallationLocations[i]) + strlen(relativePaths[i]) + subPathLength + 1; + if (strchr(relativePaths[i], '%') != NULL) + totalSize += architectureSize - 1; } // allocate storage @@ -440,7 +642,7 @@ __find_paths(path_base_directory baseDirectory, const char* subPath, const char* pathBufferEnd = pathBuffer + totalSize; for (size_t i = 0; i < kInstallationLocationCount; i++) { ssize_t pathSize = process_path(kInstallationLocations[i], - relativePaths[i], subPath, flags, pathBuffer, + architecture, relativePaths[i], subPath, flags, pathBuffer, pathBufferEnd - pathBuffer); if (pathSize < 0) return pathSize; @@ -461,6 +663,65 @@ __find_paths(path_base_directory baseDirectory, const char* subPath, } +const char* +__guess_secondary_architecture_from_path(const char* path, + const char* const* secondaryArchitectures, + size_t secondaryArchitectureCount) +{ + // Get the longest existing prefix path and normalize it. + char prefix[B_PATH_NAME_LENGTH]; + if (normalize_longest_existing_path_prefix(path, prefix, sizeof(prefix)) + != B_OK) { + return NULL; + } + + // get an installation location relative path + size_t installationLocationIndex; + const char* installationLocation = get_installation_location(prefix, + installationLocationIndex); + if (installationLocation == NULL) + return NULL; + + const char* relativePath = prefix + strlen(installationLocation); + if (relativePath[0] != '/') + return NULL; + + // Iterate through the known paths that would indicate a secondary + // architecture and try to match them with our given path. + for (size_t i = 0; i < kArchitectureSpecificBaseDirectoryCount; i++) { + const char* basePath = get_relative_directory_path( + installationLocationIndex, kArchitectureSpecificBaseDirectories[i]); + const char* placeholder = strchr(basePath, '%'); + if (placeholder == NULL) + continue; + + // match the part up to the architecture placeholder + size_t prefixLength = placeholder - basePath; + if (strncmp(relativePath, basePath, prefixLength) != 0 + || relativePath[prefixLength] != '/') { + continue; + } + + // match the architecture + const char* architecturePart = relativePath + prefixLength + 1; + for (size_t k = 0; k < secondaryArchitectureCount; k++) { + const char* architecture = secondaryArchitectures[k]; + size_t architectureLength = strlen(architecture); + if (strncmp(architecturePart, architecture, architectureLength) == 0 + && (architecturePart[architectureLength] == '/' + || architecturePart[architectureLength] == '\0')) { + return architecture; + } + } + } + + return NULL; +} + + B_DEFINE_WEAK_ALIAS(__find_path, find_path); +B_DEFINE_WEAK_ALIAS(__find_path_etc, find_path_etc); B_DEFINE_WEAK_ALIAS(__find_path_for_path, find_path_for_path); +B_DEFINE_WEAK_ALIAS(__find_path_for_path_etc, find_path_for_path_etc); B_DEFINE_WEAK_ALIAS(__find_paths, find_paths); +B_DEFINE_WEAK_ALIAS(__find_paths_etc, find_paths_etc); ############################################################################ Commit: 2c2f3ab4e1b8dd6b043cb571384c25746b53e4a4 URL: http://cgit.haiku-os.org/haiku/commit/?id=2c2f3ab Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat Nov 16 21:28:36 2013 UTC findpaths: Add an -a <architecture> option ---------------------------------------------------------------------------- diff --git a/src/bin/findpaths.cpp b/src/bin/findpaths.cpp index 5b99632..607f9f4 100644 --- a/src/bin/findpaths.cpp +++ b/src/bin/findpaths.cpp @@ -92,6 +92,12 @@ static const char* kUsage = "modify this behavior.\n" "\n" "Options:\n" + " -a <architecture>\n" + " If the path(s) specified by <kind> are architecture specific, use\n" + " architecture <architecture>. If not specified, the primary\n" + " architecture is used, unless the -p/--path option is specified, in\n" + " which case the architecture associated with the given <path> is\n" + " used.\n" " -c <separator>\n" " Concatenate the resulting paths, separated only by <separator>,\n" " instead of printing a path per line.\n" @@ -123,6 +129,7 @@ print_usage_and_exit(bool error) int main(int argc, const char* const* argv) { + const char* architecture = NULL; const char* dependency = NULL; const char* referencePath = NULL; bool existingOnly = false; @@ -130,6 +137,7 @@ main(int argc, const char* const* argv) while (true) { static struct option sLongOptions[] = { + { "architecture", required_argument, 0, 'a' }, { "dependency", required_argument, 0, 'd' }, { "help", no_argument, 0, 'h' }, { "path", required_argument, 0, 'p' }, @@ -137,12 +145,16 @@ main(int argc, const char* const* argv) }; opterr = 0; // don't print errors - int c = getopt_long(argc, (char**)argv, "+c:d:ehlp:", + int c = getopt_long(argc, (char**)argv, "+a:c:d:ehlp:", sLongOptions, NULL); if (c == -1) break; switch (c) { + case 'a': + architecture = optarg; + break; + case 'c': separator = optarg; break; @@ -208,7 +220,7 @@ main(int argc, const char* const* argv) if (referencePath != NULL) { BPath path; status_t error = BPathFinder(referencePath, dependency).FindPath( - baseDirectory, subPath, + architecture, baseDirectory, subPath, existingOnly ? B_FIND_PATH_EXISTING_ONLY : 0, path); if (error != B_OK) { fprintf(stderr, "Error: Failed to find path: %s\n", @@ -219,8 +231,8 @@ main(int argc, const char* const* argv) printf("%s\n", path.Path()); } else { BStringList paths; - status_t error = BPathFinder::FindPaths(baseDirectory, subPath, - existingOnly ? B_FIND_PATH_EXISTING_ONLY : 0, paths); + status_t error = BPathFinder::FindPaths(architecture, baseDirectory, + subPath, existingOnly ? B_FIND_PATH_EXISTING_ONLY : 0, paths); if (error != B_OK) { fprintf(stderr, "Error: Failed to find paths: %s\n", strerror(error)); ############################################################################ Commit: c39c9283aa34b0b694e61c9edbd0a8fb1d490d93 URL: http://cgit.haiku-os.org/haiku/commit/?id=c39c928 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sun Nov 17 01:04:31 2013 UTC Add get_architectures() and C++ versions * get_architectures() returns the primary and the secondary architectures in one array. That turned out to be convenient. * Add C++ versions for get[_secondary]_architectures(), returning a BStringList. ---------------------------------------------------------------------------- diff --git a/headers/os/support/Architecture.h b/headers/os/support/Architecture.h index 7781397..5ed8bc7 100644 --- a/headers/os/support/Architecture.h +++ b/headers/os/support/Architecture.h @@ -18,10 +18,25 @@ const char* get_architecture(); const char* get_primary_architecture(); size_t get_secondary_architectures(const char** architectures, size_t count); +size_t get_architectures(const char** architectures, size_t count); const char* guess_architecture_for_path(const char* path); __END_DECLS +/* C++ API */ +#ifdef __cplusplus + + +#include <StringList.h> + + +status_t get_secondary_architectures(BStringList& _architectures); +status_t get_architectures(BStringList& _architectures); + + +#endif + + #endif /* _SUPPORT_ARCHITECTURE_H */ diff --git a/headers/private/system/architecture_private.h b/headers/private/system/architecture_private.h index e638375..1000537 100644 --- a/headers/private/system/architecture_private.h +++ b/headers/private/system/architecture_private.h @@ -16,6 +16,7 @@ const char* __get_architecture(); const char* __get_primary_architecture(); size_t __get_secondary_architectures(const char** architectures, size_t count); +size_t __get_architectures(const char** architectures, size_t count); const char* __guess_architecture_for_path(const char* path); diff --git a/src/kits/support/Architecture.cpp b/src/kits/support/Architecture.cpp new file mode 100644 index 0000000..93afc16 --- /dev/null +++ b/src/kits/support/Architecture.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include <Architecture.h> + +#include <algorithm> + +#include <StringList.h> + + +static const size_t kMaxArchitectureCount = 16; + + +static status_t +string_array_to_string_list(const char* const* architectures, size_t count, + BStringList& _architectures) +{ + _architectures.MakeEmpty(); + + for (size_t i = 0; i < count; i++) { + BString architecture(architectures[i]); + if (architecture.IsEmpty() || !_architectures.Add(architecture)) { + _architectures.MakeEmpty(); + return B_NO_MEMORY; + } + } + + return B_OK; +} + + +status_t +get_secondary_architectures(BStringList& _architectures) +{ + const char* architectures[kMaxArchitectureCount]; + size_t count = get_secondary_architectures(architectures, + kMaxArchitectureCount); + return string_array_to_string_list(architectures, + std::min(count, kMaxArchitectureCount), _architectures); +} + + +status_t +get_architectures(BStringList& _architectures) +{ + const char* architectures[kMaxArchitectureCount]; + size_t count = get_architectures(architectures, kMaxArchitectureCount); + return string_array_to_string_list(architectures, + std::min(count, kMaxArchitectureCount), _architectures); +} diff --git a/src/kits/support/Jamfile b/src/kits/support/Jamfile index a6e8249..e2ac639 100644 --- a/src/kits/support/Jamfile +++ b/src/kits/support/Jamfile @@ -10,6 +10,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { local architecture = $(TARGET_PACKAGING_ARCH) ; MergeObject <libbe!$(architecture)>support_kit.o : + Architecture.cpp Archivable.cpp ArchivingManagers.cpp Beep.cpp diff --git a/src/system/libroot/os/Architecture.cpp b/src/system/libroot/os/Architecture.cpp index 9b96256..d0a849c 100644 --- a/src/system/libroot/os/Architecture.cpp +++ b/src/system/libroot/os/Architecture.cpp @@ -85,6 +85,17 @@ __get_secondary_architectures(const char** architectures, size_t count) } +size_t +__get_architectures(const char** architectures, size_t count) +{ + if (count == 0) + return __get_secondary_architectures(NULL, 0) + 1; + + architectures[0] = __get_primary_architecture(); + return __get_secondary_architectures(architectures + 1, count -1) + 1; +} + + const char* __guess_architecture_for_path(const char* path) { @@ -114,4 +125,5 @@ __guess_architecture_for_path(const char* path) B_DEFINE_WEAK_ALIAS(__get_architecture, get_architecture); B_DEFINE_WEAK_ALIAS(__get_primary_architecture, get_primary_architecture); B_DEFINE_WEAK_ALIAS(__get_secondary_architectures, get_secondary_architectures); +B_DEFINE_WEAK_ALIAS(__get_architectures, get_architectures); B_DEFINE_WEAK_ALIAS(__guess_architecture_for_path, guess_architecture_for_path); ############################################################################ Revision: hrev46378 Commit: 4f5e93857671d231372096457d024926635dcee6 URL: http://cgit.haiku-os.org/haiku/commit/?id=4f5e938 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sun Nov 17 01:05:23 2013 UTC Add setarch and getarch commands ---------------------------------------------------------------------------- diff --git a/build/jam/images/HaikuImage b/build/jam/images/HaikuImage index 74bfa6d..9ecd191 100644 --- a/build/jam/images/HaikuImage +++ b/build/jam/images/HaikuImage @@ -14,7 +14,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures echo eject env error expand expr factor false fdinfo ffm filepanel find finddir findpaths FirstBootPrompt fmt fold fortune frcode fstrim ftp ftpd funzip fwcontrol@x86 - gawk gdb@x86 getlimits groupadd groupdel groupmod groups gzip gzexe + gawk gdb@x86 getarch getlimits groupadd groupdel groupmod groups gzip gzexe hd head hey hostname id ident ifconfig <bin>install installsound iroster isvolume ideinfo@ide idestatus@ide @@ -31,9 +31,9 @@ SYSTEM_BIN = [ FFilterByBuildFeatures query quit rc readlink reindex release renice rlog rm rmattr rmindex rmdir roster route - safemode screen_blanker screenmode screenshot sdiff setdecor setmime settype - setversion setvolume seq sha1sum sha256sum shar shred shuf shutdown sleep - sort spamdbm split stat strace stty su sum sync sysinfo + safemode screen_blanker screenmode screenshot sdiff setarch setdecor setmime + settype setversion setvolume seq sha1sum sha256sum shar shred shuf shutdown + sleep sort spamdbm split stat strace stty su sum sync sysinfo tac tail tcpdump tcptester tee telnet telnetd test timeout top touch tr traceroute translate trash true truncate tsort tty uname unchop unexpand unmount uniq unlink unshar unzip unzipsfx diff --git a/build/jam/images/HaikuImageBootstrap b/build/jam/images/HaikuImageBootstrap index 9ea5d4d..8bb57e9 100644 --- a/build/jam/images/HaikuImageBootstrap +++ b/build/jam/images/HaikuImageBootstrap @@ -14,7 +14,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures echo eject env error expand expr factor false fdinfo ffm filepanel find finddir findpaths fmt fold fortune frcode ftp ftpd funzip - gawk gdb@x86 getlimits groupadd groupdel groupmod groups gzip gzexe + gawk gdb@x86 getarch getlimits groupadd groupdel groupmod groups gzip gzexe hd head hey hostname id ident ifconfig <bin>install isvolume ideinfo@ide idestatus@ide @@ -31,7 +31,7 @@ SYSTEM_BIN = [ FFilterByBuildFeatures query quit rc readlink reindex release renice rlog rm rmattr rmindex rmdir roster route - safemode screen_blanker screenmode sdiff setmime settype + safemode screen_blanker screenmode sdiff setarch setmime settype setversion setvolume seq sha1sum sha256sum shar shred shuf shutdown sleep sort split stat strace stty su sum sync sysinfo tac tail tcpdump tcptester tee telnet telnetd test timeout top touch diff --git a/src/bin/Jamfile b/src/bin/Jamfile index e2fb698..eb7c6b2 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -116,9 +116,11 @@ StdBinCommands alert.cpp eject.cpp findpaths.cpp + getarch.cpp hey.cpp reindex.cpp resattr.cpp + setarch.cpp setdecor.cpp settype.cpp spybmessage.cpp diff --git a/src/bin/getarch.cpp b/src/bin/getarch.cpp new file mode 100644 index 0000000..47a7fe6 --- /dev/null +++ b/src/bin/getarch.cpp @@ -0,0 +1,169 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <Architecture.h> +#include <Path.h> +#include <PathFinder.h> +#include <StringList.h> + + +extern const char* __progname; +const char* kCommandName = __progname; + + +static const char* kUsage = + "Usage: %s [ <options> ] [ <path> ]\n" + "Prints the architecture currently set via the PATH environment variable,\n" + "when no arguments are given. When <path> is specified, the architecture\n" + "associated with that path is printed. The options allow to print the\n" + "primary architecture or the secondary architectures.\n" + "\n" + "Options:\n" + " -h, --help\n" + " Print this usage info.\n" + " -p, --primary\n" + " Print the primary architecture.\n" + " -s, --secondary\n" + " Print all secondary architectures for which support is installed.\n" +; + + +static void +print_usage_and_exit(bool error) +{ + fprintf(error ? stderr : stdout, kUsage, kCommandName); + exit(error ? 1 : 0); +} + + +static BString +get_current_architecture() +{ + // get the system installation location path + BPath systemPath; + if (find_directory(B_SYSTEM_DIRECTORY, &systemPath) != B_OK) + return BString(); + + // get all architectures + BStringList architectures; + get_architectures(architectures); + if (architectures.CountStrings() < 2) + return BString(); + + // get the system bin directory for each architecture + BStringList binDirectories; + BPathFinder pathFinder(systemPath.Path()); + int32 architectureCount = architectures.CountStrings(); + for (int32 i = 0; i < architectureCount; i++) { + BPath path; + if (pathFinder.FindPath(architectures.StringAt(i), + B_FIND_PATH_BIN_DIRECTORY, NULL, 0, path) != B_OK + || !binDirectories.Add(path.Path())) { + return BString(); + } + } + + // Get and split the PATH environmental variable value. The first system + // bin path we encounter implies the architecture. + char* pathVariableValue = getenv("PATH"); + BStringList paths; + if (pathVariableValue != NULL + && BString(pathVariableValue).Split(":", true, paths)) { + int32 count = paths.CountStrings(); + for (int32 i = 0; i < count; i++) { + // normalize the path, but skip a relative one + BPath path; + if (paths.StringAt(i)[0] != '/' + || path.SetTo(paths.StringAt(i), NULL, true) != B_OK) { + continue; + } + + int32 index = binDirectories.IndexOf(path.Path()); + if (index >= 0) + return architectures.StringAt(index); + } + } + + return BString(); +} + + +int +main(int argc, const char* const* argv) +{ + bool printPrimary = false; + bool printSecondary = false; + + while (true) { + static struct option sLongOptions[] = { + { "help", no_argument, 0, 'h' }, + { "primary", no_argument, 0, 'p' }, + { "secondary", no_argument, 0, 's' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "+hps", + sLongOptions, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage_and_exit(false); + break; + + case 'p': + printPrimary = true; + break; + + case 's': + printSecondary = true; + break; + + default: + print_usage_and_exit(true); + break; + } + } + + // The remaining argument is the optional path. + const char* path = optind < argc ? argv[optind++] : NULL; + if (optind < argc) + print_usage_and_exit(true); + + // only one of path, printPrimary, printSecondary may be specified + if (int(path != NULL) + int(printPrimary) + int(printSecondary) > 1) + print_usage_and_exit(true); + + if (path != NULL) { + // architecture for given path + printf("%s\n", guess_architecture_for_path(path)); + } else if (printPrimary) { + // primary architecture + printf("%s\n", get_primary_architecture()); + } else if (printSecondary) { + // secondary architectures + BStringList architectures; + get_secondary_architectures(architectures); + int32 count = architectures.CountStrings(); + for (int32 i = 0; i < count; i++) + printf("%s\n", architectures.StringAt(i).String()); + } else { + // current architecture as implied by PATH + BString architecture = get_current_architecture(); + printf("%s\n", + architecture.IsEmpty() + ? get_primary_architecture() : architecture.String()); + } + + return 0; +} diff --git a/src/bin/setarch.cpp b/src/bin/setarch.cpp new file mode 100644 index 0000000..b6fc26c --- /dev/null +++ b/src/bin/setarch.cpp @@ -0,0 +1,230 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@xxxxxx. + * Distributed under the terms of the MIT License. + */ + + +#include <errno.h> +#include <getopt.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <Architecture.h> +#include <Path.h> +#include <PathFinder.h> +#include <StringList.h> + + +extern const char* __progname; +const char* kCommandName = __progname; + + +static const char* kUsage = + "Usage: %s [ <options> ] <architecture> [ <command> ... ]\n" + "Executes the given command or, by default, a shell with a PATH\n" + "environment variable modified such that commands for the given\n" + "architecture will be preferred, respectively used exclusively in case of\n" + "the primary architecture.\n" + "\n" + "Options:\n" + " -h, --help\n" + " Print this usage info.\n" + " -p, --print-path\n" + " Only print the modified PATH variable value; don't execute any\n" + " command.\n" +; + + +static void +print_usage_and_exit(bool error) +{ + fprintf(error ? stderr : stdout, kUsage, kCommandName); + exit(error ? 1 : 0); +} + + +static bool +is_primary_architecture(const char* architecture) +{ + return strcmp(architecture, get_primary_architecture()) == 0; +} + + +static void +get_bin_directories(const char* architecture, BStringList& _directories) +{ + status_t error = BPathFinder::FindPaths(architecture, + B_FIND_PATH_BIN_DIRECTORY, NULL, 0, _directories); + if (error != B_OK) { + fprintf(stderr, "Error: Failed to get bin directories for architecture " + "%s: %s\n", architecture, strerror(error)); + exit(1); + } +} + + +static void +compute_new_paths(const char* architecture, BStringList& _paths) +{ + // get the primary architecture bin paths + BStringList primaryBinDirectories; + get_bin_directories(get_primary_architecture(), primaryBinDirectories); + + // get the bin paths to insert + BStringList binDirectoriesToInsert; + if (!is_primary_architecture(architecture)) + get_bin_directories(architecture, binDirectoriesToInsert); + + // split the PATH variable + char* pathVariableValue = getenv("PATH"); + BStringList paths; + if (pathVariableValue != NULL + && !BString(pathVariableValue).Split(":", true, paths)) { + fprintf(stderr, "Error: Out of memory!\n"); + exit(1); + } + + // Filter the paths, removing any path that isn't associated with the + // primary architecture. Also find the insertion index for the architecture + // bin paths. + int32 insertionIndex = -1; + int32 count = paths.CountStrings(); + for (int32 i = 0; i < count; i++) { + // We always keep relative paths. Filter absolute ones only. + const char* path = paths.StringAt(i); + if (path[0] == '/') { + // try to normalize the path + BPath normalizedPath; + if (normalizedPath.SetTo(path, NULL, true) == B_OK) + path = normalizedPath.Path(); + + // Check, if this is a primary bin directory. If not, determine the + // path's architecture. + int32 index = primaryBinDirectories.IndexOf(path); + if (index >= 0) { + if (insertionIndex < 0) + insertionIndex = i; + } else if (!is_primary_architecture( + guess_architecture_for_path(path))) { + // a non-primary architecture path -- skip + continue; + } + } + + if (!_paths.Add(paths.StringAt(i))) { + fprintf(stderr, "Error: Out of memory!\n"); + exit(1); + } + } + + // Insert the paths for the specified architecture, if any. + if (!binDirectoriesToInsert.IsEmpty()) { + if (!(insertionIndex < 0 + ? _paths.Add(binDirectoriesToInsert) + : _paths.Add(binDirectoriesToInsert, insertionIndex))) { + fprintf(stderr, "Error: Out of memory!\n"); + exit(1); + } + } +} + + +int +main(int argc, const char* const* argv) +{ + bool printPath = false; + + while (true) { + static struct option sLongOptions[] = { + { "help", no_argument, 0, 'h' }, + { "print-path", no_argument, 0, 'p' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "+hp", + sLongOptions, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage_and_exit(false); + break; + + case 'p': + printPath = true; + break; + + default: + print_usage_and_exit(true); + break; + } + } + + // The remaining arguments are the architecture and optionally the command + // to execute. + if (optind >= argc) + print_usage_and_exit(true); + const char* architecture = optind < argc ? argv[optind++] : NULL; + + int commandArgCount = argc - optind; + const char* const* commandArgs = commandArgCount > 0 ? argv + optind : NULL; + + if (printPath && commandArgs != NULL) + print_usage_and_exit(true); + + // check the architecture + BStringList architectures; + status_t error = get_architectures(architectures); + if (error != B_OK) { + fprintf(stderr, "Error: Failed to get architectures: %s\n", + strerror(error)); + exit(1); + } + + if (!architectures.HasString(architecture)) { + fprintf(stderr, "Error: Unsupported architecture \"%s\"\n", + architecture); + exit(1); + } + + // get the new paths + BStringList paths; + compute_new_paths(architecture, paths); + + BString pathVariableValue = paths.Join(":"); + if (!paths.IsEmpty() && pathVariableValue.IsEmpty()) + fprintf(stderr, "Error: Out of memory!\n"); + + if (printPath) { + printf("%s\n", pathVariableValue.String()); + return 0; + } + + // set PATH + if (setenv("PATH", pathVariableValue, 1) != 0) { + fprintf(stderr, "Error: Failed to set PATH: %s\n", strerror(errno)); + exit(1); + } + + // if no command is given, get the user's shell + const char* shellCommand[2]; + if (commandArgs == NULL) { + struct passwd* pwd = getpwuid(geteuid()); + shellCommand[0] = pwd != NULL ? pwd->pw_shell : "/bin/sh"; + shellCommand[1] = NULL; + commandArgs = shellCommand; + commandArgCount = 1; + } + + // exec the command + execvp(commandArgs[0], (char* const*)commandArgs); + + fprintf(stderr, "Error: Executing \"%s\" failed: %s\n", commandArgs[0], + strerror(errno)); + return 1; +}