mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-21 04:22:50 +02:00
Merge pull request #14 from devkitPro/dynamic-parsing
Linkscript overhaul & .dynamic section parsing
This commit is contained in:
commit
5ef41f9e07
@ -6,137 +6,157 @@ PHDRS
|
||||
code PT_LOAD FLAGS(5) /* Read | Execute */;
|
||||
rodata PT_LOAD FLAGS(4) /* Read */;
|
||||
data PT_LOAD FLAGS(6) /* Read | Write */;
|
||||
dyn PT_DYNAMIC;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* =========== CODE section =========== */
|
||||
|
||||
PROVIDE(__start__ = 0x0);
|
||||
. = __start__;
|
||||
|
||||
.text ALIGN(0x1000) :
|
||||
.crt0 :
|
||||
{
|
||||
KEEP (*(.crt0))
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
.init :
|
||||
{
|
||||
/* .init */
|
||||
KEEP( *(.crt0) )
|
||||
KEEP( *(.init) )
|
||||
. = ALIGN(4);
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
/* .text */
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.glue_7)
|
||||
*(.glue_7t)
|
||||
*(.stub)
|
||||
*(.gnu.warning)
|
||||
*(.gnu.linkonce.t*)
|
||||
. = ALIGN(4);
|
||||
.plt :
|
||||
{
|
||||
*(.plt)
|
||||
*(.iplt)
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
/* .fini */
|
||||
.text :
|
||||
{
|
||||
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
|
||||
*(.text.exit .text.exit.*)
|
||||
*(.text.startup .text.startup.*)
|
||||
*(.text.hot .text.hot.*)
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
.fini :
|
||||
{
|
||||
KEEP( *(.fini) )
|
||||
. = ALIGN(4);
|
||||
} : code
|
||||
. = ALIGN(8);
|
||||
} :code
|
||||
|
||||
/* =========== RODATA section =========== */
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
.rodata ALIGN(0x1000) :
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata)
|
||||
*(.roda)
|
||||
*(.rodata.*)
|
||||
*all.rodata*(*)
|
||||
*(.gnu.linkonce.r*)
|
||||
SORT(CONSTRUCTORS)
|
||||
. = ALIGN(4);
|
||||
} : rodata
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
. = ALIGN(8);
|
||||
} :rodata
|
||||
|
||||
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } : rodata
|
||||
__exidx_start = .;
|
||||
ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } : rodata
|
||||
__exidx_end = .;
|
||||
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } :rodata
|
||||
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } :rodata
|
||||
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } :rodata
|
||||
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } : rodata
|
||||
|
||||
.dynamic : { *(.dynamic) } :rodata :dyn
|
||||
.interp : { *(.interp) } :rodata
|
||||
.note.gnu.build-id : { *(.note.gnu.build-id) } :rodata
|
||||
.hash : { *(.hash) } :rodata
|
||||
.gnu.hash : { *(.gnu.hash) } :rodata
|
||||
.gnu.version : { *(.gnu.version) } :rodata
|
||||
.gnu.version_d : { *(.gnu.version_d) } :rodata
|
||||
.gnu.version_r : { *(.gnu.version_r) } :rodata
|
||||
.dynsym : { *(.dynsym) } :rodata
|
||||
.dynstr : { *(.dynstr) } :rodata
|
||||
.rela.dyn : { *(.rela.*) } :rodata
|
||||
|
||||
/* =========== DATA section =========== */
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
.data ALIGN(0x1000) :
|
||||
{
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.gnu.linkonce.d*)
|
||||
CONSTRUCTORS
|
||||
. = ALIGN(4);
|
||||
__got_start__ = .;
|
||||
*(.got)
|
||||
__got_end__ = .;
|
||||
} : data
|
||||
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } :data
|
||||
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } :data
|
||||
.gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } : data
|
||||
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } :data
|
||||
|
||||
|
||||
.tdata ALIGN(4) :
|
||||
.tdata ALIGN(8) :
|
||||
{
|
||||
__tdata_lma = .;
|
||||
*(.tdata)
|
||||
*(.tdata.*)
|
||||
*(.gnu.linkonce.td.*)
|
||||
. = ALIGN(4);
|
||||
*(.tdata .tdata.* .gnu.linkonce.td.*)
|
||||
. = ALIGN(8);
|
||||
__tdata_lma_end = .;
|
||||
} : data
|
||||
} :data
|
||||
|
||||
.tbss ALIGN(4) :
|
||||
.tbss ALIGN(8) :
|
||||
{
|
||||
*(.tbss)
|
||||
*(.tbss.*)
|
||||
*(.gnu.linkonce.tb.*)
|
||||
*(.tcommon)
|
||||
. = ALIGN(4);
|
||||
} : data
|
||||
*(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon)
|
||||
. = ALIGN(8);
|
||||
} :data
|
||||
|
||||
.preinit_array ALIGN(4) :
|
||||
.preinit_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE (__preinit_array_end = .);
|
||||
} : data
|
||||
} :data
|
||||
|
||||
.init_array ALIGN(4) :
|
||||
.init_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array))
|
||||
PROVIDE (__init_array_end = .);
|
||||
} : data
|
||||
} :data
|
||||
|
||||
.fini_array ALIGN(4) :
|
||||
.fini_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__fini_array_start = .);
|
||||
KEEP (*(.fini_array))
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
PROVIDE (__fini_array_end = .);
|
||||
} : data
|
||||
} :data
|
||||
|
||||
.ctors ALIGN(4) :
|
||||
.ctors ALIGN(8) :
|
||||
{
|
||||
KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
} : data
|
||||
} :data
|
||||
|
||||
.dtors ALIGN(4) :
|
||||
.dtors ALIGN(8) :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
} : data
|
||||
} :data
|
||||
|
||||
__got_start__ = .;
|
||||
|
||||
.got : { *(.got) *(.igot) } :data
|
||||
.got.plt : { *(.got.plt) *(.igot.plt) } :data
|
||||
|
||||
__got_end__ = .;
|
||||
|
||||
.data ALIGN(8) :
|
||||
{
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
SORT(CONSTRUCTORS)
|
||||
} :data
|
||||
|
||||
__bss_start__ = .;
|
||||
.bss ALIGN(4) :
|
||||
.bss ALIGN(8) :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(.gnu.linkonce.b*)
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
. = ALIGN(8);
|
||||
|
||||
/* Reserve space for the TLS segment of the main thread */
|
||||
__tls_start = .;
|
||||
|
@ -1,7 +1,7 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T switch.ld%s -d --gc-sections
|
||||
%(old_link) -T switch.ld%s -pie --gc-sections -z text
|
||||
|
||||
*startfile:
|
||||
switch_crt0%O%s crti%O%s crtbegin%O%s
|
||||
|
@ -3,55 +3,39 @@
|
||||
|
||||
_start:
|
||||
bl startup
|
||||
|
||||
.word 0x454d4f48
|
||||
.word 0x57455242
|
||||
.ascii "HOMEBREW"
|
||||
|
||||
startup:
|
||||
// get aslr base
|
||||
sub x28, x30, #4
|
||||
|
||||
// clear .bss
|
||||
ldr x0, =__bss_start__
|
||||
ldr x1, =__bss_end__
|
||||
adrp x0, __bss_start__
|
||||
adrp x1, __bss_end__
|
||||
add x0, x0, #:lo12:__bss_start__
|
||||
add x1, x1, #:lo12:__bss_end__
|
||||
sub x1, x1, x0 // calculate size
|
||||
add x1, x1, #7 // round up to 8
|
||||
bic x1, x1, #7
|
||||
mov x2, #0
|
||||
add x0, x0, x28 // relocate ptr
|
||||
|
||||
bss_loop:
|
||||
str x2, [x0], #8
|
||||
str xzr, [x0], #8
|
||||
subs x1, x1, #8
|
||||
bne bss_loop
|
||||
|
||||
// relocate .got
|
||||
ldr x0, =__got_start__
|
||||
ldr x1, =__got_end__
|
||||
sub x1, x1, x0 // calculate size
|
||||
add x1, x1, #7 // round up to 8
|
||||
bic x1, x1, #7
|
||||
add x0, x0, x28 // relocate ptr
|
||||
|
||||
got_loop:
|
||||
ldr x2, [x0]
|
||||
add x2, x2, x28
|
||||
str x2, [x0], #8
|
||||
subs x1, x1, #8
|
||||
bne got_loop
|
||||
|
||||
// process .dynamic section
|
||||
mov x0, x28
|
||||
ldr x3, =initSystem
|
||||
add x3, x3, x28
|
||||
blr x3
|
||||
adrp x1, _DYNAMIC
|
||||
add x1, x1, #:lo12:_DYNAMIC
|
||||
bl __nx_dynamic
|
||||
|
||||
// initialize system
|
||||
mov x0, x28
|
||||
bl __nx_init
|
||||
|
||||
// call entrypoint
|
||||
mov x0, #0 // argc
|
||||
mov x1, #0 // argv
|
||||
|
||||
ldr x3, =main
|
||||
add x3, x3, x28
|
||||
ldr x2, =__nx_exit
|
||||
add x2, x2, x28
|
||||
mov x30, x2
|
||||
br x3
|
||||
|
||||
adrp x30, __nx_exit
|
||||
add x30, x30, #:lo12:__nx_exit
|
||||
b main
|
||||
|
@ -32,9 +32,9 @@ INCLUDES := include
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a
|
||||
ARCH := -march=armv8-a -fPIC
|
||||
|
||||
CFLAGS := -g -Wall -Werror -fPIC \
|
||||
CFLAGS := -g -Wall -Werror \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
$(ARCH) \
|
||||
|
37
nx/source/system/dynamic.c
Normal file
37
nx/source/system/dynamic.c
Normal file
@ -0,0 +1,37 @@
|
||||
#include <switch/types.h>
|
||||
#include <elf.h>
|
||||
|
||||
void __nx_dynamic(uintptr_t base, const Elf64_Dyn* dyn)
|
||||
{
|
||||
const Elf64_Rela* rela = NULL;
|
||||
u64 relasz = 0;
|
||||
|
||||
for (; dyn->d_tag != DT_NULL; dyn++)
|
||||
{
|
||||
switch (dyn->d_tag)
|
||||
{
|
||||
case DT_RELA:
|
||||
rela = (const Elf64_Rela*)(base + dyn->d_un.d_ptr);
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
relasz = dyn->d_un.d_val / sizeof(Elf64_Rela);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rela == NULL)
|
||||
for (;;); // Panic
|
||||
|
||||
for (; relasz--; rela++)
|
||||
{
|
||||
switch (ELF64_R_TYPE(rela->r_info))
|
||||
{
|
||||
case R_AARCH64_RELATIVE:
|
||||
{
|
||||
u64* ptr = (u64*)(base + rela->r_offset);
|
||||
*ptr = base + rela->r_addend;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,99 +1,19 @@
|
||||
.text
|
||||
.global initSystem
|
||||
.type initSystem, %function
|
||||
.global __nx_init
|
||||
.type __nx_init, %function
|
||||
|
||||
initSystem:
|
||||
__nx_init:
|
||||
stp x29, x30, [sp, #-16]!
|
||||
adr x1, __nx_binarybase
|
||||
str x0, [x1]
|
||||
bl __libnx_init
|
||||
|
||||
bl __appInit
|
||||
bl __nx_libc_init_array
|
||||
bl __libc_init_array
|
||||
ldp x29, x30, [sp], #16
|
||||
ret
|
||||
|
||||
.global __nx_exit
|
||||
.type __nx_exit, %function
|
||||
.type __nx_exit, %function
|
||||
|
||||
__nx_exit:
|
||||
bl __nx_libc_fini_array
|
||||
bl __libc_fini_array
|
||||
bl __appExit
|
||||
|
||||
b __libnx_exit
|
||||
|
||||
__nx_libc_init_array:
|
||||
stp x29, x30, [sp, #-16]!
|
||||
stp x21, x22, [sp, #-16]!
|
||||
stp x19, x20, [sp, #-16]!
|
||||
adr x3, __nx_binarybase
|
||||
ldr x20, [x3]
|
||||
|
||||
ldr x0, =__preinit_array_start
|
||||
ldr x1, =__preinit_array_end
|
||||
sub x1, x1, x0
|
||||
add x21, x0, x20
|
||||
lsr x19, x1, #3
|
||||
cbz x19, __nx_libc_init_array_end0
|
||||
|
||||
__nx_libc_init_array_lp0:
|
||||
ldr x3, [x21], #8
|
||||
sub x19, x19, #1
|
||||
add x3, x3, x20
|
||||
blr x3
|
||||
cbnz x19, __nx_libc_init_array_lp0
|
||||
|
||||
__nx_libc_init_array_end0:
|
||||
bl _init
|
||||
|
||||
ldr x0, =__init_array_start
|
||||
ldr x1, =__init_array_end
|
||||
sub x1, x1, x0
|
||||
add x21, x0, x20
|
||||
lsr x19, x1, #3
|
||||
cbz x19, __nx_libc_init_array_end1
|
||||
|
||||
__nx_libc_init_array_lp1:
|
||||
ldr x3, [x21], #8
|
||||
sub x19, x19, #1
|
||||
add x3, x3, x20
|
||||
blr x3
|
||||
cbnz x19, __nx_libc_init_array_lp1
|
||||
|
||||
__nx_libc_init_array_end1:
|
||||
ldp x19, x20, [sp], #16
|
||||
ldp x21, x22, [sp], #16
|
||||
ldp x29, x30, [sp], #16
|
||||
ret
|
||||
|
||||
__nx_libc_fini_array:
|
||||
stp x29, x30, [sp, #-16]!
|
||||
stp x21, x22, [sp, #-16]!
|
||||
stp x19, x20, [sp, #-16]!
|
||||
adr x3, __nx_binarybase
|
||||
ldr x20, [x3]
|
||||
|
||||
ldr x0, =__fini_array_start
|
||||
ldr x1, =__fini_array_end
|
||||
sub x1, x1, x0
|
||||
add x21, x0, x20
|
||||
lsr x19, x1, #3
|
||||
cbz x19, __nx_libc_fini_array_end
|
||||
|
||||
__nx_libc_fini_array_lp:
|
||||
sub x19, x19, #1
|
||||
ldr x3, [x21, x19, lsl #3]
|
||||
add x3, x3, x20
|
||||
blr x3
|
||||
cbnz x19, __nx_libc_fini_array_lp
|
||||
|
||||
__nx_libc_fini_array_end:
|
||||
ldp x19, x20, [sp], #16
|
||||
ldp x21, x22, [sp], #16
|
||||
ldp x29, x30, [sp], #16
|
||||
ret
|
||||
|
||||
.data
|
||||
__nx_binarybase:
|
||||
.dword 0
|
||||
|
||||
|
@ -62,8 +62,8 @@ uint8_t* ReadEntireFile(const char* fn, size_t* len_out) {
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 3) {
|
||||
printf("%s <elf-file> <nso-file>\n", argv[0]);
|
||||
return 1;
|
||||
fprintf(stderr, "%s <elf-file> <nso-file>\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
NsoHeader nso_hdr;
|
||||
@ -72,48 +72,57 @@ int main(int argc, char* argv[]) {
|
||||
nso_hdr.Unk3 = 0x3f;
|
||||
|
||||
if (sizeof(NsoHeader) != 0x100) {
|
||||
printf("Bad compile environment!\n");
|
||||
return 1;
|
||||
fprintf(stderr, "Bad compile environment!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
size_t elf_len;
|
||||
uint8_t* elf = ReadEntireFile(argv[1], &elf_len);
|
||||
if (elf == NULL) {
|
||||
printf("Failed to open input!\n");
|
||||
return 1;
|
||||
fprintf(stderr, "Failed to open input!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (elf_len < sizeof(Elf64_Ehdr)) {
|
||||
printf("Input file doesn't fit ELF header!\n");
|
||||
return 1;
|
||||
fprintf(stderr, "Input file doesn't fit ELF header!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Elf64_Ehdr* hdr = (Elf64_Ehdr*) elf;
|
||||
if (hdr->e_machine != EM_AARCH64) {
|
||||
printf("Invalid ELF: expected AArch64!\n");
|
||||
return 1;
|
||||
fprintf(stderr, "Invalid ELF: expected AArch64!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Elf64_Off ph_end = hdr->e_phoff + 3 * sizeof(Elf64_Phdr);
|
||||
Elf64_Off ph_end = hdr->e_phoff + hdr->e_phnum * sizeof(Elf64_Phdr);
|
||||
|
||||
if (ph_end < hdr->e_phoff || ph_end > elf_len) {
|
||||
printf("Invalid ELF: phdrs outside file!\n");
|
||||
return 1;
|
||||
fprintf(stderr, "Invalid ELF: phdrs outside file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (hdr->e_phnum != 3) {
|
||||
printf("Invalid ELF: expected 3 phdrs!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Elf64_Phdr* phdr = (Elf64_Phdr*) &elf[hdr->e_phoff];
|
||||
size_t i;
|
||||
Elf64_Phdr* phdrs = (Elf64_Phdr*) &elf[hdr->e_phoff];
|
||||
size_t i, j = 0;
|
||||
size_t file_off = sizeof(NsoHeader);
|
||||
|
||||
uint8_t* comp_buf[3];
|
||||
int comp_sz[3];
|
||||
|
||||
for (i=0; i<3; i++, phdr++) {
|
||||
for (i=0; i<3; i++) {
|
||||
Elf64_Phdr* phdr = NULL;
|
||||
while (j < hdr->e_phnum) {
|
||||
Elf64_Phdr* cur = &phdrs[j++];
|
||||
if (cur->p_type == PT_LOAD) {
|
||||
phdr = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (phdr == NULL) {
|
||||
fprintf(stderr, "Invalid ELF: expected 3 loadable phdrs!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
nso_hdr.Segments[i].FileOff = file_off;
|
||||
nso_hdr.Segments[i].DstOff = phdr->p_vaddr;
|
||||
nso_hdr.Segments[i].DecompSz = phdr->p_filesz;
|
||||
@ -133,16 +142,16 @@ int main(int argc, char* argv[]) {
|
||||
comp_buf[i] = malloc(comp_max);
|
||||
|
||||
if (comp_buf[i] == NULL) {
|
||||
printf("Compressing: Out of memory!\n");
|
||||
return 1;
|
||||
fprintf(stderr, "Compressing: Out of memory!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// TODO check p_offset
|
||||
comp_sz[i] = LZ4_compress_default(&elf[phdr->p_offset], comp_buf[i], phdr->p_filesz, comp_max);
|
||||
|
||||
if (comp_sz[i] < 0) {
|
||||
printf("Failed to compress!\n");
|
||||
return 1;
|
||||
fprintf(stderr, "Failed to compress!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
nso_hdr.CompSz[i] = comp_sz[i];
|
||||
@ -152,8 +161,8 @@ int main(int argc, char* argv[]) {
|
||||
FILE* out = fopen(argv[2], "wb");
|
||||
|
||||
if (out == NULL) {
|
||||
printf("Failed to open output file!\n");
|
||||
return 1;
|
||||
fprintf(stderr, "Failed to open output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// TODO check retvals
|
||||
@ -162,5 +171,5 @@ int main(int argc, char* argv[]) {
|
||||
for (i=0; i<3; i++)
|
||||
fwrite(comp_buf[i], comp_sz[i], 1, out);
|
||||
|
||||
return 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user