Compare commits

...

5 Commits

Author SHA1 Message Date
Michael Davidsaver
f5194b2274 older binutils compat 2018-12-13 11:22:07 -08:00
Michael Davidsaver
7bdbded47d travis-ci test rpath $ORIGIN 2018-11-20 22:00:42 -08:00
Michael Davidsaver
84831e13e7 epicsGetExecName WIN32, Darwin, solaris, freebsd 2018-11-20 22:00:42 -08:00
Michael Davidsaver
32340584b4 epicsGetExecDir() paths relative to executable
For linux, enable softIoc to find .dbd relative to the executable
location.

The same could be done for other targets

*bsd
  may have symlink /proc/curproc/file
  fallback to sysctl() with KERN_PROC_PATHNAME

solaris
  getexecname()

mac
  _NSGetExecutablePath()

WIN32
  GetModuleFileName(NULL)

others
  out of luck...
2018-11-20 22:00:42 -08:00
Michael Davidsaver
4ee3cbf382 LINKER_USE_RPATH=ORIGIN
Use Linux specific linker trick to allow relocation
of built tree.

relative rpath

use $ORIGIN to reference libraries in other modules
by relative path.

fix rel. RPATH
2018-11-20 22:00:42 -08:00
21 changed files with 464 additions and 4 deletions

View File

@@ -15,7 +15,7 @@ addons:
script:
- .ci/travis-build.sh
env:
- CMPLR=gcc
- CMPLR=gcc EXTRA=LINKER_USE_RPATH=ORIGIN
- CMPLR=clang
- CMPLR=gcc STATIC=YES
- CMPLR=clang STATIC=YES

View File

@@ -44,6 +44,8 @@ FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl
TAPTOJUNIT = $(PERL) $(TOOLS)/tap-to-junit-xml.pl
GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG)
MAKERPATH = $(PYTHON) $(TOOLS)/makeRPath.py
#---------------------------------------------------------------
# tools for installing libraries and products
INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG)

View File

@@ -38,6 +38,8 @@ BUILD_ARCHS = $(EPICS_HOST_ARCH) $(CROSS1) $(CROSS2)
# otherwise override this in os/CONFIG_SITE.<host_arch>.Common
PERL = perl -CSD
PYTHON = python
#-------------------------------------------------------
# Check configure/RELEASE file for consistency
CHECK_RELEASE_YES = checkRelease

View File

@@ -169,10 +169,17 @@ EPICS_SITE_VERSION =
GCC_PIPE = NO
# Set RPATH when linking executables and libraries.
# Must be either YES or NO. If you set this to NO you must also provide a
# Must be either YES, NO, or ORIGIN. If you set this to NO you must also provide a
# way for Base executables to find their shared libraries when they are
# run at build-time, e.g. set the LD_LIBRARY_PATH environment variable.
# ORIGIN is Linux specific.
LINKER_USE_RPATH = YES
# Only used when LINKER_USE_RPATH=ORIGIN
# The build time root of the relocatable tree.
# Linking to libraries under this root directory will be relative.
# Linking to libraries outside of this root will be absolute.
LINKER_ORIGIN_ROOT = $(INSTALL_LOCATION)
# Overrides for the settings above may appear in a CONFIG_SITE.local file
-include $(CONFIG)/CONFIG_SITE.local

View File

@@ -194,6 +194,13 @@ ifeq ($(EPICS_HOST_ARCH),$(T_A))
$(info Warning: RELEASE file consistency checks have been disabled)
endif
# $(FINAL_DIR) signals eventual install locations to makeRPath script
$(TESTPRODNAME): FINAL_DIR=.
$(PRODNAME): FINAL_DIR=$(INSTALL_BIN)
$(TESTSHRLIBNAME): FINAL_DIR=.
$(SHRLIBNAME): FINAL_DIR=$(INSTALL_SHRLIB)
$(LOADABLE_SHRLIBNAME): FINAL_DIR=$(INSTALL_SHRLIB)
#---------------------------------------------------------------
# The order of the following rules is
# VERY IMPORTANT !!!!

View File

@@ -25,11 +25,13 @@ STATIC_LDLIBS_YES= -Wl,-Bdynamic
# Set runtime path for shared libraries if USE_RPATH=YES and STATIC_BUILD=NO
SHRLIBDIR_RPATH_LDFLAGS_YES_NO = $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath,%)
SHRLIBDIR_RPATH_LDFLAGS_ORIGIN_NO = $(shell $(MAKERPATH) -O '\$$ORIGIN' -F $(FINAL_DIR) -R $(LINKER_ORIGIN_ROOT) $(SHRLIB_DEPLIB_DIRS))
SHRLIBDIR_LDFLAGS += \
$(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)_$(STATIC_BUILD))
# Set runtime path for products if USE_RPATH=YES and STATIC_BUILD=NO
PRODDIR_RPATH_LDFLAGS_YES_NO = $(PROD_DEPLIB_DIRS:%=-Wl,-rpath,%)
PRODDIR_RPATH_LDFLAGS_ORIGIN_NO = $(shell $(MAKERPATH) -O '\$$ORIGIN' -F $(FINAL_DIR) -R $(LINKER_ORIGIN_ROOT) $(PROD_DEPLIB_DIRS))
PRODDIR_LDFLAGS += \
$(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)_$(STATIC_BUILD))

View File

@@ -62,23 +62,59 @@
#include "epicsThread.h"
#include "epicsExit.h"
#include "epicsStdio.h"
#include "epicsString.h"
#include "dbStaticLib.h"
#include "subRecord.h"
#include "dbAccess.h"
#include "asDbLib.h"
#include "iocInit.h"
#include "iocsh.h"
#include "osiFileName.h"
#include "epicsInstallDir.h"
extern "C" int softIoc_registerRecordDeviceDriver(struct dbBase *pdbbase);
#define DBD_FILE EPICS_BASE "/dbd/softIoc.dbd"
#define EXIT_FILE EPICS_BASE "/db/softIocExit.db"
#define DBD_BASE "dbd/softIoc.dbd"
#define EXIT_BASE "db/softIocExit.db"
#define DBD_FILE_REL "../../" DBD_BASE
#define EXIT_FILE_REL "../../" EXIT_BASE
#define DBD_FILE EPICS_BASE "/" DBD_BASE
#define EXIT_FILE EPICS_BASE "/" EXIT_BASE
const char *arg0;
const char *base_dbd = DBD_FILE;
const char *exit_db = EXIT_FILE;
static void preparePath(void)
{
FILE *fp;
char *prefix = epicsGetExecDir();
char *dbd, *exit;
if(!prefix) return;
dbd = (char*)malloc(strlen(prefix) + strlen(DBD_FILE_REL) + 1);
if(dbd) {
dbd[0] = '\0';
strcat(dbd, prefix);
strcat(dbd, DBD_FILE_REL);
printf("Testing '%s'\n", dbd);
if((fp = fopen(dbd, "rb"))!=NULL) {
fclose(fp);
base_dbd = dbd;
}
}
exit = (char*)malloc(strlen(prefix) + strlen(EXIT_FILE_REL) + 1);
if(exit) {
exit[0] = '\0';
strcat(exit, prefix);
strcat(exit, EXIT_FILE_REL);
if((fp = fopen(exit, "rb"))!=NULL) {
fclose(fp);
exit_db = exit;
}
}
}
static void exitSubroutine(subRecord *precord) {
epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
@@ -96,6 +132,7 @@ static void usage(int status) {
int main(int argc, char *argv[])
{
preparePath();
char *dbd_file = const_cast<char*>(base_dbd);
char *macros = NULL;
char xmacro[PVNAME_STRINGSZ + 4];

View File

@@ -14,7 +14,29 @@
#ifndef unixFileNameH
#define unixFileNameH
#include <shareLib.h>
#ifdef __cplusplus
extern "C" {
#endif
#define OSI_PATH_LIST_SEPARATOR ":"
#define OSI_PATH_SEPARATOR "/"
/** Return the absolute path of the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecName(void);
/** Return the absolute path of the directory containing the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecDir(void);
#ifdef __cplusplus
}
#endif
#endif /* unixFileNameH */

View File

@@ -123,6 +123,7 @@ Com_SRCS += osdMonotonic.c
Com_SRCS += osdProcess.c
Com_SRCS += osdNetIntf.c
Com_SRCS += osdMessageQueue.c
Com_SRCS += osdgetexec.c
Com_SRCS += devLibVME.c
Com_SRCS += devLibVMEOSD.c

View File

@@ -0,0 +1,50 @@
#include <string.h>
#include <stdlib.h>
#include <mach-o/dyld.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
uint32_t max = 64u;
char *ret = NULL;
while(1) {
char *temp = realloc(ret, max);
if(!temp) {
/* we treat alloc failure as terminal */
free(ret);
ret = NULL;
break;
}
ret = temp;
/* cf. "man 3 dyld" */
if(_NSGetExecutablePath(ret, &max)==0) {
/* max left unchanged */
ret[max-1] = '\0';
break;
}
/* max has been updated with required size */
}
/* TODO: _NSGetExecutablePath() doesn't follow symlinks */
return ret;
}
char *epicsGetExecDir(void)
{
char *ret = epicsGetExecName();
if(ret) {
char *sep = strrchr(ret, '/');
if(sep) {
/* nil the charactor after the / */
sep[1] = '\0';
}
}
return ret;
}

View File

@@ -0,0 +1,54 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
size_t max = PATH_MAX;
char *ret = NULL;
ssize_t n;
while(1) {
char *temp = realloc(ret, max);
if(!temp) {
/* we treat alloc failure as terminal */
free(ret);
ret = NULL;
break;
}
ret = temp;
n = readlink("/proc/self/exe", ret, max);
if(n == -1) {
free(ret);
ret = NULL;
break;
} else if(n < max) {
/* readlink() never adds a nil */
ret[n] = '\0';
break;
}
max += 64;
}
return ret;
}
char *epicsGetExecDir(void)
{
char *ret = epicsGetExecName();
if(ret) {
char *sep = strrchr(ret, '/');
if(sep) {
/* nil the charactor after the / */
sep[1] = '\0';
}
}
return ret;
}

View File

@@ -0,0 +1,52 @@
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
size_t max = 128;
char *ret = NULL;
DWORD n;
while(1) {
char *temp = realloc(ret, max);
if(!temp) {
/* we treat alloc failure as terminal */
free(ret);
ret = NULL;
break;
}
ret = temp;
n = GetModuleFileName(NULL, ret, max);
if(n == 0) {
free(ret);
ret = NULL;
break;
} else if(n < max) {
ret[n] = '\0';
break;
}
max += 64;
}
return ret;
}
char *epicsGetExecDir(void)
{
char *ret = epicsGetExecName();
if(ret) {
char *sep = strrchr(ret, '\\');
if(sep) {
/* nil the charactor after the / */
sep[1] = '\0';
}
}
return ret;
}

View File

@@ -15,7 +15,29 @@
#ifndef osiFileNameH
#define osiFileNameH
#include <shareLib.h>
#ifdef __cplusplus
extern "C" {
#endif
#define OSI_PATH_LIST_SEPARATOR ";"
#define OSI_PATH_SEPARATOR "\\"
/** Return the absolute path of the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecName(void);
/** Return the absolute path of the directory containing the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecDir(void);
#ifdef __cplusplus
}
#endif
#endif /* osiFileNameH */

View File

@@ -14,7 +14,29 @@
#ifndef osiFileNameH
#define osiFileNameH
#include <shareLib.h>
#ifdef __cplusplus
extern "C" {
#endif
#define OSI_PATH_LIST_SEPARATOR ";"
#define OSI_PATH_SEPARATOR "\\"
/** Return the absolute path of the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecName(void);
/** Return the absolute path of the directory containing the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecDir(void);
#ifdef __cplusplus
}
#endif
#endif /* osiFileNameH */

View File

@@ -0,0 +1,14 @@
#include <stdlib.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
return NULL;
}
char *epicsGetExecDir(void)
{
return NULL;
}

View File

@@ -0,0 +1,68 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
size_t max = PATH_MAX;
char *ret = NULL;
ssize_t n;
while(1) {
char *temp = realloc(ret, max);
if(!temp) {
/* we treat alloc failure as terminal */
free(ret);
ret = NULL;
break;
}
ret = temp;
n = readlink("/proc/curproc/file", ret, max);
if(n == -1) {
free(ret);
ret = NULL;
break;
} else if(n < max) {
/* readlink() never adds a nil */
ret[n] = '\0';
break;
}
max += 64;
}
if(!ret) {
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
ret = malloc(max);
if(ret) {
sysctl(mib, 4, ret, &cb, NULL, 0);
/* TODO: error check */
}
}
return ret;
}
char *epicsGetExecDir(void)
{
char *ret = epicsGetExecName();
if(ret) {
char *sep = strrchr(ret, '/');
if(sep) {
/* nil the charactor after the / */
sep[1] = '\0';
}
}
return ret;
}

View File

@@ -0,0 +1,30 @@
#include <string.h>
#include <stdlib.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
const char *raw = getexecname();
char *ret = NULL;
/* manpage says getexecname() might return a relative path. we treat this as an error */
if(raw[0]=='/') {
ret = strdup(raw);
}
return ret;
}
char *epicsGetExecDir(void)
{
char *ret = epicsGetExecName();
if(ret) {
char *sep = strrchr(ret, '/');
if(sep) {
/* nil the charactor after the / */
sep[1] = '\0';
}
}
return ret;
}

View File

@@ -232,6 +232,11 @@ osiSockTest_SRCS += osiSockTest.c
testHarness_SRCS += osiSockTest.c
TESTS += osiSockTest
TESTPROD_HOST += testexecname
testexecname_SRCS += testexecname.c
# no point in including in testHarness. Not implemented for RTEMS/vxWorks.
TESTS += testexecname
ifeq ($(BUILD_CLASS),HOST)
ifneq ($(OS_CLASS),WIN32)
# This test can only be run on a build host, and is broken on Windows

View File

@@ -0,0 +1,24 @@
#include <string.h>
#include <epicsUnitTest.h>
#include <testMain.h>
#include <osiFileName.h>
MAIN(testexecname)
{
testPlan(1);
{
char *buf = epicsGetExecName();
if(!buf) {
testSkip(1, "epicsGetExecName() not available for this target");
} else {
char *loc = strstr(buf, "testexecname");
testOk(!!loc, "Find \"testexecname\" in \"%s\"", buf);
}
}
return testDone();
}

View File

@@ -39,6 +39,8 @@ PERL_SCRIPTS += tap-to-junit-xml.pl
PERL_SCRIPTS += useManifestTool.pl
PERL_SCRIPTS += genVersionHeader.pl
PERL_SCRIPTS += makeRPath.py
HTMLS = style.css
HTMLS += EPICS/Getopts.html
HTMLS += EPICS/Path.html

37
src/tools/makeRPath.py Normal file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env python
from __future__ import print_function
import sys
import os
from argparse import ArgumentParser
if os.environ.get('EPICS_DEBUG_RPATH','')=='YES':
sys.stderr.write('%s'%sys.argv)
P = ArgumentParser()
P.add_argument('-F','--final',default=os.getcwd(), help='Final install location for ELF file')
P.add_argument('-R','--root',default='/')
P.add_argument('-O', '--origin', default='$ORIGIN')
P.add_argument('path', nargs='*')
args = P.parse_args()
fdir = os.path.abspath(args.final)
output = []
for path in args.path:
path = os.path.abspath(path)
if args.root and os.path.relpath(path, args.root).startswith('../'):
pass # absolute rpath
else:
# some older binutils don't seem to handle $ORIGIN correctly
# when locating dependencies of libraries. So also provide
# the absolute path for internal use by 'ld' only.
output.append('-Wl,-rpath-link,'+path)
path = os.path.relpath(path, fdir)
output.append('-Wl,-rpath,'+os.path.join(args.origin, path))
print(' '.join(output))