Skip to content

Commit b7c4f3b

Browse files
committed
Use real filename to determine relocation for remote GDB
1 parent ba17cc6 commit b7c4f3b

File tree

10 files changed

+240
-28
lines changed

10 files changed

+240
-28
lines changed

lib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ endif()
1515

1616
set (SOURCES
1717
tinykvm/machine.cpp
18+
tinykvm/machine_debug.cpp
1819
tinykvm/machine_elf.cpp
1920
tinykvm/machine_env.cpp
2021
tinykvm/machine_utils.cpp

lib/tinykvm/machine.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ struct Machine
212212
void print_registers() const { vcpu.print_registers(); }
213213
void print_pagetables() const;
214214
void print_exception_handlers() const;
215+
void print_remote_gdb_backtrace(const std::string& filename, const std::string& gdb_path = "/usr/bin/gdb");
215216

216217
void install_memory(uint32_t idx, const VirtualMem&, bool ro);
217218
void delete_memory(uint32_t idx);

lib/tinykvm/machine_debug.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#include "machine.hpp"
2+
3+
#include <cstring>
4+
#include <stdexcept>
5+
#include <unistd.h>
6+
#include "rsp_client.hpp"
7+
extern char** environ;
8+
9+
namespace tinykvm {
10+
void Machine::print_remote_gdb_backtrace(
11+
const std::string& filename,
12+
const std::string& gdb_path)
13+
{
14+
const uint16_t port = 2159;
15+
16+
if (0 == fork())
17+
{
18+
char scrname[64];
19+
strncpy(scrname, "/tmp/dbgscript-XXXXXX", sizeof(scrname));
20+
const int fd = mkstemp(scrname);
21+
if (fd < 0)
22+
{
23+
throw std::runtime_error("Unable to create script for debugging");
24+
}
25+
26+
const std::string debugscript =
27+
// Delete the script file (after GDB closes it)
28+
"shell unlink " + std::string(scrname)
29+
+ "\n"
30+
+ "set debuginfod enabled on\n"
31+
// Load the original file used by the script
32+
"file " + filename + "\n"
33+
// Connect remotely to the given port @port
34+
"target remote localhost:"
35+
+ std::to_string(port)
36+
+ "\n"
37+
+ "up\nbt\n";
38+
39+
ssize_t len = write(fd, debugscript.c_str(), debugscript.size());
40+
if (len < (ssize_t)debugscript.size())
41+
{
42+
throw std::runtime_error(
43+
"Unable to write script file for debugging");
44+
}
45+
close(fd);
46+
47+
const char* argv[]
48+
= {gdb_path.c_str(), "-x", scrname, nullptr};
49+
// XXX: This is not kosher, but GDB is open-source, safe and let's not
50+
// pretend that anyone downloads gdb-multiarch from a website anyway.
51+
// There is a finite list of things we should pass to GDB to make it
52+
// behave well, but I haven't been able to find the right combination.
53+
if (-1 == execve(argv[0], (char* const*)argv, environ))
54+
{
55+
throw std::runtime_error(
56+
"Unable to start gdb-multiarch for debugging");
57+
}
58+
}
59+
60+
RSP server {filename, *this, port};
61+
auto client = server.accept();
62+
if (client != nullptr)
63+
{
64+
printf("GDB connected\n");
65+
// client->set_verbose(true);
66+
while (client->process_one())
67+
;
68+
}
69+
}
70+
71+
} // namespace tinykvm

lib/tinykvm/machine_utils.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,23 @@ bool Machine::mmap_backed_area(
298298
}
299299
// Now we need to install this memory region as guest physical memory
300300
this->install_memory(region_idx, VirtualMem(mmap_phys_base, (char*)real_addr, size_memory), false);
301-
this->memory.mmap_ranges.emplace_back(mmap_phys_base, (char*)real_addr, virt_base, size_memory);
301+
// Discover the filename of the fd
302+
char fd_path[64];
303+
snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", fd);
304+
std::string filename(PATH_MAX, '\0');
305+
const ssize_t r = readlink(fd_path, filename.data(), filename.size() - 1);
306+
if (r > 0) {
307+
filename[r] = '\0';
308+
filename.resize(r);
309+
} else {
310+
int len = snprintf(filename.data(), filename.size(), "fd%d", fd);
311+
filename.resize(len);
312+
}
313+
if constexpr (VERBOSE_FILE_BACKED_MMAP) {
314+
printf("mmap: fd %d is file '%s'\n", fd, filename.c_str());
315+
}
316+
// Record the mmap range
317+
this->memory.mmap_ranges.emplace_back(mmap_phys_base, (char*)real_addr, virt_base, size_memory, std::move(filename));
302318
// XXX: TODO: madvise(MADV_DONTNEED) on the old pages using gather_buffers_from_range
303319
// With the new physical memory, we now need to create pagetable entries
304320
// we'll do it the slow way by allocating the same range and for each page redirect it to the new phys

lib/tinykvm/rsp_client.cpp

Lines changed: 133 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
#include "rsp_client.hpp"
22

3-
#include <sys/types.h>
4-
#include <sys/socket.h>
5-
#include <sys/select.h>
3+
#include <cstdarg>
4+
#include <cstring>
5+
#include <fcntl.h>
66
#include <linux/kvm.h>
77
#include <netinet/in.h>
88
#include <netinet/tcp.h>
9-
#include <unistd.h>
10-
#include <cstdarg>
11-
#include <cstring>
129
#include <stdexcept>
10+
#include <sys/types.h>
11+
#include <sys/socket.h>
12+
#include <sys/select.h>
13+
#include <sys/stat.h>
14+
#include <unistd.h>
1315
#include "amd64/memory_layout.hpp"
1416

1517
/**
@@ -18,8 +20,8 @@
1820

1921
namespace tinykvm {
2022

21-
RSP::RSP(vCPU& cpu, uint16_t port)
22-
: m_cpu{cpu}
23+
RSP::RSP(const std::string& filename, vCPU& cpu, uint16_t port)
24+
: m_cpu{cpu}, m_filename{filename}
2325
{
2426
this->server_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
2527

@@ -45,8 +47,8 @@ RSP::RSP(vCPU& cpu, uint16_t port)
4547
/* We need to make sure the VM can be stepped through */
4648
m_cpu.machine().stop(false);
4749
}
48-
RSP::RSP(Machine& machine, uint16_t port)
49-
: RSP(machine.cpu(), port)
50+
RSP::RSP(const std::string& filename, Machine& machine, uint16_t port)
51+
: RSP(filename, machine.cpu(), port)
5052
{}
5153
std::unique_ptr<RSPClient> RSP::accept(int timeout_secs)
5254
{
@@ -85,14 +87,14 @@ std::unique_ptr<RSPClient> RSP::accept(int timeout_secs)
8587
close(sockfd);
8688
return nullptr;
8789
}
88-
return std::make_unique<RSPClient>(m_cpu, sockfd);
90+
return std::make_unique<RSPClient>(m_filename, m_cpu, sockfd);
8991
}
9092
RSP::~RSP() {
9193
close(server_fd);
9294
}
9395

94-
RSPClient::RSPClient(vCPU& cpu, int fd)
95-
: m_cpu{&cpu}, sockfd(fd) {}
96+
RSPClient::RSPClient(const std::string& filename, vCPU& cpu, int fd)
97+
: m_cpu{&cpu}, sockfd(fd), m_filename{filename} {}
9698
RSPClient::~RSPClient() {
9799
if (!is_closed())
98100
close(this->sockfd);
@@ -267,11 +269,11 @@ void RSPClient::handle_query()
267269
{
268270
if (strncmp("qSupported", buffer.data(), strlen("qSupported")) == 0)
269271
{
270-
sendf("PacketSize=%x;swbreak-;hwbreak+", PACKET_SIZE);
272+
sendf("PacketSize=%x;swbreak-;hwbreak+;qXfer:libraries:read+;", PACKET_SIZE);
271273
}
272274
else if (strncmp("qAttached", buffer.data(), strlen("qC")) == 0)
273275
{
274-
send("1");
276+
send("0");
275277
}
276278
else if (strncmp("qC", buffer.data(), strlen("qC")) == 0)
277279
{
@@ -281,7 +283,14 @@ void RSPClient::handle_query()
281283
else if (strncmp("qOffsets", buffer.data(), strlen("qOffsets")) == 0)
282284
{
283285
// Section relocation offsets
284-
send("Text=0;Data=0;Bss=0");
286+
uint64_t offset = 0x0;
287+
for (const auto& mapping : machine().main_memory().mmap_ranges) {
288+
if (mapping.filename == this->m_filename) {
289+
offset = mapping.virtbase;
290+
break;
291+
}
292+
}
293+
sendf("TextSeg=%lx", offset);
285294
}
286295
else if (strncmp("qfThreadInfo", buffer.data(), strlen("qfThreadInfo")) == 0)
287296
{
@@ -407,7 +416,115 @@ void RSPClient::handle_executing()
407416
{
408417
send("");
409418
}
419+
else if (strncmp("vFile:setfs", buffer.data(), strlen("vFile:setfs")) == 0)
420+
{
421+
send("");
422+
}
410423
else {
424+
// Check for file operations
425+
if (strncmp("vFile:open", buffer.data(), strlen("vFile:open")) == 0)
426+
{
427+
// vFile:open:filename,flags,mode
428+
char filename[512];
429+
int flags = 0, mode = 0;
430+
int ret = sscanf(buffer.c_str(), "vFile:open:%511[^,],%d,%o",
431+
filename, &flags, &mode);
432+
if (ret == 3) {
433+
printf("Opening file: %s (flags=%d, mode=%o)\n",
434+
filename, flags, mode);
435+
// NOTE: We are not doing any sandboxing here!
436+
int fd = open(filename, flags, mode);
437+
if (fd < 0) {
438+
send("");
439+
return;
440+
} else {
441+
sendf("F%d", fd);
442+
}
443+
} else {
444+
send("F01");
445+
}
446+
return;
447+
}
448+
else if (strncmp("vFile:pread", buffer.data(), strlen("vFile:pread")) == 0)
449+
{
450+
// vFile:pread:fd,buf,bufsize,offset
451+
int fd = 0;
452+
uint32_t bufsize = 0;
453+
uint64_t offset = 0;
454+
int ret = sscanf(buffer.c_str(), "vFile:pread:%d,0,%x,%lx",
455+
&fd, &bufsize, &offset);
456+
if (ret == 3 && bufsize < 4096) {
457+
char data[4096];
458+
ssize_t rlen = pread(fd, data, bufsize, offset);
459+
if (rlen < 0) {
460+
sendf("F%02x", -errno);
461+
return;
462+
}
463+
char out[8192 + 1];
464+
char* d = out;
465+
for (ssize_t i = 0; i < rlen; i++) {
466+
uint8_t val = data[i];
467+
*d++ = lut[(val >> 4) & 0xF];
468+
*d++ = lut[(val >> 0) & 0xF];
469+
}
470+
*d++ = 0;
471+
sendf("F00;%s", out);
472+
} else {
473+
send("F01");
474+
}
475+
return;
476+
}
477+
else if (strncmp("vFile:close", buffer.data(), strlen("vFile:close")) == 0)
478+
{
479+
// vFile:close:fd
480+
int fd = 0;
481+
int ret = sscanf(buffer.c_str(), "vFile:close:%d", &fd);
482+
if (ret == 1) {
483+
int r = close(fd);
484+
if (r < 0)
485+
r = -errno;
486+
sendf("F%02x", r);
487+
} else {
488+
send("F01");
489+
}
490+
return;
491+
}
492+
else if (strncmp("vFile:fstat", buffer.data(), strlen("vFile:fstat")) == 0)
493+
{
494+
// vFile:fstat:fd
495+
int fd = 0;
496+
int ret = sscanf(buffer.c_str(), "vFile:fstat:%d", &fd);
497+
if (ret == 1) {
498+
struct stat st;
499+
int r = fstat(fd, &st);
500+
if (r < 0) {
501+
sendf("F%02x", -errno);
502+
return;
503+
}
504+
sendf("F00;0%llo,0%llo,0%llo,0%llo,0%llo,0%llo,0%llo,0%llo,0%llo,0%llo,0%llo,0%llo,0%llo,0%llo,0%llo,0%llo",
505+
(__u64)st.st_dev,
506+
(__u64)st.st_ino,
507+
(__u64)st.st_mode,
508+
(__u64)st.st_nlink,
509+
(__u64)st.st_uid,
510+
(__u64)st.st_gid,
511+
(__u64)st.st_rdev,
512+
(__u64)st.st_size,
513+
(__u64)st.st_blksize,
514+
(__u64)st.st_blocks,
515+
(__u64)st.st_atim.tv_sec,
516+
(__u64)st.st_atim.tv_nsec,
517+
(__u64)st.st_mtim.tv_sec,
518+
(__u64)st.st_mtim.tv_nsec,
519+
(__u64)st.st_ctim.tv_sec,
520+
(__u64)st.st_ctim.tv_nsec
521+
);
522+
} else {
523+
send("F01");
524+
}
525+
return;
526+
}
527+
// Unknown v packet
411528
if (UNLIKELY(m_verbose)) {
412529
fprintf(stderr, "Unknown executor: %s\n",
413530
buffer.data());

lib/tinykvm/rsp_client.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ struct RSP
1010
std::unique_ptr<RSPClient> accept(int timeout_secs = 10);
1111
int fd() const noexcept { return server_fd; }
1212

13-
RSP(vCPU&, uint16_t);
14-
RSP(Machine&, uint16_t);
13+
RSP(const std::string& filename, vCPU&, uint16_t port);
14+
RSP(const std::string& filename, Machine&, uint16_t port);
1515
~RSP();
1616

1717
private:
1818
vCPU& m_cpu;
1919
int server_fd;
20+
std::string m_filename;
2021
};
2122

2223
struct RSPClient
@@ -40,7 +41,7 @@ struct RSPClient
4041
void set_verbose(bool v) { m_verbose = v; }
4142
void on_stopped(StopFunc f) { m_on_stopped = f; }
4243

43-
RSPClient(vCPU& cpu, int fd);
44+
RSPClient(const std::string& filename, vCPU& cpu, int fd);
4445
~RSPClient();
4546

4647
private:
@@ -74,6 +75,7 @@ struct RSPClient
7475
std::array<uint64_t, 4> m_bp = {0};
7576
size_t bp_iterator = 0;
7677
StopFunc m_on_stopped = nullptr;
78+
const std::string& m_filename;
7779
};
7880

7981
}

lib/tinykvm/virtual_mem.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ struct VirtualMem {
88
char * ptr;
99
uint64_t virtbase = 0;
1010
uint64_t size;
11+
std::string filename; // Optional, for file-backed mappings
1112

1213
VirtualMem(uint64_t phys, char* p, uint64_t s)
1314
: physbase(phys), ptr(p), size(s) {}
1415

15-
VirtualMem(uint64_t phys, char* p, uint64_t v, uint64_t s)
16-
: physbase(phys), ptr(p), virtbase(v), size(s) {}
16+
VirtualMem(uint64_t phys, char* p, uint64_t v, uint64_t s, std::string f = "")
17+
: physbase(phys), ptr(p), virtbase(v), size(s), filename(std::move(f)) {}
1718

1819
static VirtualMem New(uint64_t physical, char* ptr, uint64_t size) {
1920
return VirtualMem { physical, ptr, size };

src/bench.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ int main(int argc, char** argv)
3232
fprintf(stderr, "Missing argument: 64-bit ELF binary\n");
3333
exit(1);
3434
}
35-
binary = load_file(argv[1]);
35+
std::string filename = argv[1];
36+
binary = load_file(filename);
3637
std::vector<tinykvm::Machine*> vms;
3738
vms.reserve(NUM_GUESTS);
3839

@@ -75,7 +76,7 @@ int main(int argc, char** argv)
7576
master_vm.set_registers(regs);
7677
}
7778

78-
tinykvm::RSP server {*vm, 2159};
79+
tinykvm::RSP server {filename, *vm, 2159};
7980
printf("Waiting for connection localhost:2159...\n");
8081
auto client = server.accept();
8182
if (client != nullptr) {

0 commit comments

Comments
 (0)