Merge branch 'dynamic' into 'master'
Add tests for ld_so See merge request redox-os/relibc!314
This commit is contained in:
@@ -86,6 +86,7 @@ install-libs: libs
|
||||
mkdir -pv "$(DESTDIR)/lib"
|
||||
cp -v "$(BUILD)/release/libc.a" "$(DESTDIR)/lib"
|
||||
cp -v "$(BUILD)/release/libc.so" "$(DESTDIR)/lib"
|
||||
ln -sr "$(DESTDIR)/lib/libc.so" "$(DESTDIR)/lib/libc.so.6"
|
||||
cp -v "$(BUILD)/release/crt0.o" "$(DESTDIR)/lib"
|
||||
cp -v "$(BUILD)/release/crti.o" "$(DESTDIR)/lib"
|
||||
cp -v "$(BUILD)/release/crtn.o" "$(DESTDIR)/lib"
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ SEARCH_DIR("/usr/lib");
|
||||
SECTIONS
|
||||
{
|
||||
/* Read-only sections, merged into text segment: */
|
||||
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x12340000)); . = SEGMENT_START("text-segment", 0x1234000) + SIZEOF_HEADERS;
|
||||
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x20000000) + SIZEOF_HEADERS;
|
||||
.interp : { *(.interp) }
|
||||
.note.gnu.build-id : { *(.note.gnu.build-id) }
|
||||
.hash : { *(.hash) }
|
||||
|
||||
@@ -36,6 +36,7 @@ pub struct Library {
|
||||
pub dep_tree: DepTree,
|
||||
/// A set used to detect circular dependencies in the Linker::load function
|
||||
pub cir_dep: BTreeSet<String>,
|
||||
pub runpath: Option<String>,
|
||||
}
|
||||
impl Library {
|
||||
pub fn new() -> Library {
|
||||
|
||||
+34
-8
@@ -14,7 +14,7 @@ use goblin::{
|
||||
elf::{
|
||||
header::ET_DYN,
|
||||
program_header,
|
||||
r#dyn::{Dyn, DT_DEBUG},
|
||||
r#dyn::{Dyn, DT_DEBUG, DT_RUNPATH},
|
||||
reloc, sym, Elf,
|
||||
},
|
||||
error::{Error, Result},
|
||||
@@ -63,7 +63,8 @@ impl Symbol {
|
||||
pub struct Linker {
|
||||
// Used by load
|
||||
/// Library path to search when loading library by name
|
||||
library_path: String,
|
||||
default_library_path: String,
|
||||
ld_library_path: Option<String>,
|
||||
root: Library,
|
||||
verbose: bool,
|
||||
tls_index_offset: usize,
|
||||
@@ -73,9 +74,10 @@ pub struct Linker {
|
||||
}
|
||||
|
||||
impl Linker {
|
||||
pub fn new(library_path: &str, verbose: bool) -> Self {
|
||||
pub fn new(ld_library_path: Option<String>, verbose: bool) -> Self {
|
||||
Self {
|
||||
library_path: library_path.to_string(),
|
||||
default_library_path: "/lib".to_string(),
|
||||
ld_library_path: ld_library_path,
|
||||
root: Library::new(),
|
||||
verbose,
|
||||
tls_index_offset: 0,
|
||||
@@ -139,13 +141,30 @@ impl Linker {
|
||||
) -> Result<Vec<DepTree>> {
|
||||
let elf = Elf::parse(&data)?;
|
||||
//println!("{:#?}", elf);
|
||||
|
||||
// search for RUNPATH
|
||||
lib.runpath = if let Some(dynamic) = elf.dynamic {
|
||||
let entry = dynamic.dyns.iter().find(|d| d.d_tag == DT_RUNPATH);
|
||||
match entry {
|
||||
Some(entry) => {
|
||||
let path = elf
|
||||
.dynstrtab
|
||||
.get(entry.d_val as usize)
|
||||
.ok_or(Error::Malformed("Missing RUNPATH in dynstrtab".to_string()))??;
|
||||
Some(path.to_string())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut deps = Vec::new();
|
||||
for library in elf.libraries.iter() {
|
||||
if let Some(dep) = self._load_library(library, lib)? {
|
||||
deps.push(dep);
|
||||
}
|
||||
}
|
||||
let elf = Elf::parse(&data)?;
|
||||
let key = match elf.soname {
|
||||
Some(soname) => soname,
|
||||
_ => name,
|
||||
@@ -171,8 +190,15 @@ impl Linker {
|
||||
} else if name.contains('/') {
|
||||
Ok(Some(self.load_recursive(name, name, lib)?))
|
||||
} else {
|
||||
let library_path = self.library_path.clone();
|
||||
for part in library_path.split(PATH_SEP) {
|
||||
let mut paths = Vec::new();
|
||||
if let Some(ld_library_path) = &self.ld_library_path {
|
||||
paths.push(ld_library_path);
|
||||
}
|
||||
if let Some(runpath) = &lib.runpath {
|
||||
paths.push(runpath);
|
||||
}
|
||||
paths.push(&self.default_library_path);
|
||||
for part in paths.iter() {
|
||||
let path = if part.is_empty() {
|
||||
format!("./{}", name)
|
||||
} else {
|
||||
@@ -647,7 +673,7 @@ impl Linker {
|
||||
} as usize;
|
||||
|
||||
let mut tcb_master = Master {
|
||||
ptr: unsafe { mmap.as_ptr().add(ph.p_vaddr as usize) },
|
||||
ptr: unsafe { mmap.as_ptr().add(ph.p_vaddr as usize - base_addr) },
|
||||
len: ph.p_filesz as usize,
|
||||
offset: tls_size - valign,
|
||||
};
|
||||
|
||||
+2
-5
@@ -183,10 +183,7 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
|
||||
}
|
||||
|
||||
// Some variables that will be overridden by environment and auxiliary vectors
|
||||
let library_path = match envs.get("LD_LIBRARY_PATH") {
|
||||
Some(lib_path) => lib_path.to_owned() + ":/lib",
|
||||
None => "/lib".to_owned(),
|
||||
};
|
||||
let ld_library_path = envs.get("LD_LIBRARY_PATH").map(|s| s.to_owned());
|
||||
|
||||
let name_or_path = if is_manual {
|
||||
// ld.so is run directly by user and not via execve() or similar systemcall
|
||||
@@ -234,7 +231,7 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
|
||||
}
|
||||
pr
|
||||
};
|
||||
let mut linker = Linker::new(&library_path, false);
|
||||
let mut linker = Linker::new(ld_library_path, false);
|
||||
match linker.load(&path, &path) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
|
||||
+16
-6
@@ -86,9 +86,23 @@ extern "C" fn init_array() {
|
||||
// init_array runs first or if relibc_start runs first
|
||||
// Still whoever gets to run first must initialize rust
|
||||
// memory allocator before doing anything else.
|
||||
|
||||
unsafe {
|
||||
if init_complete {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
alloc_init();
|
||||
io_init();
|
||||
unsafe { init_complete = true };
|
||||
|
||||
extern "C" {
|
||||
fn pthread_init();
|
||||
}
|
||||
unsafe {
|
||||
pthread_init();
|
||||
init_complete = true
|
||||
}
|
||||
}
|
||||
fn io_init() {
|
||||
unsafe {
|
||||
@@ -108,7 +122,6 @@ pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! {
|
||||
static __init_array_start: extern "C" fn();
|
||||
static __init_array_end: extern "C" fn();
|
||||
|
||||
fn pthread_init();
|
||||
fn _init();
|
||||
fn main(argc: isize, argv: *mut *mut c_char, envp: *mut *mut c_char) -> c_int;
|
||||
}
|
||||
@@ -138,10 +151,7 @@ pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! {
|
||||
platform::inner_environ = copy_string_array(envp, len);
|
||||
platform::environ = platform::inner_environ.as_mut_ptr();
|
||||
|
||||
if !init_complete {
|
||||
init_array();
|
||||
}
|
||||
pthread_init();
|
||||
init_array();
|
||||
|
||||
// Run preinit array
|
||||
{
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
/bins/
|
||||
/bins_static/
|
||||
/bins_dynamic/
|
||||
/gen/
|
||||
/*.out
|
||||
|
||||
+68
-40
@@ -6,43 +6,33 @@ EXPECT_NAMES=\
|
||||
assert \
|
||||
constructor \
|
||||
ctype \
|
||||
destructor \
|
||||
dirent/scandir \
|
||||
errno \
|
||||
error \
|
||||
fcntl/create \
|
||||
fcntl/fcntl \
|
||||
fnmatch \
|
||||
futimens \
|
||||
libgen \
|
||||
locale \
|
||||
math \
|
||||
netdb/getaddrinfo \
|
||||
netdb/netdb \
|
||||
ptrace \
|
||||
regex \
|
||||
select \
|
||||
setjmp \
|
||||
sigaction \
|
||||
signal \
|
||||
stdio/all \
|
||||
stdio/buffer \
|
||||
stdio/fgets \
|
||||
stdio/fputs \
|
||||
stdio/fread \
|
||||
stdio/freopen \
|
||||
stdio/fseek \
|
||||
stdio/fwrite \
|
||||
stdio/getc_unget \
|
||||
stdio/mutex \
|
||||
stdio/popen \
|
||||
stdio/printf \
|
||||
stdio/rename \
|
||||
stdio/scanf \
|
||||
stdio/setvbuf \
|
||||
stdio/sprintf \
|
||||
stdio/printf_space_pad \
|
||||
stdio/ungetc_multiple \
|
||||
stdio/ungetc_ftell \
|
||||
stdio/fscanf_offby1 \
|
||||
stdio/fscanf \
|
||||
@@ -79,12 +69,10 @@ EXPECT_NAMES=\
|
||||
sys_mman \
|
||||
time/asctime \
|
||||
time/gmtime \
|
||||
time/localtime \
|
||||
time/macros \
|
||||
time/mktime \
|
||||
time/strftime \
|
||||
time/time \
|
||||
tls \
|
||||
unistd/access \
|
||||
unistd/brk \
|
||||
unistd/dup \
|
||||
@@ -93,8 +81,6 @@ EXPECT_NAMES=\
|
||||
unistd/fork \
|
||||
unistd/fsync \
|
||||
unistd/ftruncate \
|
||||
unistd/getopt \
|
||||
unistd/getopt_long \
|
||||
unistd/pipe \
|
||||
unistd/rmdir \
|
||||
unistd/sleep \
|
||||
@@ -111,14 +97,37 @@ EXPECT_NAMES=\
|
||||
wchar/wcsrchr \
|
||||
wchar/wcsstr \
|
||||
wchar/wcstod \
|
||||
wchar/wcstok \
|
||||
wchar/wcstol \
|
||||
wchar/wcscasecmp \
|
||||
wchar/wcsncasecmp \
|
||||
wctype/towlower \
|
||||
wctype/towupper
|
||||
# TODO: Fix these
|
||||
# mkfifo
|
||||
# netdb/netdb \
|
||||
|
||||
# issues with linking stdin, stdout, stderr
|
||||
STATIC_ONLY_NAMES=\
|
||||
futimens \
|
||||
stdio/all \
|
||||
stdio/buffer \
|
||||
stdio/fgets \
|
||||
stdio/freopen \
|
||||
stdio/getc_unget \
|
||||
stdio/setvbuf \
|
||||
stdio/ungetc_multiple \
|
||||
time/localtime \
|
||||
wchar/wcstok \
|
||||
wctype/towlower \
|
||||
wctype/towupper \
|
||||
# need to call fini in ld_so's _start
|
||||
STATIC_ONLY_NAMES+=\
|
||||
destructor \
|
||||
# comparison issue
|
||||
STATIC_ONLY_NAMES+=\
|
||||
tls \
|
||||
# issues with linking optarg, optind etc.
|
||||
STATIC_ONLY_NAMES+=\
|
||||
unistd/getopt \
|
||||
unistd/getopt_long \
|
||||
|
||||
# Binaries that may generate varied output
|
||||
NAMES=\
|
||||
@@ -147,8 +156,12 @@ NAMES=\
|
||||
# resource/getrusage
|
||||
# time/times
|
||||
|
||||
BINS=$(patsubst %,bins/%,$(NAMES))
|
||||
EXPECT_BINS=$(patsubst %,bins/%,$(EXPECT_NAMES))
|
||||
BINS=$(patsubst %,bins_static/%,$(NAMES))
|
||||
BINS+=$(patsubst %,bins_static/%,$(STATIC_ONLY_NAMES))
|
||||
BINS+=$(patsubst %,bins_dynamic/%,$(NAMES))
|
||||
EXPECT_BINS=$(patsubst %,bins_static/%,$(EXPECT_NAMES))
|
||||
EXPECT_BINS+=$(patsubst %,bins_static/%,$(STATIC_ONLY_NAMES))
|
||||
EXPECT_BINS+=$(patsubst %,bins_dynamic/%,$(EXPECT_NAMES))
|
||||
|
||||
TEST_RUNNER?=sh --
|
||||
|
||||
@@ -157,61 +170,76 @@ TEST_RUNNER?=sh --
|
||||
all: $(BINS)
|
||||
|
||||
clean:
|
||||
rm -rf bins gen *.out
|
||||
rm -rf bins_* gen *.out
|
||||
|
||||
run: | $(BINS)
|
||||
for name in $(NAMES); \
|
||||
for bin in $(BINS); \
|
||||
do \
|
||||
echo "# $${name} #"; \
|
||||
"bins/$${name}" test args || exit $$?; \
|
||||
echo "# $${bin} #"; \
|
||||
"$${bin}" test args || exit $$?; \
|
||||
done
|
||||
|
||||
expected: | $(EXPECT_BINS)
|
||||
rm -rf expected
|
||||
mkdir -p expected
|
||||
for name in $(EXPECT_NAMES); \
|
||||
for bin in $(EXPECT_BINS); \
|
||||
do \
|
||||
echo "# $${name} #"; \
|
||||
mkdir -p expected/`dirname $${name}`; \
|
||||
"bins/$${name}" test args > "expected/$${name}.stdout" 2> "expected/$${name}.stderr" || exit $$?; \
|
||||
echo "# $${bin} #"; \
|
||||
mkdir -p expected/`dirname $${bin}`; \
|
||||
"$${bin}" test args > "expected/$${bin}.stdout" 2> "expected/$${bin}.stderr" || exit $$?; \
|
||||
done
|
||||
|
||||
verify: | $(EXPECT_BINS)
|
||||
$(TEST_RUNNER) ./verify.sh $(EXPECT_NAMES)
|
||||
$(TEST_RUNNER) ./verify.sh $(EXPECT_BINS)
|
||||
|
||||
CFLAGS=\
|
||||
FLAGS=\
|
||||
-std=c11 \
|
||||
-fno-builtin \
|
||||
-fno-stack-protector \
|
||||
-static \
|
||||
-Wall \
|
||||
-pedantic \
|
||||
-g \
|
||||
-I .
|
||||
|
||||
LIBS=
|
||||
STATIC_FLAGS=\
|
||||
../sysroot/lib/libc.a \
|
||||
-static
|
||||
|
||||
NATIVE_RELIBC?=0
|
||||
ifeq ($(NATIVE_RELIBC),0)
|
||||
CFLAGS+=\
|
||||
FLAGS+=\
|
||||
-nostdinc \
|
||||
-nostdlib \
|
||||
-isystem ../sysroot/include \
|
||||
../sysroot/lib/crt0.o \
|
||||
../sysroot/lib/crti.o
|
||||
|
||||
LIBS=\
|
||||
../sysroot/lib/libc.a \
|
||||
../sysroot/lib/crti.o \
|
||||
../sysroot/lib/crtn.o
|
||||
|
||||
../sysroot:
|
||||
$(MAKE) -C .. sysroot
|
||||
|
||||
bins/%: %.c ../sysroot
|
||||
bins_static/%: %.c ../sysroot
|
||||
mkdir -p "$$(dirname "$@")"
|
||||
$(CC) $(CFLAGS) "$<" $(LIBS) -o "$@"
|
||||
$(CC) "$<" -o "$@" $(FLAGS) $(STATIC_FLAGS)
|
||||
|
||||
SYSROOT_LIB=$(shell realpath ../sysroot/lib/)
|
||||
|
||||
DYNAMIC_FLAGS=\
|
||||
-Wl,-dynamic-linker=$(SYSROOT_LIB)/ld64.so.1 \
|
||||
-Wl,--enable-new-dtags \
|
||||
-Wl,-rpath=$(SYSROOT_LIB) \
|
||||
-L $(SYSROOT_LIB) \
|
||||
-lc
|
||||
|
||||
bins_dynamic/%: %.c ../sysroot
|
||||
mkdir -p "$$(dirname "$@")"
|
||||
$(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS)
|
||||
else
|
||||
bins/%: %.c
|
||||
bins_static/%: %.c
|
||||
mkdir -p "$$(dirname "$@")"
|
||||
$(CC) $(CFLAGS) "$<" $(LIBS) -o "$@"
|
||||
$(CC) "$<" -o "$@" $(FLAGS) $(STATIC_FLAGS)
|
||||
|
||||
bins_dynamic/%: %.c
|
||||
mkdir -p "$$(dirname "$@")"
|
||||
$(CC) "$<" -o "$@" $(FLAGS)
|
||||
endif
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
bins/args test args
|
||||
@@ -0,0 +1 @@
|
||||
bins_dynamic/args test args
|
||||
@@ -0,0 +1,4 @@
|
||||
bins_dynamic/errno
|
||||
bins_dynamic/errno
|
||||
yes, you can change this
|
||||
yes, you can change this
|
||||
@@ -0,0 +1 @@
|
||||
bins_static/args test args
|
||||
@@ -1,4 +1,4 @@
|
||||
bins/errno
|
||||
bins/errno
|
||||
bins_static/errno
|
||||
bins_static/errno
|
||||
yes, you can change this
|
||||
yes, you can change this
|
||||
+35
-16
@@ -3,42 +3,61 @@
|
||||
rm -rf gen || exit 1
|
||||
mkdir -p gen || exit 1
|
||||
|
||||
summary=""
|
||||
|
||||
while [ "$#" -gt 0 ]
|
||||
do
|
||||
name="$1"
|
||||
bin="$1"
|
||||
shift
|
||||
|
||||
echo "# ${name} #"
|
||||
mkdir -p "gen/$(dirname ${name})" || exit 1
|
||||
echo "# ${bin} #"
|
||||
mkdir -p "gen/$(dirname ${bin})" || exit 1
|
||||
|
||||
"bins/${name}" test args > "gen/${name}.stdout" 2> "gen/${name}.stderr"
|
||||
status="$?"
|
||||
"${bin}" test args > "gen/${bin}.stdout" 2> "gen/${bin}.stderr"
|
||||
retcode="$?"
|
||||
status=""
|
||||
|
||||
for output in stdout stderr
|
||||
do
|
||||
gen="$(sha256sum "gen/${name}.${output}" | cut -d " " -f 1)"
|
||||
expected="$(sha256sum "expected/${name}.${output}" | cut -d " " -f 1)"
|
||||
gen="$(sha256sum "gen/${bin}.${output}" | cut -d " " -f 1)"
|
||||
|
||||
# look for expected output file that is specific to binary type (either static or dynamic)
|
||||
expected_file="expected/${bin}.${output}"
|
||||
if [ ! -e $expected_file ]
|
||||
then
|
||||
# if unable to find above, the expected output file is the same to both static and dynamic binary
|
||||
name=$(echo $bin | cut -d "/" -f2-)
|
||||
expected_file="expected/${name}.${output}"
|
||||
fi
|
||||
expected="$(sha256sum "${expected_file}" | cut -d " " -f 1)"
|
||||
if [ "${gen}" != "${expected}" ]
|
||||
then
|
||||
echo "# ${name}: ${output}: expected #"
|
||||
cat "expected/${name}.${output}"
|
||||
echo "# ${bin}: ${output}: expected #"
|
||||
cat "${expected_file}"
|
||||
|
||||
echo "# ${name}: ${output}: generated #"
|
||||
cat "gen/${name}.${output}"
|
||||
echo "# ${bin}: ${output}: generated #"
|
||||
cat "gen/${bin}.${output}"
|
||||
|
||||
# FIXME: Make diff available on Redox
|
||||
if [ $(uname) != "Redox" ]
|
||||
then
|
||||
echo "# ${name}: ${output}: diff #"
|
||||
diff --color -u "expected/${name}.${output}" "gen/${name}.${output}"
|
||||
echo "# ${bin}: ${output}: diff #"
|
||||
diff --color -u "${expected_file}" "gen/${bin}.${output}"
|
||||
fi
|
||||
|
||||
status="${status}, ${output} mismatch"
|
||||
status="${bin} failed - retcode ${retcode}, ${output} mismatch"
|
||||
summary="${summary}${status}\n"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${status}" != "0" ]
|
||||
if [ -n "${status}" ]
|
||||
then
|
||||
echo "# ${name}: failed with status ${status} #"
|
||||
echo "# ${status} #"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$summary" ]
|
||||
then
|
||||
echo -e "$summary"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user