diff --git a/programs/develop/clink/epep/epep.h b/programs/develop/clink/epep/epep.h index 9443b2e5ce..22954d3daa 100644 --- a/programs/develop/clink/epep/epep.h +++ b/programs/develop/clink/epep/epep.h @@ -61,6 +61,8 @@ typedef enum { EPEP_ERR_INVALID_BASE_RELOCATION_BLOCK_BASE_RELOCATION_OFFSET, EPEP_ERR_INVALID_SECTION_RELOCATION_OFFSET, EPEP_ERR_INVALID_LINENUMBER_OFFSET, + EPEP_ERR_INVALID_NUMBER_OF_RELOCATIONS_FOR_EXTENDED, + EPEP_ERR_END } EpepError; // @@ -285,10 +287,10 @@ typedef union { /// Returns non-zero if export table exists in the file int epep_has_export_table(Epep *epep); -/// Palces offset of export table into epep structrue +/// Palces offset of export table into epep structure int epep_read_export_table_offset(Epep *epep); -/// Palces export table into epep structrue +/// Palces export table into epep structure //! Needs to be called before next export functions int epep_read_export_directory(Epep *epep); @@ -360,8 +362,23 @@ typedef struct { uint16_t Type; } EpepCoffRelocation; +/// Gives a COFF Relocation by its index int epep_get_section_relocation_by_index(Epep *epep, EpepSectionHeader *sh, EpepCoffRelocation *rel, size_t index); +/// Checks if the section contains more than 2^16 - 1 relocations +int epep_section_contains_extended_relocations(Epep *epep, EpepSectionHeader *sh, int *result); + +/// Gives the section relocation count if the section contains more than 2^16 - 1 relocations +int epep_get_section_extended_number_of_relocations(Epep *epep, EpepSectionHeader *sh, size_t *result); + +/// Gives the meaningful COFF Relocation count +//! Returns the value to pass to the following _x functions in the last parameter +int epep_get_section_number_of_relocations_x(Epep *epep, EpepSectionHeader *sh, size_t *result, int *extended); + +/// Gives a meaningful COFF Relocation by its index +//! Requires the value returned by epep_get_section_number_of_relocations_x as the last argument +int epep_get_section_relocation_by_index_x(Epep *epep, EpepSectionHeader *sh, EpepCoffRelocation *rel, size_t index, int extended); + // // COFF Line Numbers // @@ -1004,6 +1021,49 @@ int epep_get_section_relocation_by_index(Epep *epep, EpepSectionHeader *sh, Epep return 1; } +int epep_section_contains_extended_relocations(Epep *epep, EpepSectionHeader *sh, int *result) { + const uint32_t flag_IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000; + if (sh->Characteristics & flag_IMAGE_SCN_LNK_NRELOC_OVFL) { + if (sh->NumberOfRelocations != 0xffff) { + epep->error_code = EPEP_ERR_INVALID_NUMBER_OF_RELOCATIONS_FOR_EXTENDED; + return 0; + } + *result = 1; + } else { + *result = 0; + } + return 1; +} + +int epep_get_section_extended_number_of_relocations(Epep *epep, EpepSectionHeader *sh, size_t *result) { + EpepCoffRelocation first_relocation; + if (!epep_get_section_relocation_by_index(epep, sh, &first_relocation, 0)) { + return 0; + } + *result = first_relocation.VirtualAddress; + return 1; +} + +int epep_get_section_number_of_relocations_x(Epep *epep, EpepSectionHeader *sh, size_t *result, int *extended) { + if (!epep_section_contains_extended_relocations(epep, sh, extended)) { + return 0; + } + if (*extended) { + size_t real_number_of_relocations; + if (!epep_get_section_extended_number_of_relocations(epep, sh, &real_number_of_relocations)) { + return 0; + } + *result = real_number_of_relocations - 1; + } else { + *result = sh->NumberOfRelocations; + } + return 1; +} + +int epep_get_section_relocation_by_index_x(Epep *epep, EpepSectionHeader *sh, EpepCoffRelocation *rel, size_t index, int extended) { + return epep_get_section_relocation_by_index(epep, sh, rel, index + extended); +} + // // COFF Line Numbers // diff --git a/programs/develop/clink/main.c b/programs/develop/clink/main.c index 5c67da14dd..4e5da370ca 100644 --- a/programs/develop/clink/main.c +++ b/programs/develop/clink/main.c @@ -43,8 +43,12 @@ const char *epep_errors[] = { "EPEP_ERR_INVALID_BASE_RELOCATION_BLOCK_BASE_RELOCATION_OFFSET", "EPEP_ERR_INVALID_SECTION_RELOCATION_OFFSET", "EPEP_ERR_INVALID_LINENUMBER_OFFSET", + "EPEP_ERR_INVALID_NUMBER_OF_RELOCATIONS_FOR_EXTENDED", }; +static_assert(sizeof(epep_errors) / sizeof(epep_errors[0]) == EPEP_ERR_END, + "Each EPEP error should be stringified."); + typedef char *pchar; typedef struct { @@ -63,6 +67,8 @@ typedef struct { uint32_t characteristics; size_t size; size_t number_of_relocations; + // Number of relocations is greater than 2^16 - 1 + int number_of_relocations_is_extended; } SectionInfo; typedef struct { @@ -240,7 +246,12 @@ static void build(ObjectIr *ir, const char *outname) { fwrite32(out, offset_to_next_relocation); // PointerToRelocations offset_to_next_relocation += si.number_of_relocations * 10; fwrite32(out, 0); // PointerToLinenumbers - fwrite16(out, si.number_of_relocations); // NumberOfRelocations + // NumberOfRelocations + if (si.number_of_relocations_is_extended) { + fwrite16(out, 0xffff); + } else { + fwrite16(out, si.number_of_relocations); + } fwrite16(out, 0); // NumberOfLinenumbers fwrite32(out, si.characteristics); // Characteristics log_info("Done.\n"); @@ -294,6 +305,11 @@ static void build(ObjectIr *ir, const char *outname) { SectionInfo si = cdict_CStr_SectionInfo_get_v(&ir->info_per_section, name); log_info(" Writing relocations of %s {\n", name); + if (si.number_of_relocations_is_extended) { + EpepCoffRelocation rel = { 0 }; + rel.VirtualAddress = si.number_of_relocations; + fwrite(&rel, 1, 10, out); + } for (size_t i = 0; i < cvec_ObjIdSecId_size(&si.source); i++) { ObjIdSecId id = cvec_ObjIdSecId_at(&si.source, i); CoffObject *object = &ir->objects[id.obj_id]; @@ -313,10 +329,15 @@ static void build(ObjectIr *ir, const char *outname) { if (!epep_get_section_header_by_index(epep, &sh, id.sec_id)) { ERROR_EPEP(epep); } - for (size_t rel_i = 0; rel_i < sh.NumberOfRelocations; rel_i++) { + size_t number_of_relocations = 0; + int extended = 0; + if (!epep_get_section_number_of_relocations_x(epep, &sh, &number_of_relocations, &extended)) { + ERROR_EPEP(epep); + } + for (size_t rel_i = 0; rel_i < number_of_relocations; rel_i++) { EpepCoffRelocation rel = { 0 }; - if (!epep_get_section_relocation_by_index(epep, &sh, &rel, rel_i)) { + if (!epep_get_section_relocation_by_index_x(epep, &sh, &rel, rel_i, extended)) { ERROR_EPEP(epep); } log_info(" { %02x, %02x, %02x }", rel.VirtualAddress, rel.SymbolTableIndex, rel.Type); @@ -604,9 +625,22 @@ static ObjectIr parse_objects(int argc, char **argv) { size_t sec_offset = si.size; cvec_size_t_push_back(&objects[i].section_offsets, sec_offset); + size_t number_of_relocations = 0; + int unused = 0; + if (!epep_get_section_number_of_relocations_x(epep, &sh, &number_of_relocations, &unused)) { + ERROR_EPEP(epep); + } + si.size += sh.SizeOfRawData; si.characteristics |= sh.Characteristics; - si.number_of_relocations += sh.NumberOfRelocations; + si.number_of_relocations += number_of_relocations; + if (si.number_of_relocations > 0xffff && !si.number_of_relocations_is_extended) { + // One more relocation to store the actual relocation number + si.number_of_relocations++; + si.number_of_relocations_is_extended = 1; + const uint32_t flag_IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000; + si.characteristics |= flag_IMAGE_SCN_LNK_NRELOC_OVFL; + } cvec_ObjIdSecId_push_back(&si.source, (ObjIdSecId){ i, sec_i }); cdict_CStr_SectionInfo_add_vv(&info_per_section, strdup(name), si, CDICT_REPLACE_EXIST);