blob: 7a969441daa26ace7658c40b592fab1b36a4f282 [file] [log] [blame]
// Copyright 2011 The Goma Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <string>
#include <vector>
#include <glog/logging.h>
#include <glog/stl_logging.h>
#include <gtest/gtest.h>
#include "elf_parser.h"
#include "file_dir.h"
#include "mypath.h"
#include "path.h"
#include "simple_timer.h"
#include "subprocess.h"
using std::string;
namespace devtools_goma {
class ElfParserTest : public testing::Test {
protected:
void SetUp() override {
data_dir_ = file::JoinPath(GetMyDirectory(), "../../test");
}
void GetObjdumpOutput(const string& filename, std::vector<string>* needed) {
std::vector<string> argv;
argv.push_back("objdump");
argv.push_back("-p");
argv.push_back(filename);
std::vector<string> env;
env.push_back("LC_ALL=C");
string output = ReadCommandOutputByPopen("objdump", argv, env, ".",
MERGE_STDOUT_STDERR, nullptr);
size_t pos = 0;
while ((pos = output.find("NEEDED", pos)) != string::npos) {
pos += strlen("NEEDED");
while (pos < output.size()) {
if (output[pos] != ' ')
break;
++pos;
}
size_t spos = pos;
while (pos < output.size()) {
if (output[pos] == '\n')
break;
++pos;
}
needed->push_back(output.substr(spos, pos - spos));
++pos;
if (output[pos] != ' ')
break;
++pos;
}
}
string data_dir_;
};
TEST_F(ElfParserTest, GetObjdumpOutput) {
std::vector<string> needed;
GetObjdumpOutput(file::JoinPath(data_dir_, "libdl.so"), &needed);
EXPECT_EQ(2U, needed.size());
EXPECT_EQ("libc.so.6", needed[0]);
EXPECT_EQ("ld-linux-x86-64.so.2", needed[1]);
}
TEST_F(ElfParserTest, ReadDynamicNeeded) {
std::unique_ptr<ElfParser> parser(ElfParser::NewElfParser(
file::JoinPath(data_dir_, "libdl.so")));
ASSERT_TRUE(parser != nullptr);
EXPECT_TRUE(parser->valid());
std::vector<string> needed;
EXPECT_TRUE(parser->ReadDynamicNeeded(&needed));
EXPECT_EQ(2U, needed.size());
EXPECT_EQ("libc.so.6", needed[0]);
EXPECT_EQ("ld-linux-x86-64.so.2", needed[1]);
}
TEST_F(ElfParserTest, IsElf) {
EXPECT_TRUE(ElfParser::IsElf(file::JoinPath(data_dir_, "libdl.so")));
EXPECT_FALSE(ElfParser::IsElf(file::JoinPath(data_dir_, "libc.so")));
}
TEST_F(ElfParserTest, UsrLib) {
std::vector<DirEntry> entries;
ASSERT_TRUE(ListDirectory("/usr/lib", &entries));
int num = 0;
SimpleTimer timer;
double elf_parser_p_time = 0;
double elf_parser_s_time = 0;
double objdump_time = 0;
for (size_t i = 0; i < entries.size(); ++i) {
string name = entries[i].name;
if (name == "." || name == "..")
continue;
string fullname = file::JoinPath("/usr/lib", name);
VLOG(1) << fullname;
if (fullname.find(".so") == string::npos)
continue;
struct stat st;
if (stat(fullname.c_str(), &st) < 0)
continue;
if (!S_ISREG(st.st_mode))
continue;
if (!ElfParser::IsElf(fullname))
continue;
std::vector<string> p_needed;
timer.Start();
std::unique_ptr<ElfParser> parser(ElfParser::NewElfParser(fullname));
ASSERT_TRUE(parser != nullptr) << fullname;
EXPECT_TRUE(parser->valid()) << fullname;
parser->UseProgramHeader(true);
EXPECT_TRUE(parser->ReadDynamicNeeded(&p_needed)) << fullname;
elf_parser_p_time += timer.Get();
std::vector<string> s_needed;
timer.Start();
parser = ElfParser::NewElfParser(fullname);
ASSERT_TRUE(parser != nullptr) << fullname;
EXPECT_TRUE(parser->valid()) << fullname;
parser->UseProgramHeader(false);
EXPECT_TRUE(parser->ReadDynamicNeeded(&s_needed)) << fullname;
elf_parser_s_time += timer.Get();
std::vector<string> expected_needed;
timer.Start();
GetObjdumpOutput(fullname, &expected_needed);
objdump_time += timer.Get();
EXPECT_EQ(expected_needed, p_needed) << fullname;
EXPECT_EQ(expected_needed, s_needed) << fullname;
++num;
}
EXPECT_GT(num, 0);
LOG(INFO) << "check elf files:" << num;
LOG(INFO) << "time"
<< " p:" << elf_parser_p_time
<< " s:" << elf_parser_s_time
<< " objdump:" << objdump_time;
}
TEST_F(ElfParserTest, ReadDynamicNeededAndRpath) {
std::vector<string> argv;
std::vector<string> env;
argv.push_back("gcc");
argv.push_back("-xc");
argv.push_back("/dev/null");
argv.push_back("-shared");
argv.push_back("-Wl,-rpath=/lib");
argv.push_back("-o");
argv.push_back("/tmp/null.so");
ReadCommandOutputByPopen("gcc", argv, env, ".", MERGE_STDOUT_STDERR, nullptr);
std::unique_ptr<ElfParser> parser(ElfParser::NewElfParser("/tmp/null.so"));
ASSERT_TRUE(parser != nullptr);
EXPECT_TRUE(parser->valid());
std::vector<string> needed, rpath;
EXPECT_TRUE(parser->ReadDynamicNeededAndRpath(&needed, &rpath));
EXPECT_EQ(1U, needed.size());
EXPECT_EQ("libc.so.6", needed[0]);
EXPECT_EQ(1U, rpath.size());
EXPECT_EQ("/lib", rpath[0]);
}
} // namespace devtools_goma