diff --git a/Makefile b/Makefile index d1381ab4ab0631d2460df7b3965fecff68e1d127..1834aa6d244ba407f79b455255a0e9a44aed4a8b 100644 --- a/Makefile +++ b/Makefile @@ -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" diff --git a/src/ld_so/ld_script b/src/ld_so/ld_script index 1ea6287d8ce7eab08b47715a93260a8374dee33b..5f05fed75705ce9c24f512afa7a3ca1cab33fdfe 100644 --- a/src/ld_so/ld_script +++ b/src/ld_so/ld_script @@ -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) } diff --git a/src/ld_so/library.rs b/src/ld_so/library.rs index 88958e01c8ffd7ab974fda218f1781848eb897d6..19862f7e3d4281b908bf86903a54914b3cb7eb87 100644 --- a/src/ld_so/library.rs +++ b/src/ld_so/library.rs @@ -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 { diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index 670d38e77612a071d5a66e3ec8af0a519dfbd8b5..3f6b4d4c46b0a67e8d87c563bef38a18aaf3348c 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -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, }; diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index 153b899265feaf559b12212db64d9454ef9841e3..6a9439d001d6aabc03bd6f6ae9a8879294f80c8d 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -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) => { diff --git a/src/start.rs b/src/start.rs index d001d9dde0aaaa7b74a06512eb29ce3d7c4a2375..2082dc84efa8f3d18c7fdfe7a5a6b5f371595381 100644 --- a/src/start.rs +++ b/src/start.rs @@ -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 { diff --git a/tests/.gitignore b/tests/.gitignore index 4764d3daef449032ea949f7f7bec7085a6d21522..748ff9d390dd6f07397b222de5591b2cfc268d4a 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,3 +1,4 @@ -/bins/ +/bins_static/ +/bins_dynamic/ /gen/ /*.out diff --git a/tests/Makefile b/tests/Makefile index e5de5e3aaec8cd1996820f1c73b2825815f2b017..77113745eb66c3f1adcd5bffa27d23edd3089b95 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -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) "$<" -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) $(CFLAGS) "$<" $(LIBS) -o "$@" + $(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS) else -bins/%: %.c +bins_static/%: %.c + mkdir -p "$$(dirname "$@")" + $(CC) "$<" -o "$@" $(FLAGS) $(STATIC_FLAGS) + +bins_dynamic/%: %.c mkdir -p "$$(dirname "$@")" - $(CC) $(CFLAGS) "$<" $(LIBS) -o "$@" + $(CC) "$<" -o "$@" $(FLAGS) endif diff --git a/tests/expected/args.stdout b/tests/expected/args.stdout deleted file mode 100644 index 4076ce936a6d1f14e24710d78e2f8f9b8a955c74..0000000000000000000000000000000000000000 --- a/tests/expected/args.stdout +++ /dev/null @@ -1 +0,0 @@ -bins/args test args diff --git a/tests/expected/args.stderr b/tests/expected/bins_dynamic/args.stderr similarity index 100% rename from tests/expected/args.stderr rename to tests/expected/bins_dynamic/args.stderr diff --git a/tests/expected/bins_dynamic/args.stdout b/tests/expected/bins_dynamic/args.stdout new file mode 100644 index 0000000000000000000000000000000000000000..05c95c249b66f952da4509deea112b569de2c576 --- /dev/null +++ b/tests/expected/bins_dynamic/args.stdout @@ -0,0 +1 @@ +bins_dynamic/args test args diff --git a/tests/expected/errno.stderr b/tests/expected/bins_dynamic/errno.stderr similarity index 100% rename from tests/expected/errno.stderr rename to tests/expected/bins_dynamic/errno.stderr diff --git a/tests/expected/bins_dynamic/errno.stdout b/tests/expected/bins_dynamic/errno.stdout new file mode 100644 index 0000000000000000000000000000000000000000..4c4608349b07c566aec56afba6cf755868ec8393 --- /dev/null +++ b/tests/expected/bins_dynamic/errno.stdout @@ -0,0 +1,4 @@ +bins_dynamic/errno +bins_dynamic/errno +yes, you can change this +yes, you can change this diff --git a/tests/expected/bins_static/args.stderr b/tests/expected/bins_static/args.stderr new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/expected/bins_static/args.stdout b/tests/expected/bins_static/args.stdout new file mode 100644 index 0000000000000000000000000000000000000000..3026e24bea00ad56fb0074c123117d1e1683a12e --- /dev/null +++ b/tests/expected/bins_static/args.stdout @@ -0,0 +1 @@ +bins_static/args test args diff --git a/tests/expected/bins_static/errno.stderr b/tests/expected/bins_static/errno.stderr new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/expected/errno.stdout b/tests/expected/bins_static/errno.stdout similarity index 58% rename from tests/expected/errno.stdout rename to tests/expected/bins_static/errno.stdout index 62770a791141177b9744b5c96ba106ac2d39be7e..9d4b10bd4fc502a3e5b28adb0e6d09d403a8e098 100644 --- a/tests/expected/errno.stdout +++ b/tests/expected/bins_static/errno.stdout @@ -1,4 +1,4 @@ -bins/errno -bins/errno +bins_static/errno +bins_static/errno yes, you can change this yes, you can change this diff --git a/tests/verify.sh b/tests/verify.sh index 2f778e1a64916f82380bf854d103e8f3f79f6753..26ebff0495d58b832ef179d646b7302c5e300320 100755 --- a/tests/verify.sh +++ b/tests/verify.sh @@ -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