Rev 34 | Rev 38 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 34 | Rev 35 | ||
---|---|---|---|
Line 166... | Line 166... | ||
166 | static char *bootfile_pathname = NULL; // FIXME: HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS in BIOS mode |
166 | static char *bootfile_pathname = NULL; // FIXME: HACK: pathname to bootcode binary blob file to put at the start of a bootable IFS in BIOS mode |
167 | static size_t bootfile_size = 0; // FIXME: HACK: size of the bootcode binary blob file to put at the start of a bootable IFS in BIOS mode |
167 | static size_t bootfile_size = 0; // FIXME: HACK: size of the bootcode binary blob file to put at the start of a bootable IFS in BIOS mode |
168 | static char *startupfile_pathname = NULL; // FIXME: HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS |
168 | static char *startupfile_pathname = NULL; // FIXME: HACK: pathname to precompiled startup file blob to put in the startup header of a bootable IFS |
169 | static size_t startupfile_ep_from_imagebase = 0; // FIXME: HACK: startup code entrypoint offset from image base for a bootable IFS |
169 | static size_t startupfile_ep_from_imagebase = 0; // FIXME: HACK: startup code entrypoint offset from image base for a bootable IFS |
170 | static size_t kernelfile_offset = 0; // kernel file offset in the IFS (first offset rounded at pagesize after the dirents table) |
170 | static size_t kernelfile_offset = 0; // kernel file offset in the IFS (first offset rounded at pagesize after the dirents table) |
- | 171 | static size_t procnto_bootargs_offset = 0; // offset in the procnto file to the boot args structure, so that it can be patched late |
|
171 | 172 | ||
172 | 173 | ||
173 | // exported function prototypes |
174 | // exported function prototypes |
174 | int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness); // compute an IFS image or startup checksum to store in the trailer |
175 | int32_t update_checksum (const void *data, const size_t data_len, const bool is_foreign_endianness); // compute an IFS image or startup checksum to store in the trailer |
175 | 176 | ||
Line 883... | Line 884... | ||
883 | LOG_INFO ("fifo: ino 0x%x uid %d gid %d mode 0%o path \"%s\" dev:rdev %s)", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data.bytes); |
884 | LOG_INFO ("fifo: ino 0x%x uid %d gid %d mode 0%o path \"%s\" dev:rdev %s)", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data.bytes); |
884 | } |
885 | } |
885 | else // necessarily a regular file (either S_IFREG is specified, or st_mode is zero) |
886 | else // necessarily a regular file (either S_IFREG is specified, or st_mode is zero) |
886 | { |
887 | { |
887 | entry_parms->st_mode |= S_IFREG; // make this explicit |
888 | entry_parms->st_mode |= S_IFREG; // make this explicit |
- | 889 | entry_parms->mtime = entry_parms->mtime_for_inline_files; // set a default mtime equal to the mtime to use for inline files until told otherwise |
|
888 | 890 | ||
- | 891 | ASSERT ((entry_parms->data.bytes != NULL) || (buildhost_pathname != NULL), "unexpected code path: can't store a file without neither explicit contents nor a host pathname. This is a bug in the program. Please contact the author."); |
|
- | 892 | ||
- | 893 | // do we NOT know the file data yet AND was a build host pathname specified ? which means we need to resolve the file on the build host's filesystem |
|
- | 894 | if ((entry_parms->data.bytes == NULL) && (buildhost_pathname != NULL)) |
|
- | 895 | { |
|
- | 896 | resolved_pathname = resolve_pathname (buildhost_pathname, entry_parms->search); // locate the file |
|
- | 897 | if (resolved_pathname == NULL) |
|
- | 898 | { |
|
- | 899 | if (entry_parms->should_allow_nonexistent_files) |
|
- | 900 | { |
|
- | 901 | LOG_WARNING ("filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: ignoring", buildhost_pathname, buildfile_pathname, lineno); |
|
- | 902 | return; // if we're allowed to continue when a file to add doesn't exist, do so, else die with an error message |
|
- | 903 | } |
|
- | 904 | DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: %s", buildhost_pathname, buildfile_pathname, lineno, strerror (errno)); |
|
- | 905 | } |
|
- | 906 | if (!Buffer_ReadFromFile (&entry_parms->data, resolved_pathname)) |
|
- | 907 | DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d can't be read from \"%s\": %s", buildhost_pathname, buildfile_pathname, lineno, resolved_pathname, strerror (errno)); |
|
- | 908 | stat (resolved_pathname, &stat_buf); // can't fail, since we could read it |
|
- | 909 | if (entry_parms->mtime == UINT32_MAX) |
|
- | 910 | entry_parms->mtime = (uint32_t) stat_buf.st_mtime; // now we know which mtime to set this file |
|
- | 911 | } |
|
- | 912 | ||
- | 913 | // is it the bootstrap file [startup=], or a "compiled" bootscript [+script] ? |
|
889 | if (entry_parms->is_bootstrap_file) // |
914 | if (entry_parms->is_bootstrap_file) // [startup=...] |
890 | { |
915 | { |
891 | // parse each line of contents |
916 | // parse each line of contents |
892 | ASSERT (entry_parms->data.size > 0, "kernel specification without inline contents"); |
917 | ASSERT (entry_parms->data.size > 0, "kernel specification without inline contents"); |
893 | 918 | ||
894 | // parse buffer (non-destructively) line after line |
919 | // parse buffer (non-destructively) line after line |
Line 1100... | Line 1125... | ||
1100 | fputc ('\n', stderr); |
1125 | fputc ('\n', stderr); |
1101 | } |
1126 | } |
1102 | #ifdef _WIN32 |
1127 | #ifdef _WIN32 |
1103 | _spawnv (_P_WAIT, linker_pathname, linker_argv.args); // spawn the linker and produce a stripped procnto (wait for completion) |
1128 | _spawnv (_P_WAIT, linker_pathname, linker_argv.args); // spawn the linker and produce a stripped procnto (wait for completion) |
1104 | #else // !_WIN32, thus POSIX |
1129 | #else // !_WIN32, thus POSIX |
1105 | do { |
1130 | do { // QNX does have spawnv(), but Linux does not. So let's stick to common POSIX ground, i.e. fork/exec/wait. |
1106 | int status; |
1131 | int status; |
1107 | pid_t pid = fork (); // duplicate ourselves so as to create a new process |
1132 | pid_t pid = fork (); // duplicate ourselves so as to create a new process |
1108 | ASSERT_WITH_ERRNO (pid != -1); |
1133 | ASSERT_WITH_ERRNO (pid != -1); |
1109 | if (pid == 0) // we are the child |
1134 | if (pid == 0) // we are the child |
1110 | { |
1135 | { |
Line 1118... | Line 1143... | ||
1118 | STRINGARRAY_FREE (&linker_argv); |
1143 | STRINGARRAY_FREE (&linker_argv); |
1119 | if (!Buffer_ReadFromFile (&entry_parms->data, procnto_sym_filename)) // load the output file |
1144 | if (!Buffer_ReadFromFile (&entry_parms->data, procnto_sym_filename)) // load the output file |
1120 | DIE_WITH_EXITCODE (1, "the host cross-linker failed to produce a readable stripped \"%s\" kernel: %s", procnto_sym_filename, strerror (errno)); |
1145 | DIE_WITH_EXITCODE (1, "the host cross-linker failed to produce a readable stripped \"%s\" kernel: %s", procnto_sym_filename, strerror (errno)); |
1121 | if (!entry_parms->should_keep_ld_output) |
1146 | if (!entry_parms->should_keep_ld_output) |
1122 | unlink (procnto_sym_filename); // remove the linker output file if we want to |
1147 | unlink (procnto_sym_filename); // remove the linker output file if we want to |
- | 1148 | ||
- | 1149 | // strip this prelinked ELF kernel file from all the sections we don't need |
|
- | 1150 | ASSERT_WITH_ERRNO (Buffer_StripELFFile (&entry_parms->data, (const char **) saved_ELF_sections, 1, true, stored_pathname)); // strip the ELF file as per QNX docs (only keep ONE section, which is "QNX_info", and align the segment size in file with the size it occupies in memory) |
|
1123 | 1151 | ||
1124 | // save the boot arguments. The magic to look for is "ddpvbskr" -- whatever that means |
1152 | // save the boot arguments. The magic to look for is "ddpvbskr" -- whatever that means |
1125 | if ((bootargs_location = Buffer_FindFirstByteArray (&entry_parms->data, "ddpvbskr")) == NULL) |
1153 | if ((bootargs_location = Buffer_FindFirstByteArray (&entry_parms->data, "ddpvbskr")) == NULL) |
1126 | DIE_WITH_EXITCODE (1, "unable to find boot args location in the stripped \"%s\" kernel", stored_pathname); |
1154 | DIE_WITH_EXITCODE (1, "unable to find boot args location in the stripped \"%s\" kernel", stored_pathname); |
1127 | Buffer_InitWithSize (&bootargs_buffer, sizeof (bootargs_entry_t)); // prepare a boot args entry |
1155 | Buffer_InitWithSize (&bootargs_buffer, sizeof (bootargs_entry_t)); // prepare a boot args entry |
1128 | ((bootargs_entry_t *) bootargs_buffer.bytes)->argc = (uint8_t) procnto_argv.count; |
1156 | ((bootargs_entry_t *) bootargs_buffer.bytes)->argc = (uint8_t) procnto_argv.count; |
1129 | ((bootargs_entry_t *) bootargs_buffer.bytes)->envc = (uint8_t) (global_envp.count + procnto_envp.count); |
1157 | ((bootargs_entry_t *) bootargs_buffer.bytes)->envc = (uint8_t) (global_envp.count + procnto_envp.count); |
1130 | ((bootargs_entry_t *) bootargs_buffer.bytes)->shdr_addr = |
1158 | ((bootargs_entry_t *) bootargs_buffer.bytes)->shdr_addr = WILL_BE_FILLED_LATER; // same value as startup_header.image_paddr (which is not set yet) (TODO: support 64-bit shdr_addr offsets -- see comment in bootargs_entry_t struct) |
1131 | for (array_index = 0; array_index < procnto_argv.count; array_index++) |
1159 | for (array_index = 0; array_index < procnto_argv.count; array_index++) |
1132 | ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_argv.args[array_index], strlen (procnto_argv.args[array_index]) + 1)); // append string including NUL terminator |
1160 | ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_argv.args[array_index], strlen (procnto_argv.args[array_index]) + 1)); // append string including NUL terminator |
1133 | for (array_index = 0; array_index < global_envp.count; array_index++) |
1161 | for (array_index = 0; array_index < global_envp.count; array_index++) |
1134 | ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, global_envp.args[array_index], strlen (global_envp.args[array_index]) + 1)); // append string including NUL terminator |
1162 | ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, global_envp.args[array_index], strlen (global_envp.args[array_index]) + 1)); // append string including NUL terminator |
1135 | for (array_index = 0; array_index < procnto_envp.count; array_index++) |
1163 | for (array_index = 0; array_index < procnto_envp.count; array_index++) |
1136 | ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_envp.args[array_index], strlen (procnto_envp.args[array_index]) + 1)); // append string including NUL terminator |
1164 | ASSERT_WITH_ERRNO (Buffer_Append (&bootargs_buffer, procnto_envp.args[array_index], strlen (procnto_envp.args[array_index]) + 1)); // append string including NUL terminator |
1137 | ((bootargs_entry_t *) bootargs_buffer.bytes)->size_hi = (uint8_t) ((bootargs_buffer.size >> 8) & 0xff); |
1165 | ((bootargs_entry_t *) bootargs_buffer.bytes)->size_hi = (uint8_t) ((bootargs_buffer.size >> 8) & 0xff); |
1138 | ((bootargs_entry_t *) bootargs_buffer.bytes)->size_lo = (uint8_t) ((bootargs_buffer.size >> 0) & 0xff); |
1166 | ((bootargs_entry_t *) bootargs_buffer.bytes)->size_lo = (uint8_t) ((bootargs_buffer.size >> 0) & 0xff); |
- | 1167 | procnto_bootargs_offset = (size_t) bootargs_location - (size_t) entry_parms->data.bytes; // save the boot args offset so that the section header address in it can be patched late |
|
1139 | ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&entry_parms->data, |
1168 | ASSERT_WITH_ERRNO (Buffer_WriteBufferAt (&entry_parms->data, procnto_bootargs_offset, &bootargs_buffer)); |
1140 | Buffer_Forget (&bootargs_buffer); // release the boot args buffer once it's written |
1169 | Buffer_Forget (&bootargs_buffer); // release the boot args buffer once it's written |
1141 | - | ||
1142 | // now strip this prelinked ELF kernel file |
- | |
1143 | ASSERT_WITH_ERRNO (Buffer_StripELFFile (&entry_parms->data, (const char **) saved_ELF_sections, 1, true, stored_pathname)); // strip the ELF file as per QNX docs (only keep ONE section, which is "QNX_info", and align the segment size in file with the size it occupies in memory) |
- | |
1144 | 1170 | ||
1145 | sprintf_s (candidate_pathname, MAXPATHLEN, "%s/%s", (entry_parms->prefix != NULL ? entry_parms->prefix : ""), procnto_argv.args[0]); // fix the entry name |
1171 | sprintf_s (candidate_pathname, MAXPATHLEN, "%s/%s", (entry_parms->prefix != NULL ? entry_parms->prefix : ""), procnto_argv.args[0]); // fix the entry name |
1146 | stored_pathname = candidate_pathname; |
1172 | stored_pathname = candidate_pathname; |
1147 | 1173 | ||
1148 | entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // mark this inode as a preprocessed *bootstrap* ELF file |
1174 | entry_parms->extra_ino_flags |= IFS_INO_PROCESSED_ELF | IFS_INO_BOOTSTRAP_EXE; // mark this inode as a preprocessed *bootstrap* ELF file |
Line 1151... | Line 1177... | ||
1151 | 1177 | ||
1152 | STRINGARRAY_FREE (&procnto_argv); // release procnto's argv array |
1178 | STRINGARRAY_FREE (&procnto_argv); // release procnto's argv array |
1153 | STRINGARRAY_FREE (&procnto_envp); // release procnto's envp array |
1179 | STRINGARRAY_FREE (&procnto_envp); // release procnto's envp array |
1154 | //STRINGARRAY_FREE (&global_envp); // DO NOT release the global envp array. It is inherited by the boot scripts. |
1180 | //STRINGARRAY_FREE (&global_envp); // DO NOT release the global envp array. It is inherited by the boot scripts. |
1155 | } // end of "is bootstrap file" |
1181 | } // end of "is bootstrap file" |
1156 | else if (entry_parms->is_compiled_bootscript) // |
1182 | else if (entry_parms->is_compiled_bootscript) // [+script] |
1157 | { |
1183 | { |
1158 | image_bootscript_ino = inode_count + 1; // save boot script inode number for image header |
1184 | image_bootscript_ino = inode_count + 1; // save boot script inode number for image header |
1159 | Buffer_Initialize (&compiled_script); |
1185 | Buffer_Initialize (&compiled_script); |
1160 | 1186 | ||
1161 | // parse buffer (non-destructively) line after line |
1187 | // parse buffer (non-destructively) line after line |
Line 1423... | Line 1449... | ||
1423 | ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_script, "\x00\x00\x00\x00")); // terminate the compiled boot script with a 4-byte trailer |
1449 | ASSERT_WITH_ERRNO (Buffer_AppendByteArray (&compiled_script, "\x00\x00\x00\x00")); // terminate the compiled boot script with a 4-byte trailer |
1424 | entry_parms->data.bytes = compiled_script.bytes; // and steal the compiled boot script buffer |
1450 | entry_parms->data.bytes = compiled_script.bytes; // and steal the compiled boot script buffer |
1425 | entry_parms->data.size = compiled_script.size; |
1451 | entry_parms->data.size = compiled_script.size; |
1426 | } // end of "is compiled bootscript" |
1452 | } // end of "is compiled bootscript" |
1427 | 1453 | ||
1428 | // do we already know the data for this data blob ? |
- | |
1429 | if (entry_parms->data.bytes != NULL) |
- | |
1430 | { |
- | |
1431 | entry_parms->mtime = entry_parms->mtime_for_inline_files; // if so, set it a mtime equal to the mtime to use for inline files |
- | |
1432 | LOG_INFO ("file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" blob (len %zd)", entry_parms->extra_ino_flags | (inode_count + 1), entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, entry_parms->data.size); |
- | |
1433 | } |
- | |
1434 | else if (buildhost_pathname != NULL) // else was a source file pathname supplied ? |
- | |
1435 | { |
- | |
1436 | resolved_pathname = resolve_pathname (buildhost_pathname, entry_parms->search); // locate the file |
- | |
1437 | if (resolved_pathname == NULL) |
- | |
1438 | { |
- | |
1439 | if (entry_parms->should_allow_nonexistent_files) |
- | |
1440 | { |
- | |
1441 | LOG_WARNING ("filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: ignoring", buildhost_pathname, buildfile_pathname, lineno); |
- | |
1442 | return; // if we're allowed to continue when a file to add doesn't exist, do so, else die with an error message |
- | |
1443 | } |
- | |
1444 | DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d not found on build host: %s", buildhost_pathname, buildfile_pathname, lineno, strerror (errno)); |
- | |
1445 | } |
- | |
1446 | if (!Buffer_ReadFromFile (&entry_parms->data, resolved_pathname)) |
- | |
1447 | DIE_WITH_EXITCODE (1, "filesystem entry \"%s\" specified in \"%s\" line %d can't be read from \"%s\": %s", buildhost_pathname, buildfile_pathname, lineno, resolved_pathname, strerror (errno)); |
- | |
1448 | stat (resolved_pathname, &stat_buf); // can't fail, since we could read it |
- | |
1449 | if (entry_parms->mtime == UINT32_MAX) |
- | |
1450 | entry_parms->mtime = (uint32_t) stat_buf.st_mtime; |
- | |
1451 | if ((entry_parms->data.size > 52) && (memcmp (entry_parms->data.bytes, ELF_MAGIC_STR, 4) == 0)) |
- | |
1452 | entry_parms->st_mode |= 0111; // add +x permissions to ELF entries (undocumented mkifs behaviour) |
- | |
1453 |
|
1454 | LOG_INFO ("file: ino 0x%x uid %d gid %d mode 0%o path \"%s\" buildhost_file \"%s\" (len %zd)", inode_count + 1, entry_parms->uid, entry_parms->gid, entry_parms->st_mode, stored_pathname, (buildhost_pathname != NULL ? buildhost_pathname : "<explicit blob>"), entry_parms->data.size); |
1454 | } |
- | |
1455 | else |
- | |
1456 | DIE_WITH_EXITCODE (1, "unexpected code path: can't store a file without neither explicit contents nor a host pathname. This is a bug in the program. Please contact the author."); |
- | |
1457 | 1455 | ||
1458 | // is the file we're storing an ELF file ? |
1456 | // is the file we're storing an ELF file ? |
1459 | #define ELFHDR ((elf_header_t *) entry_parms->data.bytes) // this convenient definition will make sure the ELF header points at the right location, even after entry_parms.data->byte is reallocated |
1457 | #define ELFHDR ((elf_header_t *) entry_parms->data.bytes) // this convenient definition will make sure the ELF header points at the right location, even after entry_parms.data->byte is reallocated |
1460 | if ((entry_parms->data.size > 52) // file is big enough to contain an ELF header |
1458 | if ((entry_parms->data.size > 52) // file is big enough to contain an ELF header |
1461 | && (memcmp (ELF_GET_STRING (ELFHDR, ELFHDR, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic |
1459 | && (memcmp (ELF_GET_STRING (ELFHDR, ELFHDR, magic), ELF_MAGIC_STR, 4) == 0)) // file starts with the ELF magic |
1462 | { |
1460 | { |
- | 1461 | if ((entry_parms->st_mode & 0111) == 0) |
|
- | 1462 | entry_parms->st_mode |= 0111; // add +x permissions to ELF entries if they have none (undocumented mkifs behaviour) |
|
- | 1463 | ||
1463 | // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ? |
1464 | // is the file we're storing a relocatable executable (i.e. a dynamic library) and should we check for its canonical name ? |
1464 | if ((ELF_GET_NUMERIC (ELFHDR, ELFHDR, type) == ELF_TYPE_DYNAMICLIB) && entry_parms->should_autosymlink_dylib) |
1465 | if ((ELF_GET_NUMERIC (ELFHDR, ELFHDR, type) == ELF_TYPE_DYNAMICLIB) && entry_parms->should_autosymlink_dylib) |
1465 | { |
1466 | { |
1466 | // locate the sections we need (the dynamic section and its strings table) |
1467 | // locate the sections we need (the dynamic section and its strings table) |
1467 | const elf_section_header_t *shdr_dynamic = elf_get_section_header_by_name (ELFHDR, ".dynamic"); |
1468 | const elf_section_header_t *shdr_dynamic = elf_get_section_header_by_name (ELFHDR, ".dynamic"); |
Line 2557... | Line 2558... | ||
2557 | // now write the QNX kernel |
2558 | // now write the QNX kernel |
2558 | for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) |
2559 | for (fsentry_index = 1; fsentry_index < fsentry_count; fsentry_index++) |
2559 | if (fsentries[fsentry_index].header.ino == image_kernel_ino) |
2560 | if (fsentries[fsentry_index].header.ino == image_kernel_ino) |
2560 | break; // locate the kernel directory entry (can't fail) |
2561 | break; // locate the kernel directory entry (can't fail) |
2561 | fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure |
2562 | fsentries[fsentry_index].u.file.offset = (uint32_t) (ifs.data.size - ifs.offsets.imageheader); // save file data blob offset in file structure |
- | 2563 | ASSERT (procnto_bootargs_offset + sizeof (bootargs_entry_t) < fsentries[fsentry_index].u.file.size, "can't fix boot args in procnto, would write beyond the end of file! This is a bug in the program. Please contact the author."); |
|
- | 2564 | ((bootargs_entry_t *) &fsentries[fsentry_index].UNSAVED_databuf[procnto_bootargs_offset])->shdr_addr = (uint32_t) (image_base + bootfile_size); // fix shdr_addr in procnto's arguments structure, same value as startup_header.image_paddr (which is not set yet) (TODO: support 64-bit shdr_addr offsets -- see comment in bootargs_entry_t struct) |
|
2562 | Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write kernel file data |
2565 | Buffer_AppendIFSFileData (&ifs.data, &fsentries[fsentry_index]); // write kernel file data |
2563 | fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written |
2566 | fsentries[fsentry_index].UNSAVED_was_data_written = true; // and remember this file's data was written |
2564 | } |
2567 | } |
2565 | 2568 | ||
2566 | // then write all the other files by increasing inode number: ELF files first |
2569 | // then write all the other files by increasing inode number: ELF files first |