From a5b1a66d21437328a97004e256cb148114354a69 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Fri, 16 Sep 2022 13:23:05 +0200 Subject: [PATCH] Add platform abstraction This will allow us to iterate conservative roots from stacks and static data segments. --- Makefile | 6 ++- gc-platform-gnu-linux.c | 101 ++++++++++++++++++++++++++++++++++++++++ gc-platform.h | 20 ++++++++ 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 gc-platform-gnu-linux.c create mode 100644 gc-platform.h diff --git a/Makefile b/Makefile index 9244c246c..f8e2c9ba9 100644 --- a/Makefile +++ b/Makefile @@ -6,11 +6,15 @@ CFLAGS=-Wall -O2 -g -flto -fno-strict-aliasing -fvisibility=hidden -Wno-unused - INCLUDES=-I. LDFLAGS=-lpthread -flto COMPILE=$(CC) $(CFLAGS) $(INCLUDES) +PLATFORM=gnu-linux ALL_TESTS=$(foreach COLLECTOR,$(COLLECTORS),$(addprefix $(COLLECTOR)-,$(TESTS))) all: $(ALL_TESTS) +gc-platform.o: gc-platform.h gc-platform-$(PLATFORM).c gc-visibility.h + $(COMPILE) -o $@ -c gc-platform-$(PLATFORM).c + bdw-%-gc.o: semi.c %-embedder.h %.c $(COMPILE) `pkg-config --cflags bdw-gc` -include $*-embedder.h -o $@ -c bdw.c bdw-%.o: semi.c %.c @@ -43,7 +47,7 @@ parallel-generational-whippet-%-gc.o: whippet.c %-embedder.h large-object-space. parallel-generational-whippet-%.o: whippet.c %.c $(COMPILE) -DGC_PARALLEL=1 -DGC_GENERATIONAL=1 -DGC_PRECISE=1 -include whippet-attrs.h -o $@ -c $*.c -%: %.o %-gc.o +%: %.o %-gc.o gc-platform.o $(CC) $(LDFLAGS) $($*_LDFLAGS) -o $@ $^ check: $(addprefix test-$(TARGET),$(TARGETS)) diff --git a/gc-platform-gnu-linux.c b/gc-platform-gnu-linux.c new file mode 100644 index 000000000..b0b9f0983 --- /dev/null +++ b/gc-platform-gnu-linux.c @@ -0,0 +1,101 @@ +// For pthread_getattr_np. +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#define GC_IMPL 1 + +#include "debug.h" +#include "gc-assert.h" +#include "gc-inline.h" +//#include "gc-stack.h" + +void gc_platform_init(void) { + // Nothing to do. +} + +static uintptr_t fallback_current_thread_stack_base(void) GC_NEVER_INLINE; +static uintptr_t fallback_current_thread_stack_base(void) { + // Sloppily assume that there are very few frames between us and the + // thread entry or main function, and that therefore we haven't + // consumed more than a page of stack; we can then just round up the + // stack pointer to the page boundary. + fprintf(stderr, + "Using fallback strategy to capture stack base for thread %p.\n", + (void*)pthread_self()); + int local; + uintptr_t hot = (uintptr_t)&local; + size_t page_size = getpagesize(); + return (hot + page_size) & ~(page_size - 1); +} + +uintptr_t gc_platform_current_thread_stack_base(void) { + pthread_t me = pthread_self(); + pthread_attr_t attr; + int err = pthread_getattr_np(me, &attr); + if (err) { + errno = err; + // This case can occur for the main thread when running in a + // filesystem without /proc/stat. + perror("Failed to capture stack base via pthread_getattr_np"); + return fallback_current_thread_stack_base(); + } + + void *stack_low_addr; + size_t stack_size; + err = pthread_attr_getstack(&attr, &stack_low_addr, &stack_size); + pthread_attr_destroy(&attr); + if (err) { + // Should never occur. + errno = err; + perror("pthread_attr_getstack"); + return fallback_current_thread_stack_base(); + } + + return (uintptr_t)stack_low_addr + stack_size; +} + +struct visit_data { + void (*f)(uintptr_t start, uintptr_t end, void *data); + void *data; +}; + +static int visit_roots(struct dl_phdr_info *info, size_t size, void *data) { + struct visit_data *visit_data = data; + uintptr_t object_addr = info->dlpi_addr; + const char *object_name = info->dlpi_name; + const ElfW(Phdr) *program_headers = info->dlpi_phdr; + size_t program_headers_count = info->dlpi_phnum; + + // From the loader's perspective, an ELF image is broken up into + // "segments", each of which is described by a "program header". + // Treat all writable data segments as potential edges into the + // GC-managed heap. + // + // Note that there are some RELRO segments which are initially + // writable but then remapped read-only. BDW-GC will exclude these, + // but we just punt for the time being and treat them as roots + for (size_t i = 0; i < program_headers_count; i++) { + const ElfW(Phdr) *p = &program_headers[i]; + if (p->p_type == PT_LOAD && (p->p_flags & PF_W)) { + uintptr_t start = p->p_vaddr + object_addr; + uintptr_t end = start + p->p_memsz; + DEBUG("found roots for '%s': [%p,%p)\n", object_name, + (void*)start, (void*)end); + visit_data->f(start, end, visit_data->data); + } + } + + return 0; +} + +void gc_platform_visit_global_conservative_roots(void (*f)(uintptr_t start, + uintptr_t end, + void *data), + void *data) { + struct visit_data visit_data = { f, data }; + dl_iterate_phdr(visit_roots, &visit_data); +} diff --git a/gc-platform.h b/gc-platform.h new file mode 100644 index 000000000..acc0096f9 --- /dev/null +++ b/gc-platform.h @@ -0,0 +1,20 @@ +#ifndef GC_PLATFORM_H +#define GC_PLATFORM_H + +#ifndef GC_IMPL +#error internal header file, not part of API +#endif + +#include + +#include "gc-visibility.h" + +GC_INTERNAL void gc_platform_init(void); +GC_INTERNAL uintptr_t gc_platform_current_thread_stack_base(void); +GC_INTERNAL +void gc_platform_visit_global_conservative_roots(void (*f)(uintptr_t start, + uintptr_t end, + void *data), + void *data); + +#endif // GC_PLATFORM_H