1//===- InputCmd.cpp -------------------------------------------------------===//
2//
3//                     The MCLinker Project
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include "mcld/Script/InputCmd.h"
10
11#include "mcld/LD/Archive.h"
12#include "mcld/LD/ArchiveReader.h"
13#include "mcld/LD/DynObjReader.h"
14#include "mcld/LD/ObjectReader.h"
15#include "mcld/MC/Attribute.h"
16#include "mcld/MC/InputBuilder.h"
17#include "mcld/Script/InputToken.h"
18#include "mcld/Script/StringList.h"
19#include "mcld/Support/MsgHandling.h"
20#include "mcld/Support/Path.h"
21#include "mcld/Support/raw_ostream.h"
22#include "mcld/InputTree.h"
23#include "mcld/LinkerScript.h"
24#include "mcld/LinkerConfig.h"
25#include "mcld/Module.h"
26
27#include <llvm/Support/Casting.h>
28
29#include <cassert>
30#include <iostream>
31
32namespace mcld {
33
34//===----------------------------------------------------------------------===//
35// InputCmd
36//===----------------------------------------------------------------------===//
37InputCmd::InputCmd(StringList& pStringList,
38                   InputTree& pInputTree,
39                   InputBuilder& pBuilder,
40                   ObjectReader& pObjectReader,
41                   ArchiveReader& pArchiveReader,
42                   DynObjReader& pDynObjReader,
43                   const LinkerConfig& pConfig)
44    : ScriptCommand(ScriptCommand::INPUT),
45      m_StringList(pStringList),
46      m_InputTree(pInputTree),
47      m_Builder(pBuilder),
48      m_ObjectReader(pObjectReader),
49      m_ArchiveReader(pArchiveReader),
50      m_DynObjReader(pDynObjReader),
51      m_Config(pConfig) {
52}
53
54InputCmd::~InputCmd() {
55}
56
57void InputCmd::dump() const {
58  mcld::outs() << "INPUT ( ";
59  bool prev = false, cur = false;
60  for (StringList::const_iterator it = m_StringList.begin(),
61                                  ie = m_StringList.end();
62       it != ie;
63       ++it) {
64    assert((*it)->kind() == StrToken::Input);
65    InputToken* input = llvm::cast<InputToken>(*it);
66    cur = input->asNeeded();
67    if (!prev && cur)
68      mcld::outs() << "AS_NEEDED ( ";
69    else if (prev && !cur)
70      mcld::outs() << " )";
71
72    if (input->type() == InputToken::NameSpec)
73      mcld::outs() << "-l";
74    mcld::outs() << input->name() << " ";
75
76    prev = cur;
77  }
78
79  if (!m_StringList.empty() && prev)
80    mcld::outs() << " )";
81
82  mcld::outs() << " )\n";
83}
84
85void InputCmd::activate(Module& pModule) {
86  LinkerScript& script = pModule.getScript();
87  // construct the INPUT tree
88  m_Builder.setCurrentTree(m_InputTree);
89
90  bool is_begin_marked = false;
91  InputTree::iterator input_begin;
92
93  for (StringList::const_iterator it = m_StringList.begin(),
94                                  ie = m_StringList.end();
95       it != ie;
96       ++it) {
97    assert((*it)->kind() == StrToken::Input);
98    InputToken* token = llvm::cast<InputToken>(*it);
99    if (token->asNeeded())
100      m_Builder.getAttributes().setAsNeeded();
101    else
102      m_Builder.getAttributes().unsetAsNeeded();
103
104    switch (token->type()) {
105      case InputToken::File: {
106        sys::fs::Path path;
107
108        // 1. Looking for file in the sysroot prefix, if a sysroot prefix is
109        // configured and the filename starts with '/'
110        if (script.hasSysroot() &&
111            (token->name().size() > 0 && token->name()[0] == '/')) {
112          path = script.sysroot();
113          path.append(token->name());
114        } else {
115          // 2. Try to open the file in CWD
116          path.assign(token->name());
117          if (!sys::fs::exists(path)) {
118            // 3. Search through the library search path
119            sys::fs::Path* p =
120                script.directories().find(token->name(), Input::Script);
121            if (p != NULL)
122              path = *p;
123          }
124        }
125
126        if (!sys::fs::exists(path))
127          fatal(diag::err_cannot_open_input) << path.filename() << path;
128
129        m_Builder.createNode<InputTree::Positional>(
130            path.filename().native(), path, Input::Unknown);
131        break;
132      }
133      case InputToken::NameSpec: {
134        const sys::fs::Path* path = NULL;
135        // find out the real path of the namespec.
136        if (m_Builder.getConstraint().isSharedSystem()) {
137          // In the system with shared object support, we can find both archive
138          // and shared object.
139          if (m_Builder.getAttributes().isStatic()) {
140            // with --static, we must search an archive.
141            path = script.directories().find(token->name(), Input::Archive);
142          } else {
143            // otherwise, with --Bdynamic, we can find either an archive or a
144            // shared object.
145            path = script.directories().find(token->name(), Input::DynObj);
146          }
147        } else {
148          // In the system without shared object support, only look for an
149          // archive
150          path = script.directories().find(token->name(), Input::Archive);
151        }
152
153        if (path == NULL)
154          fatal(diag::err_cannot_find_namespec) << token->name();
155
156        m_Builder.createNode<InputTree::Positional>(
157            token->name(), *path, Input::Unknown);
158        break;
159      }
160      default:
161        assert(0 && "Invalid script token in INPUT!");
162        break;
163    }  // end of switch
164
165    InputTree::iterator input = m_Builder.getCurrentNode();
166    if (!is_begin_marked) {
167      input_begin = input;
168      is_begin_marked = true;
169    }
170    assert(*input != NULL);
171    if (!m_Builder.setMemory(**input,
172                             FileHandle::OpenMode(FileHandle::ReadOnly),
173                             FileHandle::Permission(FileHandle::System))) {
174      error(diag::err_cannot_open_input) << (*input)->name()
175                                         << (*input)->path();
176    }
177    m_Builder.setContext(**input);
178  }
179
180  for (InputTree::iterator input = input_begin, ie = m_InputTree.end();
181       input != ie;
182       ++input) {
183    bool doContinue = false;
184    if (m_ObjectReader.isMyFormat(**input, doContinue)) {
185      (*input)->setType(Input::Object);
186      m_ObjectReader.readHeader(**input);
187      m_ObjectReader.readSections(**input);
188      m_ObjectReader.readSymbols(**input);
189      pModule.getObjectList().push_back(*input);
190    } else if (doContinue && m_DynObjReader.isMyFormat(**input, doContinue)) {
191      (*input)->setType(Input::DynObj);
192      m_DynObjReader.readHeader(**input);
193      m_DynObjReader.readSymbols(**input);
194      pModule.getLibraryList().push_back(*input);
195    } else if (doContinue && m_ArchiveReader.isMyFormat(**input, doContinue)) {
196      (*input)->setType(Input::Archive);
197      if (m_Config.options().isInExcludeLIBS(**input)) {
198        (*input)->setNoExport();
199      }
200      Archive archive(**input, m_Builder);
201      m_ArchiveReader.readArchive(m_Config, archive);
202      if (archive.numOfObjectMember() > 0) {
203        m_InputTree.merge<InputTree::Inclusive>(input, archive.inputs());
204      }
205    } else {
206      if (m_Config.options().warnMismatch())
207        warning(diag::warn_unrecognized_input_file)
208            << (*input)->path() << m_Config.targets().triple().str();
209    }
210  }
211}
212
213}  // namespace mcld
214