Skip to content

Commit bf02942

Browse files
gamemasterplcrasky
authored andcommitted
Add DSOs: dynamic library support
1 parent 6fe5fba commit bf02942

17 files changed

+2301
-39
lines changed

Diff for: Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ libdragon.a: $(BUILD_DIR)/n64sys.o $(BUILD_DIR)/interrupt.o $(BUILD_DIR)/backtra
5555
$(BUILD_DIR)/rdpq/rdpq_debug.o $(BUILD_DIR)/rdpq/rdpq_tri.o \
5656
$(BUILD_DIR)/rdpq/rdpq_rect.o $(BUILD_DIR)/rdpq/rdpq_mode.o \
5757
$(BUILD_DIR)/rdpq/rdpq_sprite.o $(BUILD_DIR)/rdpq/rdpq_tex.o \
58-
$(BUILD_DIR)/rdpq/rdpq_attach.o
58+
$(BUILD_DIR)/rdpq/rdpq_attach.o $(BUILD_DIR)/dlfcn.o
5959
@echo " [AR] $@"
6060
$(N64_AR) -rcs -o $@ $^
6161

@@ -88,6 +88,7 @@ install: install-mk libdragon
8888
mkdir -p $(INSTALLDIR)/mips64-elf/lib
8989
install -Cv -m 0644 libdragon.a $(INSTALLDIR)/mips64-elf/lib/libdragon.a
9090
install -Cv -m 0644 n64.ld $(INSTALLDIR)/mips64-elf/lib/n64.ld
91+
install -Cv -m 0644 dso.ld $(INSTALLDIR)/mips64-elf/lib/dso.ld
9192
install -Cv -m 0644 rsp.ld $(INSTALLDIR)/mips64-elf/lib/rsp.ld
9293
install -Cv -m 0644 libdragonsys.a $(INSTALLDIR)/mips64-elf/lib/libdragonsys.a
9394
mkdir -p $(INSTALLDIR)/mips64-elf/include
@@ -149,6 +150,7 @@ install: install-mk libdragon
149150
install -Cv -m 0644 include/rdpq_debug.h $(INSTALLDIR)/mips64-elf/include/rdpq_debug.h
150151
install -Cv -m 0644 include/rdpq_macros.h $(INSTALLDIR)/mips64-elf/include/rdpq_macros.h
151152
install -Cv -m 0644 include/rdpq_constants.h $(INSTALLDIR)/mips64-elf/include/rdpq_constants.h
153+
install -Cv -m 0644 include/dlfcn.h $(INSTALLDIR)/mips64-elf/include/dlfcn.h
152154
install -Cv -m 0644 include/rsp_rdpq.inc $(INSTALLDIR)/mips64-elf/include/rsp_rdpq.inc
153155
mkdir -p $(INSTALLDIR)/mips64-elf/include/libcart
154156
install -Cv -m 0644 src/libcart/cart.h $(INSTALLDIR)/mips64-elf/include/libcart/cart.h

Diff for: dso.ld

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
SECTIONS {
2+
/* Write text section */
3+
.text : {
4+
*(.text)
5+
*(.text.*)
6+
*(.init)
7+
*(.fini)
8+
*(.gnu.linkonce.t.*)
9+
}
10+
.eh_frame_hdr : { *(.eh_frame_hdr) }
11+
/* Write exception frames which must be 4-byte aligned to satisfy MIPS requirements */
12+
.eh_frame ALIGN(4) : {
13+
__EH_FRAME_BEGIN__ = .; /* Define symbol for accessing eh_frame section */
14+
KEEP (*(.eh_frame))
15+
/* Add terminator to section */
16+
LONG(0);
17+
}
18+
.gcc_except_table : { *(.gcc_except_table*) }
19+
20+
/* Write read-only data */
21+
.rodata : {
22+
*(.rdata)
23+
*(.rodata)
24+
*(.rodata.*)
25+
*(.gnu.linkonce.r.*)
26+
}
27+
28+
/* Write constructors and destructors which each must be 4-byte aligned */
29+
.ctors ALIGN(4) : {
30+
LONG(0); /* Add terminator to CTOR list */
31+
KEEP(*(.ctors))
32+
KEEP(*(SORT(.ctors.*)))
33+
__CTOR_LIST__ = .-4; /* Define symbol for CTOR list */
34+
}
35+
36+
.dtors ALIGN(4) : {
37+
__DTOR_LIST__ = .; /* Define symbol for DTOR list */
38+
KEEP(*(SORT(.dtors.*)))
39+
KEEP(*(.dtors))
40+
LONG(0); /* Add terminator to DTOR list */
41+
}
42+
43+
/* Write data sections */
44+
45+
.data : {
46+
*(.data)
47+
*(.data.*)
48+
*(.gnu.linkonce.d.*)
49+
}
50+
51+
.sdata : {
52+
*(.sdata)
53+
*(.sdata.*)
54+
*(.gnu.linkonce.s.*)
55+
/* Define 4 bytes of space for __dso_handle */
56+
. = ALIGN(4);
57+
__dso_handle = .;
58+
LONG(0);
59+
}
60+
61+
.lit8 : {
62+
*(.lit8)
63+
}
64+
65+
.lit4 : {
66+
*(.lit4)
67+
}
68+
69+
/* Write bss sections */
70+
.sbss (NOLOAD) : {
71+
*(.sbss)
72+
*(.sbss.*)
73+
*(.gnu.linkonce.sb.*)
74+
*(.scommon)
75+
*(.scommon.*)
76+
}
77+
78+
.bss (NOLOAD) : {
79+
*(.bss)
80+
*(.bss*)
81+
*(.gnu.linkonce.b.*)
82+
*(COMMON)
83+
}
84+
}

Diff for: include/dlfcn.h

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* @file dlfcn.h
3+
* @brief Dynamic linker subsystem
4+
* @ingroup dl
5+
*/
6+
#ifndef __LIBDRAGON_DLFCN_H
7+
#define __LIBDRAGON_DLFCN_H
8+
9+
/** @brief Flag for compatibility */
10+
#define RTLD_LAZY 0x0
11+
/** @brief Flag for compatibility */
12+
#define RTLD_NOW 0x0
13+
/** @brief Export symbols to other dynamic libraries */
14+
#define RTLD_GLOBAL 0x1
15+
/** @brief Don't export symbols to other dynamic libraries */
16+
#define RTLD_LOCAL 0x0
17+
/** @brief Never unload dynamic library from memory */
18+
#define RTLD_NODELETE 0x2
19+
/** @brief Don't load dynamic library to memory if not loaded */
20+
#define RTLD_NOLOAD 0x4
21+
22+
/** @brief Handle for dlsym to find first occurrence of symbol */
23+
#define RTLD_DEFAULT ((void *)-1)
24+
/** @brief Handle for dlsym to find next occurrence of symbol */
25+
#define RTLD_NEXT ((void *)-2)
26+
27+
/** @brief dl_addr info structure */
28+
typedef struct {
29+
/** @brief Pathname of shared object that contains address */
30+
const char *dli_fname;
31+
/** @brief Base address at which shared object is loaded */
32+
void *dli_fbase;
33+
/** @brief Name of symbol whose definition overlaps addr */
34+
const char *dli_sname;
35+
/** @brief Exact address of symbol named in dli_sname */
36+
void *dli_saddr;
37+
} Dl_info;
38+
39+
#ifdef __cplusplus
40+
extern "C" {
41+
#endif
42+
43+
// Embedded GDB script to auto-load DSO symbols
44+
#ifndef N64_DSO
45+
/// @cond
46+
asm(
47+
".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1\n"
48+
".byte 4\n"
49+
".ascii \"gdb.inlined-script-dso-autoload\\n\"\n"
50+
".ascii \"import gdb\\n\"\n"
51+
".ascii \"class BreakpointDsoLoad(gdb.Breakpoint):\\n\"\n"
52+
".ascii \" def stop(self):\\n\"\n"
53+
".ascii \" frame = gdb.selected_frame()\\n\"\n"
54+
".ascii \" src_elf = gdb.execute('printf \\\"%s\\\", module->src_elf', False, True)\\n\"\n"
55+
".ascii \" prog_base = int(gdb.execute('printf \\\"%x\\\", module->prog_base', False, True), 16)\\n\"\n"
56+
".ascii \" print(\\\"Loading overlay: \\\", src_elf, \\\"(text:\\\", hex(prog_base), \\\")\\\")\\n\"\n"
57+
".ascii \" gdb.execute(\\\"add-symbol-file -readnow \\\" + src_elf + \\\" \\\" + hex(prog_base), False, True)\\n\"\n"
58+
".ascii \" return False\\n\"\n"
59+
".ascii \"class BreakpointDsoFree(gdb.Breakpoint):\\n\"\n"
60+
".ascii \" def stop(self):\\n\"\n"
61+
".ascii \" frame = gdb.selected_frame()\\n\"\n"
62+
".ascii \" src_elf = gdb.execute('printf \\\"%s\\\", module->src_elf', False, True)\\n\"\n"
63+
".ascii \" prog_base = int(gdb.execute('printf \\\"%x\\\", module->prog_base', False, True), 16)\\n\"\n"
64+
".ascii \" print(\\\"Unloading overlay: \\\", src_elf, \\\"(text:\\\", hex(prog_base), \\\")\\\")\\n\"\n"
65+
".ascii \" gdb.execute(\\\"remove-symbol-file -a \\\" + hex(prog_base), False, True)\\n\"\n"
66+
".ascii \" return False\\n\"\n"
67+
".ascii \"bp_load = BreakpointDsoLoad(\\\"__dl_insert_module\\\")\\n\"\n"
68+
".ascii \"bp_load.silent = True\\n\"\n"
69+
".ascii \"bl_free = BreakpointDsoFree(\\\"__dl_remove_module\\\")\\n\"\n"
70+
".ascii \"bl_free.silent = True\\n\"\n"
71+
".byte 0\n"
72+
".popsection\n"
73+
);
74+
/// @endcond
75+
#endif
76+
77+
/**
78+
* @brief Open dynamic library
79+
*
80+
* @param filename Path to dynamic library
81+
* @param mode Flags for loading dynamic library
82+
* @return Handle for loaded dynamic library
83+
*/
84+
void *dlopen(const char *filename, int mode);
85+
86+
/**
87+
* @brief Grab symbol from loaded dynamic library
88+
*
89+
* @param handle Dynamic library handle to search symbol from
90+
* @param symbol Name of symbol to search for
91+
* @return Pointer to symbol
92+
*/
93+
void *dlsym(void *handle, const char *symbol);
94+
95+
/**
96+
* @brief Close loaded dynamic library
97+
*
98+
* @param handle Dynamic library handle to close
99+
* @return Whether an error occurred
100+
*/
101+
int dlclose(void *handle);
102+
103+
/**
104+
* @brief Convert address to symbol
105+
*
106+
* @param addr Address to search
107+
* @param info Info of symbol found
108+
* @return Zero on success and non-zero on failure
109+
*/
110+
int dladdr(const void *addr, Dl_info *info);
111+
112+
/**
113+
* @brief Return last error that occurred in dynamic linker
114+
*
115+
* @return String describing last error occurring in dynamic linker
116+
*/
117+
char *dlerror(void);
118+
119+
#ifdef __cplusplus
120+
}
121+
#endif
122+
123+
#endif

Diff for: include/libdragon.h

+1
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,6 @@
6464
#include "surface.h"
6565
#include "sprite.h"
6666
#include "debugcpp.h"
67+
#include "dlfcn.h"
6768

6869
#endif

Diff for: n64.mk

+50-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ N64_ROM_SAVETYPE = # Supported savetypes: none eeprom4k eeprom16 sram256k sram76
99
N64_ROM_RTC = # Set to true to enable the Joybus Real-Time Clock
1010
N64_ROM_REGIONFREE = # Set to true to allow booting on any console region
1111
N64_ROM_ELFCOMPRESS = 1 # Set compression level of ELF file in ROM
12+
N64_ROM_DSOCOMPRESS ?= 1 # Set compression level of DSOs file in ROM
1213

1314
# Override this to use a toolchain installed separately from libdragon
1415
N64_GCCPREFIX ?= $(N64_INST)
@@ -39,6 +40,9 @@ N64_SYM = $(N64_BINDIR)/n64sym
3940
N64_ELFCOMPRESS = $(N64_BINDIR)/n64elfcompress
4041
N64_AUDIOCONV = $(N64_BINDIR)/audioconv64
4142
N64_MKSPRITE = $(N64_BINDIR)/mksprite
43+
N64_DSO = $(N64_BINDIR)/n64dso
44+
N64_DSOEXTERN = $(N64_BINDIR)/n64dso-extern
45+
N64_DSOMSYM = $(N64_BINDIR)/n64dso-msym
4246

4347
N64_C_AND_CXX_FLAGS = -march=vr4300 -mtune=vr4300 -I$(N64_INCLUDEDIR)
4448
N64_C_AND_CXX_FLAGS += -falign-functions=32 # NOTE: if you change this, also change backtrace() in backtrace.c
@@ -51,6 +55,7 @@ N64_CXXFLAGS = $(N64_C_AND_CXX_FLAGS) -std=gnu++17
5155
N64_ASFLAGS = -mtune=vr4300 -march=vr4300 -Wa,--fatal-warnings -I$(N64_INCLUDEDIR)
5256
N64_RSPASFLAGS = -march=mips1 -mabi=32 -Wa,--fatal-warnings -I$(N64_INCLUDEDIR)
5357
N64_LDFLAGS = -g -L$(N64_LIBDIR) -ldragon -lm -ldragonsys -Tn64.ld --gc-sections --wrap __do_global_ctors
58+
N64_DSOLDFLAGS = --emit-relocs --unresolved-symbols=ignore-all --nmagic -T$(N64_LIBDIR)/dso.ld
5459

5560
N64_TOOLFLAGS = --title $(N64_ROM_TITLE)
5661
N64_ED64ROMCONFIGFLAGS = $(if $(N64_ROM_SAVETYPE),--savetype $(N64_ROM_SAVETYPE))
@@ -90,9 +95,14 @@ RSPASFLAGS+=-MMD
9095
@rm -f $@
9196
DFS_FILE="$(filter %.dfs, $^)"; \
9297
if [ -z "$$DFS_FILE" ]; then \
93-
$(N64_TOOL) $(N64_TOOLFLAGS) --toc --output $@ --align 256 $<.stripped --align 8 $<.sym; \
98+
$(N64_TOOL) $(N64_TOOLFLAGS) --toc --output $@ --align 256 $<.stripped --align 8 $<.sym --align 8; \
9499
else \
95-
$(N64_TOOL) $(N64_TOOLFLAGS) --toc --output $@ --align 256 $<.stripped --align 8 $<.sym --align 16 "$$DFS_FILE"; \
100+
MSYM_FILE="$(filter %.msym, $^)"; \
101+
if [ -z "$$MSYM_FILE" ]; then \
102+
$(N64_TOOL) $(N64_TOOLFLAGS) --toc --output $@ --align 256 $<.stripped --align 8 $<.sym --align 16 "$$DFS_FILE"; \
103+
else \
104+
$(N64_TOOL) $(N64_TOOLFLAGS) --toc --output $@ --align 256 $<.stripped --align 8 $<.sym --align 8 "$$MSYM_FILE" --align 16 "$$DFS_FILE"; \
105+
fi \
96106
fi
97107
if [ ! -z "$(strip $(N64_ED64ROMCONFIGFLAGS))" ]; then \
98108
$(N64_ED64ROMCONFIG) $(N64_ED64ROMCONFIGFLAGS) $@; \
@@ -157,11 +167,47 @@ $(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cpp
157167
%.elf: $(N64_LIBDIR)/libdragon.a $(N64_LIBDIR)/libdragonsys.a $(N64_LIBDIR)/n64.ld
158168
@mkdir -p $(dir $@)
159169
@echo " [LD] $@"
160-
# We always use g++ to link except for ucode because of the inconsistencies
170+
# We always use g++ to link except for ucode and DSO files because of the inconsistencies
161171
# between ld when it comes to global ctors dtors. Also see __do_global_ctors
162-
$(CXX) -o $@ $(filter-out $(N64_LIBDIR)/n64.ld,$^) -lc $(patsubst %,-Wl$(COMMA)%,$(LDFLAGS)) -Wl,-Map=$(BUILD_DIR)/$(notdir $(basename $@)).map
172+
EXTERNS_FILE="$(filter %.externs, $^)"; \
173+
if [ -z "$$EXTERNS_FILE" ]; then \
174+
$(CXX) -o $@ $(filter %.o, $^) $(filter-out $(N64_LIBDIR)/libdragon.a $(N64_LIBDIR)/libdragonsys.a, $(filter %.a, $^)) \
175+
-lc $(patsubst %,-Wl$(COMMA)%,$(LDFLAGS)) -Wl,-Map=$(BUILD_DIR)/$(notdir $(basename $@)).map; \
176+
else \
177+
$(CXX) -o $@ $(filter %.o, $^) $(filter-out $(N64_LIBDIR)/libdragon.a $(N64_LIBDIR)/libdragonsys.a, $(filter %.a, $^)) \
178+
-lc $(patsubst %,-Wl$(COMMA)%,$(LDFLAGS)) -Wl,-T"$$EXTERNS_FILE" -Wl,-Map=$(BUILD_DIR)/$(notdir $(basename $@)).map; \
179+
fi
163180
$(N64_SIZE) -G $@
164181

182+
183+
# Change all the dependency chain of DSO files to use the N64 toolchain.
184+
%.dso: CC=$(N64_CC)
185+
%.dso: CXX=$(N64_CXX)
186+
%.dso: AS=$(N64_AS)
187+
%.dso: LD=$(N64_LD)
188+
%.dso: CFLAGS+=$(N64_CFLAGS) -mno-gpopt -DN64_DSO $(DSO_CFLAGS)
189+
%.dso: CXXFLAGS+=$(N64_CXXFLAGS) -mno-gpopt -DN64_DSO $(DSO_CXXFLAGS)
190+
%.dso: ASFLAGS+=$(N64_ASFLAGS)
191+
%.dso: RSPASFLAGS+=$(N64_RSPASFLAGS)
192+
193+
%.dso: $(N64_LIBDIR)/dso.ld
194+
$(eval DSO_ELF=$(basename $(BUILD_DIR)/dso_elf/$@).elf)
195+
@mkdir -p $(dir $@)
196+
@mkdir -p $(dir $(DSO_ELF))
197+
@echo " [DSO] $@"
198+
$(N64_LD) $(N64_DSOLDFLAGS) -Map=$(basename $(DSO_ELF)).map -o $(DSO_ELF) $(filter %.o, $^)
199+
$(N64_SIZE) -G $(DSO_ELF)
200+
$(N64_DSO) -o $(dir $@) -c $(N64_ROM_DSOCOMPRESS) $(DSO_ELF)
201+
$(N64_SYM) $(DSO_ELF) $@.sym
202+
203+
%.externs:
204+
@echo " [DSOEXTERN] $@"
205+
$(N64_DSOEXTERN) -o $@ $^
206+
207+
%.msym: %.elf
208+
@echo " [MSYM] $@"
209+
$(N64_DSOMSYM) $< $@
210+
165211
ifneq ($(V),1)
166212
.SILENT:
167213
endif

0 commit comments

Comments
 (0)