arrange_local_symbols() added

ELF standard requires that all STB_LOCAL symbols will go prior others and sh_info entry will contain the number of the local symbols
This commit is contained in:
Serge Lamikhov-Center 2020-08-18 12:57:45 -07:00
parent d2c3fb6a14
commit 8e0b5754e4
5 changed files with 570 additions and 393 deletions

1
.gitignore vendored
View File

@ -44,6 +44,7 @@ tests/elf_examples/write_exe_i386_32_section_added
tests/elf_examples/ppc-32bit-testcopy*.elf tests/elf_examples/ppc-32bit-testcopy*.elf
tests/elf_examples/null_section_inside_segment* tests/elf_examples/null_section_inside_segment*
tests/elf_examples/segment_containing_no_section* tests/elf_examples/segment_containing_no_section*
tests/elf_examples/test_symbols_order.elf
examples/writer/hello_x86_64 examples/writer/hello_x86_64
examples/write_obj/hello examples/write_obj/hello

9
.vscode/launch.json vendored
View File

@ -8,10 +8,13 @@
"name": "Run ELFIO Tests", "name": "Run ELFIO Tests",
"type": "cppdbg", "type": "cppdbg",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/ELFIOTest/ELFIOTest", "program": "${workspaceFolder}/tests/ELFIOTest",
"args": [], "args": [
"-r",
"short"
],
"stopAtEntry": false, "stopAtEntry": false,
"cwd": "${workspaceFolder}/ELFIOTest", "cwd": "${workspaceFolder}/tests",
"environment": [], "environment": [],
"externalConsole": false, "externalConsole": false,
"MIMode": "gdb", "MIMode": "gdb",

9
.vscode/tasks.json vendored
View File

@ -9,12 +9,15 @@
"CXXFLAGS='-g -O0'" "CXXFLAGS='-g -O0'"
], ],
"options": { "options": {
"cwd": "${workspaceRoot}/ELFIOTest", "cwd": "${workspaceRoot}/tests"
}, },
"group": { "group": {
"kind": "build", "kind": "build",
"isDefault": true "isDefault": true
} },
"problemMatcher": [
"$gcc"
]
}, },
{ {
"type": "shell", "type": "shell",
@ -25,7 +28,7 @@
"CXXFLAGS='-g -O0'" "CXXFLAGS='-g -O0'"
], ],
"options": { "options": {
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}"
}, },
"group": { "group": {
"kind": "build", "kind": "build",

View File

@ -214,6 +214,22 @@ class symbol_section_accessor_template
return add_symbol( pStrWriter, str, value, size, ELF_ST_INFO( bind, type ), other, shndx ); return add_symbol( pStrWriter, str, value, size, ELF_ST_INFO( bind, type ), other, shndx );
} }
//------------------------------------------------------------------------------
Elf_Xword
arrange_local_symbols()
{
int nRet = 0;
if (elf_file.get_class() == ELFCLASS32) {
nRet = generic_arrange_local_symbols<Elf32_Sym>();
}
else {
nRet = generic_arrange_local_symbols<Elf64_Sym>();
}
return nRet;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
private : private :
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -345,6 +361,61 @@ class symbol_section_accessor_template
return nRet; return nRet;
} }
//------------------------------------------------------------------------------
template <class T>
Elf_Xword
generic_arrange_local_symbols()
{
const endianess_convertor &convertor = elf_file.get_convertor();
const Elf_Xword size = symbol_section->get_entry_size();
Elf_Xword first_not_local = 1; // Skip the first entry
Elf_Xword current = 0;
Elf_Xword count = get_symbols_num();
while (true)
{
T *p1 = nullptr;
T *p2 = nullptr;
while (first_not_local < count)
{
p1 = const_cast<T *>(generic_get_symbol_ptr<T>(first_not_local));
if (ELF_ST_BIND(convertor(p1->st_info)) != STB_LOCAL)
break;
++first_not_local;
}
current = first_not_local + 1;
while (current < count)
{
p2 = const_cast<T *>(generic_get_symbol_ptr<T>(current));
if (ELF_ST_BIND(convertor(p2->st_info)) == STB_LOCAL)
break;
++current;
}
if (first_not_local < count && current < count)
{
// Swap the symbols
T tmp;
memcpy(&tmp, p1, size);
memcpy(p1, p2, size);
memcpy(p2, &tmp, size);
}
else
{
// Update 'info' field of the section
symbol_section->set_info(first_not_local);
break;
}
}
// Elf_Word nRet = symbol_section->get_size() / sizeof(entry) - 1;
return first_not_local;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
private: private:
const elfio& elf_file; const elfio& elf_file;

View File

@ -11,7 +11,12 @@ using boost::test_tools::output_test_stream;
using namespace ELFIO; using namespace ELFIO;
enum Tests
{
SEG_ALIGN = 1
};
////////////////////////////////////////////////////////////////////////////////
bool write_obj_i386(bool is64bit) bool write_obj_i386(bool is64bit)
{ {
elfio writer; elfio writer;
@ -28,7 +33,8 @@ bool write_obj_i386( bool is64bit )
text_sec->set_addr_align(0x10); text_sec->set_addr_align(0x10);
// Add data into it // Add data into it
char text[] = { '\xB8', '\x04', '\x00', '\x00', '\x00', // mov eax, 4 char text[] = {
'\xB8', '\x04', '\x00', '\x00', '\x00', // mov eax, 4
'\xBB', '\x01', '\x00', '\x00', '\x00', // mov ebx, 1 '\xBB', '\x01', '\x00', '\x00', '\x00', // mov ebx, 1
'\xB9', '\x00', '\x00', '\x00', '\x00', // mov ecx, msg '\xB9', '\x00', '\x00', '\x00', '\x00', // mov ecx, msg
'\xBA', '\x0E', '\x00', '\x00', '\x00', // mov edx, 14 '\xBA', '\x0E', '\x00', '\x00', '\x00', // mov edx, 14
@ -46,8 +52,7 @@ bool write_obj_i386( bool is64bit )
char data[] = {'\x48', '\x65', '\x6C', '\x6C', '\x6F', // msg: db 'Hello, World!', 10 char data[] = {'\x48', '\x65', '\x6C', '\x6C', '\x6F', // msg: db 'Hello, World!', 10
'\x2C', '\x20', '\x57', '\x6F', '\x72', '\x2C', '\x20', '\x57', '\x6F', '\x72',
'\x6C', '\x64', '\x21', '\x0A' '\x6C', '\x64', '\x21', '\x0A'};
};
data_sec->set_data(data, sizeof(data)); data_sec->set_data(data, sizeof(data));
section *str_sec = writer.sections.add(".strtab"); section *str_sec = writer.sections.add(".strtab");
@ -103,36 +108,12 @@ bool write_obj_i386( bool is64bit )
// Create ELF file // Create ELF file
writer.save( writer.save(
is64bit ? is64bit ? "elf_examples/write_obj_i386_64.o" : "elf_examples/write_obj_i386_32.o");
"elf_examples/write_obj_i386_64.o" :
"elf_examples/write_obj_i386_32.o"
);
return true; return true;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE( write_obj_i386_32 )
{
BOOST_CHECK_EQUAL( true, write_obj_i386( false ) );
output_test_stream output( "elf_examples/write_obj_i386_32_match.o", true, false );
std::ifstream input( "elf_examples/write_obj_i386_32.o", std::ios::binary );
output << input.rdbuf();
BOOST_CHECK( output.match_pattern() );
}
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE( write_obj_i386_64 )
{
BOOST_CHECK_EQUAL( true, write_obj_i386( true ) );
output_test_stream output( "elf_examples/write_obj_i386_64_match.o", true, false );
std::ifstream input( "elf_examples/write_obj_i386_64.o", std::ios::binary );
output << input.rdbuf();
BOOST_CHECK( output.match_pattern() );
}
bool write_exe_i386(const std::string &filename, bool is64bit, bool set_addr = false, Elf64_Addr addr = 0) bool write_exe_i386(const std::string &filename, bool is64bit, bool set_addr = false, Elf64_Addr addr = 0)
{ {
elfio writer; elfio writer;
@ -147,12 +128,14 @@ bool write_exe_i386( const std::string& filename, bool is64bit, bool set_addr =
text_sec->set_type(SHT_PROGBITS); text_sec->set_type(SHT_PROGBITS);
text_sec->set_flags(SHF_ALLOC | SHF_EXECINSTR); text_sec->set_flags(SHF_ALLOC | SHF_EXECINSTR);
text_sec->set_addr_align(0x10); text_sec->set_addr_align(0x10);
if ( set_addr ) { if (set_addr)
{
text_sec->set_address(addr); text_sec->set_address(addr);
} }
// Add data into it // Add data into it
char text[] = { '\xB8', '\x04', '\x00', '\x00', '\x00', // mov eax, 4 char text[] = {
'\xB8', '\x04', '\x00', '\x00', '\x00', // mov eax, 4
'\xBB', '\x01', '\x00', '\x00', '\x00', // mov ebx, 1 '\xBB', '\x01', '\x00', '\x00', '\x00', // mov ebx, 1
'\xB9', '\x20', '\x80', '\x04', '\x08', // mov ecx, msg '\xB9', '\x20', '\x80', '\x04', '\x08', // mov ecx, msg
'\xBA', '\x0E', '\x00', '\x00', '\x00', // mov edx, 14 '\xBA', '\x0E', '\x00', '\x00', '\x00', // mov edx, 14
@ -178,8 +161,7 @@ bool write_exe_i386( const std::string& filename, bool is64bit, bool set_addr =
char data[] = {'\x48', '\x65', '\x6C', '\x6C', '\x6F', // msg: db 'Hello, World!', 10 char data[] = {'\x48', '\x65', '\x6C', '\x6C', '\x6F', // msg: db 'Hello, World!', 10
'\x2C', '\x20', '\x57', '\x6F', '\x72', '\x2C', '\x20', '\x57', '\x6F', '\x72',
'\x6C', '\x64', '\x21', '\x0A' '\x6C', '\x64', '\x21', '\x0A'};
};
data_sec->set_data(data, sizeof(data)); data_sec->set_data(data, sizeof(data));
segment *data_seg = writer.segments.add(); segment *data_seg = writer.segments.add();
@ -206,21 +188,7 @@ bool write_exe_i386( const std::string& filename, bool is64bit, bool set_addr =
return true; return true;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE( write_exe_i386_32 )
{
const std::string generated_file ( "elf_examples/write_exe_i386_32" );
const std::string reference_file ( "elf_examples/write_exe_i386_32_match" );
BOOST_CHECK_EQUAL( true, write_exe_i386( generated_file, false ) );
output_test_stream output( reference_file, true, false );
std::ifstream input( generated_file, std::ios::binary );
output << input.rdbuf();
BOOST_CHECK_MESSAGE( output.match_pattern(), "Comparing " + generated_file + " and " + reference_file );
}
enum Tests { SEG_ALIGN = 1 };
void checkObjestsAreEqual(std::string file_name1, std::string file_name2) void checkObjestsAreEqual(std::string file_name1, std::string file_name2)
{ {
elfio file1; elfio file1;
@ -230,7 +198,8 @@ void checkObjestsAreEqual( std::string file_name1, std::string file_name2 )
BOOST_REQUIRE_EQUAL(file1.load(file_name1), true); BOOST_REQUIRE_EQUAL(file1.load(file_name1), true);
BOOST_REQUIRE_EQUAL(file2.load(file_name2), true); BOOST_REQUIRE_EQUAL(file2.load(file_name2), true);
for (int i = 0; i < file1.sections.size(); ++i ) { for (int i = 0; i < file1.sections.size(); ++i)
{
BOOST_CHECK_EQUAL(file1.sections[i]->get_address(), BOOST_CHECK_EQUAL(file1.sections[i]->get_address(),
file2.sections[i]->get_address()); file2.sections[i]->get_address());
BOOST_CHECK_EQUAL(file1.sections[i]->get_addr_align(), BOOST_CHECK_EQUAL(file1.sections[i]->get_addr_align(),
@ -254,9 +223,9 @@ void checkObjestsAreEqual( std::string file_name1, std::string file_name2 )
BOOST_CHECK_EQUAL(file1.sections[i]->get_type(), BOOST_CHECK_EQUAL(file1.sections[i]->get_type(),
file2.sections[i]->get_type()); file2.sections[i]->get_type());
if (file1.sections[i]->get_type() == SHT_NULL || if (file1.sections[i]->get_type() == SHT_NULL ||
file1.sections[i]->get_type() == SHT_NOBITS ) { file1.sections[i]->get_type() == SHT_NOBITS)
{
continue; continue;
} }
BOOST_REQUIRE_NE(file1.sections[i]->get_data(), (const char *)0); BOOST_REQUIRE_NE(file1.sections[i]->get_data(), (const char *)0);
@ -271,14 +240,15 @@ void checkObjestsAreEqual( std::string file_name1, std::string file_name2 )
BOOST_CHECK_EQUAL(file1.sections[i]->get_size(), BOOST_CHECK_EQUAL(file1.sections[i]->get_size(),
file2.sections[i]->get_size()); file2.sections[i]->get_size());
if ((file2.sections[i]->get_type() != SHT_NULL) && if ((file2.sections[i]->get_type() != SHT_NULL) &&
( file2.sections[i]->get_type() != SHT_NOBITS ) ) { (file2.sections[i]->get_type() != SHT_NOBITS))
{
BOOST_CHECK_EQUAL_COLLECTIONS(pdata1.begin(), pdata1.end(), BOOST_CHECK_EQUAL_COLLECTIONS(pdata1.begin(), pdata1.end(),
pdata2.begin(), pdata2.end()); pdata2.begin(), pdata2.end());
} }
} }
} }
////////////////////////////////////////////////////////////////////////////////
void checkExeAreEqual(std::string file_name1, std::string file_name2, int skipTests = 0) void checkExeAreEqual(std::string file_name1, std::string file_name2, int skipTests = 0)
{ {
checkObjestsAreEqual(file_name1, file_name2); checkObjestsAreEqual(file_name1, file_name2);
@ -289,7 +259,8 @@ void checkExeAreEqual( std::string file_name1, std::string file_name2, int skipT
BOOST_REQUIRE_EQUAL(file1.load(file_name1), true); BOOST_REQUIRE_EQUAL(file1.load(file_name1), true);
BOOST_REQUIRE_EQUAL(file2.load(file_name2), true); BOOST_REQUIRE_EQUAL(file2.load(file_name2), true);
for (int i = 0; i < file1.segments.size(); ++i ) { for (int i = 0; i < file1.segments.size(); ++i)
{
if (!(skipTests & SEG_ALIGN)) if (!(skipTests & SEG_ALIGN))
BOOST_CHECK_EQUAL(file1.segments[i]->get_align(), BOOST_CHECK_EQUAL(file1.segments[i]->get_align(),
file2.segments[i]->get_align()); file2.segments[i]->get_align());
@ -318,7 +289,8 @@ void checkExeAreEqual( std::string file_name1, std::string file_name2, int skipT
// part of the segment // part of the segment
Elf64_Off afterPHDR = file1.get_segments_offset() + Elf64_Off afterPHDR = file1.get_segments_offset() +
file1.get_segment_entry_size() * (Elf64_Off)file1.segments.size(); file1.get_segment_entry_size() * (Elf64_Off)file1.segments.size();
if( file1.segments[i]->get_offset() < afterPHDR ) { if (file1.segments[i]->get_offset() < afterPHDR)
{
pdata1 = pdata1.substr((unsigned int)afterPHDR); pdata1 = pdata1.substr((unsigned int)afterPHDR);
pdata2 = pdata2.substr((unsigned int)afterPHDR); pdata2 = pdata2.substr((unsigned int)afterPHDR);
} }
@ -328,6 +300,37 @@ void checkExeAreEqual( std::string file_name1, std::string file_name2, int skipT
} }
} }
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE(write_obj_i386_32)
{
BOOST_CHECK_EQUAL(true, write_obj_i386(false));
output_test_stream output("elf_examples/write_obj_i386_32_match.o", true, false);
std::ifstream input("elf_examples/write_obj_i386_32.o", std::ios::binary);
output << input.rdbuf();
BOOST_CHECK(output.match_pattern());
}
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE(write_obj_i386_64)
{
BOOST_CHECK_EQUAL(true, write_obj_i386(true));
output_test_stream output("elf_examples/write_obj_i386_64_match.o", true, false);
std::ifstream input("elf_examples/write_obj_i386_64.o", std::ios::binary);
output << input.rdbuf();
BOOST_CHECK(output.match_pattern());
}
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE(write_exe_i386_32)
{
const std::string generated_file("elf_examples/write_exe_i386_32");
const std::string reference_file("elf_examples/write_exe_i386_32_match");
BOOST_CHECK_EQUAL(true, write_exe_i386(generated_file, false));
output_test_stream output(reference_file, true, false);
std::ifstream input(generated_file, std::ios::binary);
output << input.rdbuf();
BOOST_CHECK_MESSAGE(output.match_pattern(), "Comparing " + generated_file + " and " + reference_file);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE(elf_object_copy_32) BOOST_AUTO_TEST_CASE(elf_object_copy_32)
@ -344,7 +347,6 @@ BOOST_AUTO_TEST_CASE( elf_object_copy_32 )
"elf_examples/write_obj_i386_64_copy.o"); "elf_examples/write_obj_i386_64_copy.o");
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE(section_header_address_update) BOOST_AUTO_TEST_CASE(section_header_address_update)
{ {
@ -357,7 +359,6 @@ BOOST_AUTO_TEST_CASE( section_header_address_update )
BOOST_REQUIRE_NE(sec, (section *)0); BOOST_REQUIRE_NE(sec, (section *)0);
BOOST_CHECK_EQUAL(sec->get_address(), 0x08048100); BOOST_CHECK_EQUAL(sec->get_address(), 0x08048100);
const std::string file_wo_addr("elf_examples/write_exe_i386_32_wo_addr"); const std::string file_wo_addr("elf_examples/write_exe_i386_32_wo_addr");
write_exe_i386(file_wo_addr, false, false, 0); write_exe_i386(file_wo_addr, false, false, 0);
reader.load(file_wo_addr); reader.load(file_wo_addr);
@ -366,7 +367,6 @@ BOOST_AUTO_TEST_CASE( section_header_address_update )
BOOST_CHECK_EQUAL(sec->get_address(), 0x08048000); BOOST_CHECK_EQUAL(sec->get_address(), 0x08048000);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE(elfio_copy) BOOST_AUTO_TEST_CASE(elfio_copy)
{ {
@ -585,3 +585,102 @@ BOOST_AUTO_TEST_CASE(invalid_file)
symbols.get_symbol(0x00400498, name, size, bind, symbols.get_symbol(0x00400498, name, size, bind,
type, section_index, other)); type, section_index, other));
} }
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE(rearrange_local_symbols)
{
std::string name = "";
ELFIO::Elf64_Addr value = 0;
ELFIO::Elf_Xword size = 0;
unsigned char bind = STB_LOCAL;
unsigned char type = STT_FUNC;
ELFIO::Elf_Half section_index = 0;
unsigned char other = 0;
const std::string file_name = "elf_examples/test_symbols_order.elf";
elfio writer;
writer.create(ELFCLASS64, ELFDATA2LSB);
writer.set_os_abi(ELFOSABI_LINUX);
writer.set_type(ET_EXEC);
writer.set_machine(EM_X86_64);
section *str_sec = writer.sections.add(".strtab");
str_sec->set_type(SHT_STRTAB);
str_sec->set_addr_align(0x1);
string_section_accessor str_writer(str_sec);
section *sym_sec = writer.sections.add(".symtab");
sym_sec->set_type(SHT_SYMTAB);
sym_sec->set_info(0);
sym_sec->set_link(str_sec->get_index());
sym_sec->set_addr_align(4);
sym_sec->set_entry_size(writer.get_default_entry_size(SHT_SYMTAB));
symbol_section_accessor symbols(writer, sym_sec);
name = "Str1";
bind = STB_GLOBAL;
value = 1;
symbols.add_symbol(str_writer, name.c_str(), value, size, bind, type, other, section_index);
name = "Str2";
bind = STB_LOCAL;
value = 2;
symbols.add_symbol(str_writer, name.c_str(), value, size, bind, type, other, section_index);
name = "Str3";
bind = STB_WEAK;
value = 3;
symbols.add_symbol(str_writer, name.c_str(), value, size, bind, type, other, section_index);
name = "Str4";
bind = STB_LOCAL;
value = 4;
symbols.add_symbol(str_writer, name.c_str(), value, size, bind, type, other, section_index);
name = "Str5";
bind = STB_LOCAL;
value = 5;
symbols.add_symbol(str_writer, name.c_str(), value, size, bind, type, other, section_index);
name = "Str6";
bind = STB_GLOBAL;
value = 6;
symbols.add_symbol(str_writer, name.c_str(), value, size, bind, type, other, section_index);
name = "Str7";
bind = STB_LOCAL;
value = 7;
symbols.add_symbol(str_writer, name.c_str(), value, size, bind, type, other, section_index);
name = "Str8";
bind = STB_WEAK;
value = 8;
symbols.add_symbol(str_writer, name.c_str(), value, size, bind, type, other, section_index);
symbols.arrange_local_symbols();
BOOST_REQUIRE_EQUAL(writer.save(file_name), true);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
elfio reader;
BOOST_REQUIRE_EQUAL(reader.load(file_name), true);
auto psymsec = reader.sections[".symtab"];
BOOST_REQUIRE_NE(psymsec, nullptr);
const_symbol_section_accessor rsymbols(reader, psymsec);
auto bound = psymsec->get_info();
auto num = rsymbols.get_symbols_num();
BOOST_CHECK_LE(bound, num);
// Check that all symbols are LOCAL until the bound value
for (auto i = 0; i < bound; i++)
{
rsymbols.get_symbol(i, name, value, size, bind, type, section_index, other);
BOOST_CHECK_EQUAL(bind, STB_LOCAL);
}
// Check that all symbols are not LOCAL after the bound value
for (auto i = bound; i < num; i++)
{
rsymbols.get_symbol(i, name, value, size, bind, type, section_index, other);
BOOST_CHECK_NE(bind, STB_LOCAL);
}
}