summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--mld/link.c46
-rw-r--r--mld/link.h8
-rw-r--r--mld/obj.c103
-rw-r--r--mld/seg.c50
-rw-r--r--mld/symtab.c4
5 files changed, 151 insertions, 60 deletions
diff --git a/mld/link.c b/mld/link.c
index 032c275..8e61ce2 100644
--- a/mld/link.c
+++ b/mld/link.c
@@ -12,8 +12,6 @@
#include "link.h"
#include "mips.h"
-#define SEC_ALIGN 0x1000
-
static int load_objects(struct linker *linker)
{
int max_entries = linker->args->in_count + 1;
@@ -168,7 +166,7 @@ static int relocate_symbol(struct linker *linker, struct object *obj,
struct symbol_table *symtab, const Elf32_Sym *sym)
{
size_t shndx = B16(sym->st_shndx);
- if (shndx == 0)
+ if (shndx == 0 || shndx == SHN_ABS)
return M_SUCCESS; // ignore this symbol
// find the given section
@@ -184,11 +182,9 @@ static int relocate_symbol(struct linker *linker, struct object *obj,
return M_ERROR;
}
- Elf32_Shdr const *shdr = &obj->shdr[shndx];
struct segment *sec = NULL;
for (size_t i = 0; i < obj->phdr_len; i++) {
- Elf32_Phdr *temp = &obj->phdr[i];
- if (PHDR_SHDR_MATCH(temp, shdr)) {
+ if (obj->phdr_to_shdr_mapping[i] == shndx) {
sec = &obj->segments[i];
break;
}
@@ -219,6 +215,20 @@ static int relocate_symbol(struct linker *linker, struct object *obj,
new.st_shndx = B16(new_shndx);
new.st_size = 0;
+ // rename symbol if its name is empty to perm placeholder
+ //if (B32(sym->st_name) == 0) {
+ // #define __MAX 512
+ // char name[__MAX];
+ // memset(name, 0, __MAX);
+ // strcat(name, "__sym");
+ // snprintf(name + strlen(name), __MAX - strlen(name), "%d",
+ // B32(sym->st_value));
+ // if (strtab_push(linker->symtab.strtab, name, &str_off))
+ // return M_ERROR;
+ // new.st_name = B32(str_off);
+ // #undef __MAX
+ //}
+
if (symtab_get(&linker->symtab, NULL, name, obj->index) == M_SUCCESS) {
ERROR("cannot link doubly defiend symbol '%s'", name);
return M_ERROR;
@@ -447,33 +457,38 @@ static int relocate_instruction_rela(struct linker *linker,
// 16bit absolute
if (vaddr_abs > UINT16_MAX)
warn = true;
- ins.immd = (uint16_t)vaddr_abs;
+ ins.immd += (uint16_t)vaddr_abs;
break;
case R_MIPS_PC16:
// 16bit relative shifted
if (vaddr_rel > INT16_MAX || vaddr_rel < INT16_MIN)
warn = true;
- ins.offset = vaddr_rel;
+ ins.offset += vaddr_rel;
break;
case R_MIPS_26:
+ case R_MIPS_JALR:
// 26bit absolute shifted
if (vaddr_abs >= (1 << 25))
warn = true;
- ins.target = (vaddr_abs & 0x0FFFFFFF) >> 2;
+ ins.target += (vaddr_abs & 0x0FFFFFFF) >> 2;
break;
case R_MIPS_PC26_S2:
// 26bit relative shifted
if (vaddr_rel >= (1 << 24) || -vaddr_rel > (1 << 24))
warn = true;
- ins.offs26 = vaddr_rel;
+ ins.offs26 += vaddr_rel;
break;
case R_MIPS_LO16:
// lo 16bit absolute
- ins.immd = (uint16_t)(vaddr_abs & 0xFFFF);
+ ins.immd += (uint16_t)(vaddr_abs & 0xFFFF);
break;
case R_MIPS_HI16:
// hi 16bit absolute
- ins.immd = (uint16_t)(vaddr_abs >> 16);
+ ins.immd += (uint16_t)(vaddr_abs >> 16);
+ break;
+ case R_MIPS_32:
+ // 32bit absolute
+ ins.raw = vaddr_abs;
break;
default:
ERROR("do not know how do handle relocation type [%d]", typ);
@@ -596,9 +611,6 @@ static void update_offsets(struct linker *linker)
static int write_file(struct linker *linker)
{
- extern char *current_file;
- current_file = linker->args->out_file;
-
int fd = open(linker->args->out_file, O_RDWR | O_CREAT, 0711);
if (fd < 0) {
PERROR("cannot write");
@@ -739,12 +751,16 @@ static void linker_free(struct linker *linker)
int link_files(struct linker_arguments args) {
struct linker linker;
+ extern char *current_file;
+
int res = M_SUCCESS;
if (res == M_SUCCESS)
res = linker_init(&linker, &args);
if (res == M_SUCCESS)
res = load_objects(&linker);
+
+ current_file = args.out_file;
if (res == M_SUCCESS)
res = relocate_segments(&linker);
if (res == M_SUCCESS)
diff --git a/mld/link.h b/mld/link.h
index 435a8d5..93a2ccd 100644
--- a/mld/link.h
+++ b/mld/link.h
@@ -35,6 +35,9 @@
#define TEXT_VADDR_MIN 0x00400000
#define DATA_VADDR_MIN 0x10000000
+// alignment of a section
+#define SEC_ALIGN 0x1000
+
// pre define
struct linker;
struct object;
@@ -42,6 +45,7 @@ struct segment;
struct string_table;
struct symbol_table;
struct symbol_table_mapping;
+struct glboff_table;
///
/// relocation table
@@ -220,6 +224,10 @@ struct object {
// program table
Elf32_Phdr *phdr;
size_t phdr_len;
+ bool phdr_local; // if phdr was created though malloc
+
+ // phdr <=> shdr mappings
+ uint32_t *phdr_to_shdr_mapping;
// object meta
const char *name;
diff --git a/mld/obj.c b/mld/obj.c
index b065316..e986a75 100644
--- a/mld/obj.c
+++ b/mld/obj.c
@@ -49,10 +49,10 @@ static int load_ehdr(struct object *object)
EHDR_ASSERT(type, sizeof(Elf32_Half));
EHDR_ASSERT(machine, sizeof(Elf32_Half));
EHDR_ASSERT(version, sizeof(Elf32_Word));
- EHDR_ASSERT(flags, sizeof(Elf32_Word));
+ // EHDR_ASSERT(flags, sizeof(Elf32_Word));
EHDR_ASSERT(ehsize, sizeof(Elf32_Half));
- EHDR_ASSERT(phentsize, sizeof(Elf32_Half));
- EHDR_ASSERT(shentsize, sizeof(Elf32_Half));
+ // EHDR_ASSERT(phentsize, sizeof(Elf32_Half));
+ // EHDR_ASSERT(shentsize, sizeof(Elf32_Half));
#undef EHDR_ASSERT
@@ -82,25 +82,96 @@ static int load_shdr(struct object *object)
}
/**
- * Map the phdr
+ * Create the phdr
*/
-static int load_phdr(struct object *object)
+static int create_phdr(struct object *object)
{
- size_t phdr_len = B16(object->ehdr->e_phentsize) *
- B16(object->ehdr->e_phnum);
- size_t phdr_off = B32(object->ehdr->e_phoff);
- object->phdr = (Elf32_Phdr *) (object->mapped + phdr_off);
- object->phdr_len = B16(object->ehdr->e_phnum);
+ uint32_t entries = 0;
+ for (uint32_t i = 0; i < object->shdr_len; i++) {
+ Elf32_Shdr *hdr = &object->shdr[i];
+ uint32_t type = B32(hdr->sh_type);
+
+ if (type != SHT_PROGBITS && type != SHT_NOBITS)
+ continue;
+
+ entries += 1;
+ }
- if (BOUND_CHK(object, phdr_len, phdr_off)) {
- ERROR("cannot read phdr");
+ Elf32_Phdr *phdr = malloc(entries * sizeof(Elf32_Phdr));
+ if (phdr == NULL) {
+ PERROR("cannot alloc");
return M_ERROR;
}
+ object->phdr = phdr;
+ object->phdr_len = entries;
+ object->phdr_local = true;
+
+ uint32_t *mapping = malloc(entries * sizeof(uint32_t));
+ if (mapping == NULL) {
+ PERROR("cannot alloc");
+ return M_ERROR;
+ }
+
+ object->phdr_to_shdr_mapping = mapping;
+
+ uint32_t index = 0;
+ for (uint32_t i = 0; i < object->shdr_len; i++) {
+ Elf32_Shdr *hdr = &object->shdr[i];
+ uint32_t type = B32(hdr->sh_type);
+
+ if (type != SHT_PROGBITS && type != SHT_NOBITS)
+ continue;
+ mapping[index] = i;
+ phdr[index++] = (Elf32_Phdr) {
+ .p_type = B32(PT_LOAD),
+ .p_flags = B32(
+ // execute
+ ((B32(hdr->sh_flags) & SHF_EXECINSTR)
+ ? PF_X : 0) |
+ // write
+ ((B32(hdr->sh_flags) & SHF_WRITE)
+ ? PF_W : 0) |
+ // read
+ ((B32(hdr->sh_flags) & SHF_ALLOC)
+ ? PF_R : 0)
+ ),
+ .p_offset = hdr->sh_offset,
+ .p_vaddr = hdr->sh_addr,
+ .p_paddr = hdr->sh_addr,
+ .p_filesz = hdr->sh_size,
+ .p_memsz = hdr->sh_size,
+ .p_align = B32(SEC_ALIGN),
+ };
+ }
+
return M_SUCCESS;
}
/**
+ * Map the phdr
+ */
+static int load_phdr(struct object *object)
+{
+ //size_t phdr_len = B16(object->ehdr->e_phentsize) *
+ // B16(object->ehdr->e_phnum);
+
+ //if (phdr_len < 1)
+ return create_phdr(object);
+
+ //size_t phdr_off = B32(object->ehdr->e_phoff);
+ //object->phdr = (Elf32_Phdr *) (object->mapped + phdr_off);
+ //object->phdr_len = B16(object->ehdr->e_phnum);
+
+ //if (BOUND_CHK(object, phdr_len, phdr_off)) {
+ // ERROR("cannot read phdr");
+ // return M_ERROR;
+ //}
+
+ //return M_SUCCESS;
+}
+
+/**
* Load the strtabs
*/
static int load_strtabs(struct object *object)
@@ -231,7 +302,7 @@ static int load_shstrtab(struct object *object)
*/
static int load_segments(struct object *object)
{
- object->segment_len = B16(object->ehdr->e_phnum);
+ object->segment_len = object->phdr_len;
object->segments = malloc(sizeof(struct segment) *
object->segment_len);
@@ -287,6 +358,8 @@ int object_load(struct object *object, char *path, uint32_t index)
object->mapped = NULL;
object->name = path;
object->index = index;
+ object->phdr_local = false;
+ object->phdr_to_shdr_mapping = NULL;
/** load the file */
if (map_file(object, path))
@@ -331,6 +404,10 @@ void object_free(struct object *obj)
free(obj->strtabs);
if (obj->segments != NULL)
free(obj->segments);
+ if (obj->phdr_local)
+ free(obj->phdr);
+ if (obj->phdr_to_shdr_mapping)
+ free(obj->phdr_to_shdr_mapping);
if (obj->fd > 0)
close(obj->fd);
if (obj->mapped != NULL && obj->mapped != MAP_FAILED)
diff --git a/mld/seg.c b/mld/seg.c
index 61c5296..61c337a 100644
--- a/mld/seg.c
+++ b/mld/seg.c
@@ -25,44 +25,30 @@ static int load_phdr(struct object *obj, struct segment *seg, size_t index)
*/
static int load_shdr(struct object *obj, struct segment *seg, size_t index)
{
- bool found = false;
+ uint32_t shndx = obj->phdr_to_shdr_mapping[index];
+ Elf32_Shdr *hdr = &obj->shdr[shndx];
- for (size_t i = 0; i < obj->shdr_len; i++) {
- Elf32_Shdr *hdr = &obj->shdr[i];
-
- // find shdr that matches the offset in phdr
- if (!PHDR_SHDR_MATCH(seg->phdr, hdr))
- continue;
-
- // get name
- uint32_t name = B32(hdr->sh_name);
- if (name >= obj->shstrtab->len) {
- ERROR("section name index [%d] out of bounds", name);
- return M_ERROR;
- }
- seg->name = &obj->shstrtab->data[name];
-
- // map bytes
- uint32_t len = B32(hdr->sh_size);
- uint32_t off = B32(hdr->sh_offset);
- seg->bytes = (unsigned char *) (obj->mapped + off);
-
- if (BOUND_CHK(obj, len, off)) {
- ERROR("cannot map seg %s:%d", seg->name, index);
- return M_ERROR;
- }
-
- found = true;
- seg->shdr = hdr;
- seg->shdr_idx = i;
- break;
+ // get name
+ uint32_t name = B32(hdr->sh_name);
+ if (name >= obj->shstrtab->len) {
+ ERROR("section name index [%d] out of bounds", name);
+ return M_ERROR;
}
+ seg->name = &obj->shstrtab->data[name];
- if (!found) {
- ERROR("cannot find shdr for segment [%d]", index);
+ // map bytes
+ uint32_t len = B32(hdr->sh_size);
+ uint32_t off = B32(hdr->sh_offset);
+ seg->bytes = (unsigned char *) (obj->mapped + off);
+
+ if (BOUND_CHK(obj, len, off)) {
+ ERROR("cannot map seg %s:%d", seg->name, index);
return M_ERROR;
}
+ seg->shdr = hdr;
+ seg->shdr_idx = shndx;
+
return M_SUCCESS;
}
diff --git a/mld/symtab.c b/mld/symtab.c
index 9417673..314a1c3 100644
--- a/mld/symtab.c
+++ b/mld/symtab.c
@@ -56,6 +56,10 @@ int symtab_get(struct symbol_table *symtab, Elf32_Sym **res, const char *name,
if (strcmp(name, symname) != 0)
continue;
+ // ignore absolute symbols
+ if (B16(sym->st_shndx) == SHN_ABS)
+ continue;
+
// only allow retrevial of local variables from the
// same object
if (sym->st_info >> 4 != STB_GLOBAL && symtab->map != NULL &&