Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add end-to-end test for #875 #879

Merged
merged 1 commit into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions data/test-block-augmented.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* Linker script meant to augment the default one and insert some
* fill bytes at a relatively low address (hopefully before any of the
* regular relevant code. */

SECTIONS {
.whatevs (0x100000): {
FILL(0xdead)
. = ABSOLUTE(. + 0x300000);
}
}
46 changes: 46 additions & 0 deletions data/test-block.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* A binary basically just blocking waiting for input and exiting. It also write
* the address of its `_start` function to stdout (unformatted; just a byte
* dump).
*
* It uses raw system calls to avoid dependency on libc, which pulls in
* start up code and various other artifacts that perturb ELF layout in
* semi-unforeseeable ways, in an attempt to provide us with maximum
* control over the final binary.
*
* Likely only works on x86_64.
*/

#include <unistd.h>
#include <sys/syscall.h>


void _start(void) {
char buf[2];
int rc;
void* addr = (void*)&_start;
/* Write the address of `_start` to stderr. We use stderr because it's
unbuffered, so we spare ourselves from the pains of writing a
newline as well... */
asm volatile (
"syscall"
: "=a"(rc)
: "a"(SYS_write), "D"(STDERR_FILENO), "S"(&addr), "d"(sizeof(addr))
: "rcx", "r11", "memory"
);
asm volatile (
"syscall"
: "=a"(rc)
: "a"(SYS_read), "D"(STDIN_FILENO), "S"(buf), "d"(sizeof(buf))
: "rcx", "r11", "memory"
);
if (rc > 0) {
/* No error, so we can exit successfully. */
rc = 0;
}
asm volatile (
"syscall"
: "=a"(rc)
: "a"(SYS_exit), "D"(rc)
: "rcx", "r11", "memory"
);
}
12 changes: 12 additions & 0 deletions dev/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,18 @@ fn prepare_test_files() {
let src = data_dir.join("test-mnt-ns.c");
cc(&src, "test-mnt-ns.bin", &[]);

let src = data_dir.join("test-block.c");
let ld_script = data_dir.join("test-block-augmented.ld");
let args = &[
"-static",
"-Wl,--build-id=none",
"-nostdlib",
// Just passing the linker script as "regular" input file causes
// it to "augment" the default linker script.
ld_script.to_str().unwrap(),
];
cc(&src, "test-block.bin", args);

cc_stable_addrs(
"test-stable-addrs.bin",
&["-gdwarf-4", "-Wl,--build-id=none", "-O0"],
Expand Down
64 changes: 64 additions & 0 deletions tests/blazesym.rs
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,70 @@ fn symbolize_process_with_custom_dispatch() {
test(process_no_dispatch);
}

/// Symbolize a normalized address from a binary with an artificially
/// inflated ELF segment.
///
/// This is a regression test for the case that a program header
/// with a memory size greater than file size is located before a
/// program header that would otherwise match the file offset. Refer
/// to commit 1a4e10740652 ("Use file size in file offset -> virtual
/// offset translation").
#[cfg(linux)]
#[test]
fn symbolize_normalized_large_memsize() {
let test_block = Path::new(&env!("CARGO_MANIFEST_DIR"))
.join("data")
.join("test-block.bin");
let mut child = Command::new(&test_block)
.stdin(Stdio::piped())
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()
.unwrap();
let pid = child.id();
defer!({
// Best effort only. The child may end up terminating gracefully
// if everything goes as planned.
// TODO: Ideally this kill would be pid FD based to eliminate
// any possibility of killing the wrong entity.
let _rc = unsafe { libc::kill(pid as _, libc::SIGKILL) };
});

let mut buf = [0u8; size_of::<Addr>()];
let count = child
.stderr
.as_mut()
.unwrap()
.read(&mut buf)
.expect("failed to read child output");
assert_eq!(count, buf.len());
let addr = Addr::from_ne_bytes(buf);
let pid = Pid::from(child.id());

let normalizer = Normalizer::new();
let normalized = normalizer
.normalize_user_addrs(pid, [addr].as_slice())
.unwrap();

assert_eq!(normalized.outputs.len(), 1);
assert_eq!(normalized.meta.len(), 1);
let file_offset = normalized.outputs[0].0;

let elf = symbolize::Elf::new(test_block);
let src = symbolize::Source::Elf(elf);
let symbolizer = Symbolizer::new();
let sym = symbolizer
.symbolize_single(&src, symbolize::Input::FileOffset(file_offset))
.unwrap()
.into_sym()
.unwrap();
assert_eq!(sym.name, "_start");

// "Signal" the child to terminate gracefully.
let () = child.stdin.as_ref().unwrap().write_all(&[0x04]).unwrap();
let _status = child.wait().unwrap();
}

/// Check that we can normalize addresses in an ELF shared object.
#[cfg(linux)]
#[test]
Expand Down