[clink] Make it able to read and write more than 64k - 1 COFF relocation

These are changes from two commits from the upstream clink repo (https://github.com/mkostoevr/clink) and one commit from the upstream epep repo (https://github.com/mkostoevr/epep).

clink:

    Commit e63ed12ead17e47d77e848da0e7b9f4dd3ad5127
    Bugfix: Make it able to write more than 64k - 1 COFF relocations
    
    If the relocation count is greater than 0xffff then it can't fit
    in the NumberOfRelocations field of a section header. In order to
    specify greater relocation count IMAGE_SCN_LNK_NRELOC_OVFL flag
    should be added to Characteristics and the actual relocation count
    should be written into VirtualAddress field of the first COFF
    relocation.
    
    Commit 328fc3112a30fcaa808f2cad34028a6507d602a3
    Bugfix: Make it able to read more than 64k - 1 COFF relocations
    
    New EPEP API allows to read more than 64k - 1 relocations. Use it
    when reading relocation count for filling SectionInfo and when
    reading relocations for wriring the output file.

epep:

    Commit 3bed4e348a486c346d0a452c58c1d85e1805c09d
    Feature: Higher-level COFF relocations API
    
    Number of COFF relocations of a section is stored in the 16-bit
    NumberOfRelocations field of a section header. If a COFF object has
    more than 2^16 - 1 relocations, then the value does not fit in the
    field.
    
    To solve this problem, IMAGE_SCN_LNK_NRELOC_OVFL flag of a section
    header has been introduced. If this flag is set for the section,
    then the actual number of relocations is stored in the
    VirtualAddress field of the first relocation.
    
    If the flag is set, then NumberOfRelocations field of the section
    header should be equal to 0xffff, othervice the linker should give
    an error.
    
    So this patch introduces few functions adressing this mechanism.
    
    epep_section_contains_extended_relocations:
    
        Checks whether the section has more than 2^16 - 1 relocations.
        Retrns error if the IMAGE_SCN_LNK_NRELOC_OVFL flag is set, but
        the NumberOfRelocations field is not equal to 0xffff.
    
    epep_get_section_extended_number_of_relocations:
    
        Reads the number of COFF relocations from the VirtualAddress
        field of the first COFF relocation.
    
    epep_get_section_number_of_relocations_x:
    
        Gives the number of meaningful relocations of the section.
    
        If the section has less than 2^16 relocations, then returns the
        value of the NumberOfRelocations field of the section header,
        othervice reads the number of relocations from the first COFF
        relocation, but: since the first relocation in this case is not
        meaningful, gives the actual number of relocations minus one.
        This is used in the function documented below.
    
        Returns 1 in the last argument if the section contains extended
        number of relocations, 0 othervice.
    
    epep_get_section_relocation_by_index_x:
    
        If the section has less than 2^16 relocations, then just reads
        a relocation by the given index. In case if the section has
        extended number of relocations, the first relocation is not
        meaningful, so it is skipped, and the relocation at index + 1
        is read instead.



git-svn-id: svn://kolibrios.org@9927 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
boppan 2023-08-06 14:41:09 +00:00
parent 9a2d26f2a7
commit 18f26e7cf9
2 changed files with 100 additions and 6 deletions

View File

@ -61,6 +61,8 @@ typedef enum {
EPEP_ERR_INVALID_BASE_RELOCATION_BLOCK_BASE_RELOCATION_OFFSET, EPEP_ERR_INVALID_BASE_RELOCATION_BLOCK_BASE_RELOCATION_OFFSET,
EPEP_ERR_INVALID_SECTION_RELOCATION_OFFSET, EPEP_ERR_INVALID_SECTION_RELOCATION_OFFSET,
EPEP_ERR_INVALID_LINENUMBER_OFFSET, EPEP_ERR_INVALID_LINENUMBER_OFFSET,
EPEP_ERR_INVALID_NUMBER_OF_RELOCATIONS_FOR_EXTENDED,
EPEP_ERR_END
} EpepError; } EpepError;
// //
@ -285,10 +287,10 @@ typedef union {
/// Returns non-zero if export table exists in the file /// Returns non-zero if export table exists in the file
int epep_has_export_table(Epep *epep); 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); 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 //! Needs to be called before next export functions
int epep_read_export_directory(Epep *epep); int epep_read_export_directory(Epep *epep);
@ -360,8 +362,23 @@ typedef struct {
uint16_t Type; uint16_t Type;
} EpepCoffRelocation; } EpepCoffRelocation;
/// Gives a COFF Relocation by its index
int epep_get_section_relocation_by_index(Epep *epep, EpepSectionHeader *sh, EpepCoffRelocation *rel, size_t 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 // COFF Line Numbers
// //
@ -1004,6 +1021,49 @@ int epep_get_section_relocation_by_index(Epep *epep, EpepSectionHeader *sh, Epep
return 1; 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 // COFF Line Numbers
// //

View File

@ -43,8 +43,12 @@ const char *epep_errors[] = {
"EPEP_ERR_INVALID_BASE_RELOCATION_BLOCK_BASE_RELOCATION_OFFSET", "EPEP_ERR_INVALID_BASE_RELOCATION_BLOCK_BASE_RELOCATION_OFFSET",
"EPEP_ERR_INVALID_SECTION_RELOCATION_OFFSET", "EPEP_ERR_INVALID_SECTION_RELOCATION_OFFSET",
"EPEP_ERR_INVALID_LINENUMBER_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 char *pchar;
typedef struct { typedef struct {
@ -63,6 +67,8 @@ typedef struct {
uint32_t characteristics; uint32_t characteristics;
size_t size; size_t size;
size_t number_of_relocations; size_t number_of_relocations;
// Number of relocations is greater than 2^16 - 1
int number_of_relocations_is_extended;
} SectionInfo; } SectionInfo;
typedef struct { typedef struct {
@ -240,7 +246,12 @@ static void build(ObjectIr *ir, const char *outname) {
fwrite32(out, offset_to_next_relocation); // PointerToRelocations fwrite32(out, offset_to_next_relocation); // PointerToRelocations
offset_to_next_relocation += si.number_of_relocations * 10; offset_to_next_relocation += si.number_of_relocations * 10;
fwrite32(out, 0); // PointerToLinenumbers 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 fwrite16(out, 0); // NumberOfLinenumbers
fwrite32(out, si.characteristics); // Characteristics fwrite32(out, si.characteristics); // Characteristics
log_info("Done.\n"); 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); SectionInfo si = cdict_CStr_SectionInfo_get_v(&ir->info_per_section, name);
log_info(" Writing relocations of %s {\n", 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++) { for (size_t i = 0; i < cvec_ObjIdSecId_size(&si.source); i++) {
ObjIdSecId id = cvec_ObjIdSecId_at(&si.source, i); ObjIdSecId id = cvec_ObjIdSecId_at(&si.source, i);
CoffObject *object = &ir->objects[id.obj_id]; 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)) { if (!epep_get_section_header_by_index(epep, &sh, id.sec_id)) {
ERROR_EPEP(epep); 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 }; 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); ERROR_EPEP(epep);
} }
log_info(" { %02x, %02x, %02x }", rel.VirtualAddress, rel.SymbolTableIndex, rel.Type); 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; size_t sec_offset = si.size;
cvec_size_t_push_back(&objects[i].section_offsets, sec_offset); 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.size += sh.SizeOfRawData;
si.characteristics |= sh.Characteristics; 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 }); cvec_ObjIdSecId_push_back(&si.source, (ObjIdSecId){ i, sec_i });
cdict_CStr_SectionInfo_add_vv(&info_per_section, strdup(name), si, CDICT_REPLACE_EXIST); cdict_CStr_SectionInfo_add_vv(&info_per_section, strdup(name), si, CDICT_REPLACE_EXIST);