Merge branch 'dynamic' into 'master'

Add tests for ld_so

See merge request redox-os/relibc!314
This commit is contained in:
Jeremy Soller
2020-09-30 00:42:45 +00:00
18 changed files with 168 additions and 80 deletions
+1
View File
@@ -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
View File
@@ -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) }
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,3 +1,4 @@
/bins/
/bins_static/
/bins_dynamic/
/gen/
/*.out
+68 -40
View File
@@ -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
View File
@@ -1 +0,0 @@
bins/args test args
+1
View File
@@ -0,0 +1 @@
bins_dynamic/args test args
+4
View File
@@ -0,0 +1,4 @@
bins_dynamic/errno
bins_dynamic/errno
yes, you can change this
yes, you can change this
+1
View File
@@ -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
View File
@@ -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