Compare commits
37 Commits
0f26f00cf6
...
30fb21c4eb
Author | SHA1 | Date | |
---|---|---|---|
30fb21c4eb | |||
c390b6b28e | |||
adfa28be47 | |||
6b89074f58 | |||
eaaf782392 | |||
e748ae9c77 | |||
b8e2be6b38 | |||
cf5dc3ec31 | |||
27065714b9 | |||
3f13adcfe7 | |||
30e84f1cdd | |||
4be997abcf | |||
052dda78f8 | |||
aa5579f234 | |||
c6ef27d809 | |||
ad45de9049 | |||
986bcdd0c7 | |||
0876e25f90 | |||
0cad759415 | |||
33a951687b | |||
5cf0863e0a | |||
90430c47f4 | |||
12869f0ec7 | |||
30f80bcb88 | |||
a78e08521e | |||
c210c55a17 | |||
e7d8f9116a | |||
27197c7725 | |||
99a1ef5f96 | |||
25657ac36d | |||
2c396ece17 | |||
a3e3be5ec7 | |||
fe805ca893 | |||
4dbf3ddb47 | |||
d2516cb76c | |||
3a6d89767b | |||
f37700a02d |
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,3 +1,7 @@
|
||||
/.dub/
|
||||
/dub.selections.json
|
||||
/build/
|
||||
.cache/
|
||||
CMakeFiles/
|
||||
CMakeCache.txt
|
||||
node_modules/
|
||||
/dist/
|
||||
/dist-newstyle/
|
||||
|
5
CHANGELOG.md
Normal file
5
CHANGELOG.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Revision history for elna
|
||||
|
||||
## 1.0 -- YYYY-mm-dd
|
||||
|
||||
* First version. Released on an unsuspecting world.
|
28
CMakeLists.txt
Normal file
28
CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
project(Elna)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
find_package(Boost COMPONENTS process program_options REQUIRED)
|
||||
find_package(FLEX)
|
||||
include_directories(${Boost_INCLUDE_DIR})
|
||||
|
||||
FLEX_TARGET(scanner source/scanner.l ${CMAKE_CURRENT_BINARY_DIR}/scanner.cpp)
|
||||
|
||||
add_executable(elna cli/main.cpp
|
||||
source/lexer.cpp include/elna/source/lexer.hpp
|
||||
source/parser.cpp include/elna/source/parser.hpp
|
||||
source/types.cpp include/elna/source/types.hpp
|
||||
source/symbol_table.cpp include/elna/source/symbol_table.hpp
|
||||
source/result.cpp include/elna/source/result.hpp
|
||||
source/semantic.cpp include/elna/source/semantic.hpp
|
||||
source/optimizer.cpp include/elna/source/optimizer.hpp
|
||||
backend/riscv.cpp include/elna/backend/riscv.hpp
|
||||
backend/target.cpp include/elna/backend/target.hpp
|
||||
cli/cl.cpp include/elna/cli/cl.hpp
|
||||
${FLEX_scanner_OUTPUTS}
|
||||
)
|
||||
target_include_directories(elna PRIVATE include)
|
||||
target_link_libraries(elna LINK_PUBLIC ${Boost_LIBRARIES})
|
373
LICENSE
Normal file
373
LICENSE
Normal file
@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
69
Rakefile
69
Rakefile
@ -1,69 +0,0 @@
|
||||
require 'pathname'
|
||||
require 'rake/clean'
|
||||
require 'open3'
|
||||
|
||||
DFLAGS = ['--warn-no-deprecated', '-L/usr/lib64/gcc-12']
|
||||
BINARY = 'build/bin/elna'
|
||||
TESTS = FileList['tests/*.elna']
|
||||
.map { |test| (Pathname.new('build') + test).sub_ext('').to_path }
|
||||
SOURCES = FileList['source/**/*.d']
|
||||
|
||||
directory 'build'
|
||||
|
||||
CLEAN.include 'build'
|
||||
CLEAN.include '.dub'
|
||||
|
||||
rule(/build\/tests\/.+/ => ->(file) { test_for_out(file) }) do |t|
|
||||
Pathname.new(t.name).dirname.mkpath
|
||||
sh BINARY, t.source
|
||||
sh 'gcc', '-o', t.name, "#{t.name}.o"
|
||||
# Open3.pipeline [BINARY, t.source], ['gcc', '-x', 'assembler', '-o', t.name, '-']
|
||||
end
|
||||
|
||||
file BINARY => SOURCES do |t|
|
||||
sh({ 'DFLAGS' => (DFLAGS * ' ') }, 'dub', 'build', '--compiler=gdc-12')
|
||||
end
|
||||
|
||||
file 'build/tests/sample' => BINARY do |t|
|
||||
sh t.source
|
||||
sh 'gcc', '-o', t.name, 'build/tests/sample.o'
|
||||
end
|
||||
|
||||
task default: BINARY
|
||||
|
||||
desc 'Run all tests and check the results'
|
||||
task test: TESTS
|
||||
task test: BINARY do
|
||||
TESTS.each do |test|
|
||||
expected = Pathname
|
||||
.new(test)
|
||||
.sub_ext('.txt')
|
||||
.sub(/^build\/tests\//, 'tests/expectations/')
|
||||
.read
|
||||
.to_i
|
||||
|
||||
puts "Running #{test}"
|
||||
system test
|
||||
actual = $?.exitstatus
|
||||
|
||||
fail "#{test}: Expected #{expected}, got #{actual}" unless expected == actual
|
||||
end
|
||||
|
||||
# system './build/tests/sample'
|
||||
# actual = $?.exitstatus
|
||||
# fail "./build/tests/sample: Expected 3, got #{actual}" unless 3 == actual
|
||||
end
|
||||
|
||||
desc 'Run unittest blocks'
|
||||
task unittest: SOURCES do |t|
|
||||
sh('dub', 'test', '--compiler=gdc-12')
|
||||
end
|
||||
|
||||
def test_for_out(out_file)
|
||||
test_source = Pathname
|
||||
.new(out_file)
|
||||
.sub_ext('.elna')
|
||||
.sub(/^build\//, '')
|
||||
.to_path
|
||||
[test_source, BINARY]
|
||||
end
|
14
TODO
Normal file
14
TODO
Normal file
@ -0,0 +1,14 @@
|
||||
# Compiler
|
||||
|
||||
- Catch exceptions thrown by the argument parser and print them normally.
|
||||
- Structs or records.
|
||||
- Unions.
|
||||
- Support immediates greater than 12 bits.
|
||||
- It seems instructions are correctly encoded only if the compiler is running
|
||||
on a little endian architecture.
|
||||
- Assignment to a pointer.
|
||||
- Static array.
|
||||
- Syscalls.
|
||||
- Error message with an empty file wrongly says that a ")" is expected.
|
||||
- Support any expressions for constants.
|
||||
- Name analysis should fail if there are undefined symbols.
|
479
backend/riscv.cpp
Normal file
479
backend/riscv.cpp
Normal file
@ -0,0 +1,479 @@
|
||||
#include "elna/backend/riscv.hpp"
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace elna::riscv
|
||||
{
|
||||
instruction::instruction(base_opcode opcode)
|
||||
{
|
||||
this->representation = static_cast<std::underlying_type<base_opcode>::type>(opcode);
|
||||
}
|
||||
|
||||
instruction& instruction::i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate)
|
||||
{
|
||||
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7)
|
||||
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
|
||||
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
|
||||
| (immediate << 20);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
instruction& instruction::s(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2)
|
||||
{
|
||||
this->representation |= ((imm & 0x1f) << 7)
|
||||
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
|
||||
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
|
||||
| (static_cast<std::underlying_type<x_register>::type>(rs2) << 20)
|
||||
| ((imm & 0xfe0) << 20);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
instruction& instruction::b(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2)
|
||||
{
|
||||
this->representation |= ((imm & 0x800) >> 4) | ((imm & 0x1e) << 7)
|
||||
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
|
||||
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
|
||||
| (static_cast<std::underlying_type<x_register>::type>(rs2) << 20)
|
||||
| ((imm & 0x7e0) << 20) | ((imm & 0x1000) << 19);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
instruction& instruction::r(x_register rd, funct3_t funct3, x_register rs1, x_register rs2, funct7_t funct7)
|
||||
{
|
||||
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7)
|
||||
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
|
||||
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
|
||||
| (static_cast<std::underlying_type<x_register>::type>(rs2) << 20)
|
||||
| (static_cast<std::underlying_type<funct7_t>::type>(funct7) << 25);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
instruction& instruction::u(x_register rd, std::uint32_t imm)
|
||||
{
|
||||
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7) | (imm << 12);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
instruction& instruction::j(x_register rd, std::uint32_t imm)
|
||||
{
|
||||
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7)
|
||||
| (imm & 0xff000) | ((imm & 0x800) << 9) | ((imm & 0x7fe) << 20) | ((imm & 0x100000) << 11);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::byte *instruction::cbegin() const
|
||||
{
|
||||
return reinterpret_cast<const std::byte *>(&this->representation);
|
||||
}
|
||||
|
||||
const std::byte *instruction::cend() const
|
||||
{
|
||||
return reinterpret_cast<const std::byte *>(&this->representation) + sizeof(this->representation);
|
||||
}
|
||||
|
||||
static void relocate(std::string_view name, address_t target, std::vector<reference>& references,
|
||||
std::vector<instruction>& instructions, std::shared_ptr<source::writer<std::byte>> writer)
|
||||
{
|
||||
references.push_back(reference());
|
||||
references.back().name = name;
|
||||
references.back().offset = writer->size() + instructions.size() * 4;
|
||||
references.back().target = target;
|
||||
}
|
||||
|
||||
static void prologue(std::vector<instruction>& instructions)
|
||||
{
|
||||
instructions.push_back(instruction(base_opcode::opImm));
|
||||
instructions.push_back(instruction(base_opcode::store));
|
||||
instructions.push_back(instruction(base_opcode::store));
|
||||
instructions.push_back(instruction(base_opcode::opImm));
|
||||
}
|
||||
|
||||
static void epilogue(const std::size_t stack_size, std::vector<instruction>& instructions)
|
||||
{
|
||||
instructions[0].i(x_register::sp, funct3_t::addi, x_register::sp, -stack_size);
|
||||
instructions[1].s(0, funct3_t::sw, x_register::sp, x_register::s0);
|
||||
instructions[2].s(4, funct3_t::sw, x_register::sp, x_register::ra);
|
||||
instructions[3].i(x_register::s0, funct3_t::addi, x_register::sp, stack_size);
|
||||
|
||||
// Epilogue.
|
||||
instructions.push_back(instruction(base_opcode::load)
|
||||
.i(x_register::s0, funct3_t::lw, x_register::sp, 0));
|
||||
instructions.push_back(instruction(base_opcode::load)
|
||||
.i(x_register::ra, funct3_t::lw, x_register::sp, 4));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::sp, funct3_t::addi, x_register::sp, stack_size));
|
||||
instructions.push_back(instruction(base_opcode::jalr)
|
||||
.i(x_register::zero, funct3_t::jalr, x_register::ra, 0));
|
||||
}
|
||||
|
||||
static void generate_intrinsics(std::shared_ptr<source::writer<std::byte>> writer,
|
||||
std::vector<reference>& references)
|
||||
{
|
||||
{
|
||||
std::vector<instruction> instructions;
|
||||
prologue(instructions);
|
||||
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a1, funct3_t::addi, x_register::zero, 't'));
|
||||
instructions.push_back(instruction(base_opcode::branch)
|
||||
.b(8, funct3_t::bne, x_register::zero, x_register::a0));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a1, funct3_t::addi, x_register::zero, 'f'));
|
||||
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::t0, funct3_t::addi, x_register::a0, 0));
|
||||
instructions.push_back(instruction(base_opcode::store)
|
||||
.s(0, funct3_t::sw, x_register::s0, x_register::a1));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a0, funct3_t::addi, x_register::zero, 1));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a1, funct3_t::addi, x_register::s0, 0));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a2, funct3_t::addi, x_register::zero, 1));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a7, funct3_t::addi, x_register::zero, 64));
|
||||
instructions.push_back(instruction(base_opcode::system)
|
||||
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
||||
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::t1, funct3_t::addi, x_register::zero, '\n'));
|
||||
instructions.push_back(instruction(base_opcode::store)
|
||||
.s(0, funct3_t::sw, x_register::s0, x_register::t1));
|
||||
instructions.push_back(instruction(base_opcode::system)
|
||||
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
||||
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a0, funct3_t::addi, x_register::t0, 0));
|
||||
|
||||
epilogue(12, instructions);
|
||||
|
||||
writer->sink("writeb", reinterpret_cast<const std::byte *>(instructions.data()),
|
||||
instructions.size() * sizeof(instruction));
|
||||
}
|
||||
{
|
||||
std::vector<instruction> instructions;
|
||||
prologue(instructions);
|
||||
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::t0, funct3_t::addi, x_register::a0, 0));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a0, funct3_t::addi, x_register::a0, '0'));
|
||||
instructions.push_back(instruction(base_opcode::store)
|
||||
.s(0, funct3_t::sw, x_register::s0, x_register::a0));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a0, funct3_t::addi, x_register::zero, 1));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a1, funct3_t::addi, x_register::s0, 0));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a2, funct3_t::addi, x_register::zero, 1));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a7, funct3_t::addi, x_register::zero, 64));
|
||||
instructions.push_back(instruction(base_opcode::system)
|
||||
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
||||
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::t1, funct3_t::addi, x_register::zero, '\n'));
|
||||
instructions.push_back(instruction(base_opcode::store)
|
||||
.s(0, funct3_t::sw, x_register::s0, x_register::t1));
|
||||
instructions.push_back(instruction(base_opcode::system)
|
||||
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
||||
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a0, funct3_t::addi, x_register::t0, 0));
|
||||
|
||||
epilogue(12, instructions);
|
||||
|
||||
writer->sink("writei", reinterpret_cast<const std::byte *>(instructions.data()),
|
||||
instructions.size() * sizeof(instruction));
|
||||
}
|
||||
}
|
||||
|
||||
static void load_in_register(std::shared_ptr<source::operand> operand, const x_register target,
|
||||
std::shared_ptr<source::procedure_info> procedure_info, std::vector<instruction>& instructions)
|
||||
{
|
||||
std::shared_ptr<source::integer_operand> integer_operand{ nullptr };
|
||||
std::shared_ptr<source::variable_operand> variable_operand{ nullptr };
|
||||
std::shared_ptr<source::temporary_variable> temporary_variable{ nullptr };
|
||||
std::shared_ptr<source::variable_info> variable_symbol{ nullptr };
|
||||
std::shared_ptr<source::parameter_info> parameter_symbol{ nullptr };
|
||||
|
||||
if ((integer_operand = std::dynamic_pointer_cast<source::integer_operand>(operand)) != nullptr)
|
||||
{
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(target, funct3_t::addi, x_register::zero, integer_operand->value()));
|
||||
}
|
||||
else if ((variable_operand = std::dynamic_pointer_cast<source::variable_operand>(operand)) != nullptr)
|
||||
{
|
||||
const auto& name = procedure_info->scope()->lookup(variable_operand->name());
|
||||
if ((variable_symbol = std::dynamic_pointer_cast<source::variable_info>(name)) != nullptr)
|
||||
{
|
||||
instructions.push_back(instruction(base_opcode::load)
|
||||
.i(target, funct3_t::lw, x_register::s0, variable_symbol->offset));
|
||||
}
|
||||
else if ((parameter_symbol = std::dynamic_pointer_cast<source::parameter_info>(name)) != nullptr)
|
||||
{
|
||||
instructions.push_back(instruction(base_opcode::load)
|
||||
.i(target, funct3_t::lw, x_register::s0, parameter_symbol->offset));
|
||||
}
|
||||
}
|
||||
else if ((temporary_variable = std::dynamic_pointer_cast<source::temporary_variable>(operand)) != nullptr)
|
||||
{
|
||||
instructions.push_back(instruction(base_opcode::load));
|
||||
instructions.back().i(target, funct3_t::lw, x_register::s0,
|
||||
procedure_info->local_stack_size + 4 * temporary_variable->counter());
|
||||
}
|
||||
}
|
||||
|
||||
static void store_from_register(std::shared_ptr<source::operand> destination, const x_register target,
|
||||
std::shared_ptr<source::procedure_info> procedure_info, std::vector<instruction>& instructions)
|
||||
{
|
||||
std::shared_ptr<source::variable_operand> variable_operand{ nullptr };
|
||||
std::shared_ptr<source::temporary_variable> temporary_variable{ nullptr };
|
||||
std::shared_ptr<source::variable_info> variable_symbol{ nullptr };
|
||||
|
||||
if ((variable_operand = std::dynamic_pointer_cast<source::variable_operand>(destination)) != nullptr)
|
||||
{
|
||||
variable_symbol = std::dynamic_pointer_cast<source::variable_info>(
|
||||
procedure_info->scope()->lookup(variable_operand->name()));
|
||||
instructions.push_back(instruction(base_opcode::store)
|
||||
.s(variable_symbol->offset, funct3_t::sw, x_register::s0, target));
|
||||
}
|
||||
else if ((temporary_variable = std::dynamic_pointer_cast<source::temporary_variable>(destination)) != nullptr)
|
||||
{
|
||||
instructions.push_back(instruction(base_opcode::store));
|
||||
instructions.back().s(procedure_info->local_stack_size + 4 * temporary_variable->counter(),
|
||||
funct3_t::sw, x_register::s0, target);
|
||||
}
|
||||
}
|
||||
|
||||
static void perform_binary_operation(const source::binary_operator operation, std::shared_ptr<source::operand> lhs,
|
||||
std::shared_ptr<source::operand> rhs, std::shared_ptr<source::operand> destination,
|
||||
std::shared_ptr<source::procedure_info> procedure_info, std::vector<instruction>& instructions)
|
||||
{
|
||||
constexpr auto lhs_register = x_register::a0;
|
||||
std::shared_ptr<source::variable_operand> variable_operand{ nullptr };
|
||||
std::shared_ptr<source::temporary_variable> temporary_variable{ nullptr };
|
||||
std::shared_ptr<source::variable_info> variable_symbol{ nullptr };
|
||||
|
||||
load_in_register(lhs, x_register::a0, procedure_info, instructions);
|
||||
load_in_register(rhs, x_register::t0, procedure_info, instructions);
|
||||
|
||||
// Calculate the result and assign it to a variable on the stack.
|
||||
switch (operation)
|
||||
{
|
||||
case source::binary_operator::sum:
|
||||
instructions.push_back(instruction(base_opcode::op)
|
||||
.r(lhs_register, funct3_t::add, x_register::a0, x_register::t0));
|
||||
break;
|
||||
case source::binary_operator::subtraction:
|
||||
instructions.push_back(instruction(base_opcode::op)
|
||||
.r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub));
|
||||
break;
|
||||
case source::binary_operator::multiplication:
|
||||
instructions.push_back(instruction(base_opcode::op)
|
||||
.r(lhs_register, funct3_t::mul, x_register::a0, x_register::t0, funct7_t::muldiv));
|
||||
break;
|
||||
case source::binary_operator::division:
|
||||
instructions.push_back(instruction(base_opcode::op)
|
||||
.r(lhs_register, funct3_t::div, x_register::a0, x_register::t0, funct7_t::muldiv));
|
||||
break;
|
||||
case source::binary_operator::equals:
|
||||
instructions.push_back(instruction(base_opcode::op)
|
||||
.r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(lhs_register, funct3_t::sltiu, lhs_register, 1));
|
||||
break;
|
||||
case source::binary_operator::not_equals:
|
||||
instructions.push_back(instruction(base_opcode::op)
|
||||
.r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub));
|
||||
instructions.push_back(instruction(base_opcode::op)
|
||||
.r(lhs_register, funct3_t::sltu, x_register::zero, lhs_register));
|
||||
break;
|
||||
case source::binary_operator::less:
|
||||
instructions.push_back(instruction(base_opcode::op)
|
||||
.r(lhs_register, funct3_t::sltu, x_register::a0, x_register::t0));
|
||||
break;
|
||||
case source::binary_operator::greater_equal:
|
||||
instructions.push_back(instruction(base_opcode::op)
|
||||
.r(lhs_register, funct3_t::sltu, x_register::t0, x_register::a0));
|
||||
break;
|
||||
case source::binary_operator::greater:
|
||||
instructions.push_back(instruction(base_opcode::op)
|
||||
.r(lhs_register, funct3_t::slt, x_register::a0, x_register::t0));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(lhs_register, funct3_t::xori, lhs_register, 1));
|
||||
break;
|
||||
case source::binary_operator::less_equal:
|
||||
instructions.push_back(instruction(base_opcode::op)
|
||||
.r(lhs_register, funct3_t::slt, x_register::t0, x_register::a0));
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(lhs_register, funct3_t::xori, lhs_register, 1));
|
||||
break;
|
||||
}
|
||||
store_from_register(destination, lhs_register, procedure_info, instructions);
|
||||
}
|
||||
|
||||
std::vector<reference> generate(source::intermediate_code_generator generator,
|
||||
std::shared_ptr<source::symbol_table> table, std::shared_ptr<source::writer<std::byte>> writer)
|
||||
{
|
||||
std::vector<reference> references;
|
||||
|
||||
generate_intrinsics(writer, references);
|
||||
|
||||
for (auto& [identifier, intermediate_code] : generator)
|
||||
{
|
||||
std::vector<instruction> instructions;
|
||||
auto main_symbol = std::dynamic_pointer_cast<source::procedure_info>(table->lookup(identifier));
|
||||
std::size_t argument_offset{ 0 };
|
||||
const auto stack_size = static_cast<std::uint32_t>(
|
||||
intermediate_code.variable_counter() * 4 + 8 + main_symbol->stack_size());
|
||||
std::unordered_map<std::size_t, std::function<void(std::size_t)>> missing_labels;
|
||||
std::unordered_map<std::size_t, std::function<void(std::size_t)>>::iterator missing_label =
|
||||
missing_labels.end();
|
||||
std::unordered_map<std::size_t, std::size_t> local_labels;
|
||||
|
||||
for (auto& quadruple : intermediate_code)
|
||||
{
|
||||
switch (quadruple.operation())
|
||||
{
|
||||
case source::quadruple_operator::start:
|
||||
prologue(instructions);
|
||||
break;
|
||||
case source::quadruple_operator::stop:
|
||||
epilogue(stack_size, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::add:
|
||||
perform_binary_operation(source::binary_operator::sum, quadruple.operand1(),
|
||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::sub:
|
||||
perform_binary_operation(source::binary_operator::subtraction, quadruple.operand1(),
|
||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::mul:
|
||||
perform_binary_operation(source::binary_operator::multiplication, quadruple.operand1(),
|
||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::div:
|
||||
perform_binary_operation(source::binary_operator::division, quadruple.operand1(),
|
||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::eq:
|
||||
perform_binary_operation(source::binary_operator::equals, quadruple.operand1(),
|
||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::neq:
|
||||
perform_binary_operation(source::binary_operator::not_equals, quadruple.operand1(),
|
||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::lt:
|
||||
perform_binary_operation(source::binary_operator::less, quadruple.operand1(),
|
||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::ge:
|
||||
perform_binary_operation(source::binary_operator::greater_equal, quadruple.operand1(),
|
||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::gt:
|
||||
perform_binary_operation(source::binary_operator::greater, quadruple.operand1(),
|
||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::le:
|
||||
perform_binary_operation(source::binary_operator::less_equal, quadruple.operand1(),
|
||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::load:
|
||||
{
|
||||
auto operand_identifier =
|
||||
std::dynamic_pointer_cast<source::variable_operand>(quadruple.operand1())->name();
|
||||
auto variable_symbol = std::dynamic_pointer_cast<source::variable_info>(
|
||||
main_symbol->scope()->lookup(operand_identifier));
|
||||
|
||||
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
|
||||
instructions.push_back(instruction(base_opcode::load)
|
||||
.i(x_register::a0, funct3_t::lw, x_register::a0, 0));
|
||||
store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions);
|
||||
}
|
||||
break;
|
||||
case source::quadruple_operator::ref:
|
||||
{
|
||||
auto operand_identifier =
|
||||
std::dynamic_pointer_cast<source::variable_operand>(quadruple.operand1())->name();
|
||||
auto variable_symbol = std::dynamic_pointer_cast<source::variable_info>(
|
||||
main_symbol->scope()->lookup(operand_identifier));
|
||||
|
||||
instructions.push_back(instruction(base_opcode::opImm)
|
||||
.i(x_register::a0, funct3_t::addi, x_register::s0, variable_symbol->offset));
|
||||
store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions);
|
||||
}
|
||||
break;
|
||||
case source::quadruple_operator::beqz:
|
||||
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
|
||||
instructions.push_back(instruction(base_opcode::branch));
|
||||
missing_labels.emplace(
|
||||
std::dynamic_pointer_cast<source::label_operand>(quadruple.operand3())->counter(),
|
||||
[before_branch = instructions.size() - 1, &instructions](std::size_t target) {
|
||||
instructions[before_branch].b((target - before_branch) * 4,
|
||||
funct3_t::beq, x_register::zero, x_register::a0);
|
||||
});
|
||||
break;
|
||||
case source::quadruple_operator::j:
|
||||
{
|
||||
auto local_label = local_labels.find(
|
||||
std::dynamic_pointer_cast<source::label_operand>(quadruple.operand3())->counter());
|
||||
if (local_label != local_labels.end())
|
||||
{
|
||||
auto offset = -(instructions.size() - local_label->second) * 4;
|
||||
instructions.push_back(instruction(base_opcode::auipc).u(x_register::a0, 0));
|
||||
instructions.push_back(instruction(base_opcode::jalr)
|
||||
.i(x_register::zero, funct3_t::jalr, x_register::a0, offset));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case source::quadruple_operator::label:
|
||||
{
|
||||
auto label_counter =
|
||||
std::dynamic_pointer_cast<source::label_operand>(quadruple.operand3())->counter();
|
||||
missing_label = missing_labels.find(label_counter);
|
||||
|
||||
if (missing_label != missing_labels.end())
|
||||
{
|
||||
missing_label->second(instructions.size());
|
||||
}
|
||||
local_labels[label_counter] = instructions.size();
|
||||
}
|
||||
break;
|
||||
case source::quadruple_operator::assign:
|
||||
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
|
||||
store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions);
|
||||
break;
|
||||
case source::quadruple_operator::param:
|
||||
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
|
||||
instructions.push_back(instruction(base_opcode::store)
|
||||
.s(argument_offset, funct3_t::sw, x_register::sp, x_register::a0));
|
||||
argument_offset += 4;
|
||||
break;
|
||||
case source::quadruple_operator::call:
|
||||
relocate(std::dynamic_pointer_cast<source::variable_operand>(quadruple.operand1())->name(),
|
||||
address_t::text, references, instructions, writer);
|
||||
instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0));
|
||||
instructions.push_back(instruction(base_opcode::jalr)
|
||||
.i(x_register::ra, funct3_t::jalr, x_register::ra, 0));
|
||||
argument_offset = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
writer->sink(identifier,
|
||||
reinterpret_cast<const std::byte *>(instructions.data()),
|
||||
instructions.size() * sizeof(instruction));
|
||||
}
|
||||
return references;
|
||||
}
|
||||
}
|
132
backend/target.cpp
Normal file
132
backend/target.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
#include "elna/backend/target.hpp"
|
||||
#include "elna/backend/riscv.hpp"
|
||||
#include <cstring>
|
||||
|
||||
namespace elna::riscv
|
||||
{
|
||||
elfio_writer::elfio_writer(ELFIO::section *text,
|
||||
ELFIO::symbol_section_accessor symbol_accessor, ELFIO::string_section_accessor string_accessor)
|
||||
: text(text),
|
||||
symbol_accessor(symbol_accessor), string_accessor(string_accessor)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t elfio_writer::sink(const std::string& label, const std::byte *data, std::size_t size)
|
||||
{
|
||||
auto offset = text->get_size();
|
||||
text->append_data(reinterpret_cast<const char *>(data), size);
|
||||
|
||||
this->symbol_accessor.add_symbol(this->string_accessor, label.data(), offset, label.size(),
|
||||
ELFIO::STB_GLOBAL, ELFIO::STT_FUNC, 0, text->get_index());
|
||||
|
||||
return text->get_size();
|
||||
}
|
||||
|
||||
void elfio_writer::sink(const std::string& label)
|
||||
{
|
||||
this->symbol_accessor.add_symbol(this->string_accessor, "printf", 0x00000000, 0,
|
||||
ELFIO::STB_GLOBAL, ELFIO::STT_NOTYPE, 0, ELFIO::SHN_UNDEF);
|
||||
}
|
||||
|
||||
std::size_t elfio_writer::size() const
|
||||
{
|
||||
return this->text->get_size();
|
||||
}
|
||||
|
||||
std::ptrdiff_t lookup(ELFIO::symbol_section_accessor symbol_accessor, const std::string& label)
|
||||
{
|
||||
for (ptrdiff_t j = 0; j < symbol_accessor.get_symbols_num(); ++j)
|
||||
{
|
||||
std::string name;
|
||||
ELFIO::Elf64_Addr value;
|
||||
ELFIO::Elf_Xword size;
|
||||
unsigned char bind;
|
||||
unsigned char type;
|
||||
ELFIO::Elf_Half section_index;
|
||||
unsigned char other;
|
||||
|
||||
symbol_accessor.get_symbol(j, name, value, size, bind, type, section_index, other);
|
||||
if (name == label)
|
||||
{
|
||||
return j;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void riscv32_elf(source::program *ast, source::intermediate_code_generator intermediate_code_generator,
|
||||
std::shared_ptr<source::symbol_table> table, const std::filesystem::path& out_file)
|
||||
{
|
||||
ELFIO::elfio writer;
|
||||
|
||||
writer.create(ELFIO::ELFCLASS32, ELFIO::ELFDATA2LSB);
|
||||
|
||||
writer.set_os_abi(ELFIO::ELFOSABI_NONE);
|
||||
writer.set_type(ELFIO::ET_REL);
|
||||
writer.set_flags(0x4); // EF_RISCV_FLOAT_ABI_DOUBLE
|
||||
writer.set_machine(ELFIO::EM_RISCV);
|
||||
|
||||
// Create code section
|
||||
ELFIO::section* text_sec = writer.sections.add(".text");
|
||||
text_sec->set_type(ELFIO::SHT_PROGBITS);
|
||||
text_sec->set_flags(ELFIO::SHF_ALLOC | ELFIO::SHF_EXECINSTR);
|
||||
text_sec->set_addr_align(0x1);
|
||||
|
||||
// Create string table section
|
||||
ELFIO::section* str_sec = writer.sections.add(".strtab");
|
||||
str_sec->set_type(ELFIO::SHT_STRTAB);
|
||||
|
||||
// Create string table writer
|
||||
ELFIO::string_section_accessor stra(str_sec);
|
||||
|
||||
// Create symbol table section
|
||||
ELFIO::section* sym_sec = writer.sections.add(".symtab");
|
||||
sym_sec->set_type(ELFIO::SHT_SYMTAB);
|
||||
sym_sec->set_info(2);
|
||||
sym_sec->set_addr_align(0x4);
|
||||
sym_sec->set_entry_size(writer.get_default_entry_size(ELFIO::SHT_SYMTAB));
|
||||
sym_sec->set_link(str_sec->get_index());
|
||||
|
||||
// Create relocation table section
|
||||
ELFIO::section* rel_sec = writer.sections.add(".rel.text");
|
||||
rel_sec->set_type(ELFIO::SHT_REL);
|
||||
rel_sec->set_info(text_sec->get_index());
|
||||
rel_sec->set_addr_align(0x4);
|
||||
rel_sec->set_entry_size(writer.get_default_entry_size(ELFIO::SHT_REL));
|
||||
rel_sec->set_link(sym_sec->get_index());
|
||||
rel_sec->set_flags(ELFIO::SHF_ALLOC);
|
||||
|
||||
// Create symbol relocation table writers
|
||||
ELFIO::symbol_section_accessor syma(writer, sym_sec);
|
||||
ELFIO::relocation_section_accessor rela(writer, rel_sec);
|
||||
|
||||
auto _writer = std::make_shared<elfio_writer>(text_sec, syma, stra);
|
||||
auto references = generate(intermediate_code_generator, table, _writer);
|
||||
|
||||
syma.arrange_local_symbols();
|
||||
|
||||
for (auto& reference : references)
|
||||
{
|
||||
ELFIO::Elf_Word relocated_symbol = lookup(syma, reference.name);
|
||||
|
||||
switch (reference.target)
|
||||
{
|
||||
case address_t::high20:
|
||||
rela.add_entry(reference.offset, relocated_symbol, 26 /* ELFIO::R_RISCV_HI20 */);
|
||||
// rela.add_entry(reference.offset, relocated_symbol, 51 /* ELFIO::R_RISCV_RELAX */);
|
||||
break;
|
||||
case address_t::lower12i:
|
||||
rela.add_entry(reference.offset, relocated_symbol, 27 /* ELFIO::R_RISCV_LO12_I */);
|
||||
// rela.add_entry(reference.offset, relocated_symbol, 51 /* ELFIO::R_RISCV_RELAX */);
|
||||
break;
|
||||
case address_t::text:
|
||||
rela.add_entry(reference.offset, relocated_symbol, 18 /* ELFIO::R_RISCV_CALL */);
|
||||
// rela.add_entry(reference.offset, relocated_symbol, 51 /* ELFIO::R_RISCV_RELAX */);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create ELF object file
|
||||
writer.save(out_file);
|
||||
}
|
||||
}
|
78
cli/cl.cpp
Normal file
78
cli/cl.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "elna/cli/cl.hpp"
|
||||
#include "elna/backend/target.hpp"
|
||||
#include "elna/source/semantic.hpp"
|
||||
#include "elna/source/optimizer.hpp"
|
||||
#include "elna/source/types.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace elna::cli
|
||||
{
|
||||
void print_error(const std::unique_ptr<source::error>& compile_error)
|
||||
{
|
||||
std::cerr << compile_error->path().string() << ":"
|
||||
<< compile_error->line() << ':' << compile_error->column()
|
||||
<< ": " << compile_error->what() << std::endl;
|
||||
}
|
||||
|
||||
constexpr std::size_t pointer_size = 4;
|
||||
|
||||
static std::shared_ptr<source::symbol_table> add_builtin_symbols()
|
||||
{
|
||||
source::symbol_table result;
|
||||
std::vector<std::shared_ptr<const source::type>> intrinsic_arguments;
|
||||
|
||||
auto boolean_info = std::make_shared<source::type_info>(source::boolean_type);
|
||||
auto int_info = std::make_shared<source::type_info>(source::int_type);
|
||||
result.enter("Boolean", boolean_info);
|
||||
result.enter("Int", int_info);
|
||||
|
||||
intrinsic_arguments.push_back(int_info->type());
|
||||
auto writei = std::make_shared<source::intrinsic_info>(
|
||||
source::procedure_type{ intrinsic_arguments, pointer_size });
|
||||
result.enter("writei", writei);
|
||||
intrinsic_arguments.clear();
|
||||
|
||||
intrinsic_arguments.push_back(boolean_info->type());
|
||||
auto writeb = std::make_shared<source::intrinsic_info>(
|
||||
source::procedure_type{ intrinsic_arguments, pointer_size });
|
||||
result.enter("writeb", writeb);
|
||||
intrinsic_arguments.clear();
|
||||
|
||||
return std::make_shared<source::symbol_table>(std::move(result));
|
||||
}
|
||||
|
||||
int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file)
|
||||
{
|
||||
try
|
||||
{
|
||||
source::result<source::lexer> lex_result = source::tokenize(in_file);
|
||||
|
||||
if (lex_result.has_errors())
|
||||
{
|
||||
print_errors(lex_result.errors().cbegin(), lex_result.errors().cend());
|
||||
return 1;
|
||||
}
|
||||
source::parser parser{ std::move(lex_result.success()) };
|
||||
auto ast = parser.parse();
|
||||
if (ast == nullptr)
|
||||
{
|
||||
print_errors(parser.errors().cbegin(), parser.errors().cend());
|
||||
return 2;
|
||||
}
|
||||
auto global_scope = add_builtin_symbols();
|
||||
source::name_analysis_visitor(global_scope, in_file, pointer_size).visit(ast.get());
|
||||
source::type_analysis_visitor(global_scope, in_file, pointer_size).visit(ast.get());
|
||||
source::allocator_visitor(global_scope).visit(ast.get());
|
||||
|
||||
source::intermediate_code_generator intermediate_code_generator{ global_scope };
|
||||
intermediate_code_generator.visit(ast.get());
|
||||
|
||||
riscv::riscv32_elf(ast.get(), intermediate_code_generator, global_scope, out_file);
|
||||
}
|
||||
catch (std::ios_base::failure&)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
47
cli/main.cpp
Normal file
47
cli/main.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "elna/cli/cl.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
boost::program_options::options_description visible{ "Allowed options" };
|
||||
boost::program_options::options_description description;
|
||||
boost::program_options::positional_options_description positional;
|
||||
boost::program_options::variables_map variables_map;
|
||||
|
||||
visible.add_options()
|
||||
("help", "Print this help message")
|
||||
("output,o", boost::program_options::value<std::filesystem::path>(), "Output object file")
|
||||
;
|
||||
description.add_options()
|
||||
("input", boost::program_options::value<std::filesystem::path>()->required())
|
||||
;
|
||||
description.add(visible);
|
||||
positional.add("input", 1);
|
||||
|
||||
auto parsed = boost::program_options::command_line_parser(argc, argv)
|
||||
.options(description).positional(positional)
|
||||
.run();
|
||||
boost::program_options::store(parsed, variables_map);
|
||||
boost::program_options::notify(variables_map);
|
||||
|
||||
if (variables_map.count("help"))
|
||||
{
|
||||
std::cout << description << std::endl;
|
||||
return 0;
|
||||
}
|
||||
const auto in_file = variables_map["input"].as<std::filesystem::path>();
|
||||
std::filesystem::path out_file;
|
||||
|
||||
if (variables_map.count("output"))
|
||||
{
|
||||
out_file = variables_map["output"].as<std::filesystem::path>();
|
||||
}
|
||||
else
|
||||
{
|
||||
out_file = in_file.filename().replace_extension(".o");
|
||||
}
|
||||
return elna::cli::compile(in_file, out_file);
|
||||
}
|
9
dub.json
9
dub.json
@ -1,9 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"tanya": "~>0.18.0"
|
||||
},
|
||||
"name": "elna",
|
||||
"targetType": "executable",
|
||||
"targetPath": "build/bin",
|
||||
"mainSourceFile": "source/main.d"
|
||||
}
|
1347
include/elfio/elf_types.hpp
Normal file
1347
include/elfio/elf_types.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1096
include/elfio/elfio.hpp
Normal file
1096
include/elfio/elfio.hpp
Normal file
File diff suppressed because it is too large
Load Diff
88
include/elfio/elfio_array.hpp
Normal file
88
include/elfio/elfio_array.hpp
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELFIO_ARRAY_HPP
|
||||
#define ELFIO_ARRAY_HPP
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class S, typename T> class array_section_accessor_template
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
explicit array_section_accessor_template( const elfio& elf_file,
|
||||
S* section )
|
||||
: elf_file( elf_file ), array_section( section )
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Xword get_entries_num() const
|
||||
{
|
||||
Elf_Xword entry_size = sizeof( T );
|
||||
return array_section->get_size() / entry_size;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool get_entry( Elf_Xword index, Elf64_Addr& address ) const
|
||||
{
|
||||
if ( index >= get_entries_num() ) { // Is index valid
|
||||
return false;
|
||||
}
|
||||
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
const T temp = *reinterpret_cast<const T*>( array_section->get_data() +
|
||||
index * sizeof( T ) );
|
||||
address = convertor( temp );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void add_entry( Elf64_Addr address )
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
T temp = convertor( (T)address );
|
||||
array_section->append_data( reinterpret_cast<char*>( &temp ),
|
||||
sizeof( temp ) );
|
||||
}
|
||||
|
||||
private:
|
||||
//------------------------------------------------------------------------------
|
||||
const elfio& elf_file;
|
||||
S* array_section;
|
||||
};
|
||||
|
||||
template <typename T = Elf32_Word>
|
||||
using array_section_accessor = array_section_accessor_template<section, T>;
|
||||
template <typename T = Elf32_Word>
|
||||
using const_array_section_accessor =
|
||||
array_section_accessor_template<const section, T>;
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_ARRAY_HPP
|
1346
include/elfio/elfio_dump.hpp
Normal file
1346
include/elfio/elfio_dump.hpp
Normal file
File diff suppressed because it is too large
Load Diff
274
include/elfio/elfio_dynamic.hpp
Normal file
274
include/elfio/elfio_dynamic.hpp
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELFIO_DYNAMIC_HPP
|
||||
#define ELFIO_DYNAMIC_HPP
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class S> class dynamic_section_accessor_template
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
explicit dynamic_section_accessor_template( const elfio& elf_file,
|
||||
S* section )
|
||||
: elf_file( elf_file ), dynamic_section( section ), entries_num( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Xword get_entries_num() const
|
||||
{
|
||||
size_t needed_entry_size = -1;
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
needed_entry_size = sizeof( Elf32_Dyn );
|
||||
}
|
||||
else {
|
||||
needed_entry_size = sizeof( Elf64_Dyn );
|
||||
}
|
||||
|
||||
if ( ( 0 == entries_num ) &&
|
||||
( 0 != dynamic_section->get_entry_size() &&
|
||||
dynamic_section->get_entry_size() >= needed_entry_size ) ) {
|
||||
entries_num =
|
||||
dynamic_section->get_size() / dynamic_section->get_entry_size();
|
||||
Elf_Xword i;
|
||||
Elf_Xword tag = DT_NULL;
|
||||
Elf_Xword value = 0;
|
||||
std::string str;
|
||||
for ( i = 0; i < entries_num; i++ ) {
|
||||
get_entry( i, tag, value, str );
|
||||
if ( tag == DT_NULL )
|
||||
break;
|
||||
}
|
||||
entries_num = std::min<Elf_Xword>( entries_num, i + 1 );
|
||||
}
|
||||
|
||||
return entries_num;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool get_entry( Elf_Xword index,
|
||||
Elf_Xword& tag,
|
||||
Elf_Xword& value,
|
||||
std::string& str ) const
|
||||
{
|
||||
if ( index >= get_entries_num() ) { // Is index valid
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
generic_get_entry_dyn<Elf32_Dyn>( index, tag, value );
|
||||
}
|
||||
else {
|
||||
generic_get_entry_dyn<Elf64_Dyn>( index, tag, value );
|
||||
}
|
||||
|
||||
// If the tag has a string table reference - prepare the string
|
||||
if ( tag == DT_NEEDED || tag == DT_SONAME || tag == DT_RPATH ||
|
||||
tag == DT_RUNPATH ) {
|
||||
string_section_accessor strsec(
|
||||
elf_file.sections[get_string_table_index()] );
|
||||
const char* result = strsec.get_string( (Elf_Word)value );
|
||||
if ( nullptr == result ) {
|
||||
str.clear();
|
||||
return false;
|
||||
}
|
||||
str = result;
|
||||
}
|
||||
else {
|
||||
str.clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void add_entry( Elf_Xword tag, Elf_Xword value )
|
||||
{
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
generic_add_entry_dyn<Elf32_Dyn>( tag, value );
|
||||
}
|
||||
else {
|
||||
generic_add_entry_dyn<Elf64_Dyn>( tag, value );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void add_entry( Elf_Xword tag, const std::string& str )
|
||||
{
|
||||
string_section_accessor strsec(
|
||||
elf_file.sections[get_string_table_index()] );
|
||||
Elf_Xword value = strsec.add_string( str );
|
||||
add_entry( tag, value );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Half get_string_table_index() const
|
||||
{
|
||||
return (Elf_Half)dynamic_section->get_link();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void generic_get_entry_dyn( Elf_Xword index,
|
||||
Elf_Xword& tag,
|
||||
Elf_Xword& value ) const
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
// Check unusual case when dynamic section has no data
|
||||
if ( dynamic_section->get_data() == nullptr ||
|
||||
( index + 1 ) * dynamic_section->get_entry_size() >
|
||||
dynamic_section->get_size() ||
|
||||
dynamic_section->get_entry_size() < sizeof( T ) ) {
|
||||
tag = DT_NULL;
|
||||
value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const T* pEntry = reinterpret_cast<const T*>(
|
||||
dynamic_section->get_data() +
|
||||
index * dynamic_section->get_entry_size() );
|
||||
tag = convertor( pEntry->d_tag );
|
||||
switch ( tag ) {
|
||||
case DT_NULL:
|
||||
case DT_SYMBOLIC:
|
||||
case DT_TEXTREL:
|
||||
case DT_BIND_NOW:
|
||||
value = 0;
|
||||
break;
|
||||
case DT_NEEDED:
|
||||
case DT_PLTRELSZ:
|
||||
case DT_RELASZ:
|
||||
case DT_RELAENT:
|
||||
case DT_STRSZ:
|
||||
case DT_SYMENT:
|
||||
case DT_SONAME:
|
||||
case DT_RPATH:
|
||||
case DT_RELSZ:
|
||||
case DT_RELENT:
|
||||
case DT_PLTREL:
|
||||
case DT_INIT_ARRAYSZ:
|
||||
case DT_FINI_ARRAYSZ:
|
||||
case DT_RUNPATH:
|
||||
case DT_FLAGS:
|
||||
case DT_PREINIT_ARRAYSZ:
|
||||
value = convertor( pEntry->d_un.d_val );
|
||||
break;
|
||||
case DT_PLTGOT:
|
||||
case DT_HASH:
|
||||
case DT_STRTAB:
|
||||
case DT_SYMTAB:
|
||||
case DT_RELA:
|
||||
case DT_INIT:
|
||||
case DT_FINI:
|
||||
case DT_REL:
|
||||
case DT_DEBUG:
|
||||
case DT_JMPREL:
|
||||
case DT_INIT_ARRAY:
|
||||
case DT_FINI_ARRAY:
|
||||
case DT_PREINIT_ARRAY:
|
||||
default:
|
||||
value = convertor( pEntry->d_un.d_ptr );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void generic_add_entry_dyn( Elf_Xword tag, Elf_Xword value )
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
T entry;
|
||||
|
||||
switch ( tag ) {
|
||||
case DT_NULL:
|
||||
case DT_SYMBOLIC:
|
||||
case DT_TEXTREL:
|
||||
case DT_BIND_NOW:
|
||||
entry.d_un.d_val = convertor( decltype( entry.d_un.d_val )( 0 ) );
|
||||
break;
|
||||
case DT_NEEDED:
|
||||
case DT_PLTRELSZ:
|
||||
case DT_RELASZ:
|
||||
case DT_RELAENT:
|
||||
case DT_STRSZ:
|
||||
case DT_SYMENT:
|
||||
case DT_SONAME:
|
||||
case DT_RPATH:
|
||||
case DT_RELSZ:
|
||||
case DT_RELENT:
|
||||
case DT_PLTREL:
|
||||
case DT_INIT_ARRAYSZ:
|
||||
case DT_FINI_ARRAYSZ:
|
||||
case DT_RUNPATH:
|
||||
case DT_FLAGS:
|
||||
case DT_PREINIT_ARRAYSZ:
|
||||
entry.d_un.d_val =
|
||||
convertor( decltype( entry.d_un.d_val )( value ) );
|
||||
break;
|
||||
case DT_PLTGOT:
|
||||
case DT_HASH:
|
||||
case DT_STRTAB:
|
||||
case DT_SYMTAB:
|
||||
case DT_RELA:
|
||||
case DT_INIT:
|
||||
case DT_FINI:
|
||||
case DT_REL:
|
||||
case DT_DEBUG:
|
||||
case DT_JMPREL:
|
||||
case DT_INIT_ARRAY:
|
||||
case DT_FINI_ARRAY:
|
||||
case DT_PREINIT_ARRAY:
|
||||
default:
|
||||
entry.d_un.d_ptr =
|
||||
convertor( decltype( entry.d_un.d_val )( value ) );
|
||||
break;
|
||||
}
|
||||
|
||||
entry.d_tag = convertor( decltype( entry.d_tag )( tag ) );
|
||||
|
||||
dynamic_section->append_data( reinterpret_cast<char*>( &entry ),
|
||||
sizeof( entry ) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
const elfio& elf_file;
|
||||
S* dynamic_section;
|
||||
mutable Elf_Xword entries_num;
|
||||
};
|
||||
|
||||
using dynamic_section_accessor = dynamic_section_accessor_template<section>;
|
||||
using const_dynamic_section_accessor =
|
||||
dynamic_section_accessor_template<const section>;
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_DYNAMIC_HPP
|
153
include/elfio/elfio_header.hpp
Normal file
153
include/elfio/elfio_header.hpp
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELF_HEADER_HPP
|
||||
#define ELF_HEADER_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
class elf_header
|
||||
{
|
||||
public:
|
||||
virtual ~elf_header() = default;
|
||||
|
||||
virtual bool load( std::istream& stream ) = 0;
|
||||
virtual bool save( std::ostream& stream ) const = 0;
|
||||
|
||||
// ELF header functions
|
||||
ELFIO_GET_ACCESS_DECL( unsigned char, class );
|
||||
ELFIO_GET_ACCESS_DECL( unsigned char, elf_version );
|
||||
ELFIO_GET_ACCESS_DECL( unsigned char, encoding );
|
||||
ELFIO_GET_ACCESS_DECL( Elf_Half, header_size );
|
||||
ELFIO_GET_ACCESS_DECL( Elf_Half, section_entry_size );
|
||||
ELFIO_GET_ACCESS_DECL( Elf_Half, segment_entry_size );
|
||||
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, version );
|
||||
ELFIO_GET_SET_ACCESS_DECL( unsigned char, os_abi );
|
||||
ELFIO_GET_SET_ACCESS_DECL( unsigned char, abi_version );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, type );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, machine );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, entry );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, sections_num );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, sections_offset );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, segments_num );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, segments_offset );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, section_name_str_index );
|
||||
};
|
||||
|
||||
template <class T> struct elf_header_impl_types;
|
||||
template <> struct elf_header_impl_types<Elf32_Ehdr>
|
||||
{
|
||||
using Phdr_type = Elf32_Phdr;
|
||||
using Shdr_type = Elf32_Shdr;
|
||||
static const unsigned char file_class = ELFCLASS32;
|
||||
};
|
||||
template <> struct elf_header_impl_types<Elf64_Ehdr>
|
||||
{
|
||||
using Phdr_type = Elf64_Phdr;
|
||||
using Shdr_type = Elf64_Shdr;
|
||||
static const unsigned char file_class = ELFCLASS64;
|
||||
};
|
||||
|
||||
template <class T> class elf_header_impl : public elf_header
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
elf_header_impl( endianess_convertor* convertor,
|
||||
unsigned char encoding,
|
||||
const address_translator* translator )
|
||||
: convertor( convertor ), translator( translator )
|
||||
{
|
||||
header.e_ident[EI_MAG0] = ELFMAG0;
|
||||
header.e_ident[EI_MAG1] = ELFMAG1;
|
||||
header.e_ident[EI_MAG2] = ELFMAG2;
|
||||
header.e_ident[EI_MAG3] = ELFMAG3;
|
||||
header.e_ident[EI_CLASS] = elf_header_impl_types<T>::file_class;
|
||||
header.e_ident[EI_DATA] = encoding;
|
||||
header.e_ident[EI_VERSION] = EV_CURRENT;
|
||||
header.e_version = ( *convertor )( (Elf_Word)EV_CURRENT );
|
||||
header.e_ehsize = ( sizeof( header ) );
|
||||
header.e_ehsize = ( *convertor )( header.e_ehsize );
|
||||
header.e_shstrndx = ( *convertor )( (Elf_Half)1 );
|
||||
header.e_phentsize =
|
||||
sizeof( typename elf_header_impl_types<T>::Phdr_type );
|
||||
header.e_shentsize =
|
||||
sizeof( typename elf_header_impl_types<T>::Shdr_type );
|
||||
header.e_phentsize = ( *convertor )( header.e_phentsize );
|
||||
header.e_shentsize = ( *convertor )( header.e_shentsize );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool load( std::istream& stream ) override
|
||||
{
|
||||
stream.seekg( ( *translator )[0] );
|
||||
stream.read( reinterpret_cast<char*>( &header ), sizeof( header ) );
|
||||
|
||||
return ( stream.gcount() == sizeof( header ) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool save( std::ostream& stream ) const override
|
||||
{
|
||||
stream.seekp( ( *translator )[0] );
|
||||
stream.write( reinterpret_cast<const char*>( &header ),
|
||||
sizeof( header ) );
|
||||
|
||||
return stream.good();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ELF header functions
|
||||
ELFIO_GET_ACCESS( unsigned char, class, header.e_ident[EI_CLASS] );
|
||||
ELFIO_GET_ACCESS( unsigned char, elf_version, header.e_ident[EI_VERSION] );
|
||||
ELFIO_GET_ACCESS( unsigned char, encoding, header.e_ident[EI_DATA] );
|
||||
ELFIO_GET_ACCESS( Elf_Half, header_size, header.e_ehsize );
|
||||
ELFIO_GET_ACCESS( Elf_Half, section_entry_size, header.e_shentsize );
|
||||
ELFIO_GET_ACCESS( Elf_Half, segment_entry_size, header.e_phentsize );
|
||||
|
||||
ELFIO_GET_SET_ACCESS( Elf_Word, version, header.e_version );
|
||||
ELFIO_GET_SET_ACCESS( unsigned char, os_abi, header.e_ident[EI_OSABI] );
|
||||
ELFIO_GET_SET_ACCESS( unsigned char,
|
||||
abi_version,
|
||||
header.e_ident[EI_ABIVERSION] );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Half, type, header.e_type );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Half, machine, header.e_machine );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Word, flags, header.e_flags );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Half, section_name_str_index, header.e_shstrndx );
|
||||
ELFIO_GET_SET_ACCESS( Elf64_Addr, entry, header.e_entry );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Half, sections_num, header.e_shnum );
|
||||
ELFIO_GET_SET_ACCESS( Elf64_Off, sections_offset, header.e_shoff );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Half, segments_num, header.e_phnum );
|
||||
ELFIO_GET_SET_ACCESS( Elf64_Off, segments_offset, header.e_phoff );
|
||||
|
||||
private:
|
||||
T header = {};
|
||||
endianess_convertor* convertor = nullptr;
|
||||
const address_translator* translator = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELF_HEADER_HPP
|
124
include/elfio/elfio_modinfo.hpp
Normal file
124
include/elfio/elfio_modinfo.hpp
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELFIO_MODINFO_HPP
|
||||
#define ELFIO_MODINFO_HPP
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class S> class modinfo_section_accessor_template
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
explicit modinfo_section_accessor_template( S* section )
|
||||
: modinfo_section( section )
|
||||
{
|
||||
process_section();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Word get_attribute_num() const { return (Elf_Word)content.size(); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool
|
||||
get_attribute( Elf_Word no, std::string& field, std::string& value ) const
|
||||
{
|
||||
if ( no < content.size() ) {
|
||||
field = content[no].first;
|
||||
value = content[no].second;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool get_attribute( const std::string_view& field_name,
|
||||
std::string& value ) const
|
||||
{
|
||||
for ( const auto [first, second] : content ) {
|
||||
if ( field_name == first ) {
|
||||
value = second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Word add_attribute( const std::string& field, const std::string& value )
|
||||
{
|
||||
Elf_Word current_position = 0;
|
||||
|
||||
if ( modinfo_section ) {
|
||||
// Strings are addeded to the end of the current section data
|
||||
current_position = (Elf_Word)modinfo_section->get_size();
|
||||
|
||||
std::string attribute = field + "=" + value;
|
||||
|
||||
modinfo_section->append_data( attribute + '\0' );
|
||||
content.emplace_back( field, value );
|
||||
}
|
||||
|
||||
return current_position;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
void process_section()
|
||||
{
|
||||
const char* pdata = modinfo_section->get_data();
|
||||
if ( pdata ) {
|
||||
ELFIO::Elf_Xword i = 0;
|
||||
while ( i < modinfo_section->get_size() ) {
|
||||
while ( i < modinfo_section->get_size() && !pdata[i] )
|
||||
i++;
|
||||
if ( i < modinfo_section->get_size() ) {
|
||||
std::string info = pdata + i;
|
||||
size_t loc = info.find( '=' );
|
||||
content.emplace_back( info.substr( 0, loc ),
|
||||
info.substr( loc + 1 ) );
|
||||
|
||||
i += info.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
S* modinfo_section;
|
||||
std::vector<std::pair<std::string, std::string>> content;
|
||||
};
|
||||
|
||||
using modinfo_section_accessor = modinfo_section_accessor_template<section>;
|
||||
using const_modinfo_section_accessor =
|
||||
modinfo_section_accessor_template<const section>;
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_MODINFO_HPP
|
184
include/elfio/elfio_note.hpp
Normal file
184
include/elfio/elfio_note.hpp
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELFIO_NOTE_HPP
|
||||
#define ELFIO_NOTE_HPP
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// There are discrepancies in documentations. SCO documentation
|
||||
// (http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section)
|
||||
// requires 8 byte entries alignment for 64-bit ELF file,
|
||||
// but Oracle's definition uses the same structure
|
||||
// for 32-bit and 64-bit formats.
|
||||
// (https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-18048.html)
|
||||
//
|
||||
// It looks like EM_X86_64 Linux implementation is similar to Oracle's
|
||||
// definition. Therefore, the same alignment works for both formats
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class S, Elf_Xword ( S::*F_get_size )() const>
|
||||
class note_section_accessor_template
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
explicit note_section_accessor_template( const elfio& elf_file, S* section )
|
||||
: elf_file( elf_file ), notes( section )
|
||||
{
|
||||
process_section();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Word get_notes_num() const
|
||||
{
|
||||
return (Elf_Word)note_start_positions.size();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool get_note( Elf_Word index,
|
||||
Elf_Word& type,
|
||||
std::string& name,
|
||||
char*& desc,
|
||||
Elf_Word& descSize ) const
|
||||
{
|
||||
if ( index >= ( notes->*F_get_size )() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* pData = notes->get_data() + note_start_positions[index];
|
||||
int align = sizeof( Elf_Word );
|
||||
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
type = convertor( *(const Elf_Word*)( pData + 2 * (size_t)align ) );
|
||||
Elf_Word namesz = convertor( *(const Elf_Word*)( pData ) );
|
||||
descSize = convertor( *(const Elf_Word*)( pData + sizeof( namesz ) ) );
|
||||
|
||||
Elf_Xword max_name_size =
|
||||
( notes->*F_get_size )() - note_start_positions[index];
|
||||
if ( namesz < 1 || namesz > max_name_size ||
|
||||
(Elf_Xword)namesz + descSize > max_name_size ) {
|
||||
return false;
|
||||
}
|
||||
name.assign( pData + 3 * (size_t)align, namesz - 1 );
|
||||
if ( 0 == descSize ) {
|
||||
desc = nullptr;
|
||||
}
|
||||
else {
|
||||
desc = const_cast<char*>( pData + 3 * (size_t)align +
|
||||
( ( namesz + align - 1 ) / align ) *
|
||||
(size_t)align );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void add_note( Elf_Word type,
|
||||
const std::string& name,
|
||||
const char* desc,
|
||||
Elf_Word descSize )
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
int align = sizeof( Elf_Word );
|
||||
Elf_Word nameLen = (Elf_Word)name.size() + 1;
|
||||
Elf_Word nameLenConv = convertor( nameLen );
|
||||
std::string buffer( reinterpret_cast<char*>( &nameLenConv ), align );
|
||||
Elf_Word descSizeConv = convertor( descSize );
|
||||
|
||||
buffer.append( reinterpret_cast<char*>( &descSizeConv ), align );
|
||||
type = convertor( type );
|
||||
buffer.append( reinterpret_cast<char*>( &type ), align );
|
||||
buffer.append( name );
|
||||
buffer.append( 1, '\x00' );
|
||||
const char pad[] = { '\0', '\0', '\0', '\0' };
|
||||
if ( nameLen % align != 0 ) {
|
||||
buffer.append( pad, (size_t)align - nameLen % align );
|
||||
}
|
||||
if ( desc != nullptr && descSize != 0 ) {
|
||||
buffer.append( desc, descSize );
|
||||
if ( descSize % align != 0 ) {
|
||||
buffer.append( pad, (size_t)align - descSize % align );
|
||||
}
|
||||
}
|
||||
|
||||
note_start_positions.emplace_back( ( notes->*F_get_size )() );
|
||||
notes->append_data( buffer );
|
||||
}
|
||||
|
||||
private:
|
||||
//------------------------------------------------------------------------------
|
||||
void process_section()
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
const char* data = notes->get_data();
|
||||
Elf_Xword size = ( notes->*F_get_size )();
|
||||
Elf_Xword current = 0;
|
||||
|
||||
note_start_positions.clear();
|
||||
|
||||
// Is it empty?
|
||||
if ( nullptr == data || 0 == size ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Elf_Word align = sizeof( Elf_Word );
|
||||
while ( current + (Elf_Xword)3 * align <= size ) {
|
||||
Elf_Word namesz = convertor( *(const Elf_Word*)( data + current ) );
|
||||
Elf_Word descsz = convertor(
|
||||
*(const Elf_Word*)( data + current + sizeof( namesz ) ) );
|
||||
Elf_Word advance =
|
||||
(Elf_Xword)3 * sizeof( Elf_Word ) +
|
||||
( ( namesz + align - 1 ) / align ) * (Elf_Xword)align +
|
||||
( ( descsz + align - 1 ) / align ) * (Elf_Xword)align;
|
||||
if ( namesz < size && descsz < size && current + advance <= size ) {
|
||||
note_start_positions.emplace_back( current );
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
current += advance;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
const elfio& elf_file;
|
||||
S* notes;
|
||||
std::vector<Elf_Xword> note_start_positions;
|
||||
};
|
||||
|
||||
using note_section_accessor =
|
||||
note_section_accessor_template<section, §ion::get_size>;
|
||||
using const_note_section_accessor =
|
||||
note_section_accessor_template<const section, §ion::get_size>;
|
||||
using note_segment_accessor =
|
||||
note_section_accessor_template<segment, &segment::get_file_size>;
|
||||
using const_note_segment_accessor =
|
||||
note_section_accessor_template<const segment, &segment::get_file_size>;
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_NOTE_HPP
|
460
include/elfio/elfio_relocation.hpp
Normal file
460
include/elfio/elfio_relocation.hpp
Normal file
@ -0,0 +1,460 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELFIO_RELOCATION_HPP
|
||||
#define ELFIO_RELOCATION_HPP
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
template <typename T> struct get_sym_and_type;
|
||||
template <> struct get_sym_and_type<Elf32_Rel>
|
||||
{
|
||||
static int get_r_sym( Elf_Xword info )
|
||||
{
|
||||
return ELF32_R_SYM( (Elf_Word)info );
|
||||
}
|
||||
static int get_r_type( Elf_Xword info )
|
||||
{
|
||||
return ELF32_R_TYPE( (Elf_Word)info );
|
||||
}
|
||||
};
|
||||
template <> struct get_sym_and_type<Elf32_Rela>
|
||||
{
|
||||
static int get_r_sym( Elf_Xword info )
|
||||
{
|
||||
return ELF32_R_SYM( (Elf_Word)info );
|
||||
}
|
||||
static int get_r_type( Elf_Xword info )
|
||||
{
|
||||
return ELF32_R_TYPE( (Elf_Word)info );
|
||||
}
|
||||
};
|
||||
template <> struct get_sym_and_type<Elf64_Rel>
|
||||
{
|
||||
static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); }
|
||||
static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); }
|
||||
};
|
||||
template <> struct get_sym_and_type<Elf64_Rela>
|
||||
{
|
||||
static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); }
|
||||
static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class S> class relocation_section_accessor_template
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
explicit relocation_section_accessor_template( const elfio& elf_file,
|
||||
S* section )
|
||||
: elf_file( elf_file ), relocation_section( section )
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Xword get_entries_num() const
|
||||
{
|
||||
Elf_Xword nRet = 0;
|
||||
|
||||
if ( 0 != relocation_section->get_entry_size() ) {
|
||||
nRet = relocation_section->get_size() /
|
||||
relocation_section->get_entry_size();
|
||||
}
|
||||
|
||||
return nRet;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool get_entry( Elf_Xword index,
|
||||
Elf64_Addr& offset,
|
||||
Elf_Word& symbol,
|
||||
unsigned& type,
|
||||
Elf_Sxword& addend ) const
|
||||
{
|
||||
if ( index >= get_entries_num() ) { // Is index valid
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
if ( SHT_REL == relocation_section->get_type() ) {
|
||||
generic_get_entry_rel<Elf32_Rel>( index, offset, symbol, type,
|
||||
addend );
|
||||
}
|
||||
else if ( SHT_RELA == relocation_section->get_type() ) {
|
||||
generic_get_entry_rela<Elf32_Rela>( index, offset, symbol, type,
|
||||
addend );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( SHT_REL == relocation_section->get_type() ) {
|
||||
generic_get_entry_rel<Elf64_Rel>( index, offset, symbol, type,
|
||||
addend );
|
||||
}
|
||||
else if ( SHT_RELA == relocation_section->get_type() ) {
|
||||
generic_get_entry_rela<Elf64_Rela>( index, offset, symbol, type,
|
||||
addend );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool get_entry( Elf_Xword index,
|
||||
Elf64_Addr& offset,
|
||||
Elf64_Addr& symbolValue,
|
||||
std::string& symbolName,
|
||||
unsigned& type,
|
||||
Elf_Sxword& addend,
|
||||
Elf_Sxword& calcValue ) const
|
||||
{
|
||||
// Do regular job
|
||||
Elf_Word symbol = 0;
|
||||
bool ret = get_entry( index, offset, symbol, type, addend );
|
||||
|
||||
// Find the symbol
|
||||
Elf_Xword size;
|
||||
unsigned char bind;
|
||||
unsigned char symbolType;
|
||||
Elf_Half section;
|
||||
unsigned char other;
|
||||
|
||||
symbol_section_accessor symbols(
|
||||
elf_file, elf_file.sections[get_symbol_table_index()] );
|
||||
ret = ret && symbols.get_symbol( symbol, symbolName, symbolValue, size,
|
||||
bind, symbolType, section, other );
|
||||
|
||||
if ( ret ) { // Was it successful?
|
||||
switch ( type ) {
|
||||
case R_386_NONE: // none
|
||||
calcValue = 0;
|
||||
break;
|
||||
case R_386_32: // S + A
|
||||
calcValue = symbolValue + addend;
|
||||
break;
|
||||
case R_386_PC32: // S + A - P
|
||||
calcValue = symbolValue + addend - offset;
|
||||
break;
|
||||
case R_386_GOT32: // G + A - P
|
||||
calcValue = 0;
|
||||
break;
|
||||
case R_386_PLT32: // L + A - P
|
||||
calcValue = 0;
|
||||
break;
|
||||
case R_386_COPY: // none
|
||||
calcValue = 0;
|
||||
break;
|
||||
case R_386_GLOB_DAT: // S
|
||||
case R_386_JMP_SLOT: // S
|
||||
calcValue = symbolValue;
|
||||
break;
|
||||
case R_386_RELATIVE: // B + A
|
||||
calcValue = addend;
|
||||
break;
|
||||
case R_386_GOTOFF: // S + A - GOT
|
||||
calcValue = 0;
|
||||
break;
|
||||
case R_386_GOTPC: // GOT + A - P
|
||||
calcValue = 0;
|
||||
break;
|
||||
default: // Not recognized symbol!
|
||||
calcValue = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool set_entry( Elf_Xword index,
|
||||
Elf64_Addr offset,
|
||||
Elf_Word symbol,
|
||||
unsigned type,
|
||||
Elf_Sxword addend )
|
||||
{
|
||||
if ( index >= get_entries_num() ) { // Is index valid
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
if ( SHT_REL == relocation_section->get_type() ) {
|
||||
generic_set_entry_rel<Elf32_Rel>( index, offset, symbol, type,
|
||||
addend );
|
||||
}
|
||||
else if ( SHT_RELA == relocation_section->get_type() ) {
|
||||
generic_set_entry_rela<Elf32_Rela>( index, offset, symbol, type,
|
||||
addend );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( SHT_REL == relocation_section->get_type() ) {
|
||||
generic_set_entry_rel<Elf64_Rel>( index, offset, symbol, type,
|
||||
addend );
|
||||
}
|
||||
else if ( SHT_RELA == relocation_section->get_type() ) {
|
||||
generic_set_entry_rela<Elf64_Rela>( index, offset, symbol, type,
|
||||
addend );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void add_entry( Elf64_Addr offset, Elf_Xword info )
|
||||
{
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
generic_add_entry<Elf32_Rel>( offset, info );
|
||||
}
|
||||
else {
|
||||
generic_add_entry<Elf64_Rel>( offset, info );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void add_entry( Elf64_Addr offset, Elf_Word symbol, unsigned type )
|
||||
{
|
||||
Elf_Xword info;
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
info = ELF32_R_INFO( (Elf_Xword)symbol, type );
|
||||
}
|
||||
else {
|
||||
info = ELF64_R_INFO( (Elf_Xword)symbol, type );
|
||||
}
|
||||
|
||||
add_entry( offset, info );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend )
|
||||
{
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
generic_add_entry<Elf32_Rela>( offset, info, addend );
|
||||
}
|
||||
else {
|
||||
generic_add_entry<Elf64_Rela>( offset, info, addend );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void add_entry( Elf64_Addr offset,
|
||||
Elf_Word symbol,
|
||||
unsigned type,
|
||||
Elf_Sxword addend )
|
||||
{
|
||||
Elf_Xword info;
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
info = ELF32_R_INFO( (Elf_Xword)symbol, type );
|
||||
}
|
||||
else {
|
||||
info = ELF64_R_INFO( (Elf_Xword)symbol, type );
|
||||
}
|
||||
|
||||
add_entry( offset, info, addend );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void add_entry( string_section_accessor str_writer,
|
||||
const char* str,
|
||||
symbol_section_accessor sym_writer,
|
||||
Elf64_Addr value,
|
||||
Elf_Word size,
|
||||
unsigned char sym_info,
|
||||
unsigned char other,
|
||||
Elf_Half shndx,
|
||||
Elf64_Addr offset,
|
||||
unsigned type )
|
||||
{
|
||||
Elf_Word str_index = str_writer.add_string( str );
|
||||
Elf_Word sym_index = sym_writer.add_symbol( str_index, value, size,
|
||||
sym_info, other, shndx );
|
||||
add_entry( offset, sym_index, type );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void swap_symbols( Elf_Xword first, Elf_Xword second )
|
||||
{
|
||||
Elf64_Addr offset = 0;
|
||||
Elf_Word symbol = 0;
|
||||
unsigned rtype = 0;
|
||||
Elf_Sxword addend = 0;
|
||||
for ( Elf_Word i = 0; i < get_entries_num(); i++ ) {
|
||||
get_entry( i, offset, symbol, rtype, addend );
|
||||
if ( symbol == first ) {
|
||||
set_entry( i, offset, (Elf_Word)second, rtype, addend );
|
||||
}
|
||||
if ( symbol == second ) {
|
||||
set_entry( i, offset, (Elf_Word)first, rtype, addend );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Half get_symbol_table_index() const
|
||||
{
|
||||
return (Elf_Half)relocation_section->get_link();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void generic_get_entry_rel( Elf_Xword index,
|
||||
Elf64_Addr& offset,
|
||||
Elf_Word& symbol,
|
||||
unsigned& type,
|
||||
Elf_Sxword& addend ) const
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
const T* pEntry = reinterpret_cast<const T*>(
|
||||
relocation_section->get_data() +
|
||||
index * relocation_section->get_entry_size() );
|
||||
offset = convertor( pEntry->r_offset );
|
||||
Elf_Xword tmp = convertor( pEntry->r_info );
|
||||
symbol = get_sym_and_type<T>::get_r_sym( tmp );
|
||||
type = get_sym_and_type<T>::get_r_type( tmp );
|
||||
addend = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void generic_get_entry_rela( Elf_Xword index,
|
||||
Elf64_Addr& offset,
|
||||
Elf_Word& symbol,
|
||||
unsigned& type,
|
||||
Elf_Sxword& addend ) const
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
const T* pEntry = reinterpret_cast<const T*>(
|
||||
relocation_section->get_data() +
|
||||
index * relocation_section->get_entry_size() );
|
||||
offset = convertor( pEntry->r_offset );
|
||||
Elf_Xword tmp = convertor( pEntry->r_info );
|
||||
symbol = get_sym_and_type<T>::get_r_sym( tmp );
|
||||
type = get_sym_and_type<T>::get_r_type( tmp );
|
||||
addend = convertor( pEntry->r_addend );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void generic_set_entry_rel( Elf_Xword index,
|
||||
Elf64_Addr offset,
|
||||
Elf_Word symbol,
|
||||
unsigned type,
|
||||
Elf_Sxword )
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
T* pEntry = const_cast<T*>( reinterpret_cast<const T*>(
|
||||
relocation_section->get_data() +
|
||||
index * relocation_section->get_entry_size() ) );
|
||||
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type );
|
||||
}
|
||||
else {
|
||||
pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type );
|
||||
}
|
||||
pEntry->r_offset = decltype( pEntry->r_offset )( offset );
|
||||
pEntry->r_offset = convertor( pEntry->r_offset );
|
||||
pEntry->r_info = convertor( pEntry->r_info );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void generic_set_entry_rela( Elf_Xword index,
|
||||
Elf64_Addr offset,
|
||||
Elf_Word symbol,
|
||||
unsigned type,
|
||||
Elf_Sxword addend )
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
T* pEntry = const_cast<T*>( reinterpret_cast<const T*>(
|
||||
relocation_section->get_data() +
|
||||
index * relocation_section->get_entry_size() ) );
|
||||
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type );
|
||||
}
|
||||
else {
|
||||
pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type );
|
||||
}
|
||||
pEntry->r_offset = decltype( pEntry->r_offset )( offset );
|
||||
pEntry->r_addend = decltype( pEntry->r_addend )( addend );
|
||||
pEntry->r_offset = convertor( pEntry->r_offset );
|
||||
pEntry->r_info = convertor( pEntry->r_info );
|
||||
pEntry->r_addend = convertor( pEntry->r_addend );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void generic_add_entry( Elf64_Addr offset, Elf_Xword info )
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
T entry;
|
||||
entry.r_offset = decltype( entry.r_offset )( offset );
|
||||
entry.r_info = decltype( entry.r_info )( info );
|
||||
entry.r_offset = convertor( entry.r_offset );
|
||||
entry.r_info = convertor( entry.r_info );
|
||||
|
||||
relocation_section->append_data( reinterpret_cast<char*>( &entry ),
|
||||
sizeof( entry ) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
void
|
||||
generic_add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend )
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
T entry;
|
||||
entry.r_offset = offset;
|
||||
entry.r_info = info;
|
||||
entry.r_addend = addend;
|
||||
entry.r_offset = convertor( entry.r_offset );
|
||||
entry.r_info = convertor( entry.r_info );
|
||||
entry.r_addend = convertor( entry.r_addend );
|
||||
|
||||
relocation_section->append_data( reinterpret_cast<char*>( &entry ),
|
||||
sizeof( entry ) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
const elfio& elf_file;
|
||||
S* relocation_section = nullptr;
|
||||
};
|
||||
|
||||
using relocation_section_accessor =
|
||||
relocation_section_accessor_template<section>;
|
||||
using const_relocation_section_accessor =
|
||||
relocation_section_accessor_template<const section>;
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_RELOCATION_HPP
|
367
include/elfio/elfio_section.hpp
Normal file
367
include/elfio/elfio_section.hpp
Normal file
@ -0,0 +1,367 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELFIO_SECTION_HPP
|
||||
#define ELFIO_SECTION_HPP
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <new>
|
||||
#include <limits>
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
class section
|
||||
{
|
||||
friend class elfio;
|
||||
|
||||
public:
|
||||
virtual ~section() = default;
|
||||
|
||||
ELFIO_GET_ACCESS_DECL( Elf_Half, index );
|
||||
ELFIO_GET_SET_ACCESS_DECL( std::string, name );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, flags );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, info );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, link );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, addr_align );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, entry_size );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, address );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, size );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, name_string_offset );
|
||||
ELFIO_GET_ACCESS_DECL( Elf64_Off, offset );
|
||||
|
||||
virtual const char* get_data() const = 0;
|
||||
virtual void set_data( const char* raw_data, Elf_Word size ) = 0;
|
||||
virtual void set_data( const std::string& data ) = 0;
|
||||
virtual void append_data( const char* raw_data, Elf_Word size ) = 0;
|
||||
virtual void append_data( const std::string& data ) = 0;
|
||||
virtual void
|
||||
insert_data( Elf_Xword pos, const char* raw_data, Elf_Word size ) = 0;
|
||||
virtual void insert_data( Elf_Xword pos, const std::string& data ) = 0;
|
||||
virtual size_t get_stream_size() const = 0;
|
||||
virtual void set_stream_size( size_t value ) = 0;
|
||||
|
||||
protected:
|
||||
ELFIO_SET_ACCESS_DECL( Elf64_Off, offset );
|
||||
ELFIO_SET_ACCESS_DECL( Elf_Half, index );
|
||||
|
||||
virtual bool load( std::istream& stream,
|
||||
std::streampos header_offset,
|
||||
bool is_lazy ) = 0;
|
||||
virtual void save( std::ostream& stream,
|
||||
std::streampos header_offset,
|
||||
std::streampos data_offset ) = 0;
|
||||
virtual bool is_address_initialized() const = 0;
|
||||
};
|
||||
|
||||
template <class T> class section_impl : public section
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
section_impl( const endianess_convertor* convertor,
|
||||
const address_translator* translator,
|
||||
const std::shared_ptr<compression_interface>& compression )
|
||||
: convertor( convertor ), translator( translator ),
|
||||
compression( compression )
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Section info functions
|
||||
ELFIO_GET_SET_ACCESS( Elf_Word, type, header.sh_type );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Xword, flags, header.sh_flags );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Xword, size, header.sh_size );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Word, link, header.sh_link );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Word, info, header.sh_info );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Xword, addr_align, header.sh_addralign );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Xword, entry_size, header.sh_entsize );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Word, name_string_offset, header.sh_name );
|
||||
ELFIO_GET_ACCESS( Elf64_Addr, address, header.sh_addr );
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Half get_index() const override { return index; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::string get_name() const override { return name; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void set_name( const std::string& name_prm ) override
|
||||
{
|
||||
this->name = name_prm;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void set_address( const Elf64_Addr& value ) override
|
||||
{
|
||||
header.sh_addr = decltype( header.sh_addr )( value );
|
||||
header.sh_addr = ( *convertor )( header.sh_addr );
|
||||
is_address_set = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool is_address_initialized() const override { return is_address_set; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char* get_data() const override
|
||||
{
|
||||
if ( is_lazy ) {
|
||||
load_data();
|
||||
}
|
||||
return data.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void set_data( const char* raw_data, Elf_Word size ) override
|
||||
{
|
||||
if ( get_type() != SHT_NOBITS ) {
|
||||
data = std::unique_ptr<char[]>( new ( std::nothrow ) char[size] );
|
||||
if ( nullptr != data.get() && nullptr != raw_data ) {
|
||||
data_size = size;
|
||||
std::copy( raw_data, raw_data + size, data.get() );
|
||||
}
|
||||
else {
|
||||
data_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
set_size( data_size );
|
||||
if ( translator->empty() ) {
|
||||
set_stream_size( data_size );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void set_data( const std::string& str_data ) override
|
||||
{
|
||||
return set_data( str_data.c_str(), (Elf_Word)str_data.size() );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void append_data( const char* raw_data, Elf_Word size ) override
|
||||
{
|
||||
insert_data( get_size(), raw_data, size );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void append_data( const std::string& str_data ) override
|
||||
{
|
||||
return append_data( str_data.c_str(), (Elf_Word)str_data.size() );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void
|
||||
insert_data( Elf_Xword pos, const char* raw_data, Elf_Word size ) override
|
||||
{
|
||||
if ( get_type() != SHT_NOBITS ) {
|
||||
if ( get_size() + size < data_size ) {
|
||||
char* d = data.get();
|
||||
std::copy_backward( d + pos, d + get_size(),
|
||||
d + get_size() + size );
|
||||
std::copy( raw_data, raw_data + size, d + pos );
|
||||
}
|
||||
else {
|
||||
data_size = 2 * ( data_size + size );
|
||||
std::unique_ptr<char[]> new_data(
|
||||
new ( std::nothrow ) char[data_size] );
|
||||
|
||||
if ( nullptr != new_data ) {
|
||||
char* d = data.get();
|
||||
std::copy( d, d + pos, new_data.get() );
|
||||
std::copy( raw_data, raw_data + size,
|
||||
new_data.get() + pos );
|
||||
std::copy( d + pos, d + get_size(),
|
||||
new_data.get() + pos + size );
|
||||
data = std::move( new_data );
|
||||
}
|
||||
else {
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
set_size( get_size() + size );
|
||||
if ( translator->empty() ) {
|
||||
set_stream_size( get_stream_size() + size );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void insert_data( Elf_Xword pos, const std::string& str_data ) override
|
||||
{
|
||||
return insert_data( pos, str_data.c_str(), (Elf_Word)str_data.size() );
|
||||
}
|
||||
|
||||
size_t get_stream_size() const override { return stream_size; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void set_stream_size( size_t value ) override { stream_size = value; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
protected:
|
||||
//------------------------------------------------------------------------------
|
||||
ELFIO_GET_SET_ACCESS( Elf64_Off, offset, header.sh_offset );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void set_index( const Elf_Half& value ) override { index = value; }
|
||||
|
||||
bool is_compressed() const
|
||||
{
|
||||
return ( ( get_flags() & SHF_RPX_DEFLATE ) ||
|
||||
( get_flags() & SHF_COMPRESSED ) ) &&
|
||||
compression != nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool load( std::istream& stream,
|
||||
std::streampos header_offset,
|
||||
bool is_lazy_ ) override
|
||||
{
|
||||
pstream = &stream;
|
||||
is_lazy = is_lazy_;
|
||||
|
||||
if ( translator->empty() ) {
|
||||
stream.seekg( 0, std::istream::end );
|
||||
set_stream_size( size_t( stream.tellg() ) );
|
||||
}
|
||||
else {
|
||||
set_stream_size( std::numeric_limits<size_t>::max() );
|
||||
}
|
||||
|
||||
stream.seekg( ( *translator )[header_offset] );
|
||||
stream.read( reinterpret_cast<char*>( &header ), sizeof( header ) );
|
||||
|
||||
if ( !is_lazy || is_compressed() ) {
|
||||
|
||||
bool ret = load_data();
|
||||
|
||||
if ( is_compressed() ) {
|
||||
Elf_Xword size = get_size();
|
||||
Elf_Xword uncompressed_size = 0;
|
||||
auto decompressed_data = compression->inflate(
|
||||
data.get(), convertor, size, uncompressed_size );
|
||||
if ( decompressed_data != nullptr ) {
|
||||
set_size( uncompressed_size );
|
||||
data = std::move( decompressed_data );
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load_data() const
|
||||
{
|
||||
is_lazy = false;
|
||||
Elf_Xword size = get_size();
|
||||
if ( nullptr == data && SHT_NULL != get_type() &&
|
||||
SHT_NOBITS != get_type() && size < get_stream_size() ) {
|
||||
data.reset( new ( std::nothrow ) char[size_t( size ) + 1] );
|
||||
|
||||
if ( ( 0 != size ) && ( nullptr != data ) ) {
|
||||
pstream->seekg(
|
||||
( *translator )[( *convertor )( header.sh_offset )] );
|
||||
pstream->read( data.get(), size );
|
||||
if ( static_cast<Elf_Xword>( pstream->gcount() ) != size ) {
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// refresh size because it may have changed if we had to decompress data
|
||||
size = get_size();
|
||||
data.get()[size] =
|
||||
0; // Ensure data is ended with 0 to avoid oob read
|
||||
data_size = decltype( data_size )( size );
|
||||
}
|
||||
else {
|
||||
data_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void save( std::ostream& stream,
|
||||
std::streampos header_offset,
|
||||
std::streampos data_offset ) override
|
||||
{
|
||||
if ( 0 != get_index() ) {
|
||||
header.sh_offset = decltype( header.sh_offset )( data_offset );
|
||||
header.sh_offset = ( *convertor )( header.sh_offset );
|
||||
}
|
||||
|
||||
save_header( stream, header_offset );
|
||||
if ( get_type() != SHT_NOBITS && get_type() != SHT_NULL &&
|
||||
get_size() != 0 && data != nullptr ) {
|
||||
save_data( stream, data_offset );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
//------------------------------------------------------------------------------
|
||||
void save_header( std::ostream& stream, std::streampos header_offset ) const
|
||||
{
|
||||
adjust_stream_size( stream, header_offset );
|
||||
stream.write( reinterpret_cast<const char*>( &header ),
|
||||
sizeof( header ) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void save_data( std::ostream& stream, std::streampos data_offset )
|
||||
{
|
||||
adjust_stream_size( stream, data_offset );
|
||||
|
||||
if ( ( ( get_flags() & SHF_COMPRESSED ) ||
|
||||
( get_flags() & SHF_RPX_DEFLATE ) ) &&
|
||||
compression != nullptr ) {
|
||||
Elf_Xword decompressed_size = get_size();
|
||||
Elf_Xword compressed_size = 0;
|
||||
auto compressed_ptr = compression->deflate(
|
||||
data.get(), convertor, decompressed_size, compressed_size );
|
||||
stream.write( compressed_ptr.get(), compressed_size );
|
||||
}
|
||||
else {
|
||||
stream.write( get_data(), get_size() );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
mutable std::istream* pstream = nullptr;
|
||||
T header = {};
|
||||
Elf_Half index = 0;
|
||||
std::string name;
|
||||
mutable std::unique_ptr<char[]> data;
|
||||
mutable Elf_Word data_size = 0;
|
||||
const endianess_convertor* convertor = nullptr;
|
||||
const address_translator* translator = nullptr;
|
||||
const std::shared_ptr<compression_interface> compression = nullptr;
|
||||
bool is_address_set = false;
|
||||
size_t stream_size = 0;
|
||||
mutable bool is_lazy = false;
|
||||
};
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_SECTION_HPP
|
254
include/elfio/elfio_segment.hpp
Normal file
254
include/elfio/elfio_segment.hpp
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELFIO_SEGMENT_HPP
|
||||
#define ELFIO_SEGMENT_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <new>
|
||||
#include <limits>
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
class segment
|
||||
{
|
||||
friend class elfio;
|
||||
|
||||
public:
|
||||
virtual ~segment() = default;
|
||||
|
||||
ELFIO_GET_ACCESS_DECL( Elf_Half, index );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, align );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, virtual_address );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, physical_address );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, file_size );
|
||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, memory_size );
|
||||
ELFIO_GET_ACCESS_DECL( Elf64_Off, offset );
|
||||
|
||||
virtual const char* get_data() const = 0;
|
||||
|
||||
virtual Elf_Half add_section( section* psec, Elf_Xword addr_align ) = 0;
|
||||
virtual Elf_Half add_section_index( Elf_Half index,
|
||||
Elf_Xword addr_align ) = 0;
|
||||
virtual Elf_Half get_sections_num() const = 0;
|
||||
virtual Elf_Half get_section_index_at( Elf_Half num ) const = 0;
|
||||
virtual bool is_offset_initialized() const = 0;
|
||||
|
||||
protected:
|
||||
ELFIO_SET_ACCESS_DECL( Elf64_Off, offset );
|
||||
ELFIO_SET_ACCESS_DECL( Elf_Half, index );
|
||||
|
||||
virtual const std::vector<Elf_Half>& get_sections() const = 0;
|
||||
|
||||
virtual bool load( std::istream& stream,
|
||||
std::streampos header_offset,
|
||||
bool is_lazy ) = 0;
|
||||
virtual void save( std::ostream& stream,
|
||||
std::streampos header_offset,
|
||||
std::streampos data_offset ) = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T> class segment_impl : public segment
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
segment_impl( const endianess_convertor* convertor,
|
||||
const address_translator* translator )
|
||||
: convertor( convertor ), translator( translator )
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Section info functions
|
||||
ELFIO_GET_SET_ACCESS( Elf_Word, type, ph.p_type );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Word, flags, ph.p_flags );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Xword, align, ph.p_align );
|
||||
ELFIO_GET_SET_ACCESS( Elf64_Addr, virtual_address, ph.p_vaddr );
|
||||
ELFIO_GET_SET_ACCESS( Elf64_Addr, physical_address, ph.p_paddr );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Xword, file_size, ph.p_filesz );
|
||||
ELFIO_GET_SET_ACCESS( Elf_Xword, memory_size, ph.p_memsz );
|
||||
ELFIO_GET_ACCESS( Elf64_Off, offset, ph.p_offset );
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Half get_index() const override { return index; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char* get_data() const override
|
||||
{
|
||||
if ( is_lazy ) {
|
||||
load_data();
|
||||
}
|
||||
return data.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Half add_section_index( Elf_Half sec_index,
|
||||
Elf_Xword addr_align ) override
|
||||
{
|
||||
sections.emplace_back( sec_index );
|
||||
if ( addr_align > get_align() ) {
|
||||
set_align( addr_align );
|
||||
}
|
||||
|
||||
return (Elf_Half)sections.size();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Half add_section( section* psec, Elf_Xword addr_align ) override
|
||||
{
|
||||
return add_section_index( psec->get_index(), addr_align );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Half get_sections_num() const override
|
||||
{
|
||||
return (Elf_Half)sections.size();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Half get_section_index_at( Elf_Half num ) const override
|
||||
{
|
||||
if ( num < sections.size() ) {
|
||||
return sections[num];
|
||||
}
|
||||
|
||||
return Elf_Half( -1 );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
protected:
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void set_offset( const Elf64_Off& value ) override
|
||||
{
|
||||
ph.p_offset = decltype( ph.p_offset )( value );
|
||||
ph.p_offset = ( *convertor )( ph.p_offset );
|
||||
is_offset_set = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool is_offset_initialized() const override { return is_offset_set; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const std::vector<Elf_Half>& get_sections() const override
|
||||
{
|
||||
return sections;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void set_index( const Elf_Half& value ) override { index = value; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool load( std::istream& stream,
|
||||
std::streampos header_offset,
|
||||
bool is_lazy_ ) override
|
||||
{
|
||||
pstream = &stream;
|
||||
is_lazy = is_lazy_;
|
||||
|
||||
if ( translator->empty() ) {
|
||||
stream.seekg( 0, std::istream::end );
|
||||
set_stream_size( size_t( stream.tellg() ) );
|
||||
}
|
||||
else {
|
||||
set_stream_size( std::numeric_limits<size_t>::max() );
|
||||
}
|
||||
|
||||
stream.seekg( ( *translator )[header_offset] );
|
||||
stream.read( reinterpret_cast<char*>( &ph ), sizeof( ph ) );
|
||||
is_offset_set = true;
|
||||
|
||||
if ( !is_lazy ) {
|
||||
return load_data();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool load_data() const
|
||||
{
|
||||
is_lazy = false;
|
||||
if ( PT_NULL == get_type() || 0 == get_file_size() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
pstream->seekg( ( *translator )[( *convertor )( ph.p_offset )] );
|
||||
Elf_Xword size = get_file_size();
|
||||
|
||||
if ( size > get_stream_size() ) {
|
||||
data = nullptr;
|
||||
}
|
||||
else {
|
||||
data.reset( new ( std::nothrow ) char[(size_t)size + 1] );
|
||||
|
||||
if ( nullptr != data.get() && pstream->read( data.get(), size ) ) {
|
||||
data.get()[size] = 0;
|
||||
}
|
||||
else {
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void save( std::ostream& stream,
|
||||
std::streampos header_offset,
|
||||
std::streampos data_offset ) override
|
||||
{
|
||||
ph.p_offset = decltype( ph.p_offset )( data_offset );
|
||||
ph.p_offset = ( *convertor )( ph.p_offset );
|
||||
adjust_stream_size( stream, header_offset );
|
||||
stream.write( reinterpret_cast<const char*>( &ph ), sizeof( ph ) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
size_t get_stream_size() const { return stream_size; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void set_stream_size( size_t value ) { stream_size = value; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
mutable std::istream* pstream = nullptr;
|
||||
T ph = {};
|
||||
Elf_Half index = 0;
|
||||
mutable std::unique_ptr<char[]> data;
|
||||
std::vector<Elf_Half> sections;
|
||||
const endianess_convertor* convertor = nullptr;
|
||||
const address_translator* translator = nullptr;
|
||||
size_t stream_size = 0;
|
||||
bool is_offset_set = false;
|
||||
mutable bool is_lazy = false;
|
||||
};
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_SEGMENT_HPP
|
97
include/elfio/elfio_strings.hpp
Normal file
97
include/elfio/elfio_strings.hpp
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELFIO_STRINGS_HPP
|
||||
#define ELFIO_STRINGS_HPP
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class S> class string_section_accessor_template
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
explicit string_section_accessor_template( S* section )
|
||||
: string_section( section )
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char* get_string( Elf_Word index ) const
|
||||
{
|
||||
if ( string_section ) {
|
||||
const char* data = string_section->get_data();
|
||||
if ( index < string_section->get_size() && nullptr != data ) {
|
||||
size_t string_length =
|
||||
strnlen( data + index, string_section->get_size() - index );
|
||||
if ( string_length < ( string_section->get_size() - index ) )
|
||||
return data + index;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Word add_string( const char* str )
|
||||
{
|
||||
Elf_Word current_position = 0;
|
||||
|
||||
if ( string_section ) {
|
||||
// Strings are addeded to the end of the current section data
|
||||
current_position =
|
||||
static_cast<Elf_Word>( string_section->get_size() );
|
||||
|
||||
if ( current_position == 0 ) {
|
||||
char empty_string = '\0';
|
||||
string_section->append_data( &empty_string, 1 );
|
||||
current_position++;
|
||||
}
|
||||
string_section->append_data(
|
||||
str, static_cast<Elf_Word>( std::strlen( str ) + 1 ) );
|
||||
}
|
||||
|
||||
return current_position;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Word add_string( const std::string& str )
|
||||
{
|
||||
return add_string( str.c_str() );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
S* string_section;
|
||||
};
|
||||
|
||||
using string_section_accessor = string_section_accessor_template<section>;
|
||||
using const_string_section_accessor =
|
||||
string_section_accessor_template<const section>;
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_STRINGS_HPP
|
562
include/elfio/elfio_symbols.hpp
Normal file
562
include/elfio/elfio_symbols.hpp
Normal file
@ -0,0 +1,562 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELFIO_SYMBOLS_HPP
|
||||
#define ELFIO_SYMBOLS_HPP
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class S> class symbol_section_accessor_template
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
explicit symbol_section_accessor_template( const elfio& elf_file,
|
||||
S* symbol_section )
|
||||
: elf_file( elf_file ), symbol_section( symbol_section )
|
||||
{
|
||||
find_hash_section();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Xword get_symbols_num() const
|
||||
{
|
||||
Elf_Xword nRet = 0;
|
||||
|
||||
size_t minimum_symbol_size;
|
||||
switch ( elf_file.get_class() ) {
|
||||
case ELFCLASS32:
|
||||
minimum_symbol_size = sizeof( Elf32_Sym );
|
||||
break;
|
||||
case ELFCLASS64:
|
||||
minimum_symbol_size = sizeof( Elf64_Sym );
|
||||
break;
|
||||
default:
|
||||
return nRet;
|
||||
}
|
||||
|
||||
if ( symbol_section->get_entry_size() >= minimum_symbol_size &&
|
||||
symbol_section->get_size() <= symbol_section->get_stream_size() ) {
|
||||
nRet =
|
||||
symbol_section->get_size() / symbol_section->get_entry_size();
|
||||
}
|
||||
|
||||
return nRet;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool get_symbol( Elf_Xword index,
|
||||
std::string& name,
|
||||
Elf64_Addr& value,
|
||||
Elf_Xword& size,
|
||||
unsigned char& bind,
|
||||
unsigned char& type,
|
||||
Elf_Half& section_index,
|
||||
unsigned char& other ) const
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
ret = generic_get_symbol<Elf32_Sym>( index, name, value, size, bind,
|
||||
type, section_index, other );
|
||||
}
|
||||
else {
|
||||
ret = generic_get_symbol<Elf64_Sym>( index, name, value, size, bind,
|
||||
type, section_index, other );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool get_symbol( const std::string& name,
|
||||
Elf64_Addr& value,
|
||||
Elf_Xword& size,
|
||||
unsigned char& bind,
|
||||
unsigned char& type,
|
||||
Elf_Half& section_index,
|
||||
unsigned char& other ) const
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if ( 0 != get_hash_table_index() ) {
|
||||
if ( hash_section->get_type() == SHT_HASH ) {
|
||||
ret = hash_lookup( name, value, size, bind, type, section_index,
|
||||
other );
|
||||
}
|
||||
if ( hash_section->get_type() == SHT_GNU_HASH ||
|
||||
hash_section->get_type() == DT_GNU_HASH ) {
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
ret = gnu_hash_lookup<uint32_t>(
|
||||
name, value, size, bind, type, section_index, other );
|
||||
}
|
||||
else {
|
||||
ret = gnu_hash_lookup<uint64_t>(
|
||||
name, value, size, bind, type, section_index, other );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !ret ) {
|
||||
for ( Elf_Xword i = 0; !ret && i < get_symbols_num(); i++ ) {
|
||||
std::string symbol_name;
|
||||
if ( get_symbol( i, symbol_name, value, size, bind, type,
|
||||
section_index, other ) ) {
|
||||
if ( symbol_name == name ) {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool get_symbol( const Elf64_Addr& value,
|
||||
std::string& name,
|
||||
Elf_Xword& size,
|
||||
unsigned char& bind,
|
||||
unsigned char& type,
|
||||
Elf_Half& section_index,
|
||||
unsigned char& other ) const
|
||||
{
|
||||
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
Elf_Xword idx = 0;
|
||||
bool match = false;
|
||||
Elf64_Addr v = 0;
|
||||
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
match = generic_search_symbols<Elf32_Sym>(
|
||||
[&]( const Elf32_Sym* sym ) {
|
||||
return convertor( sym->st_value ) == value;
|
||||
},
|
||||
idx );
|
||||
}
|
||||
else {
|
||||
match = generic_search_symbols<Elf64_Sym>(
|
||||
[&]( const Elf64_Sym* sym ) {
|
||||
return convertor( sym->st_value ) == value;
|
||||
},
|
||||
idx );
|
||||
}
|
||||
|
||||
if ( match ) {
|
||||
return get_symbol( idx, name, v, size, bind, type, section_index,
|
||||
other );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Word add_symbol( Elf_Word name,
|
||||
Elf64_Addr value,
|
||||
Elf_Xword size,
|
||||
unsigned char info,
|
||||
unsigned char other,
|
||||
Elf_Half shndx )
|
||||
{
|
||||
Elf_Word nRet;
|
||||
|
||||
if ( symbol_section->get_size() == 0 ) {
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
nRet = generic_add_symbol<Elf32_Sym>( 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
else {
|
||||
nRet = generic_add_symbol<Elf64_Sym>( 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
nRet = generic_add_symbol<Elf32_Sym>( name, value, size, info,
|
||||
other, shndx );
|
||||
}
|
||||
else {
|
||||
nRet = generic_add_symbol<Elf64_Sym>( name, value, size, info,
|
||||
other, shndx );
|
||||
}
|
||||
|
||||
return nRet;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Word add_symbol( Elf_Word name,
|
||||
Elf64_Addr value,
|
||||
Elf_Xword size,
|
||||
unsigned char bind,
|
||||
unsigned char type,
|
||||
unsigned char other,
|
||||
Elf_Half shndx )
|
||||
{
|
||||
return add_symbol( name, value, size, ELF_ST_INFO( bind, type ), other,
|
||||
shndx );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Word add_symbol( string_section_accessor& pStrWriter,
|
||||
const char* str,
|
||||
Elf64_Addr value,
|
||||
Elf_Xword size,
|
||||
unsigned char info,
|
||||
unsigned char other,
|
||||
Elf_Half shndx )
|
||||
{
|
||||
Elf_Word index = pStrWriter.add_string( str );
|
||||
return add_symbol( index, value, size, info, other, shndx );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Word add_symbol( string_section_accessor& pStrWriter,
|
||||
const char* str,
|
||||
Elf64_Addr value,
|
||||
Elf_Xword size,
|
||||
unsigned char bind,
|
||||
unsigned char type,
|
||||
unsigned char other,
|
||||
Elf_Half shndx )
|
||||
{
|
||||
return add_symbol( pStrWriter, str, value, size,
|
||||
ELF_ST_INFO( bind, type ), other, shndx );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Xword arrange_local_symbols(
|
||||
std::function<void( Elf_Xword first, Elf_Xword second )> func =
|
||||
nullptr )
|
||||
{
|
||||
Elf_Xword nRet = 0;
|
||||
|
||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
||||
nRet = generic_arrange_local_symbols<Elf32_Sym>( func );
|
||||
}
|
||||
else {
|
||||
nRet = generic_arrange_local_symbols<Elf64_Sym>( func );
|
||||
}
|
||||
|
||||
return nRet;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
//------------------------------------------------------------------------------
|
||||
void find_hash_section()
|
||||
{
|
||||
Elf_Half nSecNo = elf_file.sections.size();
|
||||
for ( Elf_Half i = 0; i < nSecNo; ++i ) {
|
||||
const section* sec = elf_file.sections[i];
|
||||
if ( sec->get_link() == symbol_section->get_index() &&
|
||||
( sec->get_type() == SHT_HASH ||
|
||||
sec->get_type() == SHT_GNU_HASH ||
|
||||
sec->get_type() == DT_GNU_HASH ) ) {
|
||||
hash_section = sec;
|
||||
hash_section_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Half get_string_table_index() const
|
||||
{
|
||||
return (Elf_Half)symbol_section->get_link();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Half get_hash_table_index() const { return hash_section_index; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool hash_lookup( const std::string& name,
|
||||
Elf64_Addr& value,
|
||||
Elf_Xword& size,
|
||||
unsigned char& bind,
|
||||
unsigned char& type,
|
||||
Elf_Half& section_index,
|
||||
unsigned char& other ) const
|
||||
{
|
||||
bool ret = false;
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
Elf_Word nbucket = *(const Elf_Word*)hash_section->get_data();
|
||||
nbucket = convertor( nbucket );
|
||||
Elf_Word nchain =
|
||||
*(const Elf_Word*)( hash_section->get_data() + sizeof( Elf_Word ) );
|
||||
nchain = convertor( nchain );
|
||||
Elf_Word val = elf_hash( (const unsigned char*)name.c_str() );
|
||||
Elf_Word y =
|
||||
*(const Elf_Word*)( hash_section->get_data() +
|
||||
( 2 + val % nbucket ) * sizeof( Elf_Word ) );
|
||||
y = convertor( y );
|
||||
std::string str;
|
||||
get_symbol( y, str, value, size, bind, type, section_index, other );
|
||||
while ( str != name && STN_UNDEF != y && y < nchain ) {
|
||||
y = *(const Elf_Word*)( hash_section->get_data() +
|
||||
( 2 + nbucket + y ) * sizeof( Elf_Word ) );
|
||||
y = convertor( y );
|
||||
get_symbol( y, str, value, size, bind, type, section_index, other );
|
||||
}
|
||||
|
||||
if ( str == name ) {
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
bool gnu_hash_lookup( const std::string& name,
|
||||
Elf64_Addr& value,
|
||||
Elf_Xword& size,
|
||||
unsigned char& bind,
|
||||
unsigned char& type,
|
||||
Elf_Half& section_index,
|
||||
unsigned char& other ) const
|
||||
{
|
||||
bool ret = false;
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
uint32_t nbuckets = *( (uint32_t*)hash_section->get_data() + 0 );
|
||||
uint32_t symoffset = *( (uint32_t*)hash_section->get_data() + 1 );
|
||||
uint32_t bloom_size = *( (uint32_t*)hash_section->get_data() + 2 );
|
||||
uint32_t bloom_shift = *( (uint32_t*)hash_section->get_data() + 3 );
|
||||
nbuckets = convertor( nbuckets );
|
||||
symoffset = convertor( symoffset );
|
||||
bloom_size = convertor( bloom_size );
|
||||
bloom_shift = convertor( bloom_shift );
|
||||
|
||||
T* bloom_filter =
|
||||
(T*)( hash_section->get_data() + 4 * sizeof( uint32_t ) );
|
||||
|
||||
uint32_t hash = elf_gnu_hash( (const unsigned char*)name.c_str() );
|
||||
uint32_t bloom_index = ( hash / ( 8 * sizeof( T ) ) ) % bloom_size;
|
||||
T bloom_bits =
|
||||
( (T)1 << ( hash % ( 8 * sizeof( T ) ) ) ) |
|
||||
( (T)1 << ( ( hash >> bloom_shift ) % ( 8 * sizeof( T ) ) ) );
|
||||
|
||||
if ( ( convertor( bloom_filter[bloom_index] ) & bloom_bits ) !=
|
||||
bloom_bits )
|
||||
return ret;
|
||||
|
||||
uint32_t bucket = hash % nbuckets;
|
||||
auto* buckets =
|
||||
(uint32_t*)( hash_section->get_data() + 4 * sizeof( uint32_t ) +
|
||||
bloom_size * sizeof( T ) );
|
||||
auto* chains =
|
||||
(uint32_t*)( hash_section->get_data() + 4 * sizeof( uint32_t ) +
|
||||
bloom_size * sizeof( T ) +
|
||||
nbuckets * sizeof( uint32_t ) );
|
||||
|
||||
if ( convertor( buckets[bucket] ) >= symoffset ) {
|
||||
uint32_t chain_index = convertor( buckets[bucket] ) - symoffset;
|
||||
uint32_t chain_hash = convertor( chains[chain_index] );
|
||||
std::string symname;
|
||||
|
||||
while ( true ) {
|
||||
if ( ( chain_hash >> 1 ) == ( hash >> 1 ) &&
|
||||
get_symbol( chain_index + symoffset, symname, value, size,
|
||||
bind, type, section_index, other ) &&
|
||||
name == symname ) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( chain_hash & 1 )
|
||||
break;
|
||||
chain_hash = convertor( chains[++chain_index] );
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T> const T* generic_get_symbol_ptr( Elf_Xword index ) const
|
||||
{
|
||||
if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) {
|
||||
const T* pSym = reinterpret_cast<const T*>(
|
||||
symbol_section->get_data() +
|
||||
index * symbol_section->get_entry_size() );
|
||||
|
||||
return pSym;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
bool generic_search_symbols( std::function<bool( const T* )> match,
|
||||
Elf_Xword& idx ) const
|
||||
{
|
||||
for ( Elf_Xword i = 0; i < get_symbols_num(); i++ ) {
|
||||
const T* symPtr = generic_get_symbol_ptr<T>( i );
|
||||
|
||||
if ( symPtr == nullptr )
|
||||
return false;
|
||||
|
||||
if ( match( symPtr ) ) {
|
||||
idx = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
bool generic_get_symbol( Elf_Xword index,
|
||||
std::string& name,
|
||||
Elf64_Addr& value,
|
||||
Elf_Xword& size,
|
||||
unsigned char& bind,
|
||||
unsigned char& type,
|
||||
Elf_Half& section_index,
|
||||
unsigned char& other ) const
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if ( nullptr != symbol_section->get_data() &&
|
||||
index < get_symbols_num() ) {
|
||||
const T* pSym = reinterpret_cast<const T*>(
|
||||
symbol_section->get_data() +
|
||||
index * symbol_section->get_entry_size() );
|
||||
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
section* string_section =
|
||||
elf_file.sections[get_string_table_index()];
|
||||
string_section_accessor str_reader( string_section );
|
||||
const char* pStr =
|
||||
str_reader.get_string( convertor( pSym->st_name ) );
|
||||
if ( nullptr != pStr ) {
|
||||
name = pStr;
|
||||
}
|
||||
value = convertor( pSym->st_value );
|
||||
size = convertor( pSym->st_size );
|
||||
bind = ELF_ST_BIND( pSym->st_info );
|
||||
type = ELF_ST_TYPE( pSym->st_info );
|
||||
section_index = convertor( pSym->st_shndx );
|
||||
other = pSym->st_other;
|
||||
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
Elf_Word generic_add_symbol( Elf_Word name,
|
||||
Elf64_Addr value,
|
||||
Elf_Xword size,
|
||||
unsigned char info,
|
||||
unsigned char other,
|
||||
Elf_Half shndx )
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
T entry;
|
||||
entry.st_name = convertor( name );
|
||||
entry.st_value = decltype( entry.st_value )( value );
|
||||
entry.st_value = convertor( entry.st_value );
|
||||
entry.st_size = decltype( entry.st_size )( size );
|
||||
entry.st_size = convertor( entry.st_size );
|
||||
entry.st_info = convertor( info );
|
||||
entry.st_other = convertor( other );
|
||||
entry.st_shndx = convertor( shndx );
|
||||
|
||||
symbol_section->append_data( reinterpret_cast<char*>( &entry ),
|
||||
sizeof( entry ) );
|
||||
|
||||
Elf_Word nRet =
|
||||
Elf_Word( symbol_section->get_size() / sizeof( entry ) - 1 );
|
||||
|
||||
return nRet;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class T>
|
||||
Elf_Xword generic_arrange_local_symbols(
|
||||
std::function<void( Elf_Xword first, Elf_Xword second )> func )
|
||||
{
|
||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
||||
|
||||
Elf_Word first_not_local =
|
||||
1; // Skip the first entry. It is always NOTYPE
|
||||
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 ) {
|
||||
if ( func )
|
||||
func( first_not_local, current );
|
||||
|
||||
std::swap( *p1, *p2 );
|
||||
}
|
||||
else {
|
||||
// Update 'info' field of the section
|
||||
symbol_section->set_info( first_not_local );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return first_not_local;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
const elfio& elf_file;
|
||||
S* symbol_section;
|
||||
Elf_Half hash_section_index{ 0 };
|
||||
const section* hash_section{ nullptr };
|
||||
};
|
||||
|
||||
using symbol_section_accessor = symbol_section_accessor_template<section>;
|
||||
using const_symbol_section_accessor =
|
||||
symbol_section_accessor_template<const section>;
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_SYMBOLS_HPP
|
302
include/elfio/elfio_utils.hpp
Normal file
302
include/elfio/elfio_utils.hpp
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELFIO_UTILS_HPP
|
||||
#define ELFIO_UTILS_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
|
||||
#define ELFIO_GET_ACCESS_DECL( TYPE, NAME ) virtual TYPE get_##NAME() const = 0
|
||||
|
||||
#define ELFIO_SET_ACCESS_DECL( TYPE, NAME ) \
|
||||
virtual void set_##NAME( const TYPE& value ) = 0
|
||||
|
||||
#define ELFIO_GET_SET_ACCESS_DECL( TYPE, NAME ) \
|
||||
virtual TYPE get_##NAME() const = 0; \
|
||||
virtual void set_##NAME( const TYPE& value ) = 0
|
||||
|
||||
#define ELFIO_GET_ACCESS( TYPE, NAME, FIELD ) \
|
||||
TYPE get_##NAME() const override { return ( *convertor )( FIELD ); }
|
||||
|
||||
#define ELFIO_SET_ACCESS( TYPE, NAME, FIELD ) \
|
||||
void set_##NAME( const TYPE& value ) override \
|
||||
{ \
|
||||
FIELD = decltype( FIELD )( value ); \
|
||||
FIELD = ( *convertor )( FIELD ); \
|
||||
}
|
||||
#define ELFIO_GET_SET_ACCESS( TYPE, NAME, FIELD ) \
|
||||
TYPE get_##NAME() const override { return ( *convertor )( FIELD ); } \
|
||||
void set_##NAME( const TYPE& value ) override \
|
||||
{ \
|
||||
FIELD = decltype( FIELD )( value ); \
|
||||
FIELD = ( *convertor )( FIELD ); \
|
||||
}
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
class endianess_convertor
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
void setup( unsigned char elf_file_encoding )
|
||||
{
|
||||
need_conversion = ( elf_file_encoding != get_host_encoding() );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint64_t operator()( uint64_t value ) const
|
||||
{
|
||||
if ( !need_conversion ) {
|
||||
return value;
|
||||
}
|
||||
value = ( ( value & 0x00000000000000FFuLL ) << 56 ) |
|
||||
( ( value & 0x000000000000FF00uLL ) << 40 ) |
|
||||
( ( value & 0x0000000000FF0000uLL ) << 24 ) |
|
||||
( ( value & 0x00000000FF000000uLL ) << 8 ) |
|
||||
( ( value & 0x000000FF00000000uLL ) >> 8 ) |
|
||||
( ( value & 0x0000FF0000000000uLL ) >> 24 ) |
|
||||
( ( value & 0x00FF000000000000uLL ) >> 40 ) |
|
||||
( ( value & 0xFF00000000000000uLL ) >> 56 );
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int64_t operator()( int64_t value ) const
|
||||
{
|
||||
if ( !need_conversion ) {
|
||||
return value;
|
||||
}
|
||||
return (int64_t)( *this )( (uint64_t)value );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint32_t operator()( uint32_t value ) const
|
||||
{
|
||||
if ( !need_conversion ) {
|
||||
return value;
|
||||
}
|
||||
value =
|
||||
( ( value & 0x000000FF ) << 24 ) | ( ( value & 0x0000FF00 ) << 8 ) |
|
||||
( ( value & 0x00FF0000 ) >> 8 ) | ( ( value & 0xFF000000 ) >> 24 );
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int32_t operator()( int32_t value ) const
|
||||
{
|
||||
if ( !need_conversion ) {
|
||||
return value;
|
||||
}
|
||||
return (int32_t)( *this )( (uint32_t)value );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint16_t operator()( uint16_t value ) const
|
||||
{
|
||||
if ( !need_conversion ) {
|
||||
return value;
|
||||
}
|
||||
value =
|
||||
(uint16_t)( ( value & 0x00FF ) << 8 ) | ( ( value & 0xFF00 ) >> 8 );
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int16_t operator()( int16_t value ) const
|
||||
{
|
||||
if ( !need_conversion ) {
|
||||
return value;
|
||||
}
|
||||
return (int16_t)( *this )( (uint16_t)value );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int8_t operator()( int8_t value ) const { return value; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint8_t operator()( uint8_t value ) const { return value; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
//------------------------------------------------------------------------------
|
||||
unsigned char get_host_encoding() const
|
||||
{
|
||||
static const int tmp = 1;
|
||||
if ( 1 == *reinterpret_cast<const char*>( &tmp ) ) {
|
||||
return ELFDATA2LSB;
|
||||
}
|
||||
else {
|
||||
return ELFDATA2MSB;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool need_conversion = false;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
struct address_translation
|
||||
{
|
||||
address_translation( uint64_t start, uint64_t size, uint64_t mapped_to )
|
||||
: start( start ), size( size ), mapped_to( mapped_to ){};
|
||||
std::streampos start;
|
||||
std::streampos size;
|
||||
std::streampos mapped_to;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
class address_translator
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
void set_address_translation( std::vector<address_translation>& addr_trans )
|
||||
{
|
||||
addr_translations = addr_trans;
|
||||
|
||||
std::sort( addr_translations.begin(), addr_translations.end(),
|
||||
[]( const address_translation& a,
|
||||
const address_translation& b ) -> bool {
|
||||
return a.start < b.start;
|
||||
} );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::streampos operator[]( std::streampos value ) const
|
||||
{
|
||||
if ( addr_translations.empty() ) {
|
||||
return value;
|
||||
}
|
||||
|
||||
for ( auto& t : addr_translations ) {
|
||||
if ( ( t.start <= value ) && ( ( value - t.start ) < t.size ) ) {
|
||||
return value - t.start + t.mapped_to;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool empty() const { return addr_translations.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<address_translation> addr_translations;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
inline uint32_t elf_hash( const unsigned char* name )
|
||||
{
|
||||
uint32_t h = 0;
|
||||
uint32_t g = 0;
|
||||
while ( *name != '\0' ) {
|
||||
h = ( h << 4 ) + *name++;
|
||||
g = h & 0xf0000000;
|
||||
if ( g != 0 )
|
||||
h ^= g >> 24;
|
||||
h &= ~g;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
inline uint32_t elf_gnu_hash( const unsigned char* s )
|
||||
{
|
||||
uint32_t h = 0x1505;
|
||||
for ( unsigned char c = *s; c != '\0'; c = *++s )
|
||||
h = ( h << 5 ) + h + c;
|
||||
return h;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
inline std::string to_hex_string( uint64_t value )
|
||||
{
|
||||
std::string str;
|
||||
|
||||
while ( value ) {
|
||||
if ( auto digit = value & 0xF; digit < 0xA ) {
|
||||
str = char( '0' + digit ) + str;
|
||||
}
|
||||
else {
|
||||
str = char( 'A' + digit - 0xA ) + str;
|
||||
}
|
||||
value >>= 4;
|
||||
}
|
||||
|
||||
return "0x" + str;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
inline void adjust_stream_size( std::ostream& stream, std::streamsize offset )
|
||||
{
|
||||
stream.seekp( 0, std::ios_base::end );
|
||||
if ( stream.tellp() < offset ) {
|
||||
std::streamsize size = offset - stream.tellp();
|
||||
stream.write( std::string( size_t( size ), '\0' ).c_str(), size );
|
||||
}
|
||||
stream.seekp( offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumers should write an implementation of this class and pass an instance of it to the ELFIO::elfio constructor.
|
||||
*/
|
||||
class compression_interface
|
||||
{
|
||||
public:
|
||||
virtual ~compression_interface() = default;
|
||||
/**
|
||||
* decompresses a compressed section
|
||||
*
|
||||
* @param data the buffer of compressed data
|
||||
* @param endianness_convertor pointer to an endianness_convertor instance, used to convert numbers to/from the target endianness.
|
||||
* @param compressed_size the size of the data buffer, in bytes
|
||||
* @param decompressed_size a reference to a variable where the decompressed buffer size will be stored.
|
||||
* @returns a smart pointer to the decompressed data.
|
||||
*/
|
||||
virtual std::unique_ptr<char[]>
|
||||
inflate( const char* data,
|
||||
const endianess_convertor* convertor,
|
||||
Elf_Xword compressed_size,
|
||||
Elf_Xword& uncompressed_size ) const = 0;
|
||||
|
||||
/**
|
||||
* compresses a section
|
||||
*
|
||||
* @param data the buffer of uncompressed data
|
||||
* @param endianness_convertor pointer to an endianness_convertor instance, used to convert numbers to/from the target endianness.
|
||||
* @param decompressed_size the size of the data buffer, in bytes
|
||||
* @param compressed_size a reference to a variable where the compressed buffer size will be stored.
|
||||
* @returns a smart pointer to the compressed data.
|
||||
*/
|
||||
virtual std::unique_ptr<char[]>
|
||||
deflate( const char* data,
|
||||
const endianess_convertor* convertor,
|
||||
Elf_Xword decompressed_size,
|
||||
Elf_Xword& compressed_size ) const = 0;
|
||||
};
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_UTILS_HPP
|
1
include/elfio/elfio_version.hpp
Normal file
1
include/elfio/elfio_version.hpp
Normal file
@ -0,0 +1 @@
|
||||
#define ELFIO_VERSION "3.12"
|
179
include/elfio/elfio_versym.hpp
Normal file
179
include/elfio/elfio_versym.hpp
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef ELFIO_VERSYM_HPP
|
||||
#define ELFIO_VERSYM_HPP
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class S> class versym_section_accessor_template
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
explicit versym_section_accessor_template( S* section )
|
||||
: versym_section( section )
|
||||
{
|
||||
if ( section != nullptr ) {
|
||||
entries_num = decltype( entries_num )( section->get_size() /
|
||||
sizeof( Elf_Half ) );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Word get_entries_num() const
|
||||
{
|
||||
if ( versym_section ) {
|
||||
return entries_num;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool get_entry( Elf_Word no, Elf_Half& value ) const
|
||||
{
|
||||
if ( versym_section && ( no < get_entries_num() ) ) {
|
||||
value = ( (Elf_Half*)versym_section->get_data() )[no];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool modify_entry( Elf_Word no, Elf_Half value )
|
||||
{
|
||||
if ( versym_section && ( no < get_entries_num() ) ) {
|
||||
( (Elf_Half*)versym_section->get_data() )[no] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool add_entry( Elf_Half value )
|
||||
{
|
||||
if ( !versym_section ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
versym_section->append_data( (const char*)&value, sizeof( Elf_Half ) );
|
||||
++entries_num;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
S* versym_section = nullptr;
|
||||
Elf_Word entries_num = 0;
|
||||
};
|
||||
|
||||
using versym_section_accessor = versym_section_accessor_template<section>;
|
||||
using const_versym_section_accessor =
|
||||
versym_section_accessor_template<const section>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template <class S> class versym_r_section_accessor_template
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
versym_r_section_accessor_template( const elfio& elf_file,
|
||||
S* versym_r_section )
|
||||
: elf_file( elf_file ), versym_r_section( versym_r_section ),
|
||||
entries_num( 0 )
|
||||
{
|
||||
// Find .dynamic section
|
||||
const section* dynamic_section = elf_file.sections[".dynamic"];
|
||||
|
||||
if ( dynamic_section == nullptr ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const_dynamic_section_accessor dynamic_section_acc( elf_file,
|
||||
dynamic_section );
|
||||
Elf_Xword dyn_sec_num = dynamic_section_acc.get_entries_num();
|
||||
for ( Elf_Xword i = 0; i < dyn_sec_num; ++i ) {
|
||||
Elf_Xword tag;
|
||||
Elf_Xword value;
|
||||
std::string str;
|
||||
|
||||
if ( dynamic_section_acc.get_entry( i, tag, value, str ) &&
|
||||
tag == DT_VERNEEDNUM ) {
|
||||
entries_num = (Elf_Word)value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Elf_Word get_entries_num() const { return entries_num; }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool get_entry( Elf_Word no,
|
||||
Elf_Half& version,
|
||||
std::string& file_name,
|
||||
Elf_Word& hash,
|
||||
Elf_Half& flags,
|
||||
Elf_Half& other,
|
||||
std::string& dep_name ) const
|
||||
{
|
||||
if ( versym_r_section == nullptr || ( no >= get_entries_num() ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const_string_section_accessor string_section_acc(
|
||||
elf_file.sections[versym_r_section->get_link()] );
|
||||
|
||||
Elfxx_Verneed* verneed = (Elfxx_Verneed*)versym_r_section->get_data();
|
||||
Elfxx_Vernaux* veraux =
|
||||
(Elfxx_Vernaux*)( (char*)verneed + verneed->vn_aux );
|
||||
for ( Elf_Word i = 0; i < no; ++i ) {
|
||||
verneed = (Elfxx_Verneed*)( (char*)verneed + verneed->vn_next );
|
||||
veraux = (Elfxx_Vernaux*)( (char*)verneed + verneed->vn_aux );
|
||||
}
|
||||
|
||||
version = verneed->vn_version;
|
||||
file_name = string_section_acc.get_string( verneed->vn_file );
|
||||
hash = veraux->vna_hash;
|
||||
flags = veraux->vna_flags;
|
||||
other = veraux->vna_other;
|
||||
dep_name = string_section_acc.get_string( veraux->vna_name );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
const elfio& elf_file;
|
||||
S* versym_r_section = nullptr;
|
||||
Elf_Word entries_num = 0;
|
||||
};
|
||||
|
||||
using versym_r_section_accessor = versym_r_section_accessor_template<section>;
|
||||
using const_versym_r_section_accessor =
|
||||
versym_r_section_accessor_template<const section>;
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_VERSYM_HPP
|
163
include/elna/backend/riscv.hpp
Normal file
163
include/elna/backend/riscv.hpp
Normal file
@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "elna/source/optimizer.hpp"
|
||||
|
||||
namespace elna::riscv
|
||||
{
|
||||
enum class address_t
|
||||
{
|
||||
text,
|
||||
high20,
|
||||
lower12i
|
||||
};
|
||||
|
||||
struct reference
|
||||
{
|
||||
std::string name;
|
||||
std::size_t offset;
|
||||
address_t target;
|
||||
};
|
||||
|
||||
enum class x_register : std::uint8_t
|
||||
{
|
||||
zero = 0,
|
||||
ra = 1,
|
||||
sp = 2,
|
||||
gp = 3,
|
||||
tp = 4,
|
||||
t0 = 5,
|
||||
t1 = 6,
|
||||
t2 = 7,
|
||||
s0 = 8,
|
||||
s1 = 9,
|
||||
a0 = 10,
|
||||
a1 = 11,
|
||||
a2 = 12,
|
||||
a3 = 13,
|
||||
a4 = 14,
|
||||
a5 = 15,
|
||||
a6 = 16,
|
||||
a7 = 17,
|
||||
s2 = 18,
|
||||
s3 = 19,
|
||||
s4 = 20,
|
||||
s5 = 21,
|
||||
s6 = 22,
|
||||
s7 = 23,
|
||||
s8 = 24,
|
||||
s9 = 25,
|
||||
s10 = 26,
|
||||
s11 = 27,
|
||||
t3 = 28,
|
||||
t4 = 29,
|
||||
t5 = 30,
|
||||
t6 = 31,
|
||||
};
|
||||
|
||||
enum class funct3_t : std::uint8_t
|
||||
{
|
||||
addi = 0b000,
|
||||
slti = 0b001,
|
||||
sltiu = 0b011,
|
||||
andi = 0b111,
|
||||
ori = 0b110,
|
||||
xori = 0b100,
|
||||
slli = 0b000,
|
||||
srli = 0b101,
|
||||
srai = 0b101,
|
||||
add = 0b000,
|
||||
slt = 0b010,
|
||||
sltu = 0b011,
|
||||
_and = 0b111,
|
||||
_or = 0b110,
|
||||
_xor = 0b100,
|
||||
sll = 0b001,
|
||||
srl = 0b101,
|
||||
sub = 0b000,
|
||||
sra = 0b101,
|
||||
beq = 0b000,
|
||||
bne = 0b001,
|
||||
blt = 0b100,
|
||||
bltu = 0b110,
|
||||
bge = 0b101,
|
||||
bgeu = 0b111,
|
||||
fence = 0b000,
|
||||
fenceI = 0b001,
|
||||
csrrw = 0b001,
|
||||
csrrs = 0b010,
|
||||
csrrc = 0b011,
|
||||
csrrwi = 0b101,
|
||||
csrrsi = 0b110,
|
||||
csrrci = 0b111,
|
||||
priv = 0b000,
|
||||
sb = 0b000,
|
||||
sh = 0b001,
|
||||
sw = 0b010,
|
||||
lb = 0b000,
|
||||
lh = 0b001,
|
||||
lw = 0b010,
|
||||
lbu = 0b100,
|
||||
lhu = 0b101,
|
||||
jalr = 0b000,
|
||||
mul = 0b000,
|
||||
mulh = 0b001,
|
||||
mulhsu = 0b010,
|
||||
mulhu = 0b011,
|
||||
div = 0b100,
|
||||
divu = 0b101,
|
||||
rem = 0b110,
|
||||
remu = 0b111
|
||||
};
|
||||
|
||||
enum class funct12_t : std::uint8_t
|
||||
{
|
||||
ecall = 0b000000000000,
|
||||
ebreak = 0b000000000001,
|
||||
};
|
||||
|
||||
enum class funct7_t : std::uint8_t
|
||||
{
|
||||
none = 0,
|
||||
sub = 0b0100000,
|
||||
muldiv = 0b0000001
|
||||
};
|
||||
|
||||
enum class base_opcode : std::uint8_t
|
||||
{
|
||||
opImm = 0b0010011,
|
||||
lui = 0b0110111,
|
||||
auipc = 0b0010111,
|
||||
op = 0b0110011,
|
||||
jal = 0b1101111,
|
||||
jalr = 0b1100111,
|
||||
branch = 0b1100011,
|
||||
load = 0b0000011,
|
||||
store = 0b0100011,
|
||||
miscMem = 0b0001111,
|
||||
system = 0b1110011,
|
||||
};
|
||||
|
||||
struct instruction
|
||||
{
|
||||
instruction() = default; // NOP = addi x0, x0, 0.
|
||||
instruction(base_opcode opcode);
|
||||
|
||||
instruction& i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate);
|
||||
instruction& s(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2);
|
||||
instruction& b(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2);
|
||||
instruction& r(x_register rd, funct3_t funct3, x_register rs1, x_register rs2,
|
||||
funct7_t funct7 = funct7_t::none);
|
||||
instruction& u(x_register rd, std::uint32_t imm);
|
||||
instruction& j(x_register rd, std::uint32_t imm);
|
||||
|
||||
const std::byte *cbegin() const;
|
||||
const std::byte *cend() const;
|
||||
|
||||
private:
|
||||
std::uint32_t representation{ 0 };
|
||||
};
|
||||
|
||||
std::vector<reference> generate(source::intermediate_code_generator generator,
|
||||
std::shared_ptr<source::symbol_table> table, std::shared_ptr<source::writer<std::byte>> writer);
|
||||
}
|
37
include/elna/backend/target.hpp
Normal file
37
include/elna/backend/target.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "elna/source/parser.hpp"
|
||||
#include "elna/source/optimizer.hpp"
|
||||
#include <filesystem>
|
||||
#include <elfio/elfio.hpp>
|
||||
|
||||
namespace elna::riscv
|
||||
{
|
||||
class elfio_writer final : public source::writer<std::byte>
|
||||
{
|
||||
ELFIO::section *text;
|
||||
ELFIO::symbol_section_accessor symbol_accessor;
|
||||
ELFIO::string_section_accessor string_accessor;
|
||||
|
||||
public:
|
||||
elfio_writer(ELFIO::section *text, ELFIO::symbol_section_accessor symbol_accessor,
|
||||
ELFIO::string_section_accessor string_accessor);
|
||||
|
||||
std::size_t sink(const std::string& label, const std::byte *data, std::size_t size) override;
|
||||
void sink(const std::string& label) override;
|
||||
std::size_t size() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Searches the content by label and returns its index or -1 when the
|
||||
* label does not exist.
|
||||
*
|
||||
* \param symbol_accessor Object accessor.
|
||||
* \param needle Label name.
|
||||
* \return Data index.
|
||||
*/
|
||||
std::ptrdiff_t lookup(ELFIO::symbol_section_accessor symbol_accessor, const std::string& label);
|
||||
|
||||
void riscv32_elf(source::program *ast, source::intermediate_code_generator intermediate_code_generator,
|
||||
std::shared_ptr<source::symbol_table> table, const std::filesystem::path& out_file);
|
||||
}
|
37
include/elna/cli/cl.hpp
Normal file
37
include/elna/cli/cl.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include "elna/source/result.hpp"
|
||||
|
||||
namespace elna::cli
|
||||
{
|
||||
/**
|
||||
* Formats and prints the given error.
|
||||
*
|
||||
* \param compile_error The error to print.
|
||||
*/
|
||||
void print_error(const std::unique_ptr<source::error>& compile_error);
|
||||
|
||||
/**
|
||||
* Prints the given errors to the standard output.
|
||||
*
|
||||
* \param begin Pointer to the first error.
|
||||
* \param end Pointer pass the last error.
|
||||
*/
|
||||
template<typename I>
|
||||
void print_errors(I begin, I end)
|
||||
{
|
||||
std::for_each(begin, end, &print_error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles \a in_file and writes the generated code into \a out_file.
|
||||
*
|
||||
* \param in_file Input file.
|
||||
* \param out_file Output file.
|
||||
*
|
||||
* \return Exit status.
|
||||
*/
|
||||
int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file);
|
||||
}
|
212
include/elna/source/lexer.hpp
Normal file
212
include/elna/source/lexer.hpp
Normal file
@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "elna/source/result.hpp"
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
/**
|
||||
* Union type representing a single token.
|
||||
*/
|
||||
struct token
|
||||
{
|
||||
/**
|
||||
* Token type.
|
||||
*/
|
||||
enum class type : std::uint16_t
|
||||
{
|
||||
dot,
|
||||
number,
|
||||
boolean,
|
||||
term_operator,
|
||||
let,
|
||||
identifier,
|
||||
equals,
|
||||
var,
|
||||
semicolon,
|
||||
left_paren,
|
||||
right_paren,
|
||||
comma,
|
||||
factor_operator,
|
||||
eof,
|
||||
begin,
|
||||
end,
|
||||
assignment,
|
||||
colon,
|
||||
when,
|
||||
then,
|
||||
loop,
|
||||
_do,
|
||||
procedure,
|
||||
comparison_operator,
|
||||
hat,
|
||||
at
|
||||
};
|
||||
|
||||
/**
|
||||
* Type of the token value.
|
||||
*/
|
||||
union value
|
||||
{
|
||||
value();
|
||||
value(std::int32_t value);
|
||||
value(const std::string& value);
|
||||
~value();
|
||||
|
||||
std::nullptr_t nil;
|
||||
std::int32_t number;
|
||||
std::string identifier;
|
||||
};
|
||||
|
||||
token(type of, elna::source::position position);
|
||||
token(type of, std::int32_t value, const elna::source::position position);
|
||||
token(type of, const std::string& value, const elna::source::position position);
|
||||
token(type of, value&& value, const elna::source::position position);
|
||||
token(const token& that);
|
||||
token(token&& that);
|
||||
~token();
|
||||
|
||||
token& operator=(const token& that);
|
||||
token& operator=(token&& that);
|
||||
|
||||
type of() const noexcept;
|
||||
const std::string& identifier() const;
|
||||
std::int32_t number() const;
|
||||
const elna::source::position& position() const noexcept;
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
private:
|
||||
type m_type;
|
||||
value m_value{};
|
||||
elna::source::position m_position;
|
||||
|
||||
bool has_identifier() const noexcept;
|
||||
bool is_numeric() const noexcept;
|
||||
};
|
||||
|
||||
class unexpected_character final : public error
|
||||
{
|
||||
std::string character;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param character Unexpected character.
|
||||
* \param path Source file name.
|
||||
* \param position Unexpected token position.
|
||||
*/
|
||||
unexpected_character(const std::string& character, const std::filesystem::path& path,
|
||||
const source::position position);
|
||||
|
||||
std::string what() const override;
|
||||
};
|
||||
|
||||
class unexpected_token final : public error
|
||||
{
|
||||
token m_token;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param token Unexpected token.
|
||||
* \param path Source file name.
|
||||
*/
|
||||
unexpected_token(const token& token, const std::filesystem::path& path);
|
||||
|
||||
std::string what() const override;
|
||||
};
|
||||
|
||||
struct lexer
|
||||
{
|
||||
lexer(std::vector<token>&& tokens, const position last_position, const std::filesystem::path& path);
|
||||
lexer(const lexer&) = delete;
|
||||
lexer(lexer&&) = default;
|
||||
|
||||
lexer& operator=(const lexer&) = delete;
|
||||
lexer& operator=(lexer&&) = default;
|
||||
|
||||
lexer& operator++();
|
||||
const token& operator*() const;
|
||||
const token *operator->() const;
|
||||
|
||||
/**
|
||||
* This function never fails and returns \ref token::type::eof at the
|
||||
* end of the token stream.
|
||||
*
|
||||
* \return Current token.
|
||||
*/
|
||||
const token& current() const noexcept;
|
||||
|
||||
/**
|
||||
* \param token_type Token type.
|
||||
* \return Whether the current token is \a token_type.
|
||||
*/
|
||||
bool current(const token::type token_type) const noexcept;
|
||||
|
||||
/**
|
||||
* Adds an \ref unexpected_token error to the error list.
|
||||
*
|
||||
* \param expected The token was expected.
|
||||
*/
|
||||
void add_error(const token& expected);
|
||||
|
||||
/**
|
||||
* Expects the current token to be \a token_type. In this case returns
|
||||
* this token and advances to the next token in the stream.
|
||||
*
|
||||
* \param token_type Expected token type.
|
||||
* \return Current token.
|
||||
*/
|
||||
std::optional<std::reference_wrapper<const token>> advance(const token::type token_type);
|
||||
|
||||
/**
|
||||
* Returns that follows the current token. If the current token is
|
||||
* \ref token::type::eof, returns it.
|
||||
*
|
||||
* \return The token that follows the current one.
|
||||
*/
|
||||
const token& look_ahead() const;
|
||||
|
||||
/**
|
||||
* Tells whether the token following the current one is \a token_type.
|
||||
*
|
||||
* \param token_type Token type.
|
||||
* \return Whether the next token is \a token_type.
|
||||
*/
|
||||
bool look_ahead(const token::type token_type) const;
|
||||
|
||||
/**
|
||||
* Skips one token if it is of type \a token_type. Adds an
|
||||
* \ref unexpected_token error otherwise.
|
||||
*
|
||||
* \param token_type The token type was expected.
|
||||
* \return Whether the current token was \a token_type.
|
||||
*/
|
||||
bool skip(const token::type token_type);
|
||||
|
||||
/**
|
||||
* Gets produced errors.
|
||||
*
|
||||
* \return Produced error list.
|
||||
*/
|
||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
||||
|
||||
private:
|
||||
std::vector<token> tokens;
|
||||
std::vector<token>::const_iterator iterator;
|
||||
std::list<std::unique_ptr<error>> m_errors;
|
||||
std::filesystem::path source_file;
|
||||
token eof;
|
||||
};
|
||||
|
||||
/**
|
||||
* Splits the source text into tokens.
|
||||
*
|
||||
* \param path Source file location.
|
||||
* \return Tokens or error.
|
||||
*/
|
||||
elna::source::result<lexer> tokenize(const std::filesystem::path& path);
|
||||
}
|
106
include/elna/source/optimizer.hpp
Normal file
106
include/elna/source/optimizer.hpp
Normal file
@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include "elna/source/parser.hpp"
|
||||
#include "elna/source/symbol_table.hpp"
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
enum class quadruple_operator
|
||||
{
|
||||
start,
|
||||
stop,
|
||||
add,
|
||||
sub,
|
||||
mul,
|
||||
div,
|
||||
eq,
|
||||
neq,
|
||||
lt,
|
||||
ge,
|
||||
gt,
|
||||
le,
|
||||
load,
|
||||
ref,
|
||||
beqz,
|
||||
j,
|
||||
label,
|
||||
assign,
|
||||
param,
|
||||
call
|
||||
};
|
||||
|
||||
/**
|
||||
* Single instruction representation.
|
||||
*/
|
||||
struct quadruple
|
||||
{
|
||||
quadruple(const quadruple_operator operation, std::shared_ptr<operand> operand1 = nullptr,
|
||||
std::shared_ptr<operand> operand2 = nullptr, std::shared_ptr<operand> operand3 = nullptr);
|
||||
|
||||
quadruple_operator operation() const noexcept;
|
||||
std::shared_ptr<operand> operand1();
|
||||
std::shared_ptr<operand> operand2();
|
||||
std::shared_ptr<operand> operand3();
|
||||
|
||||
private:
|
||||
quadruple_operator m_operation;
|
||||
std::shared_ptr<operand> m_operand1;
|
||||
std::shared_ptr<operand> m_operand2;
|
||||
std::shared_ptr<operand> m_operand3;
|
||||
};
|
||||
|
||||
class intermediate_code final
|
||||
{
|
||||
std::vector<quadruple> instructions;
|
||||
std::uint32_t m_variable_counter;
|
||||
std::uint32_t m_label_counter;
|
||||
|
||||
public:
|
||||
intermediate_code();
|
||||
|
||||
void emplace_back(const quadruple_operator operation, std::shared_ptr<operand> operand1 = nullptr,
|
||||
std::shared_ptr<operand> operand2 = nullptr, std::shared_ptr<operand> operand3 = nullptr);
|
||||
void clear();
|
||||
|
||||
std::vector<quadruple>::iterator begin();
|
||||
std::vector<quadruple>::iterator end();
|
||||
|
||||
std::int32_t variable_counter() const noexcept;
|
||||
std::int32_t increment_variable() noexcept;
|
||||
std::int32_t label_counter() const noexcept;
|
||||
std::int32_t increment_label() noexcept;
|
||||
};
|
||||
|
||||
class intermediate_code_generator final : public empty_visitor
|
||||
{
|
||||
std::unordered_map<std::string, intermediate_code> code;
|
||||
intermediate_code current;
|
||||
std::shared_ptr<symbol_table> table;
|
||||
|
||||
quadruple_operator convert(const binary_operator operation) const;
|
||||
quadruple_operator convert(const unary_operator operation) const;
|
||||
|
||||
public:
|
||||
intermediate_code_generator(std::shared_ptr<symbol_table> table);
|
||||
|
||||
std::unordered_map<std::string, intermediate_code>::iterator begin();
|
||||
std::unordered_map<std::string, intermediate_code>::iterator end();
|
||||
|
||||
void visit(declaration *declaration) override;
|
||||
void visit(constant_definition *definition) override;
|
||||
void visit(procedure_definition *definition) override;
|
||||
void visit(call_statement *statement) override;
|
||||
void visit(assign_statement *statement) override;
|
||||
void visit(if_statement *statement) override;
|
||||
void visit(while_statement *statement) override;
|
||||
void visit(block *block) override;
|
||||
void visit(program *program) override;
|
||||
void visit(type_expression *variable) override;
|
||||
void visit(variable_expression *variable) override;
|
||||
void visit(binary_expression *expression) override;
|
||||
void visit(unary_expression *expression) override;
|
||||
void visit(integer_literal *number) override;
|
||||
void visit(boolean_literal *number) override;
|
||||
};
|
||||
}
|
526
include/elna/source/parser.hpp
Normal file
526
include/elna/source/parser.hpp
Normal file
@ -0,0 +1,526 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <elna/source/lexer.hpp>
|
||||
#include "elna/source/types.hpp"
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
enum class binary_operator
|
||||
{
|
||||
sum,
|
||||
subtraction,
|
||||
multiplication,
|
||||
division,
|
||||
equals,
|
||||
not_equals,
|
||||
less,
|
||||
greater,
|
||||
less_equal,
|
||||
greater_equal
|
||||
};
|
||||
|
||||
enum class unary_operator
|
||||
{
|
||||
reference,
|
||||
dereference
|
||||
};
|
||||
|
||||
class declaration;
|
||||
class constant_definition;
|
||||
class procedure_definition;
|
||||
class call_statement;
|
||||
class compound_statement;
|
||||
class assign_statement;
|
||||
class if_statement;
|
||||
class while_statement;
|
||||
class block;
|
||||
class program;
|
||||
class binary_expression;
|
||||
class unary_expression;
|
||||
class type_expression;
|
||||
class variable_expression;
|
||||
class integer_literal;
|
||||
class boolean_literal;
|
||||
|
||||
/**
|
||||
* Interface for AST visitors.
|
||||
*/
|
||||
struct parser_visitor
|
||||
{
|
||||
virtual void visit(declaration *) = 0;
|
||||
virtual void visit(constant_definition *) = 0;
|
||||
virtual void visit(procedure_definition *) = 0;
|
||||
virtual void visit(call_statement *) = 0;
|
||||
virtual void visit(compound_statement *) = 0;
|
||||
virtual void visit(assign_statement *) = 0;
|
||||
virtual void visit(if_statement *) = 0;
|
||||
virtual void visit(while_statement *) = 0;
|
||||
virtual void visit(block *) = 0;
|
||||
virtual void visit(program *) = 0;
|
||||
virtual void visit(binary_expression *) = 0;
|
||||
virtual void visit(unary_expression *) = 0;
|
||||
virtual void visit(type_expression *) = 0;
|
||||
virtual void visit(variable_expression *) = 0;
|
||||
virtual void visit(integer_literal *) = 0;
|
||||
virtual void visit(boolean_literal *) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A visitor which visits all nodes but does nothing.
|
||||
*/
|
||||
struct empty_visitor : parser_visitor
|
||||
{
|
||||
virtual void visit(declaration *declaration) override;
|
||||
virtual void visit(constant_definition *definition) override;
|
||||
virtual void visit(procedure_definition *definition) override;
|
||||
virtual void visit(call_statement *statement) override;
|
||||
virtual void visit(compound_statement *statement) override;
|
||||
virtual void visit(assign_statement *statement) override;
|
||||
virtual void visit(if_statement *) override;
|
||||
virtual void visit(while_statement *) override;
|
||||
virtual void visit(block *block) override;
|
||||
virtual void visit(program *program) override;
|
||||
virtual void visit(binary_expression *expression) override;
|
||||
virtual void visit(unary_expression *expression) override;
|
||||
virtual void visit(type_expression *variable) override;
|
||||
virtual void visit(variable_expression *variable) override;
|
||||
virtual void visit(integer_literal *number) override;
|
||||
virtual void visit(boolean_literal *boolean) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Operand representing a subexpression in the 3 address code.
|
||||
*/
|
||||
struct operand
|
||||
{
|
||||
public:
|
||||
virtual ~operand() noexcept = 0;
|
||||
};
|
||||
|
||||
struct integer_operand final : public operand
|
||||
{
|
||||
std::int32_t m_value;
|
||||
|
||||
public:
|
||||
explicit integer_operand(const std::int32_t value);
|
||||
|
||||
std::int32_t value() const noexcept;
|
||||
};
|
||||
|
||||
class variable_operand final : public operand
|
||||
{
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
explicit variable_operand(const std::string& name);
|
||||
|
||||
const std::string& name() const noexcept;
|
||||
};
|
||||
|
||||
struct temporary_variable final : public operand
|
||||
{
|
||||
std::size_t m_counter;
|
||||
|
||||
public:
|
||||
explicit temporary_variable(const std::size_t counter);
|
||||
|
||||
std::size_t counter() const noexcept;
|
||||
};
|
||||
|
||||
struct label_operand final : public operand
|
||||
{
|
||||
std::size_t m_counter;
|
||||
|
||||
public:
|
||||
explicit label_operand(const std::size_t counter);
|
||||
|
||||
std::size_t counter() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* AST node.
|
||||
*/
|
||||
class node
|
||||
{
|
||||
const struct position source_position;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
*/
|
||||
explicit node(const position position);
|
||||
|
||||
public:
|
||||
virtual ~node() noexcept = default;
|
||||
virtual void accept(parser_visitor *) = 0;
|
||||
|
||||
/**
|
||||
* \return Node position in the source code.
|
||||
*/
|
||||
const struct position& position() const noexcept;
|
||||
};
|
||||
|
||||
class statement : public node
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
*/
|
||||
explicit statement(const struct position position);
|
||||
};
|
||||
|
||||
class expression : public node
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<operand> place;
|
||||
std::shared_ptr<const type> data_type;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
*/
|
||||
explicit expression(const struct position position);
|
||||
};
|
||||
|
||||
/**
|
||||
* Symbol definition.
|
||||
*/
|
||||
class definition : public node
|
||||
{
|
||||
std::string m_identifier;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructs a definition identified by some name.
|
||||
*
|
||||
* \param position Source code position.
|
||||
* \param identifier Definition name.
|
||||
*/
|
||||
definition(const struct position position, const std::string& identifier);
|
||||
|
||||
public:
|
||||
/**
|
||||
* \return Definition name.
|
||||
*/
|
||||
std::string& identifier() noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Expression defining a composed type like pointer or an array.
|
||||
*/
|
||||
class type_expression : public node
|
||||
{
|
||||
std::string m_base;
|
||||
bool m_pointer{ false };
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param name Type name.
|
||||
* \param is_pointer Whether it is a pointer type.
|
||||
*/
|
||||
type_expression(const struct position position, const std::string& name, const bool is_pointer = false);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
/**
|
||||
* \return Name of the base type.
|
||||
*/
|
||||
const std::string& base() const noexcept;
|
||||
|
||||
/**
|
||||
* \return Whether the type is a pointer.
|
||||
*/
|
||||
bool is_pointer() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Variable declaration.
|
||||
*/
|
||||
class declaration : public definition
|
||||
{
|
||||
std::unique_ptr<type_expression> m_type;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a declaration with a name and a type.
|
||||
*
|
||||
* \param position Source code position.
|
||||
* \param identifier Definition name.
|
||||
* \param type Declared type.
|
||||
*/
|
||||
declaration(const struct position position, const std::string& identifier,
|
||||
std::unique_ptr<type_expression>&& type);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
type_expression& type() noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constant definition.
|
||||
*/
|
||||
class constant_definition : public definition
|
||||
{
|
||||
std::unique_ptr<integer_literal> m_body;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param identifier Constant name.
|
||||
* \param body Constant value.
|
||||
*/
|
||||
constant_definition(const struct position position, const std::string& identifier,
|
||||
std::unique_ptr<integer_literal>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
integer_literal& body();
|
||||
};
|
||||
|
||||
/**
|
||||
* Procedure definition.
|
||||
*/
|
||||
class procedure_definition : public definition
|
||||
{
|
||||
std::unique_ptr<block> m_body;
|
||||
std::vector<std::unique_ptr<declaration>> m_parameters;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param identifier Procedure name.
|
||||
* \param body Procedure body.
|
||||
*/
|
||||
procedure_definition(const struct position position, const std::string& identifier,
|
||||
std::unique_ptr<block>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
block& body();
|
||||
std::vector<std::unique_ptr<declaration>>& parameters() noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call statement.
|
||||
*/
|
||||
class call_statement : public statement
|
||||
{
|
||||
std::string m_name;
|
||||
std::vector<std::unique_ptr<expression>> m_arguments;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param name Callable's name.
|
||||
*/
|
||||
call_statement(const struct position position, const std::string& name);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
std::string& name() noexcept;
|
||||
std::vector<std::unique_ptr<expression>>& arguments() noexcept;
|
||||
};
|
||||
|
||||
class compound_statement : public statement
|
||||
{
|
||||
std::vector<std::unique_ptr<statement>> m_statements;
|
||||
|
||||
public:
|
||||
explicit compound_statement(const struct position position);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
std::vector<std::unique_ptr<statement>>& statements();
|
||||
};
|
||||
|
||||
class assign_statement : public statement
|
||||
{
|
||||
std::string m_lvalue;
|
||||
std::unique_ptr<expression> m_rvalue;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param lvalue Left-hand side.
|
||||
* \param rvalue Assigned expression.
|
||||
*/
|
||||
assign_statement(const struct position position, const std::string& lvalue,
|
||||
std::unique_ptr<expression>&& rvalue);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
std::string& lvalue() noexcept;
|
||||
expression& rvalue();
|
||||
};
|
||||
|
||||
/**
|
||||
* If-statement.
|
||||
*/
|
||||
class if_statement : public statement
|
||||
{
|
||||
std::unique_ptr<expression> m_prerequisite;
|
||||
std::unique_ptr<statement> m_body;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param prerequisite Condition.
|
||||
* \param body Statement executed if the condition is met.
|
||||
*/
|
||||
if_statement(const struct position position, std::unique_ptr<expression>&& prerequisite,
|
||||
std::unique_ptr<statement>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
expression& prerequisite();
|
||||
statement& body();
|
||||
};
|
||||
|
||||
/**
|
||||
* While-statement.
|
||||
*/
|
||||
class while_statement : public statement
|
||||
{
|
||||
std::unique_ptr<expression> m_prerequisite;
|
||||
std::unique_ptr<statement> m_body;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param prerequisite Condition.
|
||||
* \param body Statement executed while the condition is met.
|
||||
*/
|
||||
while_statement(const struct position position, std::unique_ptr<expression>&& prerequisite,
|
||||
std::unique_ptr<statement>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
expression& prerequisite();
|
||||
statement& body();
|
||||
};
|
||||
|
||||
class block : public node
|
||||
{
|
||||
std::unique_ptr<statement> m_body;
|
||||
std::vector<std::unique_ptr<definition>> m_definitions;
|
||||
std::vector<std::unique_ptr<declaration>> m_declarations;
|
||||
|
||||
public:
|
||||
block(const struct position position, std::vector<std::unique_ptr<definition>>&& definitions,
|
||||
std::vector<std::unique_ptr<declaration>>&& declarations,
|
||||
std::unique_ptr<statement>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
statement& body();
|
||||
std::vector<std::unique_ptr<definition>>& definitions() noexcept;
|
||||
std::vector<std::unique_ptr<declaration>>& declarations() noexcept;
|
||||
};
|
||||
|
||||
class program : public block
|
||||
{
|
||||
public:
|
||||
program(const struct position position, std::vector<std::unique_ptr<definition>>&& definitions,
|
||||
std::vector<std::unique_ptr<declaration>>&& declarations,
|
||||
std::unique_ptr<statement>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
};
|
||||
|
||||
class integer_literal : public expression
|
||||
{
|
||||
std::int32_t m_number;
|
||||
|
||||
public:
|
||||
integer_literal(const struct position position, const std::int32_t value);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
std::int32_t number() const noexcept;
|
||||
};
|
||||
|
||||
class boolean_literal : public expression
|
||||
{
|
||||
bool m_boolean;
|
||||
|
||||
public:
|
||||
boolean_literal(const struct position position, const bool value);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
bool boolean() const noexcept;
|
||||
};
|
||||
|
||||
class variable_expression : public expression
|
||||
{
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
variable_expression(const struct position position, const std::string& name);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
const std::string& name() const noexcept;
|
||||
};
|
||||
|
||||
class binary_expression : public expression
|
||||
{
|
||||
std::unique_ptr<expression> m_lhs;
|
||||
std::unique_ptr<expression> m_rhs;
|
||||
binary_operator m_operator;
|
||||
|
||||
public:
|
||||
binary_expression(const struct position position, std::unique_ptr<expression>&& lhs,
|
||||
std::unique_ptr<expression>&& rhs, const unsigned char operation);
|
||||
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
expression& lhs();
|
||||
expression& rhs();
|
||||
binary_operator operation() const noexcept;
|
||||
};
|
||||
|
||||
class unary_expression : public expression
|
||||
{
|
||||
std::unique_ptr<expression> m_operand;
|
||||
unary_operator m_operator;
|
||||
|
||||
public:
|
||||
unary_expression(const struct position position, std::unique_ptr<expression>&& operand,
|
||||
const unsigned char operation);
|
||||
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
expression& operand();
|
||||
unary_operator operation() const noexcept;
|
||||
};
|
||||
|
||||
class parser
|
||||
{
|
||||
std::unique_ptr<expression> parse_unary_expression();
|
||||
std::unique_ptr<expression> parse_factor();
|
||||
std::unique_ptr<expression> parse_term();
|
||||
std::unique_ptr<expression> parse_expression();
|
||||
std::unique_ptr<expression> parse_condition();
|
||||
std::unique_ptr<constant_definition> parse_constant_definition();
|
||||
std::unique_ptr<procedure_definition> parse_procedure_definition();
|
||||
std::unique_ptr<type_expression> parse_type_expression();
|
||||
std::unique_ptr<declaration> parse_declaration();
|
||||
std::unique_ptr<statement> parse_statement();
|
||||
std::unique_ptr<call_statement> parse_call_statement();
|
||||
std::unique_ptr<compound_statement> parse_compound_statement();
|
||||
std::unique_ptr<assign_statement> parse_assign_statement();
|
||||
std::unique_ptr<if_statement> parse_if_statement();
|
||||
std::unique_ptr<while_statement> parse_while_statement();
|
||||
std::vector<std::unique_ptr<constant_definition>> parse_constant_definitions();
|
||||
std::vector<std::unique_ptr<procedure_definition>> parse_procedure_definitions();
|
||||
std::vector<std::unique_ptr<declaration>> parse_declarations();
|
||||
std::unique_ptr<block> parse_block();
|
||||
|
||||
lexer iterator;
|
||||
|
||||
public:
|
||||
parser(lexer&& tokens);
|
||||
parser(const parser&) = delete;
|
||||
|
||||
/**
|
||||
* Parses a source text.
|
||||
*
|
||||
* \return Parsed program or nothing if an error occurred.
|
||||
*/
|
||||
std::unique_ptr<program> parse();
|
||||
|
||||
/**
|
||||
* Gets produced errors.
|
||||
*
|
||||
* \return Produced error list.
|
||||
*/
|
||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
||||
};
|
||||
}
|
184
include/elna/source/result.hpp
Normal file
184
include/elna/source/result.hpp
Normal file
@ -0,0 +1,184 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <variant>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include "elna/source/types.hpp"
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
/**
|
||||
* Position in the source text.
|
||||
*/
|
||||
struct position
|
||||
{
|
||||
/// Line.
|
||||
std::size_t line = 1;
|
||||
|
||||
/// Column.
|
||||
std::size_t column = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* A compilation error consists of an error message and position.
|
||||
*/
|
||||
class error
|
||||
{
|
||||
position m_position;
|
||||
std::filesystem::path m_path;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructs an error.
|
||||
*
|
||||
* \param path Source file name.
|
||||
* \param position Error position in the source text.
|
||||
*/
|
||||
error(const std::filesystem::path& path, const position position);
|
||||
|
||||
public:
|
||||
virtual ~error() noexcept = default;
|
||||
|
||||
/// Error text.
|
||||
virtual std::string what() const = 0;
|
||||
|
||||
/// Error line in the source text.
|
||||
std::size_t line() const noexcept;
|
||||
|
||||
/// Error column in the source text.
|
||||
std::size_t column() const noexcept;
|
||||
|
||||
/// Source file name.
|
||||
const std::filesystem::path& path() const noexcept;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct result
|
||||
{
|
||||
using E = std::list<std::unique_ptr<source::error>>;
|
||||
|
||||
template<typename... Args, typename = std::enable_if<std::is_constructible_v<T, Args...>>>
|
||||
explicit result(std::in_place_t, Args&&... arguments)
|
||||
: payload(std::in_place_type<T>, std::forward<Args>(arguments)...)
|
||||
{
|
||||
}
|
||||
|
||||
explicit result(T&& result)
|
||||
: payload(std::move(result))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U, typename = std::enable_if<std::is_base_of_v<error, U>>>
|
||||
explicit result(U&& first)
|
||||
: payload(E())
|
||||
{
|
||||
errors().emplace_back(std::make_unique<U>(first));
|
||||
}
|
||||
|
||||
explicit result(E&& errors)
|
||||
: payload(std::move(errors))
|
||||
{
|
||||
}
|
||||
|
||||
bool has_errors() const noexcept
|
||||
{
|
||||
return std::holds_alternative<E>(payload);
|
||||
}
|
||||
|
||||
bool is_success() const noexcept
|
||||
{
|
||||
return std::holds_alternative<T>(payload);
|
||||
}
|
||||
|
||||
T& success()
|
||||
{
|
||||
return std::get<T>(payload);
|
||||
}
|
||||
|
||||
E& errors()
|
||||
{
|
||||
return std::get<E>(payload);
|
||||
}
|
||||
|
||||
private:
|
||||
std::variant<T, E> payload;
|
||||
};
|
||||
|
||||
class name_collision final : public error
|
||||
{
|
||||
position previous;
|
||||
std::string name;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param name Symbol name.
|
||||
* \param path Source file name.
|
||||
* \param current Current symbol position.
|
||||
* \param previous Position of the previously defined symbol.
|
||||
*/
|
||||
name_collision(const std::string& name, const std::filesystem::path& path,
|
||||
const position current, const position previous);
|
||||
|
||||
std::string what() const override;
|
||||
};
|
||||
|
||||
struct type_mismatch final : public error
|
||||
{
|
||||
/**
|
||||
* Kind of the operation on the type.
|
||||
*/
|
||||
enum class operation
|
||||
{
|
||||
dereference,
|
||||
argument,
|
||||
arithmetic,
|
||||
comparison,
|
||||
condition,
|
||||
assignment
|
||||
};
|
||||
|
||||
/**
|
||||
* \param name Given type.
|
||||
* \param kind Kind of the operation on the type.
|
||||
* \param path Source file name.
|
||||
* \param position Operation position.
|
||||
*/
|
||||
type_mismatch(std::shared_ptr<const type> got, operation kind, const std::filesystem::path& path,
|
||||
const struct position position);
|
||||
|
||||
std::string what() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<const type> got;
|
||||
operation kind;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct writer
|
||||
{
|
||||
/**
|
||||
* Writes data to the table and saves it under the given label.
|
||||
*
|
||||
* \param label Symbol name.
|
||||
* \param data Data to be written.
|
||||
* \param size Data size.
|
||||
*
|
||||
* \return New size of the table.
|
||||
*/
|
||||
virtual std::size_t sink(const std::string& label, const T *data, std::size_t size) = 0;
|
||||
|
||||
/**
|
||||
* Creates an external symbol.
|
||||
*/
|
||||
virtual void sink(const std::string& label) = 0;
|
||||
|
||||
/**
|
||||
* \return Actual size of the text section.
|
||||
*/
|
||||
virtual std::size_t size() const = 0;
|
||||
};
|
||||
}
|
93
include/elna/source/semantic.hpp
Normal file
93
include/elna/source/semantic.hpp
Normal file
@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include "elna/source/parser.hpp"
|
||||
#include "elna/source/symbol_table.hpp"
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
class name_analysis_visitor final : public empty_visitor
|
||||
{
|
||||
std::shared_ptr<symbol_table> table;
|
||||
const std::filesystem::path filename;
|
||||
std::list<std::unique_ptr<error>> m_errors;
|
||||
const std::size_t pointer_size;
|
||||
|
||||
std::shared_ptr<const type> convert_declaration_type(const type_expression& ast_type) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param table Symbol table.
|
||||
* \param path Source filename.
|
||||
* \param target_pointer_size Pointer size on the target platform.
|
||||
*/
|
||||
name_analysis_visitor(std::shared_ptr<symbol_table> table, const std::filesystem::path& filename,
|
||||
const std::size_t target_pointer_size);
|
||||
|
||||
/**
|
||||
* \return Collected errors.
|
||||
*/
|
||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
||||
|
||||
void visit(constant_definition *definition) override;
|
||||
void visit(declaration *declaration) override;
|
||||
void visit(program *program) override;
|
||||
void visit(procedure_definition *procedure) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visitor which allocates registers and stack space for variables and
|
||||
* parameters.
|
||||
*/
|
||||
class allocator_visitor final : public empty_visitor
|
||||
{
|
||||
std::ptrdiff_t local_offset;
|
||||
std::ptrdiff_t argument_offset;
|
||||
std::shared_ptr<symbol_table> table;
|
||||
|
||||
public:
|
||||
allocator_visitor(std::shared_ptr<symbol_table> table);
|
||||
|
||||
void visit(declaration *declaration) override;
|
||||
void visit(program *program) override;
|
||||
void visit(procedure_definition *procedure) override;
|
||||
void visit(call_statement *statement) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* This visitor performs the type checking.
|
||||
*/
|
||||
class type_analysis_visitor final : public empty_visitor
|
||||
{
|
||||
std::shared_ptr<symbol_table> table;
|
||||
const std::filesystem::path filename;
|
||||
const std::size_t pointer_size;
|
||||
std::list<std::unique_ptr<error>> m_errors;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param table Symbol table.
|
||||
* \param path Source filename.
|
||||
* \param target_pointer_size Pointer size on the target platform.
|
||||
*/
|
||||
type_analysis_visitor(std::shared_ptr<symbol_table> table, const std::filesystem::path& filename,
|
||||
const std::size_t target_pointer_size);
|
||||
|
||||
/**
|
||||
* \return Collected errors.
|
||||
*/
|
||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
||||
|
||||
void visit(program *program) override;
|
||||
void visit(procedure_definition *procedure) override;
|
||||
void visit(integer_literal *literal) override;
|
||||
void visit(boolean_literal *literal) override;
|
||||
void visit(variable_expression *expression) override;
|
||||
void visit(unary_expression *expression) override;
|
||||
void visit(binary_expression *expression) override;
|
||||
void visit(call_statement *statement) override;
|
||||
void visit(constant_definition *definition) override;
|
||||
void visit(while_statement *statement) override;
|
||||
void visit(if_statement *statement) override;
|
||||
void visit(assign_statement *statement) override;
|
||||
};
|
||||
}
|
161
include/elna/source/symbol_table.hpp
Normal file
161
include/elna/source/symbol_table.hpp
Normal file
@ -0,0 +1,161 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
class symbol_table;
|
||||
|
||||
/**
|
||||
* Generic language entity information.
|
||||
*/
|
||||
class info
|
||||
{
|
||||
public:
|
||||
virtual ~info() = 0;
|
||||
|
||||
protected:
|
||||
info();
|
||||
};
|
||||
|
||||
/**
|
||||
* Type information.
|
||||
*/
|
||||
class type_info final : public info
|
||||
{
|
||||
std::shared_ptr<class type> m_type;
|
||||
|
||||
public:
|
||||
explicit type_info(const class type& type);
|
||||
~type_info() override;
|
||||
std::shared_ptr<const class type> type() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Information for a typed symbol.
|
||||
*/
|
||||
class typed_info : public info
|
||||
{
|
||||
std::shared_ptr<const class type> m_type;
|
||||
|
||||
protected:
|
||||
typed_info(std::shared_ptr<const class type> type);
|
||||
|
||||
public:
|
||||
~typed_info() override;
|
||||
|
||||
std::shared_ptr<const class type> type() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constant information.
|
||||
*/
|
||||
class constant_info final : public typed_info
|
||||
{
|
||||
std::int32_t m_value;
|
||||
|
||||
public:
|
||||
constant_info(std::shared_ptr<const class type> type, const std::int32_t value);
|
||||
|
||||
std::int32_t value() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Variable information.
|
||||
*/
|
||||
class variable_info final : public typed_info
|
||||
{
|
||||
public:
|
||||
std::ptrdiff_t offset{ 0 };
|
||||
|
||||
explicit variable_info(std::shared_ptr<const class type> type);
|
||||
};
|
||||
|
||||
/**
|
||||
* Procedure parameter information.
|
||||
*/
|
||||
class parameter_info final : public typed_info
|
||||
{
|
||||
public:
|
||||
std::ptrdiff_t offset{ 0 };
|
||||
|
||||
explicit parameter_info(std::shared_ptr<const class type> type);
|
||||
};
|
||||
|
||||
/**
|
||||
* Intrinsic and external procedure information.
|
||||
*/
|
||||
class intrinsic_info : public info
|
||||
{
|
||||
std::shared_ptr<const class procedure_type> m_type;
|
||||
|
||||
public:
|
||||
explicit intrinsic_info(const class procedure_type& type);
|
||||
~intrinsic_info() override;
|
||||
|
||||
std::shared_ptr<const class procedure_type> type() const noexcept;
|
||||
std::size_t parameter_stack_size() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Procedure information.
|
||||
*/
|
||||
class procedure_info final : public intrinsic_info
|
||||
{
|
||||
std::shared_ptr<symbol_table> local_table;
|
||||
|
||||
public:
|
||||
std::size_t local_stack_size{ 0 };
|
||||
std::size_t argument_stack_size{ 0 };
|
||||
|
||||
procedure_info(const class procedure_type& type, std::shared_ptr<symbol_table> outer_scope);
|
||||
~procedure_info() override;
|
||||
|
||||
std::shared_ptr<symbol_table> scope();
|
||||
std::size_t stack_size() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Symbol table.
|
||||
*/
|
||||
class symbol_table
|
||||
{
|
||||
std::unordered_map<std::string, std::shared_ptr<info>> entries;
|
||||
std::shared_ptr<symbol_table> outer_scope;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a new symbol with an optional outer scope.
|
||||
*
|
||||
* \param scope Outer scope.
|
||||
*/
|
||||
explicit symbol_table(std::shared_ptr<symbol_table> scope = nullptr);
|
||||
|
||||
/**
|
||||
* Looks for symbol in the table by name. Returns nullptr if the symbol
|
||||
* can not be found.
|
||||
*
|
||||
* \param name Symbol name.
|
||||
* \return Symbol from the table if found.
|
||||
*/
|
||||
std::shared_ptr<info> lookup(const std::string& name);
|
||||
|
||||
/**
|
||||
* Registers new symbol.
|
||||
*
|
||||
* \param name Symbol name.
|
||||
* \param entry Symbol information.
|
||||
*/
|
||||
void enter(const std::string& name, std::shared_ptr<info> entry);
|
||||
|
||||
/**
|
||||
* Returns the outer scope or nullptr if the this is the global scope.
|
||||
*
|
||||
* \return Outer scope.
|
||||
*/
|
||||
std::shared_ptr<symbol_table> scope();
|
||||
};
|
||||
}
|
99
include/elna/source/types.hpp
Normal file
99
include/elna/source/types.hpp
Normal file
@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
/**
|
||||
* Type representation.
|
||||
*/
|
||||
class type
|
||||
{
|
||||
const std::size_t byte_size;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* \param byte_size The type size in bytes.
|
||||
*/
|
||||
explicit type(const std::size_t byte_size);
|
||||
|
||||
public:
|
||||
/**
|
||||
* \return The type size in bytes.
|
||||
*/
|
||||
virtual std::size_t size() const noexcept;
|
||||
|
||||
friend bool operator==(const type& lhs, const type& rhs) noexcept;
|
||||
friend bool operator!=(const type& lhs, const type& rhs) noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Built-in type representation.
|
||||
*/
|
||||
struct primitive_type : public type
|
||||
{
|
||||
/// Type name.
|
||||
const std::string type_name;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* \param type_name Type name.
|
||||
* \param byte_size The type size in bytes.
|
||||
*/
|
||||
primitive_type(const std::string& type_name, const std::size_t byte_size);
|
||||
|
||||
bool operator==(const primitive_type& that) const noexcept;
|
||||
bool operator!=(const primitive_type& that) const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Typed pointer.
|
||||
*/
|
||||
struct pointer_type : public type
|
||||
{
|
||||
/// Pointer target type.
|
||||
std::shared_ptr<const type> base_type;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* \param base_type Pointer target type.
|
||||
* \param byte_size The type size in bytes.
|
||||
*/
|
||||
pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size);
|
||||
|
||||
bool operator==(const pointer_type& that) const noexcept;
|
||||
bool operator!=(const pointer_type& that) const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Type of a procedure.
|
||||
*/
|
||||
struct procedure_type : public type
|
||||
{
|
||||
/// Argument types.
|
||||
std::vector<std::shared_ptr<const type>> arguments;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* \param arguments Argument types.
|
||||
* \param byte_size Function pointer size.
|
||||
*/
|
||||
procedure_type(std::vector<std::shared_ptr<const type>> arguments, const std::size_t byte_size);
|
||||
|
||||
bool operator==(const procedure_type& that) const noexcept;
|
||||
bool operator!=(const procedure_type& that) const noexcept;
|
||||
};
|
||||
|
||||
bool operator==(const type& lhs, const type& rhs) noexcept;
|
||||
bool operator!=(const type& lhs, const type& rhs) noexcept;
|
||||
|
||||
inline const primitive_type boolean_type{ "Boolean", 1 };
|
||||
inline const primitive_type int_type{ "Int", 4 };
|
||||
}
|
462
package-lock.json
generated
Normal file
462
package-lock.json
generated
Normal file
@ -0,0 +1,462 @@
|
||||
{
|
||||
"name": "elna",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "elna",
|
||||
"version": "1.0.0",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"chalk": "^5.3.0",
|
||||
"glob": "^10.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||
"dependencies": {
|
||||
"string-width": "^5.1.2",
|
||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||
"strip-ansi": "^7.0.1",
|
||||
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
|
||||
"wrap-ansi": "^8.1.0",
|
||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
|
||||
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
||||
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.0",
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz",
|
||||
"integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^3.1.2",
|
||||
"minimatch": "^9.0.4",
|
||||
"minipass": "^7.1.2",
|
||||
"path-scurry": "^1.11.1"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz",
|
||||
"integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "10.2.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
|
||||
"integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
|
||||
"engines": {
|
||||
"node": "14 || >=16.14"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
|
||||
"integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^10.2.0",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs": {
|
||||
"name": "string-width",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs": {
|
||||
"name": "strip-ansi",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/node-which"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.1.0",
|
||||
"string-width": "^5.0.1",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs": {
|
||||
"name": "wrap-ansi",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
package.json
Normal file
17
package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "elna",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "tools/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node tools/cross.js",
|
||||
"test": "node tools/tester.js"
|
||||
},
|
||||
"author": "Eugen Wissner <belka@caraus.de>",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"chalk": "^5.3.0",
|
||||
"glob": "^10.4.1"
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
/**
|
||||
* File I/O that can be moved into more generic library when and if finished.
|
||||
*/
|
||||
module elna.extended;
|
||||
|
||||
struct File
|
||||
{
|
||||
@disable this(this);
|
||||
}
|
@ -1,660 +0,0 @@
|
||||
module elna.generator;
|
||||
|
||||
import core.stdc.stdio;
|
||||
import core.stdc.stdlib;
|
||||
import core.stdc.string;
|
||||
import elna.ir;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.mmappool;
|
||||
import tanya.format;
|
||||
|
||||
/// Unsigned program address.
|
||||
alias Elf64_Addr = void*;
|
||||
/// Unsigned file offset.
|
||||
alias Elf64_Off = ulong;
|
||||
/// Unsigned medium integer.
|
||||
alias Elf64_Half = ushort;
|
||||
/// Unsigned integer.
|
||||
alias Elf64_Word = uint;
|
||||
/// Signed integer.
|
||||
alias Elf64_Sword = int;
|
||||
/// Unsigned long integer.
|
||||
alias Elf64_Xword = ulong;
|
||||
/// Signed long integer.
|
||||
alias Elf64_Sxword = long;
|
||||
|
||||
enum size_t EI_INDENT = 16;
|
||||
|
||||
/**
|
||||
* File header.
|
||||
*/
|
||||
struct Elf64_Ehdr
|
||||
{
|
||||
/// ELF identification.
|
||||
ubyte[EI_INDENT] e_ident;
|
||||
/// Object file type.
|
||||
Elf64_Half e_type;
|
||||
/// Machine type.
|
||||
Elf64_Half e_machine;
|
||||
/// Object file version
|
||||
Elf64_Word e_version;
|
||||
/// Entry point address.
|
||||
Elf64_Addr e_entry;
|
||||
/// Program header offset.
|
||||
Elf64_Off e_phoff;
|
||||
/// Section header offset.
|
||||
Elf64_Off e_shoff;
|
||||
/// Processor-specific flags.
|
||||
Elf64_Word e_flags;
|
||||
/// ELF header size.
|
||||
Elf64_Half e_ehsize;
|
||||
/// Size of program header entry.
|
||||
Elf64_Half e_phentsize;
|
||||
/// Number of program header entries.
|
||||
Elf64_Half e_phnum;
|
||||
/// Size of section header entry.
|
||||
Elf64_Half e_shentsize;
|
||||
/// Number of section header entries.
|
||||
Elf64_Half e_shnum;
|
||||
/// Section name string table index.
|
||||
Elf64_Half e_shstrndx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Section header.
|
||||
*/
|
||||
struct Elf64_Shdr
|
||||
{
|
||||
/// Section name.
|
||||
Elf64_Word sh_name;
|
||||
/// Section type.
|
||||
Elf64_Word sh_type;
|
||||
/// Section attributes.
|
||||
Elf64_Xword sh_flags;
|
||||
/// Virtual address in memory.
|
||||
Elf64_Addr sh_addr;
|
||||
/// Offset in file.
|
||||
Elf64_Off sh_offset;
|
||||
/// Size of section.
|
||||
Elf64_Xword sh_size;
|
||||
/// Link to other section.
|
||||
Elf64_Word sh_link;
|
||||
/// Miscellaneous information.
|
||||
Elf64_Word sh_info;
|
||||
/// Address alignment boundary.
|
||||
Elf64_Xword sh_addralign;
|
||||
/// Size of entries, if section has table.
|
||||
Elf64_Xword sh_entsize;
|
||||
}
|
||||
|
||||
struct Elf64_Sym
|
||||
{
|
||||
/// Symbol name.
|
||||
Elf64_Word st_name;
|
||||
/// Type and Binding attributes.
|
||||
ubyte st_info;
|
||||
/// Reserved.
|
||||
ubyte st_other;
|
||||
/// Section table index.
|
||||
Elf64_Half st_shndx;
|
||||
/// Symbol value.
|
||||
Elf64_Addr st_value;
|
||||
/// Size of object (e.g., common).
|
||||
Elf64_Xword st_size;
|
||||
}
|
||||
|
||||
/// Section Types, sh_type.
|
||||
enum : Elf64_Word
|
||||
{
|
||||
/// Marks an unused section header.
|
||||
SHT_NULL = 0,
|
||||
/// Contains information defined by the program.
|
||||
SHT_PROGBITS = 1,
|
||||
/// Contains a linker symbol table.
|
||||
SHT_SYMTAB = 2,
|
||||
/// Contains a string table.
|
||||
SHT_STRTAB = 3,
|
||||
/// Contains “Rela” type relocation entries.
|
||||
SHT_RELA = 4,
|
||||
/// Contains a symbol hash table
|
||||
SHT_HASH = 5,
|
||||
/// Contains dynamic linking tables
|
||||
SHT_DYNAMIC = 6,
|
||||
/// Contains note information
|
||||
SHT_NOTE = 7,
|
||||
/// Contains uninitialized space; does not occupy any space in the file.
|
||||
SHT_NOBITS = 8,
|
||||
/// Contains "Rel" type relocation entries.
|
||||
SHT_REL = 9,
|
||||
/// Reserved.
|
||||
SHT_SHLIB = 10,
|
||||
/// Contains a dynamic loader symbol table.
|
||||
SHT_DYNSYM = 11,
|
||||
/// Environment-specific use.
|
||||
SHT_LOOS = 0x60000000,
|
||||
SHT_HIOS = 0x6FFFFFFF,
|
||||
/// Processor-specific use.
|
||||
SHT_LOPROC = 0x70000000,
|
||||
SHT_HIPROC = 0x7FFFFFFF,
|
||||
}
|
||||
|
||||
/**
|
||||
* Section Attributes, sh_flags.
|
||||
*/
|
||||
enum : Elf64_Xword
|
||||
{
|
||||
/// Section contains writable data.
|
||||
SHF_WRITE = 0x1,
|
||||
/// Section is allocated in memory image of program.
|
||||
SHF_ALLOC = 0x2,
|
||||
/// Section contains executable instructions.
|
||||
SHF_EXECINSTR = 0x4,
|
||||
/// Environment-specific use.
|
||||
SHF_MASKOS = 0x0F000000,
|
||||
/// Processor-specific use.
|
||||
SHF_MASKPROC = 0xF0000000,
|
||||
}
|
||||
|
||||
enum : Elf64_Word
|
||||
{
|
||||
/// Not visible outside the object file.
|
||||
STB_LOCAL = 0,
|
||||
/// Global symbol, visible to all object files.
|
||||
STB_GLOBAL = 1,
|
||||
/// Global scope, but with lower precedence than global symbols.
|
||||
STB_WEAK = 2,
|
||||
/// Environment-specific use.
|
||||
STB_LOOS = 10,
|
||||
STB_HIOS = 12,
|
||||
/// Processor-specific use.
|
||||
STB_LOPROC = 13,
|
||||
STB_HIPROC = 15,
|
||||
}
|
||||
|
||||
enum : Elf64_Word
|
||||
{
|
||||
/// No type specified (e.g., an absolute symbol).
|
||||
STT_NOTYPE = 0,
|
||||
/// Data object.
|
||||
STT_OBJECT = 1,
|
||||
/// Function entry point.
|
||||
STT_FUNC = 2,
|
||||
/// Symbol is associated with a section.
|
||||
STT_SECTION = 3,
|
||||
/// Source file associated with the object file.
|
||||
STT_FILE = 4,
|
||||
/// Environment-specific use.
|
||||
STT_LOOS = 10,
|
||||
STT_HIOS = 12,
|
||||
/// Processor-specific use.
|
||||
STT_LOPROC = 13,
|
||||
STT_HIPROC = 15,
|
||||
}
|
||||
|
||||
Elf64_Ehdr makeFileHeader(Elf64_Off sectionHeaderOffset,
|
||||
Elf64_Half sectionHeaderCount,
|
||||
Elf64_Half stringIndex) @nogc
|
||||
{
|
||||
Elf64_Ehdr header;
|
||||
|
||||
// Magic number.
|
||||
header.e_ident[0] = '\x7f';
|
||||
header.e_ident[1] = 'E';
|
||||
header.e_ident[2] = 'L';
|
||||
header.e_ident[3] = 'F';
|
||||
|
||||
// File class.
|
||||
header.e_ident[4] = EI_CLASS.ELFCLASS64;
|
||||
|
||||
// Data encoding.
|
||||
header.e_ident[5] = EI_DATA.ELFDATA2LSB;
|
||||
|
||||
// Version.
|
||||
header.e_ident[6] = EV_CURRENT;
|
||||
|
||||
// OS/ABI identification.
|
||||
header.e_ident[7] = EI_OSABI.ELFOSABI_SYSV;
|
||||
|
||||
// ABI version.
|
||||
header.e_ident[8] = 0;
|
||||
|
||||
// Size of e_ident[].
|
||||
header.e_ident[15] = 0;
|
||||
|
||||
header.e_type = ET_REL;
|
||||
header.e_machine = 0x3e; // EM_X86_64: AMD x86-64 architecture
|
||||
header.e_version = EV_CURRENT;
|
||||
header.e_entry = null;
|
||||
header.e_phoff = 0;
|
||||
header.e_shoff = sectionHeaderOffset;
|
||||
header.e_flags = 0;
|
||||
header.e_ehsize = Elf64_Ehdr.sizeof;
|
||||
header.e_phentsize = 0;
|
||||
header.e_phnum = 0;
|
||||
header.e_shentsize = Elf64_Shdr.sizeof;
|
||||
header.e_shnum = sectionHeaderCount;
|
||||
header.e_shstrndx = stringIndex;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
enum char[33] sectionStringTable = "\0.symtab\0.strtab\0.shstrtab\0.text\0";
|
||||
|
||||
Elf64_Shdr makeTextHeader(Elf64_Off offset, Elf64_Xword size) @nogc
|
||||
{
|
||||
Elf64_Shdr table;
|
||||
|
||||
table.sh_name = 0x1b;
|
||||
table.sh_type = SHT_PROGBITS;
|
||||
table.sh_flags = SHF_EXECINSTR | SHF_ALLOC;
|
||||
table.sh_addr = null;
|
||||
table.sh_offset = offset;
|
||||
table.sh_size = size;
|
||||
table.sh_link = SHN_UNDEF;
|
||||
table.sh_info = 0;
|
||||
table.sh_addralign = 1;
|
||||
table.sh_entsize = 0;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
Elf64_Shdr makeDataHeader(Elf64_Off offset, Elf64_Xword size) @nogc
|
||||
{
|
||||
Elf64_Shdr table;
|
||||
|
||||
table.sh_name = 0x21;
|
||||
table.sh_type = SHT_PROGBITS;
|
||||
table.sh_flags = SHF_WRITE | SHF_ALLOC;
|
||||
table.sh_addr = null;
|
||||
table.sh_offset = offset;
|
||||
table.sh_size = size;
|
||||
table.sh_link = SHN_UNDEF;
|
||||
table.sh_info = 0;
|
||||
table.sh_addralign = 1;
|
||||
table.sh_entsize = 0;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
Elf64_Shdr makeSymtableHeader(Elf64_Off offset, Elf64_Xword size, Elf64_Word entriesCount) @nogc
|
||||
{
|
||||
Elf64_Shdr table;
|
||||
|
||||
table.sh_name = 0x01;
|
||||
table.sh_type = SHT_SYMTAB;
|
||||
table.sh_flags = 0;
|
||||
table.sh_addr = null;
|
||||
table.sh_offset = offset;
|
||||
table.sh_size = size;
|
||||
table.sh_link = 0x03; // String table used by entries in this section.
|
||||
table.sh_info = entriesCount;
|
||||
table.sh_addralign = 8;
|
||||
table.sh_entsize = Elf64_Sym.sizeof;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
Elf64_Shdr makeStringHeader(Elf64_Word stringIndex, Elf64_Off offset, Elf64_Xword size) @nogc
|
||||
{
|
||||
Elf64_Shdr table;
|
||||
|
||||
table.sh_name = stringIndex;
|
||||
table.sh_type = SHT_STRTAB;
|
||||
table.sh_flags = 0;
|
||||
table.sh_addr = null;
|
||||
table.sh_offset = offset;
|
||||
table.sh_size = size;
|
||||
table.sh_link = SHN_UNDEF;
|
||||
table.sh_info = 0;
|
||||
table.sh_addralign = 1;
|
||||
table.sh_entsize = 0;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
Elf64_Shdr makeInitialHeader() @nogc
|
||||
{
|
||||
Elf64_Shdr table;
|
||||
|
||||
table.sh_name = 0;
|
||||
table.sh_type = SHT_NULL;
|
||||
table.sh_flags = 0;
|
||||
table.sh_addr = null;
|
||||
table.sh_offset = 0;
|
||||
table.sh_size = 0;
|
||||
table.sh_link = SHN_UNDEF;
|
||||
table.sh_info = 0;
|
||||
table.sh_addralign = 0;
|
||||
table.sh_entsize = 0;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
Elf64_Sym makeInitialSymTable() @nogc
|
||||
{
|
||||
Elf64_Sym table;
|
||||
|
||||
table.st_name = 0;
|
||||
table.st_info = 0;
|
||||
table.st_other = 0;
|
||||
table.st_shndx = 0;
|
||||
table.st_value = null;
|
||||
table.st_size = 0;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
Elf64_Sym makeMainSymTable(Elf64_Half textIndex) @nogc
|
||||
{
|
||||
Elf64_Sym table;
|
||||
|
||||
table.st_name = 0x01;
|
||||
table.st_info = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC);
|
||||
table.st_other = 0;
|
||||
table.st_shndx = textIndex;
|
||||
table.st_value = null;
|
||||
table.st_size = 0;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
ubyte ELF32_ST_BIND(ubyte i) @nogc nothrow pure @safe
|
||||
{
|
||||
return i >> 4;
|
||||
}
|
||||
|
||||
ubyte ELF32_ST_TYPE(ubyte i) @nogc nothrow pure @safe
|
||||
{
|
||||
return i & 0xf;
|
||||
}
|
||||
|
||||
ubyte ELF32_ST_INFO(ubyte b, ubyte t) @nogc nothrow pure @safe
|
||||
{
|
||||
return cast(ubyte) ((b << 4) + (t & 0xf));
|
||||
}
|
||||
|
||||
/// Special Section Indices.
|
||||
enum : Elf64_Half
|
||||
{
|
||||
/// Used to mark an undefined or meaningless section reference.
|
||||
SHN_UNDEF = 0,
|
||||
/// Processor-specific use.
|
||||
SHN_LOPROC = 0xFF00,
|
||||
SHN_HIPROC = 0xFF1F,
|
||||
/// Environment-specific use.
|
||||
SHN_LOOS = 0xFF20,
|
||||
SHN_HIOS = 0xFF3F,
|
||||
/// Indicates that the corresponding reference is an absolute value.
|
||||
SHN_ABS = 0xFFF1,
|
||||
/**
|
||||
* Indicates a symbol that has been declared as a common block (Fortran
|
||||
* COMMON or C tentative declaration).
|
||||
*/
|
||||
SHN_COMMON = 0xFFF2,
|
||||
}
|
||||
|
||||
/**
|
||||
* Object File Classes, e_ident[EI_CLASS].
|
||||
*/
|
||||
enum EI_CLASS : ubyte
|
||||
{
|
||||
/// 32-bit objects.
|
||||
ELFCLASS32 = 1,
|
||||
/// 64-bit objects.
|
||||
ELFCLASS64 = 2,
|
||||
}
|
||||
|
||||
enum ubyte EV_CURRENT = 1;
|
||||
|
||||
/**
|
||||
* Data Encodings, e_ident[EI_DATA].
|
||||
*/
|
||||
enum EI_DATA : ubyte
|
||||
{
|
||||
/// Object file data structures are little-endian.
|
||||
ELFDATA2LSB = 1,
|
||||
/// Object file data structures are big-endian.
|
||||
ELFDATA2MSB = 2,
|
||||
}
|
||||
|
||||
/**
|
||||
* Operating System and ABI Identifiers, e_ident[EI_OSABI].
|
||||
*/
|
||||
enum EI_OSABI : ubyte
|
||||
{
|
||||
/// System V ABI.
|
||||
ELFOSABI_SYSV = 0,
|
||||
/// HP-UX operating system.
|
||||
ELFOSABI_HPUX = 1,
|
||||
/// Standalone (embedded) application.
|
||||
ELFOSABI_STANDALONE = 255,
|
||||
}
|
||||
|
||||
enum : Elf64_Half
|
||||
{
|
||||
ET_NONE = 0, /// No file type.
|
||||
ET_REL = 1, /// Relocatable object file.
|
||||
ET_EXEC = 2, /// Executable file.
|
||||
ET_DYN = 3, /// Shared object file.
|
||||
ET_CORE = 4, /// Core file.
|
||||
ET_LOOS = 0xFE00, /// Environment-specific use.
|
||||
ET_HIOS = 0xFEFF,
|
||||
ET_LOPROC = 0xFF00, /// Processor-specific use.
|
||||
ET_HIPROC = 0xFFFF,
|
||||
}
|
||||
|
||||
private size_t pad(size_t value) @nogc
|
||||
{
|
||||
return (value / 8 + 1) * 8;
|
||||
}
|
||||
|
||||
struct Symbol
|
||||
{
|
||||
String name;
|
||||
Array!ubyte instructions;
|
||||
}
|
||||
|
||||
/*
|
||||
.text
|
||||
.globl main
|
||||
.type main, @function
|
||||
main:
|
||||
movl $3, %eax
|
||||
ret
|
||||
*/
|
||||
immutable ubyte[] instructions = [
|
||||
// Opcode of pushq is “0x50 + r”, where “r” is the register opcode.
|
||||
// Register opcode of %rbq is 5.
|
||||
0x50 + 5, // push% %rbp
|
||||
0x48, 0x89, 0xe5, // movq %rsp, %rbp
|
||||
|
||||
0xb8, 0x03, 0x00, 0x00, 0x00, // movl $3, %eax
|
||||
|
||||
// Epilogue.
|
||||
0x48, 0x89, 0xec, // movq %rbp, %rsp
|
||||
0x58 + 5, // popq %rbp
|
||||
0xc3, // ret
|
||||
];
|
||||
|
||||
void writeObject(Definition ast, String outputFilename) @nogc
|
||||
{
|
||||
auto handle = fopen(outputFilename.toStringz, "wb");
|
||||
|
||||
if (handle is null)
|
||||
{
|
||||
perror("writing sample");
|
||||
return;
|
||||
}
|
||||
scope (exit)
|
||||
{
|
||||
fclose(handle);
|
||||
}
|
||||
|
||||
size_t currentOffset = Elf64_Ehdr.sizeof;
|
||||
Array!Elf64_Shdr sectionHeaders;
|
||||
Array!Elf64_Sym symbolEntries;
|
||||
|
||||
// Prologue
|
||||
Array!ubyte asmTemplate = Array!ubyte(cast(ubyte[]) [
|
||||
// Opcode of pushq is “0x50 + r”, where “r” is the register opcode.
|
||||
// Register opcode of %rbq is 5.
|
||||
0x50 + 5, // pushq %rbp
|
||||
0x48, 0x89, 0xe5, // movq %rsp, %rbp
|
||||
]);
|
||||
int i = 1;
|
||||
foreach (statement; ast.statements[])
|
||||
{
|
||||
if ((cast(Number) statement.subroutine.lhs) !is null)
|
||||
{
|
||||
// Opcode of mov is “0xb8 + r”, where “r” is the register opcode.
|
||||
// Register opcode of %eax is 0.
|
||||
asmTemplate.insertBack(cast(ubyte) 0xb8); // movl $x, %eax; where $x is a number.
|
||||
asmTemplate.insertBack((cast(ubyte*) &(cast(Number) statement.subroutine.lhs).value)[0 .. int.sizeof]);
|
||||
}
|
||||
else if ((cast(Variable) statement.subroutine.lhs) !is null)
|
||||
{
|
||||
// movl -x(%rbp), %ebx; where x is a number.
|
||||
asmTemplate.insertBack(cast(ubyte[]) [0x8b, 0x45]);
|
||||
const disposition = (cast(Variable) statement.subroutine.lhs).counter * (-4);
|
||||
asmTemplate.insertBack((cast(ubyte*) &disposition)[0 .. 1]);
|
||||
}
|
||||
if ((cast(Number) statement.subroutine.rhs) !is null)
|
||||
{
|
||||
// Opcode of mov is “0xb8 + r”, where “r” is the register opcode.
|
||||
// Register opcode of %ebx is 3.
|
||||
asmTemplate.insertBack(cast(ubyte) 0xbb); // movl $x, %ebx; where $x is a number.
|
||||
asmTemplate.insertBack((cast(ubyte*) &(cast(Number) statement.subroutine.rhs).value)[0 .. int.sizeof]);
|
||||
}
|
||||
else if ((cast(Variable) statement.subroutine.rhs) !is null)
|
||||
{
|
||||
// movl -x(%rbp), %ebx; where x is a number.
|
||||
asmTemplate.insertBack(cast(ubyte[]) [0x8b, 0x5d]);
|
||||
const disposition = (cast(Variable) statement.subroutine.rhs).counter * (-4);
|
||||
asmTemplate.insertBack((cast(ubyte*) &disposition)[0 .. 1]);
|
||||
}
|
||||
// Calculate the result and assign it to a variable on the stack.
|
||||
asmTemplate.insertBack(cast(ubyte[]) [0x01, 0xd8]); // add %ebx, %eax
|
||||
|
||||
asmTemplate.insertBack(cast(ubyte[]) [0x89, 0x45]); // movl %eax, -x(%rbp); where x is a number.
|
||||
const disposition = i * (-4);
|
||||
asmTemplate.insertBack((cast(ubyte*) &disposition)[0 .. 1]);
|
||||
++i;
|
||||
}
|
||||
// Epilogue.
|
||||
asmTemplate.insertBack(cast(ubyte[]) [
|
||||
0x48, 0x89, 0xec, // movq %rbp, %rsp
|
||||
0x58 + 5, // popq %rbp
|
||||
0xc3, // ret
|
||||
]);
|
||||
|
||||
Symbol[1] symbols = [Symbol(String("main"), asmTemplate)];
|
||||
|
||||
sectionHeaders.insertBack(makeInitialHeader());
|
||||
symbolEntries.insertBack(makeInitialSymTable());
|
||||
|
||||
String stringTable = String("\0");
|
||||
foreach (symbol; symbols[])
|
||||
{
|
||||
stringTable.insertBack(symbol.name[]);
|
||||
stringTable.insertBack('\0');
|
||||
|
||||
sectionHeaders.insertBack(makeTextHeader(currentOffset, symbol.instructions.length));
|
||||
currentOffset = pad(currentOffset + symbol.instructions.length);
|
||||
|
||||
symbolEntries.insertBack(makeMainSymTable(cast(Elf64_Half) (sectionHeaders.length - 1)));
|
||||
}
|
||||
|
||||
const symbolTableSize = (symbols.length + 1) * Elf64_Sym.sizeof;
|
||||
sectionHeaders.insertBack(makeSymtableHeader(currentOffset, symbolTableSize, symbols.length));
|
||||
currentOffset += symbolTableSize;
|
||||
|
||||
sectionHeaders.insertBack(makeStringHeader(0x09, currentOffset, stringTable.length));
|
||||
currentOffset += stringTable.length;
|
||||
|
||||
sectionHeaders.insertBack(makeStringHeader(0x11, currentOffset, sectionStringTable.length));
|
||||
currentOffset = pad(currentOffset + sectionStringTable.length);
|
||||
|
||||
auto fileHeader = makeFileHeader(currentOffset, 5, 4);
|
||||
|
||||
version (none)
|
||||
{
|
||||
printf("%.2x\n", cast(uint) currentOffset);
|
||||
}
|
||||
ubyte[8] padding = 0;
|
||||
size_t codeLength = stringTable.length + sectionStringTable.length;
|
||||
|
||||
fwrite(&fileHeader, 8, Elf64_Ehdr.sizeof / 8, handle);
|
||||
foreach (symbol; symbols[])
|
||||
{
|
||||
immutable size_t instructionsLength = pad(symbol.instructions.length);
|
||||
fwrite(symbol.instructions.get.ptr, 1, symbol.instructions.length, handle);
|
||||
fwrite(padding.ptr, 1, instructionsLength - symbol.instructions.length, handle);
|
||||
codeLength += instructionsLength;
|
||||
}
|
||||
fwrite(symbolEntries.get.ptr, Elf64_Sym.sizeof, symbolEntries.length, handle);
|
||||
fwrite(stringTable.get.ptr, 1, stringTable.length, handle);
|
||||
fwrite(sectionStringTable.ptr, 1, sectionStringTable.length, handle);
|
||||
fwrite(padding.ptr, pad(codeLength) - codeLength, 1, handle);
|
||||
fwrite(sectionHeaders.get.ptr, Elf64_Shdr.sizeof, sectionHeaders.length, handle);
|
||||
}
|
||||
|
||||
String generate(Definition ast) @nogc
|
||||
{
|
||||
// Prologue
|
||||
String asmTemplate = ".text
|
||||
.globl main
|
||||
.type main, @function
|
||||
main:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
";
|
||||
|
||||
/* Allocate space on the stack for local variables.
|
||||
asmTemplate.insertBack(" sub $");
|
||||
asmTemplate.insertBack(format!"{}"(ast.statements.length)[]);
|
||||
asmTemplate.insertBack(", $esp\n"); */
|
||||
|
||||
int i = 1;
|
||||
foreach (statement; ast.statements[])
|
||||
{
|
||||
if ((cast(Number) statement.subroutine.lhs) !is null)
|
||||
{
|
||||
asmTemplate.insertBack(" movl $");
|
||||
asmTemplate.insertBack(format!"{}"((cast(Number) statement.subroutine.lhs).value)[]);
|
||||
asmTemplate.insertBack(", %eax\n");
|
||||
}
|
||||
else if ((cast(Variable) statement.subroutine.lhs) !is null)
|
||||
{
|
||||
asmTemplate.insertBack(" movl -");
|
||||
asmTemplate.insertBack(format!"{}"((cast(Variable) statement.subroutine.lhs).counter * 4)[]);
|
||||
asmTemplate.insertBack("(%rbp), %eax\n");
|
||||
}
|
||||
if ((cast(Number) statement.subroutine.rhs) !is null)
|
||||
{
|
||||
asmTemplate.insertBack(" movl $");
|
||||
asmTemplate.insertBack(format!"{}"((cast(Number) statement.subroutine.rhs).value)[]);
|
||||
asmTemplate.insertBack(", %ebx\n");
|
||||
}
|
||||
else if ((cast(Variable) statement.subroutine.rhs) !is null)
|
||||
{
|
||||
asmTemplate.insertBack(" movl -");
|
||||
asmTemplate.insertBack(format!"{}"((cast(Variable) statement.subroutine.rhs).counter * 4)[]);
|
||||
asmTemplate.insertBack("(%rbp), %ebx\n");
|
||||
}
|
||||
// Calculate the result and assign it to a variable on the stack.
|
||||
asmTemplate.insertBack(" add %ebx, %eax\n");
|
||||
asmTemplate.insertBack(" movl %eax, -");
|
||||
asmTemplate.insertBack(format!"{}"(i * 4)[]);
|
||||
asmTemplate.insertBack("(%rbp)\n");
|
||||
++i;
|
||||
}
|
||||
|
||||
// Epilogue.
|
||||
asmTemplate.insertBack(" movq %rbp, %rsp
|
||||
popq %rbp
|
||||
ret
|
||||
");
|
||||
|
||||
return asmTemplate;
|
||||
}
|
144
source/elna/ir.d
144
source/elna/ir.d
@ -1,144 +0,0 @@
|
||||
module elna.ir;
|
||||
|
||||
import parser = elna.parser;
|
||||
import tanya.container.array;
|
||||
import tanya.container.hashtable;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.memory.mmappool;
|
||||
|
||||
/**
|
||||
* Definition.
|
||||
*/
|
||||
class Definition
|
||||
{
|
||||
char[] identifier;
|
||||
Array!Statement statements;
|
||||
Array!VariableDeclaration variableDeclarations;
|
||||
}
|
||||
|
||||
class Statement
|
||||
{
|
||||
Subroutine subroutine;
|
||||
}
|
||||
|
||||
abstract class Expression
|
||||
{
|
||||
}
|
||||
|
||||
class Number : Expression
|
||||
{
|
||||
int value;
|
||||
}
|
||||
|
||||
class Variable : Expression
|
||||
{
|
||||
size_t counter;
|
||||
}
|
||||
|
||||
class VariableDeclaration
|
||||
{
|
||||
String identifier;
|
||||
}
|
||||
|
||||
class Subroutine
|
||||
{
|
||||
Expression lhs, rhs;
|
||||
}
|
||||
|
||||
private Number transformNumber(parser.Number number) @nogc
|
||||
{
|
||||
return MmapPool.instance.make!Number(number.value);
|
||||
}
|
||||
|
||||
private Variable transformSubroutine(parser.Subroutine subroutine,
|
||||
ref Array!Statement statements,
|
||||
ref HashTable!(String, int) constants) @nogc
|
||||
{
|
||||
auto target = MmapPool.instance.make!Subroutine;
|
||||
target.lhs = transformExpression(subroutine.lhs, statements, constants);
|
||||
target.rhs = transformExpression(subroutine.rhs, statements, constants);
|
||||
|
||||
auto newStatement = MmapPool.instance.make!Statement;
|
||||
newStatement.subroutine = target;
|
||||
statements.insertBack(newStatement);
|
||||
|
||||
auto newVariable = MmapPool.instance.make!Variable;
|
||||
newVariable.counter = statements.length;
|
||||
|
||||
return newVariable;
|
||||
}
|
||||
|
||||
private Expression transformExpression(parser.Expression expression,
|
||||
ref Array!Statement statements,
|
||||
ref HashTable!(String, int) constants) @nogc
|
||||
{
|
||||
if ((cast(parser.Number) expression) !is null)
|
||||
{
|
||||
auto numberExpression = MmapPool.instance.make!Number;
|
||||
numberExpression.value = (cast(parser.Number) expression).value;
|
||||
|
||||
return numberExpression;
|
||||
}
|
||||
if ((cast(parser.Variable) expression) !is null)
|
||||
{
|
||||
auto numberExpression = MmapPool.instance.make!Number;
|
||||
numberExpression.value = constants[(cast(parser.Variable) expression).identifier];
|
||||
|
||||
return numberExpression;
|
||||
}
|
||||
else if ((cast(parser.Subroutine) expression) !is null)
|
||||
{
|
||||
return transformSubroutine(cast(parser.Subroutine) expression, statements, constants);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Expression transformStatement(parser.Statement statement,
|
||||
ref Array!Statement statements,
|
||||
ref HashTable!(String, int) constants) @nogc
|
||||
{
|
||||
if ((cast(parser.BangStatement) statement) !is null)
|
||||
{
|
||||
return transformExpression((cast(parser.BangStatement) statement).expression, statements, constants);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
HashTable!(String, int) transformConstants(ref Array!(parser.Definition) definitions) @nogc
|
||||
{
|
||||
typeof(return) constants;
|
||||
|
||||
foreach (definition; definitions[])
|
||||
{
|
||||
constants[definition.identifier] = definition.number.value;
|
||||
}
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
Array!VariableDeclaration transformVariableDeclarations(ref Array!(parser.VariableDeclaration) variableDeclarations)
|
||||
@nogc
|
||||
{
|
||||
typeof(return) variables;
|
||||
|
||||
foreach (ref variableDeclaration; variableDeclarations)
|
||||
{
|
||||
auto newDeclaration = MmapPool.instance.make!VariableDeclaration;
|
||||
newDeclaration.identifier = variableDeclaration.identifier;
|
||||
variables.insertBack(newDeclaration);
|
||||
}
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
Definition transform(parser.Block block) @nogc
|
||||
{
|
||||
auto target = MmapPool.instance.make!Definition;
|
||||
auto constants = transformConstants(block.definitions);
|
||||
|
||||
transformStatement(block.statement, target.statements, constants);
|
||||
target.variableDeclarations = transformVariableDeclarations(block.variableDeclarations);
|
||||
|
||||
return target;
|
||||
}
|
@ -1,252 +0,0 @@
|
||||
module elna.lexer;
|
||||
|
||||
import core.stdc.stdlib;
|
||||
import core.stdc.ctype;
|
||||
import core.stdc.string;
|
||||
import elna.result;
|
||||
import std.range;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.mmappool;
|
||||
|
||||
struct Token
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
number,
|
||||
subroutine, // Operator.
|
||||
let,
|
||||
identifier,
|
||||
equals,
|
||||
var,
|
||||
semicolon,
|
||||
leftParen,
|
||||
rightParen,
|
||||
bang,
|
||||
dot,
|
||||
comma,
|
||||
}
|
||||
|
||||
union Value
|
||||
{
|
||||
int number;
|
||||
String identifier;
|
||||
}
|
||||
|
||||
private Type type;
|
||||
private Value value_;
|
||||
private Position position_;
|
||||
|
||||
@disable this();
|
||||
|
||||
this(Type type, Position position) @nogc nothrow pure @safe
|
||||
{
|
||||
this.type = type;
|
||||
this.position_ = position;
|
||||
}
|
||||
|
||||
this(Type type, int value, Position position) @nogc nothrow pure @trusted
|
||||
in (type == Type.number)
|
||||
{
|
||||
this(type, position);
|
||||
this.value_.number = value;
|
||||
}
|
||||
|
||||
this()(Type type, auto ref String value, Position position)
|
||||
@nogc nothrow pure @trusted
|
||||
in (type == Type.identifier)
|
||||
{
|
||||
this(type, position);
|
||||
this.value_.identifier = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* type = Expected type.
|
||||
*
|
||||
* Returns: Whether this token is of the expected type.
|
||||
*/
|
||||
bool ofType(Type type) const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.type == type;
|
||||
}
|
||||
|
||||
@property auto value(Type type)() @nogc nothrow pure @trusted
|
||||
in (ofType(type))
|
||||
{
|
||||
static if (type == Type.number)
|
||||
{
|
||||
return this.value_.number;
|
||||
}
|
||||
else static if (type == Type.identifier)
|
||||
{
|
||||
return this.value_.identifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(false, "This type doesn't have a value");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: The token position in the source text.
|
||||
*/
|
||||
@property const(Position) position() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.position_;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Range over the source text that keeps track of the current position.
|
||||
*/
|
||||
struct Source
|
||||
{
|
||||
char[] buffer;
|
||||
Position position;
|
||||
|
||||
this(char[] buffer) @nogc nothrow pure @safe
|
||||
{
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
@disable this();
|
||||
|
||||
bool empty() @nogc nothrow pure @safe
|
||||
{
|
||||
return this.length == 0;
|
||||
}
|
||||
|
||||
char front() @nogc nothrow pure @safe
|
||||
in (!empty)
|
||||
{
|
||||
return this.buffer[0];
|
||||
}
|
||||
|
||||
void popFront() @nogc nothrow pure @safe
|
||||
in (!empty)
|
||||
{
|
||||
this.buffer = buffer[1 .. $];
|
||||
++this.position.column;
|
||||
}
|
||||
|
||||
void breakLine() @nogc nothrow pure @safe
|
||||
in (!empty)
|
||||
{
|
||||
this.buffer = buffer[1 .. $];
|
||||
++this.position.line;
|
||||
this.position.column = 1;
|
||||
}
|
||||
|
||||
@property size_t length() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.buffer.length;
|
||||
}
|
||||
|
||||
char opIndex(size_t index) @nogc nothrow pure @safe
|
||||
in (index < length)
|
||||
{
|
||||
return this.buffer[index];
|
||||
}
|
||||
|
||||
char[] opSlice(size_t i, size_t j) @nogc nothrow pure @safe
|
||||
in
|
||||
{
|
||||
assert(i <= j);
|
||||
assert(j <= length);
|
||||
}
|
||||
do
|
||||
{
|
||||
return this.buffer[i .. j];
|
||||
}
|
||||
}
|
||||
|
||||
Array!Token lex(char[] buffer) @nogc
|
||||
{
|
||||
Array!Token tokens;
|
||||
auto source = Source(buffer);
|
||||
|
||||
while (!source.empty)
|
||||
{
|
||||
if (source.front == ' ')
|
||||
{
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front >= '0' && source.front <= '9') // Multi-digit.
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.number, source.front - '0', source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == '=')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.equals, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == '(')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.leftParen, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == ')')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.rightParen, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == ';')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.semicolon, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == ',')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.comma, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == '!')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.bang, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == '.')
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.dot, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (isalpha(source.front))
|
||||
{
|
||||
size_t i = 1;
|
||||
while (i < source.length && isalpha(source[i]))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
if (source[0 .. i] == "const")
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.let, source.position));
|
||||
}
|
||||
else if (source[0 .. i] == "var")
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.var, source.position));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto identifier = String(source[0 .. i]);
|
||||
tokens.insertBack(Token(Token.Type.identifier, identifier, source.position));
|
||||
}
|
||||
source.popFrontN(i);
|
||||
}
|
||||
else if (source.front == '+') // Multi-character, random special characters.
|
||||
{
|
||||
tokens.insertBack(Token(Token.Type.subroutine, source.position));
|
||||
source.popFront;
|
||||
}
|
||||
else if (source.front == '\n')
|
||||
{
|
||||
source.breakLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
return typeof(tokens)(); // Error.
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
@ -1,269 +0,0 @@
|
||||
module elna.parser;
|
||||
|
||||
import elna.lexer;
|
||||
import elna.result;
|
||||
import tanya.container.array;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.memory.mmappool;
|
||||
|
||||
/**
|
||||
* Constant definition.
|
||||
*/
|
||||
class Definition
|
||||
{
|
||||
Number number;
|
||||
String identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable declaration.
|
||||
*/
|
||||
class VariableDeclaration
|
||||
{
|
||||
String identifier;
|
||||
}
|
||||
|
||||
abstract class Statement
|
||||
{
|
||||
}
|
||||
|
||||
class BangStatement : Statement
|
||||
{
|
||||
Expression expression;
|
||||
}
|
||||
|
||||
class Block
|
||||
{
|
||||
Array!Definition definitions;
|
||||
Array!VariableDeclaration variableDeclarations;
|
||||
Statement statement;
|
||||
}
|
||||
|
||||
abstract class Expression
|
||||
{
|
||||
}
|
||||
|
||||
class Number : Expression
|
||||
{
|
||||
int value;
|
||||
}
|
||||
|
||||
class Variable : Expression
|
||||
{
|
||||
String identifier;
|
||||
}
|
||||
|
||||
class Subroutine : Expression
|
||||
{
|
||||
Expression lhs, rhs;
|
||||
}
|
||||
|
||||
private Result!Expression parseExpression(ref Array!(Token).Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected expression, got end of stream")
|
||||
{
|
||||
if (tokens.front.ofType(Token.Type.number))
|
||||
{
|
||||
auto number = MmapPool.instance.make!Number;
|
||||
number.value = tokens.front.value!(Token.Type.number);
|
||||
tokens.popFront;
|
||||
return Result!Expression(number);
|
||||
}
|
||||
else if (tokens.front.ofType(Token.Type.identifier))
|
||||
{
|
||||
auto variable = MmapPool.instance.make!Variable;
|
||||
variable.identifier = tokens.front.value!(Token.Type.identifier);
|
||||
tokens.popFront;
|
||||
return Result!Expression(variable);
|
||||
}
|
||||
else if (tokens.front.ofType(Token.Type.subroutine))
|
||||
{
|
||||
auto subroutine = MmapPool.instance.make!Subroutine;
|
||||
tokens.popFront;
|
||||
auto expression = parseExpression(tokens);
|
||||
if (expression.valid)
|
||||
{
|
||||
subroutine.lhs = expression.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Expression("Expected left-hand side to be an expression", tokens.front.position);
|
||||
}
|
||||
expression = parseExpression(tokens);
|
||||
if (expression.valid)
|
||||
{
|
||||
subroutine.rhs = expression.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Expression("Expected left-hand side to be an expression", tokens.front.position);
|
||||
}
|
||||
return Result!Expression(subroutine);
|
||||
}
|
||||
else if (tokens.front.ofType(Token.Type.leftParen))
|
||||
{
|
||||
tokens.popFront;
|
||||
|
||||
auto expression = parseExpression(tokens);
|
||||
|
||||
tokens.popFront;
|
||||
return expression;
|
||||
}
|
||||
return Result!Expression("Expected an expression", tokens.front.position);
|
||||
}
|
||||
|
||||
private Result!Definition parseDefinition(ref Array!Token.Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected definition, got end of stream")
|
||||
{
|
||||
auto definition = MmapPool.instance.make!Definition;
|
||||
definition.identifier = tokens.front.value!(Token.Type.identifier); // Copy.
|
||||
|
||||
tokens.popFront();
|
||||
tokens.popFront(); // Skip the equals sign.
|
||||
|
||||
if (tokens.front.ofType(Token.Type.number))
|
||||
{
|
||||
auto number = MmapPool.instance.make!Number;
|
||||
number.value = tokens.front.value!(Token.Type.number);
|
||||
definition.number = number;
|
||||
tokens.popFront;
|
||||
return Result!Definition(definition);
|
||||
}
|
||||
return Result!Definition("Expected a number", tokens.front.position);
|
||||
}
|
||||
|
||||
private Result!Statement parseStatement(ref Array!Token.Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected block, got end of stream")
|
||||
{
|
||||
if (tokens.front.ofType(Token.Type.bang))
|
||||
{
|
||||
tokens.popFront;
|
||||
auto statement = MmapPool.instance.make!BangStatement;
|
||||
auto expression = parseExpression(tokens);
|
||||
if (expression.valid)
|
||||
{
|
||||
statement.expression = expression.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Statement(expression.error.get);
|
||||
}
|
||||
return Result!Statement(statement);
|
||||
}
|
||||
return Result!Statement("Expected ! statement", tokens.front.position);
|
||||
}
|
||||
|
||||
private Result!(Array!Definition) parseDefinitions(ref Array!Token.Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected definition, got end of stream")
|
||||
{
|
||||
tokens.popFront; // Skip const.
|
||||
|
||||
Array!Definition definitions;
|
||||
|
||||
while (!tokens.empty)
|
||||
{
|
||||
auto definition = parseDefinition(tokens);
|
||||
if (!definition.valid)
|
||||
{
|
||||
return typeof(return)(definition.error.get);
|
||||
}
|
||||
definitions.insertBack(definition.result);
|
||||
if (tokens.front.ofType(Token.Type.semicolon))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (tokens.front.ofType(Token.Type.comma))
|
||||
{
|
||||
tokens.popFront;
|
||||
}
|
||||
}
|
||||
|
||||
return typeof(return)(definitions);
|
||||
}
|
||||
|
||||
private Result!(Array!VariableDeclaration) parseVariableDeclarations(ref Array!Token.Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected variable declarations, got end of stream")
|
||||
{
|
||||
tokens.popFront; // Skip var.
|
||||
|
||||
Array!VariableDeclaration variableDeclarations;
|
||||
|
||||
while (!tokens.empty)
|
||||
{
|
||||
auto currentToken = tokens.front;
|
||||
if (currentToken.ofType(Token.Type.identifier))
|
||||
{
|
||||
auto variableDeclaration = MmapPool.instance.make!VariableDeclaration;
|
||||
variableDeclaration.identifier = currentToken.value!(Token.Type.identifier);
|
||||
variableDeclarations.insertBack(variableDeclaration);
|
||||
tokens.popFront;
|
||||
}
|
||||
else
|
||||
{
|
||||
return typeof(return)("Expected variable name", tokens.front.position);
|
||||
}
|
||||
if (tokens.empty)
|
||||
{
|
||||
return typeof(return)("Expected \";\" or \",\" name", currentToken.position);
|
||||
}
|
||||
if (tokens.front.ofType(Token.Type.semicolon))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (tokens.front.ofType(Token.Type.comma))
|
||||
{
|
||||
tokens.popFront;
|
||||
}
|
||||
}
|
||||
|
||||
return typeof(return)(variableDeclarations);
|
||||
}
|
||||
|
||||
private Result!Block parseBlock(ref Array!Token.Range tokens) @nogc
|
||||
in (!tokens.empty, "Expected block, got end of stream")
|
||||
{
|
||||
auto block = MmapPool.instance.make!Block;
|
||||
if (tokens.front.ofType(Token.Type.let))
|
||||
{
|
||||
auto constDefinitions = parseDefinitions(tokens);
|
||||
if (constDefinitions.valid)
|
||||
{
|
||||
block.definitions = constDefinitions.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Block(constDefinitions.error.get);
|
||||
}
|
||||
tokens.popFront;
|
||||
}
|
||||
if (tokens.front.ofType(Token.Type.var))
|
||||
{
|
||||
auto variableDeclarations = parseVariableDeclarations(tokens);
|
||||
if (variableDeclarations.valid)
|
||||
{
|
||||
block.variableDeclarations = variableDeclarations.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Block(variableDeclarations.error.get);
|
||||
}
|
||||
tokens.popFront;
|
||||
}
|
||||
auto statement = parseStatement(tokens);
|
||||
if (statement.valid)
|
||||
{
|
||||
block.statement = statement.result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result!Block(statement.error.get);
|
||||
}
|
||||
|
||||
return Result!Block(block);
|
||||
}
|
||||
|
||||
Result!Block parse(ref Array!Token tokenStream) @nogc
|
||||
{
|
||||
auto tokens = tokenStream[];
|
||||
return parseBlock(tokens);
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
module elna.result;
|
||||
|
||||
import std.typecons;
|
||||
|
||||
/**
|
||||
* Position in the source text.
|
||||
*/
|
||||
struct Position
|
||||
{
|
||||
/// Line.
|
||||
size_t line = 1;
|
||||
|
||||
/// Column.
|
||||
size_t column = 1;
|
||||
}
|
||||
|
||||
struct CompileError
|
||||
{
|
||||
private string message_;
|
||||
|
||||
private Position position_;
|
||||
|
||||
@disable this();
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* message = Error text.
|
||||
* position = Error position in the source text.
|
||||
*/
|
||||
this(string message, Position position) @nogc nothrow pure @safe
|
||||
{
|
||||
this.message_ = message;
|
||||
this.position_ = position;
|
||||
}
|
||||
|
||||
/// Error text.
|
||||
@property string message() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.message_;
|
||||
}
|
||||
|
||||
/// Error line in the source text.
|
||||
@property size_t line() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.position_.line;
|
||||
}
|
||||
|
||||
/// Error column in the source text.
|
||||
@property size_t column() const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.position_.column;
|
||||
}
|
||||
}
|
||||
|
||||
struct Result(T)
|
||||
{
|
||||
Nullable!CompileError error;
|
||||
T result;
|
||||
|
||||
this(T result)
|
||||
{
|
||||
this.result = result;
|
||||
this.error = typeof(this.error).init;
|
||||
}
|
||||
|
||||
this(string message, Position position)
|
||||
{
|
||||
this.result = T.init;
|
||||
this.error = CompileError(message, position);
|
||||
}
|
||||
|
||||
this(CompileError compileError)
|
||||
{
|
||||
this.result = null;
|
||||
this.error = compileError;
|
||||
}
|
||||
|
||||
@disable this();
|
||||
|
||||
@property bool valid() const
|
||||
{
|
||||
return error.isNull;
|
||||
}
|
||||
}
|
329
source/lexer.cpp
Normal file
329
source/lexer.cpp
Normal file
@ -0,0 +1,329 @@
|
||||
#include "elna/source/lexer.hpp"
|
||||
#include <cassert>
|
||||
#include <variant>
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
using source_position = elna::source::position;
|
||||
using source_error = elna::source::error;
|
||||
|
||||
token::value::value()
|
||||
: nil(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
token::value::value(std::int32_t value)
|
||||
: number(value)
|
||||
{
|
||||
}
|
||||
|
||||
token::value::value(const std::string& value)
|
||||
: identifier(value)
|
||||
{
|
||||
}
|
||||
|
||||
token::value::~value()
|
||||
{
|
||||
}
|
||||
|
||||
token::token(const type of, const std::string& value, const source_position position)
|
||||
: m_type(of), m_value(value), m_position(position)
|
||||
{
|
||||
}
|
||||
|
||||
token::token(const type of, std::int32_t number, const source_position position)
|
||||
: m_type(of), m_value(number), m_position(position)
|
||||
{
|
||||
}
|
||||
|
||||
token::token(type of, value&& value, const elna::source::position position)
|
||||
: m_type(of), m_position(position)
|
||||
{
|
||||
if (has_identifier())
|
||||
{
|
||||
new((void *) &m_value.identifier) std::string(std::move(value.identifier));
|
||||
}
|
||||
else if (is_numeric())
|
||||
{
|
||||
m_value.number = value.number;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_value.nil = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
token::token(const type of, source_position position)
|
||||
: m_type(of), m_position(position)
|
||||
{
|
||||
}
|
||||
|
||||
token::token(const token& that)
|
||||
{
|
||||
*this = that;
|
||||
}
|
||||
|
||||
token::token(token&& that)
|
||||
{
|
||||
*this = std::move(that);
|
||||
}
|
||||
|
||||
token::~token()
|
||||
{
|
||||
if (has_identifier())
|
||||
{
|
||||
m_value.identifier.~basic_string();
|
||||
}
|
||||
}
|
||||
|
||||
token& token::operator=(const token& that)
|
||||
{
|
||||
if (has_identifier())
|
||||
{
|
||||
m_value.identifier.~basic_string();
|
||||
}
|
||||
m_type = that.of();
|
||||
m_position = that.position();
|
||||
if (that.has_identifier())
|
||||
{
|
||||
new((void *) &m_value.identifier) std::string(that.identifier());
|
||||
}
|
||||
else if (that.is_numeric())
|
||||
{
|
||||
m_value.number = that.number();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_value.nil = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
token& token::operator=(token&& that)
|
||||
{
|
||||
if (has_identifier())
|
||||
{
|
||||
m_value.identifier.~basic_string();
|
||||
}
|
||||
m_type = that.of();
|
||||
m_position = that.position();
|
||||
if (that.has_identifier())
|
||||
{
|
||||
new((void *) &m_value.identifier) std::string(std::move(that.identifier()));
|
||||
}
|
||||
else if (that.is_numeric())
|
||||
{
|
||||
m_value.number = that.number();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_value.nil = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
token::type token::of() const noexcept
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
const std::string& token::identifier() const
|
||||
{
|
||||
if (!has_identifier())
|
||||
{
|
||||
throw std::bad_variant_access();
|
||||
}
|
||||
return m_value.identifier;
|
||||
}
|
||||
|
||||
std::int32_t token::number() const
|
||||
{
|
||||
if (!is_numeric())
|
||||
{
|
||||
throw std::bad_variant_access();
|
||||
}
|
||||
return m_value.number;
|
||||
}
|
||||
|
||||
const source_position& token::position() const noexcept
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
bool token::has_identifier() const noexcept
|
||||
{
|
||||
return of() == type::identifier
|
||||
|| of() == type::term_operator
|
||||
|| of() == type::factor_operator
|
||||
|| of() == type::comparison_operator;
|
||||
}
|
||||
|
||||
bool token::is_numeric() const noexcept
|
||||
{
|
||||
return of() == type::number
|
||||
|| of() == type::boolean;
|
||||
}
|
||||
|
||||
std::string token::to_string() const
|
||||
{
|
||||
switch (this->m_type)
|
||||
{
|
||||
case type::number:
|
||||
return "«number»";
|
||||
case type::boolean:
|
||||
return "«boolean»";
|
||||
case type::term_operator:
|
||||
return "«term_operator»";
|
||||
case type::let:
|
||||
return "«const»";
|
||||
case type::identifier:
|
||||
return "«identifier»";
|
||||
case type::equals:
|
||||
return "«=»";
|
||||
case type::var:
|
||||
return "«var»";
|
||||
case type::semicolon:
|
||||
return "«;»";
|
||||
case type::left_paren:
|
||||
return "«(»";
|
||||
case type::right_paren:
|
||||
return "«)»";
|
||||
case type::dot:
|
||||
return "«)»";
|
||||
case type::comma:
|
||||
return "«,»";
|
||||
case type::factor_operator:
|
||||
return "«*»";
|
||||
case type::eof:
|
||||
return "«EOF»";
|
||||
case type::begin:
|
||||
return "«begin»";
|
||||
case type::end:
|
||||
return "«end»";
|
||||
case type::assignment:
|
||||
return "«:=»";
|
||||
case type::colon:
|
||||
return "«:»";
|
||||
case type::when:
|
||||
return "«if»";
|
||||
case type::then:
|
||||
return "«then»";
|
||||
case type::loop:
|
||||
return "«while»";
|
||||
case type::_do:
|
||||
return "«do»";
|
||||
case type::procedure:
|
||||
return "«proc»";
|
||||
case type::comparison_operator:
|
||||
return "«comparison_operator»";
|
||||
case type::hat:
|
||||
return "«^»";
|
||||
case type::at:
|
||||
return "«@»";
|
||||
};
|
||||
assert(false);
|
||||
}
|
||||
|
||||
unexpected_character::unexpected_character(const std::string& character, const std::filesystem::path& path,
|
||||
const source::position position)
|
||||
: error(path, position), character(character)
|
||||
{
|
||||
}
|
||||
|
||||
std::string unexpected_character::what() const
|
||||
{
|
||||
std::string ss{ "Unexpected character '" };
|
||||
|
||||
ss.insert(ss.cend(), character.cbegin(), character.cend());
|
||||
ss.push_back('\'');
|
||||
|
||||
return ss;
|
||||
}
|
||||
|
||||
unexpected_token::unexpected_token(const token& token, const std::filesystem::path& path)
|
||||
: error(path, token.position()), m_token(token)
|
||||
{
|
||||
}
|
||||
|
||||
std::string unexpected_token::what() const
|
||||
{
|
||||
return "Unexpected token " + m_token.to_string();
|
||||
}
|
||||
|
||||
lexer::lexer(std::vector<token>&& tokens, const position last_position, const std::filesystem::path& path)
|
||||
: tokens(std::move(tokens)), iterator(this->tokens.cbegin()), eof(token(token::type::eof, last_position)),
|
||||
source_file(path)
|
||||
{
|
||||
}
|
||||
|
||||
lexer& lexer::operator++()
|
||||
{
|
||||
++iterator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const token& lexer::operator*() const
|
||||
{
|
||||
return *iterator;
|
||||
}
|
||||
|
||||
const token *lexer::operator->() const
|
||||
{
|
||||
return iterator.base();
|
||||
}
|
||||
|
||||
const token& lexer::current() const noexcept
|
||||
{
|
||||
if (iterator == tokens.cend())
|
||||
{
|
||||
return this->eof;
|
||||
}
|
||||
return *iterator;
|
||||
}
|
||||
|
||||
bool lexer::current(const token::type token_type) const noexcept
|
||||
{
|
||||
return current().of() == token_type;
|
||||
}
|
||||
|
||||
void lexer::add_error(const token& expected)
|
||||
{
|
||||
m_errors.push_back(std::make_unique<unexpected_token>(expected, this->source_file));
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const token>> lexer::advance(const token::type token_type)
|
||||
{
|
||||
if (iterator != tokens.cend() && iterator->of() == token_type)
|
||||
{
|
||||
return std::make_optional<>(std::cref(*iterator++));
|
||||
}
|
||||
add_error(current());
|
||||
return std::optional<std::reference_wrapper<const token>>();
|
||||
}
|
||||
|
||||
const token& lexer::look_ahead() const
|
||||
{
|
||||
auto tmp = iterator;
|
||||
++tmp;
|
||||
if (iterator == tokens.cend() || tmp == tokens.cend())
|
||||
{
|
||||
return eof;
|
||||
}
|
||||
return *tmp;
|
||||
}
|
||||
|
||||
bool lexer::look_ahead(const token::type token_type) const
|
||||
{
|
||||
return look_ahead().of() == token_type;
|
||||
}
|
||||
|
||||
bool lexer::skip(const token::type token_type)
|
||||
{
|
||||
return advance(token_type).has_value();
|
||||
}
|
||||
|
||||
const std::list<std::unique_ptr<error>>& lexer::errors() const noexcept
|
||||
{
|
||||
return m_errors;
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
import core.stdc.stdio;
|
||||
import core.stdc.string;
|
||||
import core.stdc.stdlib;
|
||||
import elna.lexer;
|
||||
import elna.parser;
|
||||
import elna.generator;
|
||||
import elna.ir;
|
||||
import tanya.container.string;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.memory.mmappool;
|
||||
|
||||
private char[] readSource(size_t N)(string source, out char[N] buffer) @nogc
|
||||
{
|
||||
memcpy(buffer.ptr, source.ptr, source.length + 1);
|
||||
buffer[source.length] = '\0';
|
||||
auto handle = fopen(buffer.ptr, "r");
|
||||
if (handle is null)
|
||||
{
|
||||
perror(buffer.ptr);
|
||||
return null;
|
||||
}
|
||||
fseek(handle, 0, SEEK_END);
|
||||
size_t fsize = ftell(handle);
|
||||
rewind(handle);
|
||||
|
||||
fread(buffer.ptr, fsize, 1, handle);
|
||||
fclose(handle);
|
||||
buffer[fsize] = '\0';
|
||||
|
||||
return buffer[0 .. fsize];
|
||||
}
|
||||
|
||||
int main(string[] args)
|
||||
{
|
||||
char[255] buffer;
|
||||
|
||||
defaultAllocator = MmapPool.instance;
|
||||
|
||||
if (args.length < 2)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
auto sourceText = readSource(args[1], buffer);
|
||||
if (sourceText is null)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
auto tokens = lex(sourceText);
|
||||
if (tokens.length == 0)
|
||||
{
|
||||
printf("Lexical analysis failed.\n");
|
||||
return 1;
|
||||
}
|
||||
auto ast = parse(tokens);
|
||||
if (!ast.valid)
|
||||
{
|
||||
auto compileError = ast.error.get;
|
||||
printf("%lu:%lu: %s\n", compileError.line, compileError.column, compileError.message.ptr);
|
||||
return 2;
|
||||
}
|
||||
auto ir = transform(ast.result);
|
||||
|
||||
String outputFilename = String("build/");
|
||||
outputFilename.insertBack(args[1][0 .. $ - 4]);
|
||||
outputFilename.insertBack("o");
|
||||
writeObject(ir, outputFilename);
|
||||
|
||||
auto code = generate(ir);
|
||||
printf("%s", code.toStringz());
|
||||
|
||||
return 0;
|
||||
}
|
267
source/optimizer.cpp
Normal file
267
source/optimizer.cpp
Normal file
@ -0,0 +1,267 @@
|
||||
#include "elna/source/optimizer.hpp"
|
||||
#include <cassert>
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
quadruple::quadruple(const quadruple_operator operation, std::shared_ptr<operand> operand1,
|
||||
std::shared_ptr<operand> operand2, std::shared_ptr<operand> operand3)
|
||||
: m_operation(operation), m_operand1(operand1), m_operand2(operand2), m_operand3(operand3)
|
||||
{
|
||||
}
|
||||
|
||||
quadruple_operator quadruple::operation() const noexcept
|
||||
{
|
||||
return m_operation;
|
||||
}
|
||||
|
||||
std::shared_ptr<operand> quadruple::operand1()
|
||||
{
|
||||
return m_operand1;
|
||||
}
|
||||
|
||||
std::shared_ptr<operand> quadruple::operand2()
|
||||
{
|
||||
return m_operand2;
|
||||
}
|
||||
|
||||
std::shared_ptr<operand> quadruple::operand3()
|
||||
{
|
||||
return m_operand3;
|
||||
}
|
||||
|
||||
intermediate_code::intermediate_code()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void intermediate_code::emplace_back(const quadruple_operator operation, std::shared_ptr<operand> operand1,
|
||||
std::shared_ptr<operand> operand2, std::shared_ptr<operand> operand3)
|
||||
{
|
||||
this->instructions.emplace_back(operation, operand1, operand2, operand3);
|
||||
}
|
||||
|
||||
void intermediate_code::clear()
|
||||
{
|
||||
this->instructions.clear();
|
||||
this->m_variable_counter = 1;
|
||||
this->m_label_counter = 0;
|
||||
}
|
||||
|
||||
std::vector<quadruple>::iterator intermediate_code::begin()
|
||||
{
|
||||
return this->instructions.begin();
|
||||
}
|
||||
|
||||
std::vector<quadruple>::iterator intermediate_code::end()
|
||||
{
|
||||
return this->instructions.end();
|
||||
}
|
||||
|
||||
std::int32_t intermediate_code::variable_counter() const noexcept
|
||||
{
|
||||
return m_variable_counter;
|
||||
}
|
||||
|
||||
std::int32_t intermediate_code::increment_variable() noexcept
|
||||
{
|
||||
return m_variable_counter++;
|
||||
}
|
||||
|
||||
std::int32_t intermediate_code::label_counter() const noexcept
|
||||
{
|
||||
return m_label_counter;
|
||||
}
|
||||
|
||||
std::int32_t intermediate_code::increment_label() noexcept
|
||||
{
|
||||
return m_label_counter++;
|
||||
}
|
||||
|
||||
intermediate_code_generator::intermediate_code_generator(std::shared_ptr<symbol_table> table)
|
||||
: table(table)
|
||||
{
|
||||
}
|
||||
|
||||
quadruple_operator intermediate_code_generator::convert(const binary_operator operation) const
|
||||
{
|
||||
switch (operation)
|
||||
{
|
||||
case binary_operator::sum:
|
||||
return quadruple_operator::add;
|
||||
case binary_operator::subtraction:
|
||||
return quadruple_operator::sub;
|
||||
case binary_operator::multiplication:
|
||||
return quadruple_operator::mul;
|
||||
case binary_operator::division:
|
||||
return quadruple_operator::div;
|
||||
case binary_operator::equals:
|
||||
return quadruple_operator::eq;
|
||||
case source::binary_operator::not_equals:
|
||||
return quadruple_operator::neq;
|
||||
case source::binary_operator::less:
|
||||
return quadruple_operator::lt;
|
||||
case source::binary_operator::greater_equal:
|
||||
return quadruple_operator::ge;
|
||||
case source::binary_operator::greater:
|
||||
return quadruple_operator::gt;
|
||||
case source::binary_operator::less_equal:
|
||||
return quadruple_operator::le;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
quadruple_operator intermediate_code_generator::convert(const unary_operator operation) const
|
||||
{
|
||||
switch (operation)
|
||||
{
|
||||
case unary_operator::reference:
|
||||
return quadruple_operator::ref;
|
||||
case unary_operator::dereference:
|
||||
return quadruple_operator::load;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(source::declaration *declaration)
|
||||
{
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(source::constant_definition *definition)
|
||||
{
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(procedure_definition *definition)
|
||||
{
|
||||
this->current.emplace_back(quadruple_operator::start);
|
||||
definition->body().accept(this);
|
||||
this->current.emplace_back(quadruple_operator::stop);
|
||||
code[definition->identifier()] = std::move(this->current);
|
||||
this->current.clear();
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, intermediate_code>::iterator intermediate_code_generator::begin()
|
||||
{
|
||||
return code.begin();
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, intermediate_code>::iterator intermediate_code_generator::end()
|
||||
{
|
||||
return code.end();
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(block *block)
|
||||
{
|
||||
block->body().accept(this);
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(program *program)
|
||||
{
|
||||
for (auto& definition : program->definitions())
|
||||
{
|
||||
definition->accept(this);
|
||||
}
|
||||
this->current.emplace_back(quadruple_operator::start);
|
||||
program->body().accept(this);
|
||||
this->current.emplace_back(quadruple_operator::stop);
|
||||
code["_start"] = std::move(this->current);
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(call_statement *statement)
|
||||
{
|
||||
for (auto& argument : statement->arguments())
|
||||
{
|
||||
argument->accept(this);
|
||||
this->current.emplace_back(quadruple_operator::param, argument->place, nullptr, nullptr);
|
||||
}
|
||||
this->current.emplace_back(quadruple_operator::call,
|
||||
std::make_shared<variable_operand>(statement->name()),
|
||||
std::make_shared<integer_operand>(statement->arguments().size()));
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(assign_statement *statement)
|
||||
{
|
||||
statement->rvalue().accept(this);
|
||||
|
||||
this->current.emplace_back(quadruple_operator::assign, statement->rvalue().place, nullptr,
|
||||
std::make_shared<variable_operand>(statement->lvalue()));
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(if_statement *statement)
|
||||
{
|
||||
statement->prerequisite().accept(this);
|
||||
|
||||
auto end_label = std::make_shared<label_operand>(this->current.increment_label());
|
||||
this->current.emplace_back(quadruple_operator::beqz, statement->prerequisite().place, nullptr, end_label);
|
||||
|
||||
statement->body().accept(this);
|
||||
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, end_label);
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(while_statement *statement)
|
||||
{
|
||||
auto condition_label = std::make_shared<label_operand>(this->current.increment_label());
|
||||
|
||||
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, condition_label);
|
||||
statement->prerequisite().accept(this);
|
||||
|
||||
auto end_label = std::make_shared<label_operand>(this->current.increment_label());
|
||||
this->current.emplace_back(quadruple_operator::beqz, statement->prerequisite().place, nullptr, end_label);
|
||||
|
||||
statement->body().accept(this);
|
||||
this->current.emplace_back(quadruple_operator::j, nullptr, nullptr, condition_label);
|
||||
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, end_label);
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(type_expression *type)
|
||||
{
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(variable_expression *variable)
|
||||
{
|
||||
auto symbol = table->lookup(variable->name());
|
||||
if (auto constant_symbol = std::dynamic_pointer_cast<source::constant_info>(symbol))
|
||||
{
|
||||
variable->place = std::make_shared<integer_operand>(constant_symbol->value());
|
||||
}
|
||||
else
|
||||
{
|
||||
variable->place = std::make_shared<variable_operand>(variable->name());
|
||||
}
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(binary_expression *expression)
|
||||
{
|
||||
expression->lhs().accept(this);
|
||||
auto left = expression->lhs().place;
|
||||
|
||||
expression->rhs().accept(this);
|
||||
auto right = expression->rhs().place;
|
||||
auto operation = convert(expression->operation());
|
||||
auto new_place = std::make_shared<temporary_variable>(this->current.increment_variable());
|
||||
|
||||
this->current.emplace_back(operation, left, right, new_place);
|
||||
expression->place = new_place;
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(unary_expression *expression)
|
||||
{
|
||||
expression->operand().accept(this);
|
||||
auto operation = convert(expression->operation());
|
||||
auto new_place = std::make_shared<temporary_variable>(this->current.increment_variable());
|
||||
|
||||
this->current.emplace_back(operation, expression->operand().place, nullptr, new_place);
|
||||
expression->place = new_place;
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(integer_literal *number)
|
||||
{
|
||||
number->place = std::make_shared<integer_operand>(number->number());
|
||||
}
|
||||
|
||||
void intermediate_code_generator::visit(boolean_literal *number)
|
||||
{
|
||||
number->place = std::make_shared<integer_operand>(number->boolean());
|
||||
}
|
||||
}
|
1063
source/parser.cpp
Normal file
1063
source/parser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
46
source/result.cpp
Normal file
46
source/result.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include "elna/source/result.hpp"
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
error::error(const std::filesystem::path& path, const position position)
|
||||
: m_position(position), m_path(path)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t error::line() const noexcept
|
||||
{
|
||||
return this->m_position.line;
|
||||
}
|
||||
|
||||
std::size_t error::column() const noexcept
|
||||
{
|
||||
return this->m_position.column;
|
||||
}
|
||||
|
||||
const std::filesystem::path& error::path() const noexcept
|
||||
{
|
||||
return this->m_path;
|
||||
}
|
||||
|
||||
name_collision::name_collision(const std::string& name, const std::filesystem::path& path,
|
||||
const position current, const position previous)
|
||||
: error(path, current), name(name), previous(previous)
|
||||
{
|
||||
}
|
||||
|
||||
std::string name_collision::what() const
|
||||
{
|
||||
return "Name '" + name + "' was already defined";
|
||||
}
|
||||
|
||||
type_mismatch::type_mismatch(std::shared_ptr<const type> got, operation kind, const std::filesystem::path& path,
|
||||
const struct position position)
|
||||
: error(path, position), kind(kind), got(got)
|
||||
{
|
||||
}
|
||||
|
||||
std::string type_mismatch::what() const
|
||||
{
|
||||
return "Type cannot be used here.";
|
||||
}
|
||||
}
|
204
source/scanner.l
Normal file
204
source/scanner.l
Normal file
@ -0,0 +1,204 @@
|
||||
%{
|
||||
#define YY_NO_UNISTD_H
|
||||
#define YY_USER_ACTION token_position = elna::source::position{ line_no, column_no }; column_no += yyleng;
|
||||
|
||||
#include <fstream>
|
||||
#include "elna/source/lexer.hpp"
|
||||
|
||||
elna::source::token::value yylval{};
|
||||
elna::source::position token_position{};
|
||||
static std::size_t column_no = 1;
|
||||
static std::size_t line_no = 1;
|
||||
%}
|
||||
|
||||
%option noyywrap
|
||||
%option never-interactive
|
||||
%%
|
||||
\-\-.* {
|
||||
/* Skip the comment */
|
||||
}
|
||||
[\ \t\r] {
|
||||
/* Skip the whitespaces */
|
||||
}
|
||||
\n {
|
||||
++line_no;
|
||||
column_no = 1;
|
||||
}
|
||||
if {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::when);
|
||||
}
|
||||
then {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::then);
|
||||
}
|
||||
while {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::loop);
|
||||
}
|
||||
do {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::_do);
|
||||
}
|
||||
proc {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::procedure);
|
||||
}
|
||||
begin {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::begin);
|
||||
}
|
||||
end {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::end);
|
||||
}
|
||||
const {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::let);
|
||||
}
|
||||
var {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::var);
|
||||
}
|
||||
True {
|
||||
yylval.number = 1;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::boolean);
|
||||
}
|
||||
False {
|
||||
yylval.number = 0;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::boolean);
|
||||
}
|
||||
[A-Za-z_][A-Za-z0-9_]* {
|
||||
new((void *) &yylval.identifier) std::string(yytext);
|
||||
|
||||
return static_cast<int>(elna::source::token::type::identifier);
|
||||
}
|
||||
[0-9]+ {
|
||||
yylval.number = strtol(yytext, NULL, 10);
|
||||
|
||||
return static_cast<int>(elna::source::token::type::number);
|
||||
}
|
||||
\( {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::left_paren);
|
||||
}
|
||||
\) {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::right_paren);
|
||||
}
|
||||
\>= {
|
||||
new((void *) &yylval.identifier) std::string(1, 'g');
|
||||
|
||||
return static_cast<int>(elna::source::token::type::comparison_operator);
|
||||
}
|
||||
\<= {
|
||||
new((void *) &yylval.identifier) std::string(1, 'l');
|
||||
|
||||
return static_cast<int>(elna::source::token::type::comparison_operator);
|
||||
}
|
||||
(>|<) {
|
||||
new((void *) &yylval.identifier) std::string(yytext);
|
||||
|
||||
return static_cast<int>(elna::source::token::type::comparison_operator);
|
||||
}
|
||||
\/= {
|
||||
new((void *) &yylval.identifier) std::string(1, 'n');
|
||||
|
||||
return static_cast<int>(elna::source::token::type::comparison_operator);
|
||||
}
|
||||
= {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::equals);
|
||||
}
|
||||
; {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::semicolon);
|
||||
}
|
||||
\. {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::dot);
|
||||
}
|
||||
, {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::comma);
|
||||
}
|
||||
(\+|\-) {
|
||||
new((void *) &yylval.identifier) std::string(yytext);
|
||||
|
||||
return static_cast<int>(elna::source::token::type::term_operator);
|
||||
}
|
||||
(\*|\/) {
|
||||
new((void *) &yylval.identifier) std::string(yytext);
|
||||
|
||||
return static_cast<int>(elna::source::token::type::factor_operator);
|
||||
}
|
||||
:= {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::assignment);
|
||||
}
|
||||
: {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::colon);
|
||||
}
|
||||
\^ {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::hat);
|
||||
}
|
||||
@ {
|
||||
yylval.nil = nullptr;
|
||||
|
||||
return static_cast<int>(elna::source::token::type::at);
|
||||
}
|
||||
. {
|
||||
return -1;
|
||||
}
|
||||
%%
|
||||
namespace elna::source
|
||||
{
|
||||
|
||||
result<lexer> tokenize(const std::filesystem::path& path)
|
||||
{
|
||||
int yytoken;
|
||||
std::vector<token> tokens;
|
||||
|
||||
yyin = fopen(path.c_str(), "rb");
|
||||
if (yyin == nullptr)
|
||||
{
|
||||
throw std::ios_base::failure("File does not exist");
|
||||
}
|
||||
do
|
||||
{
|
||||
yytoken = yylex();
|
||||
|
||||
if (yytoken < 0)
|
||||
{
|
||||
return result<lexer>(unexpected_character{ std::string{ yytext[0] }, path, token_position });
|
||||
}
|
||||
tokens.emplace_back(static_cast<token::type>(yytoken), std::move(yylval), token_position);
|
||||
}
|
||||
while (yytoken != 0);
|
||||
|
||||
return result<lexer>(std::in_place, std::move(tokens), position{ line_no, column_no }, path);
|
||||
}
|
||||
|
||||
}
|
325
source/semantic.cpp
Normal file
325
source/semantic.cpp
Normal file
@ -0,0 +1,325 @@
|
||||
#include "elna/source/semantic.hpp"
|
||||
#include "elna/source/result.hpp"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table> table,
|
||||
const std::filesystem::path& filename, const std::size_t target_pointer_size)
|
||||
: table(table), filename(filename), pointer_size(target_pointer_size)
|
||||
{
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(constant_definition *definition)
|
||||
{
|
||||
auto constant_type = std::make_shared<const class type>(int_type);
|
||||
this->table->enter(definition->identifier(),
|
||||
std::make_shared<constant_info>(constant_type, definition->body().number()));
|
||||
}
|
||||
|
||||
std::shared_ptr<const type> name_analysis_visitor::convert_declaration_type(const type_expression& ast_type) const
|
||||
{
|
||||
auto variable_type = std::dynamic_pointer_cast<type_info>(table->lookup(ast_type.base()))
|
||||
->type();
|
||||
std::shared_ptr<type> declaration_type;
|
||||
|
||||
if (ast_type.is_pointer())
|
||||
{
|
||||
return std::make_shared<pointer_type>(variable_type, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
return variable_type;
|
||||
}
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(declaration *declaration)
|
||||
{
|
||||
std::shared_ptr<const type> declaration_type = convert_declaration_type(declaration->type());
|
||||
|
||||
this->table->enter(declaration->identifier(),
|
||||
std::make_shared<variable_info>(declaration_type));
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(program *program)
|
||||
{
|
||||
class procedure_type main_type{ std::vector<std::shared_ptr<const class type>>(), this->pointer_size };
|
||||
this->table->enter("_start", std::make_shared<procedure_info>(main_type, this->table));
|
||||
empty_visitor::visit(program);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(procedure_definition *procedure)
|
||||
{
|
||||
std::vector<std::shared_ptr<const type>> arguments;
|
||||
|
||||
for (auto& parameter : procedure->parameters())
|
||||
{
|
||||
auto declaration_type = convert_declaration_type(parameter->type());
|
||||
arguments.push_back(declaration_type);
|
||||
}
|
||||
procedure_type definition_type{ std::move(arguments), this->pointer_size };
|
||||
auto info = std::make_shared<procedure_info>(definition_type, this->table);
|
||||
|
||||
this->table->enter(procedure->identifier(), info);
|
||||
this->table = info->scope();
|
||||
|
||||
for (std::size_t i = 0; i < procedure->parameters().size(); ++i)
|
||||
{
|
||||
this->table->enter(procedure->parameters().at(i)->identifier(),
|
||||
std::make_shared<parameter_info>(definition_type.arguments.at(i)));
|
||||
}
|
||||
procedure->body().accept(this);
|
||||
|
||||
this->table = info->scope()->scope();
|
||||
}
|
||||
|
||||
const std::list<std::unique_ptr<error>>& name_analysis_visitor::errors() const noexcept
|
||||
{
|
||||
return m_errors;
|
||||
}
|
||||
|
||||
allocator_visitor::allocator_visitor(std::shared_ptr<symbol_table> table)
|
||||
: table(table)
|
||||
{
|
||||
}
|
||||
|
||||
void allocator_visitor::visit(declaration *declaration)
|
||||
{
|
||||
auto declaration_info = this->table->lookup(declaration->identifier());
|
||||
|
||||
if (auto variable = std::dynamic_pointer_cast<variable_info>(declaration_info))
|
||||
{
|
||||
this->local_offset -= sizeof(std::int32_t);
|
||||
variable->offset = this->local_offset;
|
||||
}
|
||||
else if (auto parameter = std::dynamic_pointer_cast<parameter_info>(declaration_info))
|
||||
{
|
||||
parameter->offset = this->argument_offset;
|
||||
this->argument_offset += sizeof(std::int32_t);
|
||||
}
|
||||
}
|
||||
|
||||
void allocator_visitor::visit(program *program)
|
||||
{
|
||||
this->local_offset = 0;
|
||||
this->argument_offset = 0;
|
||||
|
||||
empty_visitor::visit(program);
|
||||
std::dynamic_pointer_cast<procedure_info>(table->lookup("_start"))->local_stack_size =
|
||||
std::abs(this->local_offset);
|
||||
}
|
||||
|
||||
void allocator_visitor::visit(procedure_definition *procedure)
|
||||
{
|
||||
this->local_offset = 0;
|
||||
this->argument_offset = 0;
|
||||
auto info = std::dynamic_pointer_cast<procedure_info>(this->table->lookup(procedure->identifier()));
|
||||
this->table = info->scope();
|
||||
|
||||
empty_visitor::visit(procedure);
|
||||
|
||||
this->table = info->scope()->scope();
|
||||
info->local_stack_size = std::abs(this->local_offset);
|
||||
info->argument_stack_size = this->argument_offset;
|
||||
}
|
||||
|
||||
void allocator_visitor::visit(call_statement *statement)
|
||||
{
|
||||
auto call_info = std::dynamic_pointer_cast<intrinsic_info>(this->table->lookup(statement->name()));
|
||||
|
||||
this->argument_offset = std::max(static_cast<std::size_t>(this->argument_offset),
|
||||
call_info->parameter_stack_size());
|
||||
}
|
||||
|
||||
type_analysis_visitor::type_analysis_visitor(std::shared_ptr<symbol_table> table,
|
||||
const std::filesystem::path& filename, const std::size_t target_pointer_size)
|
||||
: table(table), filename(filename), pointer_size(target_pointer_size)
|
||||
{
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(program *program)
|
||||
{
|
||||
for (auto& definition : program->definitions())
|
||||
{
|
||||
if (dynamic_cast<procedure_definition *>(definition.get()) != nullptr)
|
||||
{
|
||||
definition->accept(this);
|
||||
}
|
||||
}
|
||||
program->body().accept(this);
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(procedure_definition *procedure)
|
||||
{
|
||||
auto info = std::dynamic_pointer_cast<procedure_info>(this->table->lookup(procedure->identifier()));
|
||||
this->table = info->scope();
|
||||
|
||||
procedure->body().accept(this);
|
||||
|
||||
this->table = info->scope()->scope();
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(integer_literal *literal)
|
||||
{
|
||||
literal->data_type = std::dynamic_pointer_cast<type_info>(table->lookup("Int"))->type();
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(boolean_literal *literal)
|
||||
{
|
||||
literal->data_type = std::dynamic_pointer_cast<type_info>(table->lookup("Boolean"))->type();
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(variable_expression *expression)
|
||||
{
|
||||
expression->data_type = std::dynamic_pointer_cast<typed_info>(table->lookup(expression->name()))->type();
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(unary_expression *expression)
|
||||
{
|
||||
empty_visitor::visit(expression);
|
||||
|
||||
switch (expression->operation())
|
||||
{
|
||||
case unary_operator::reference:
|
||||
expression->data_type = std::make_shared<const pointer_type>(expression->operand().data_type,
|
||||
this->pointer_size);
|
||||
break;
|
||||
case unary_operator::dereference:
|
||||
auto operand_type = expression->operand().data_type;
|
||||
|
||||
if (auto referenced_type = std::dynamic_pointer_cast<const pointer_type>(operand_type))
|
||||
{
|
||||
expression->data_type = referenced_type;
|
||||
}
|
||||
else if (operand_type != nullptr)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(operand_type,
|
||||
type_mismatch::operation::dereference, this->filename, expression->position());
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(binary_expression *expression)
|
||||
{
|
||||
empty_visitor::visit(expression);
|
||||
|
||||
switch (expression->operation())
|
||||
{
|
||||
case binary_operator::sum:
|
||||
case binary_operator::subtraction:
|
||||
case binary_operator::multiplication:
|
||||
case binary_operator::division:
|
||||
case binary_operator::less:
|
||||
case binary_operator::greater:
|
||||
case binary_operator::less_equal:
|
||||
case binary_operator::greater_equal:
|
||||
if (expression->lhs().data_type != nullptr && expression->rhs().data_type != nullptr)
|
||||
{
|
||||
auto lhs_type = std::dynamic_pointer_cast<const primitive_type>(expression->lhs().data_type);
|
||||
auto rhs_type = std::dynamic_pointer_cast<const primitive_type>(expression->rhs().data_type);
|
||||
|
||||
std::unique_ptr<type_mismatch> new_error;
|
||||
if (lhs_type == nullptr || *lhs_type != int_type)
|
||||
{
|
||||
new_error = std::make_unique<type_mismatch>(lhs_type,
|
||||
type_mismatch::operation::arithmetic, this->filename, expression->lhs().position());
|
||||
}
|
||||
if (rhs_type == nullptr || *rhs_type != int_type)
|
||||
{
|
||||
new_error = std::make_unique<type_mismatch>(rhs_type,
|
||||
type_mismatch::operation::arithmetic, this->filename, expression->rhs().position());
|
||||
}
|
||||
if (new_error != nullptr)
|
||||
{
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case binary_operator::equals:
|
||||
case binary_operator::not_equals:
|
||||
if (expression->lhs().data_type != nullptr && expression->rhs().data_type != nullptr)
|
||||
{
|
||||
if (expression->lhs().data_type != expression->rhs().data_type)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(expression->rhs().data_type,
|
||||
type_mismatch::operation::comparison, this->filename, expression->rhs().position());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(call_statement *statement)
|
||||
{
|
||||
auto call_info = std::dynamic_pointer_cast<intrinsic_info>(this->table->lookup(statement->name()));
|
||||
|
||||
std::size_t i{ 0 };
|
||||
for (const auto& argument : statement->arguments())
|
||||
{
|
||||
argument->accept(this);
|
||||
|
||||
if (argument->data_type != nullptr && i < call_info->type()->arguments.size()
|
||||
&& call_info->type()->arguments[i] != argument->data_type)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(argument->data_type,
|
||||
type_mismatch::operation::argument, this->filename, argument->position());
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(constant_definition *definition)
|
||||
{
|
||||
definition->body().accept(this);
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(while_statement *statement)
|
||||
{
|
||||
statement->prerequisite().accept(this);
|
||||
auto condition_type = std::dynamic_pointer_cast<const primitive_type>(statement->prerequisite().data_type);
|
||||
|
||||
if (condition_type != nullptr && *condition_type != boolean_type)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(condition_type,
|
||||
type_mismatch::operation::condition, this->filename, statement->prerequisite().position());
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
statement->body().accept(this);
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(if_statement *statement)
|
||||
{
|
||||
statement->prerequisite().accept(this);
|
||||
auto condition_type = std::dynamic_pointer_cast<const primitive_type>(statement->prerequisite().data_type);
|
||||
|
||||
if (condition_type != nullptr && *condition_type != boolean_type)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(condition_type,
|
||||
type_mismatch::operation::condition, this->filename, statement->prerequisite().position());
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
statement->body().accept(this);
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(assign_statement *statement)
|
||||
{
|
||||
statement->rvalue().accept(this);
|
||||
auto lvalue_info = std::dynamic_pointer_cast<typed_info>(this->table->lookup(statement->lvalue()));
|
||||
|
||||
if (statement->rvalue().data_type != nullptr && lvalue_info->type() == statement->rvalue().data_type)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(statement->rvalue().data_type,
|
||||
type_mismatch::operation::assignment, this->filename, statement->position());
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
}
|
||||
|
||||
const std::list<std::unique_ptr<error>>& type_analysis_visitor::errors() const noexcept
|
||||
{
|
||||
return m_errors;
|
||||
}
|
||||
}
|
129
source/symbol_table.cpp
Normal file
129
source/symbol_table.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include "elna/source/types.hpp"
|
||||
#include "elna/source/symbol_table.hpp"
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
symbol_table::symbol_table(std::shared_ptr<symbol_table> scope)
|
||||
: outer_scope(scope)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<info> symbol_table::lookup(const std::string& name)
|
||||
{
|
||||
auto entry = entries.find(name);
|
||||
|
||||
if (entry != entries.cend())
|
||||
{
|
||||
return entry->second;
|
||||
}
|
||||
if (this->outer_scope != nullptr)
|
||||
{
|
||||
return this->outer_scope->lookup(name);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void symbol_table::enter(const std::string& name, std::shared_ptr<info> entry)
|
||||
{
|
||||
entries.insert_or_assign(name, entry);
|
||||
}
|
||||
|
||||
std::shared_ptr<symbol_table> symbol_table::scope()
|
||||
{
|
||||
return this->outer_scope;
|
||||
}
|
||||
|
||||
info::~info()
|
||||
{
|
||||
}
|
||||
|
||||
info::info()
|
||||
{
|
||||
}
|
||||
|
||||
type_info::type_info(const class type& type)
|
||||
: info(), m_type(std::make_shared<class type>(type))
|
||||
{
|
||||
}
|
||||
|
||||
type_info::~type_info()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<const class type> type_info::type() const noexcept
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
typed_info::typed_info(std::shared_ptr<const class type> type)
|
||||
: m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
typed_info::~typed_info()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<const class type> typed_info::type() const noexcept
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
constant_info::constant_info(const std::shared_ptr<const class type> type, const std::int32_t value)
|
||||
: typed_info(type), m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
std::int32_t constant_info::value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
variable_info::variable_info(std::shared_ptr<const class type> type)
|
||||
: typed_info(type)
|
||||
{
|
||||
}
|
||||
|
||||
parameter_info::parameter_info(std::shared_ptr<const class type> type)
|
||||
: typed_info(type)
|
||||
{
|
||||
}
|
||||
|
||||
intrinsic_info::intrinsic_info(const class procedure_type& type)
|
||||
: m_type(std::make_shared<procedure_type>(type))
|
||||
{
|
||||
}
|
||||
|
||||
intrinsic_info::~intrinsic_info()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<const class procedure_type> intrinsic_info::type() const noexcept
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
std::size_t intrinsic_info::parameter_stack_size() const noexcept
|
||||
{
|
||||
return type()->arguments.size() * sizeof(std::int32_t);
|
||||
}
|
||||
|
||||
procedure_info::procedure_info(const class procedure_type& type, std::shared_ptr<symbol_table> outer_scope)
|
||||
: intrinsic_info(type), local_table(std::make_shared<symbol_table>(outer_scope))
|
||||
{
|
||||
}
|
||||
|
||||
procedure_info::~procedure_info()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<symbol_table> procedure_info::scope()
|
||||
{
|
||||
return local_table;
|
||||
}
|
||||
|
||||
std::size_t procedure_info::stack_size() const noexcept
|
||||
{
|
||||
return local_stack_size + argument_stack_size;
|
||||
}
|
||||
}
|
96
source/types.cpp
Normal file
96
source/types.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include <elna/source/types.hpp>
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
type::type(const std::size_t byte_size)
|
||||
: byte_size(byte_size)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t type::size() const noexcept
|
||||
{
|
||||
return this->byte_size;
|
||||
}
|
||||
|
||||
primitive_type::primitive_type(const std::string& type_name, const std::size_t byte_size)
|
||||
: type(byte_size), type_name(type_name)
|
||||
{
|
||||
}
|
||||
|
||||
bool primitive_type::operator==(const primitive_type& that) const noexcept
|
||||
{
|
||||
return this->type_name == that.type_name;
|
||||
}
|
||||
|
||||
bool primitive_type::operator!=(const primitive_type& that) const noexcept
|
||||
{
|
||||
return this->type_name != that.type_name;
|
||||
}
|
||||
|
||||
pointer_type::pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size)
|
||||
: type(byte_size), base_type(base_type)
|
||||
{
|
||||
}
|
||||
|
||||
bool pointer_type::operator==(const pointer_type& that) const noexcept
|
||||
{
|
||||
return this->base_type == that.base_type;
|
||||
}
|
||||
|
||||
bool pointer_type::operator!=(const pointer_type& that) const noexcept
|
||||
{
|
||||
return this->base_type != that.base_type;
|
||||
}
|
||||
|
||||
procedure_type::procedure_type(std::vector<std::shared_ptr<const type>> arguments, const std::size_t byte_size)
|
||||
: arguments(std::move(arguments)), type(byte_size)
|
||||
{
|
||||
}
|
||||
|
||||
bool procedure_type::operator==(const procedure_type &that) const noexcept
|
||||
{
|
||||
return this->arguments == that.arguments;
|
||||
}
|
||||
|
||||
bool procedure_type::operator!=(const procedure_type &that) const noexcept
|
||||
{
|
||||
return this->arguments != that.arguments;
|
||||
}
|
||||
|
||||
bool operator==(const type& lhs, const type& rhs) noexcept
|
||||
{
|
||||
{
|
||||
auto lhs_type = dynamic_cast<const primitive_type *>(&lhs);
|
||||
auto rhs_type = dynamic_cast<const primitive_type *>(&rhs);
|
||||
|
||||
if (lhs_type != nullptr && rhs_type != nullptr)
|
||||
{
|
||||
return *lhs_type == *rhs_type;
|
||||
}
|
||||
}
|
||||
{
|
||||
auto lhs_type = dynamic_cast<const pointer_type *>(&lhs);
|
||||
auto rhs_type = dynamic_cast<const pointer_type *>(&rhs);
|
||||
|
||||
if (lhs_type != nullptr && rhs_type != nullptr)
|
||||
{
|
||||
return *lhs_type == *rhs_type;
|
||||
}
|
||||
}
|
||||
{
|
||||
auto lhs_type = dynamic_cast<const procedure_type *>(&lhs);
|
||||
auto rhs_type = dynamic_cast<const procedure_type *>(&rhs);
|
||||
|
||||
if (lhs_type != nullptr && rhs_type != nullptr)
|
||||
{
|
||||
return *lhs_type == *rhs_type;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator!=(const type& lhs, const type& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
const a = 1, b = 2;
|
||||
! + a b
|
||||
writei(a + b)
|
||||
.
|
5
tests/declare_variable.eln
Normal file
5
tests/declare_variable.eln
Normal file
@ -0,0 +1,5 @@
|
||||
var x: Int;
|
||||
begin
|
||||
x := 5;
|
||||
writei(x)
|
||||
end.
|
1
tests/divide.eln
Normal file
1
tests/divide.eln
Normal file
@ -0,0 +1 @@
|
||||
writei(6 / 3)
|
1
tests/expectations/declare_variable.txt
Normal file
1
tests/expectations/declare_variable.txt
Normal file
@ -0,0 +1 @@
|
||||
5
|
1
tests/expectations/divide.txt
Normal file
1
tests/expectations/divide.txt
Normal file
@ -0,0 +1 @@
|
||||
2
|
2
tests/expectations/if_condition.txt
Normal file
2
tests/expectations/if_condition.txt
Normal file
@ -0,0 +1,2 @@
|
||||
8
|
||||
9
|
1
tests/expectations/left_nested_sum.txt
Normal file
1
tests/expectations/left_nested_sum.txt
Normal file
@ -0,0 +1 @@
|
||||
8
|
2
tests/expectations/multiline_output.txt
Normal file
2
tests/expectations/multiline_output.txt
Normal file
@ -0,0 +1,2 @@
|
||||
5
|
||||
7
|
1
tests/expectations/multiply.txt
Normal file
1
tests/expectations/multiply.txt
Normal file
@ -0,0 +1 @@
|
||||
8
|
1
tests/expectations/multiply_3.txt
Normal file
1
tests/expectations/multiply_3.txt
Normal file
@ -0,0 +1 @@
|
||||
H
|
1
tests/expectations/plus_minus.txt
Normal file
1
tests/expectations/plus_minus.txt
Normal file
@ -0,0 +1 @@
|
||||
4
|
1
tests/expectations/pointer_in_expression.txt
Normal file
1
tests/expectations/pointer_in_expression.txt
Normal file
@ -0,0 +1 @@
|
||||
5
|
2
tests/expectations/print_boolean.txt
Normal file
2
tests/expectations/print_boolean.txt
Normal file
@ -0,0 +1,2 @@
|
||||
t
|
||||
f
|
12
tests/expectations/print_equals.txt
Normal file
12
tests/expectations/print_equals.txt
Normal file
@ -0,0 +1,12 @@
|
||||
t
|
||||
f
|
||||
t
|
||||
f
|
||||
f
|
||||
t
|
||||
t
|
||||
f
|
||||
t
|
||||
f
|
||||
f
|
||||
t
|
5
tests/expectations/print_in_loop.txt
Normal file
5
tests/expectations/print_in_loop.txt
Normal file
@ -0,0 +1,5 @@
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
1
tests/expectations/print_number.txt
Normal file
1
tests/expectations/print_number.txt
Normal file
@ -0,0 +1 @@
|
||||
3
|
2
tests/expectations/procedure_2_statements.txt
Normal file
2
tests/expectations/procedure_2_statements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
t
|
||||
5
|
1
tests/expectations/procedure_definition.txt
Normal file
1
tests/expectations/procedure_definition.txt
Normal file
@ -0,0 +1 @@
|
||||
5
|
1
tests/expectations/procedure_print_argument.txt
Normal file
1
tests/expectations/procedure_print_argument.txt
Normal file
@ -0,0 +1 @@
|
||||
5
|
1
tests/expectations/subtraction.txt
Normal file
1
tests/expectations/subtraction.txt
Normal file
@ -0,0 +1 @@
|
||||
1
|
1
tests/failures/missing_semicolon.txt
Normal file
1
tests/failures/missing_semicolon.txt
Normal file
@ -0,0 +1 @@
|
||||
tests/missing_semicolon.eln:3:3: Unexpected token «identifier»
|
1
tests/failures/single_word_error.txt
Normal file
1
tests/failures/single_word_error.txt
Normal file
@ -0,0 +1 @@
|
||||
tests/single_word_error.eln:1:1: Unexpected token «identifier»
|
11
tests/if_condition.eln
Normal file
11
tests/if_condition.eln
Normal file
@ -0,0 +1,11 @@
|
||||
begin
|
||||
if True then writei(8);
|
||||
if False then
|
||||
begin
|
||||
writei(5);
|
||||
writei(5);
|
||||
writei(5)
|
||||
end;
|
||||
if 1 < 2 then writei(9);
|
||||
if 1 > 2 then writei(10)
|
||||
end.
|
2
tests/left_nested_sum.eln
Normal file
2
tests/left_nested_sum.eln
Normal file
@ -0,0 +1,2 @@
|
||||
writei((3 + 4) + 1)
|
||||
.
|
4
tests/missing_semicolon.eln
Normal file
4
tests/missing_semicolon.eln
Normal file
@ -0,0 +1,4 @@
|
||||
begin
|
||||
writei(1)
|
||||
writei(2)
|
||||
end.
|
4
tests/multiline_output.eln
Normal file
4
tests/multiline_output.eln
Normal file
@ -0,0 +1,4 @@
|
||||
begin
|
||||
writei(5);
|
||||
writei(7)
|
||||
end.
|
2
tests/multiply.eln
Normal file
2
tests/multiply.eln
Normal file
@ -0,0 +1,2 @@
|
||||
writei(4 * 2)
|
||||
.
|
2
tests/multiply_3.eln
Normal file
2
tests/multiply_3.eln
Normal file
@ -0,0 +1,2 @@
|
||||
writei(3 * 4 * 2)
|
||||
.
|
2
tests/plus_minus.eln
Normal file
2
tests/plus_minus.eln
Normal file
@ -0,0 +1,2 @@
|
||||
writei(2 + 3 - 1)
|
||||
.
|
6
tests/pointer_in_expression.eln
Normal file
6
tests/pointer_in_expression.eln
Normal file
@ -0,0 +1,6 @@
|
||||
var a: Int, b: ^Int;
|
||||
begin
|
||||
a := 5;
|
||||
b := @a;
|
||||
writei(b^)
|
||||
end.
|
4
tests/print_boolean.eln
Normal file
4
tests/print_boolean.eln
Normal file
@ -0,0 +1,4 @@
|
||||
begin
|
||||
writeb(True);
|
||||
writeb(False)
|
||||
end.
|
14
tests/print_equals.eln
Normal file
14
tests/print_equals.eln
Normal file
@ -0,0 +1,14 @@
|
||||
begin
|
||||
writeb(5 = 5);
|
||||
writeb(5 = 4);
|
||||
writeb(5 /= 4);
|
||||
writeb(5 /= 5);
|
||||
writeb(5 < 4);
|
||||
writeb(4 < 5);
|
||||
writeb(5 >= 4);
|
||||
writeb(4 >= 5);
|
||||
writeb(5 > 4);
|
||||
writeb(4 > 5);
|
||||
writeb(5 <= 4);
|
||||
writeb(4 <= 5)
|
||||
end.
|
10
tests/print_in_loop.eln
Normal file
10
tests/print_in_loop.eln
Normal file
@ -0,0 +1,10 @@
|
||||
var i: Int;
|
||||
begin
|
||||
i := 1;
|
||||
|
||||
while i < 6 do
|
||||
begin
|
||||
writei(i);
|
||||
i := i + 1
|
||||
end;
|
||||
end.
|
2
tests/print_number.eln
Normal file
2
tests/print_number.eln
Normal file
@ -0,0 +1,2 @@
|
||||
writei(3)
|
||||
.
|
9
tests/procedure_2_statements.eln
Normal file
9
tests/procedure_2_statements.eln
Normal file
@ -0,0 +1,9 @@
|
||||
proc g(a: Boolean, b: Int)
|
||||
begin
|
||||
writeb(a);
|
||||
writei(b)
|
||||
end;
|
||||
|
||||
begin
|
||||
g(True, 5)
|
||||
end.
|
5
tests/procedure_definition.eln
Normal file
5
tests/procedure_definition.eln
Normal file
@ -0,0 +1,5 @@
|
||||
proc f() writei(5);
|
||||
|
||||
begin
|
||||
f()
|
||||
end.
|
5
tests/procedure_print_argument.eln
Normal file
5
tests/procedure_print_argument.eln
Normal file
@ -0,0 +1,5 @@
|
||||
proc f(a: Int) writei(a);
|
||||
|
||||
begin
|
||||
f(5)
|
||||
end.
|
1
tests/single_word_error.eln
Normal file
1
tests/single_word_error.eln
Normal file
@ -0,0 +1 @@
|
||||
asdf
|
2
tests/subtraction.eln
Normal file
2
tests/subtraction.eln
Normal file
@ -0,0 +1,2 @@
|
||||
writei(5 - 4)
|
||||
.
|
2
tests/sum.eln
Normal file
2
tests/sum.eln
Normal file
@ -0,0 +1,2 @@
|
||||
writei(1 + 7)
|
||||
.
|
@ -1,2 +0,0 @@
|
||||
! + 1 7
|
||||
.
|
2
tests/sums.eln
Normal file
2
tests/sums.eln
Normal file
@ -0,0 +1,2 @@
|
||||
writei(1 + (3 + 4))
|
||||
.
|
@ -1,2 +0,0 @@
|
||||
! + 1 (+ 3 4)
|
||||
.
|
335
tools/cross.js
Normal file
335
tools/cross.js
Normal file
@ -0,0 +1,335 @@
|
||||
import fs from 'fs/promises'
|
||||
import path from 'node:path'
|
||||
import childProcess from 'node:child_process'
|
||||
import process from 'process'
|
||||
import os from 'os'
|
||||
import { Buffer } from 'node:buffer'
|
||||
|
||||
// Define constants.
|
||||
const tmp = path.resolve('./build/tools')
|
||||
const baseDirectory = path.resolve('./tools')
|
||||
|
||||
const kernelVersion = '5.15.159'
|
||||
const gccVersion = '14.1.0'
|
||||
const binutilsVersion = '2.42'
|
||||
const glibcVersion = '2.39'
|
||||
|
||||
async function gccVerbose (gccBinary) {
|
||||
const env = {
|
||||
...process.env,
|
||||
LANG: 'C'
|
||||
}
|
||||
const gccV = childProcess.spawn(gccBinary, ['--verbose'], { stdio: ['ignore', 'ignore', 'pipe'], env })
|
||||
const buffers = []
|
||||
|
||||
gccV.stderr.on('data', function (data) {
|
||||
buffers.push(data)
|
||||
})
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
gccV.on('exit', function () {
|
||||
resolve(Buffer.concat(buffers).toString())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function findBuildTarget (gccVersion) {
|
||||
let gccBinary = 'gcc'
|
||||
let output = await gccVerbose(gccBinary)
|
||||
|
||||
if (output.startsWith('Apple clang')) {
|
||||
gccBinary = `gcc-${gccVersion.split('.')[0]}`
|
||||
output = await gccVerbose(gccBinary)
|
||||
}
|
||||
return output
|
||||
.split('\n')
|
||||
.reduce(function (accumulator, line) {
|
||||
if (line.startsWith('Target: ')) {
|
||||
return {
|
||||
...accumulator,
|
||||
build: line.split(' ')[1]
|
||||
}
|
||||
} else if (line.startsWith('COLLECT_GCC')) {
|
||||
return {
|
||||
...accumulator,
|
||||
gcc: line.split('=')[1]
|
||||
}
|
||||
} else {
|
||||
return accumulator
|
||||
}
|
||||
}, {})
|
||||
}
|
||||
|
||||
async function downloadAndUnarchive (url) {
|
||||
const response = await fetch(url)
|
||||
const bodyReader = response.body.getReader()
|
||||
const basename = path.basename(url.pathname)
|
||||
let archiveType = ''
|
||||
let rootDirectory = ''
|
||||
|
||||
switch (path.extname(basename)) {
|
||||
case '.bz2':
|
||||
archiveType = '-j'
|
||||
rootDirectory = path.basename(basename, '.tar.bz2')
|
||||
break
|
||||
case '.xz':
|
||||
archiveType = '-J'
|
||||
rootDirectory = path.basename(basename, '.tar.xz')
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return new Promise(async function (resolve, reject) {
|
||||
const untar = childProcess.spawn('tar', ['-C', tmp, archiveType, '-xv'], { stdio: ['pipe', 'inherit', 'inherit'] })
|
||||
let done = false
|
||||
|
||||
untar.on('exit', function () {
|
||||
resolve(path.join(tmp, rootDirectory))
|
||||
})
|
||||
|
||||
do {
|
||||
const chunk = await bodyReader.read()
|
||||
|
||||
done = chunk.done
|
||||
if (chunk.value !== undefined) {
|
||||
untar.stdin.write(chunk.value)
|
||||
}
|
||||
} while (!done)
|
||||
|
||||
untar.stdin.end()
|
||||
})
|
||||
}
|
||||
|
||||
async function copyGlibcHeaders () {
|
||||
const sourceDirectory = await downloadAndUnarchive(
|
||||
new URL(`https://ftp.gnu.org/gnu/glibc/glibc-${glibcVersion}.tar.xz`)
|
||||
)
|
||||
const includeDirectory = path.join(tmp, 'include')
|
||||
|
||||
await fs.mkdir(includeDirectory)
|
||||
await fs.cp(path.join(sourceDirectory, 'elf/elf.h'), path.join(includeDirectory, 'elf.h'))
|
||||
|
||||
return sourceDirectory
|
||||
}
|
||||
|
||||
async function buildKernel (rootfs, options) {
|
||||
const cwd = await downloadAndUnarchive(
|
||||
new URL(`https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${kernelVersion}.tar.xz`)
|
||||
)
|
||||
const env = {
|
||||
...process.env,
|
||||
CROSS_COMPILE: `${options.target}-`,
|
||||
ARCH: 'riscv',
|
||||
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`,
|
||||
HOSTCFLAGS: `-D_UUID_T -D__GETHOSTUUID_H -I${path.join(tmp, 'include')}`
|
||||
}
|
||||
childProcess.execFileSync('make', ['rv32_defconfig'], { stdio: 'inherit', env, cwd })
|
||||
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
|
||||
childProcess.execFileSync('make', ['headers'], { stdio: 'inherit', env, cwd })
|
||||
|
||||
const userDirectory = path.join(options.sysroot, 'usr')
|
||||
await fs.cp(path.join(cwd, 'usr/include'), path.join(userDirectory, 'include'), { recursive: true })
|
||||
|
||||
return path.join(cwd, 'arch/riscv/boot/Image')
|
||||
}
|
||||
|
||||
async function buildInit (rootfs, options) {
|
||||
const env = {
|
||||
...process.env,
|
||||
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
||||
}
|
||||
const compilerArguments = [
|
||||
'-ffreestanding', '-static',
|
||||
'-o', path.join(tmp, 'init'),
|
||||
path.join(baseDirectory, 'init.c')
|
||||
]
|
||||
childProcess.execFileSync('riscv32-unknown-linux-gnu-gcc', compilerArguments, { stdio: 'inherit', env })
|
||||
}
|
||||
|
||||
async function buildGlibc (sourceDirectory, rootfs, options) {
|
||||
const configureOptions = [
|
||||
'--prefix=/usr',
|
||||
`--host=${options.target}`,
|
||||
`--target=${options.target}`,
|
||||
`--build=${options.build}`,
|
||||
`--enable-kernel=${kernelVersion}`,
|
||||
`--with-headers=${path.join(options.sysroot, 'usr/include')}`,
|
||||
'--disable-nscd',
|
||||
'--disable-libquadmath',
|
||||
'--disable-libitm',
|
||||
'--disable-werror',
|
||||
'libc_cv_forced_unwind=yes'
|
||||
]
|
||||
const bin = path.join(rootfs, 'bin')
|
||||
const env = {
|
||||
...process.env,
|
||||
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
||||
}
|
||||
const cwd = path.join(path.dirname(sourceDirectory), 'build-glibc');
|
||||
await fs.mkdir(cwd)
|
||||
|
||||
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), './configure'), configureOptions, {
|
||||
stdio: 'inherit',
|
||||
env,
|
||||
cwd
|
||||
})
|
||||
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
|
||||
childProcess.execFileSync('make', [`install_root=${options.sysroot}`, 'install'], { stdio: 'inherit', env, cwd })
|
||||
}
|
||||
|
||||
async function buildCrossBinutils (rootfs, options) {
|
||||
const sourceDirectory = await downloadAndUnarchive(
|
||||
new URL(`https://ftp.gnu.org/gnu/binutils/binutils-${binutilsVersion}.tar.xz`)
|
||||
)
|
||||
const cwd = path.join(path.dirname(sourceDirectory), 'build-binutils');
|
||||
await fs.mkdir(cwd)
|
||||
|
||||
const env = {
|
||||
...process.env,
|
||||
CC: options.gcc,
|
||||
CXX: options.gxx
|
||||
}
|
||||
const configureOptions = [
|
||||
`--prefix=${rootfs}`,
|
||||
`--target=${options.target}`,
|
||||
'--disable-nls',
|
||||
'--enable-gprofng=no',
|
||||
'--disable-werror',
|
||||
'--enable-default-hash-style=gnu',
|
||||
'--disable-libquadmath'
|
||||
]
|
||||
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
|
||||
stdio: 'inherit',
|
||||
cwd,
|
||||
env
|
||||
})
|
||||
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', cwd, env })
|
||||
childProcess.execFileSync('make', ['install'], { stdio: 'inherit', cwd, env })
|
||||
|
||||
return sourceDirectory
|
||||
}
|
||||
|
||||
async function buildGCC1 (rootfs, options) {
|
||||
const sourceDirectory = await downloadAndUnarchive(
|
||||
new URL(`https://gcc.gnu.org/pub/gcc/releases/gcc-${gccVersion}/gcc-${gccVersion}.tar.xz`)
|
||||
)
|
||||
const cwd = path.join(path.dirname(sourceDirectory), 'build-gcc');
|
||||
await fs.mkdir(cwd)
|
||||
|
||||
childProcess.execFileSync(path.join(sourceDirectory, 'contrib/download_prerequisites'), {
|
||||
stdio: 'inherit',
|
||||
cwd: sourceDirectory
|
||||
})
|
||||
const configureOptions = [
|
||||
`--prefix=${rootfs}`,
|
||||
`--with-sysroot=${options.sysroot}`,
|
||||
'--enable-languages=c,c++',
|
||||
'--disable-shared',
|
||||
'--with-arch=rv32imafdc',
|
||||
'--with-abi=ilp32d',
|
||||
'--with-tune=rocket',
|
||||
'--with-isa-spec=20191213',
|
||||
'--disable-bootstrap',
|
||||
'--disable-multilib',
|
||||
'--disable-libmudflap',
|
||||
'--disable-libssp',
|
||||
'--disable-libquadmath',
|
||||
'--disable-libsanitizer',
|
||||
'--disable-threads',
|
||||
'--disable-libatomic',
|
||||
'--disable-libgomp',
|
||||
'--disable-libvtv',
|
||||
'--disable-libstdcxx',
|
||||
'--disable-nls',
|
||||
'--with-newlib',
|
||||
'--without-headers',
|
||||
`--target=${options.target}`,
|
||||
`--build=${options.build}`,
|
||||
`--host=${options.build}`
|
||||
]
|
||||
const flags = '-O2 -fPIC'
|
||||
const env = {
|
||||
...process.env,
|
||||
CC: options.gcc,
|
||||
CXX: options.gxx,
|
||||
CFLAGS: flags,
|
||||
CXXFLAGS: flags,
|
||||
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
||||
}
|
||||
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
|
||||
stdio: 'inherit',
|
||||
env,
|
||||
cwd
|
||||
})
|
||||
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
|
||||
childProcess.execFileSync('make', ['install'], { stdio: 'inherit', env, cwd })
|
||||
|
||||
return sourceDirectory
|
||||
}
|
||||
|
||||
async function buildGCC2 (sourceDirectory, rootfs, options) {
|
||||
const cwd = path.join(path.dirname(sourceDirectory), 'build-gcc');
|
||||
await fs.rm(cwd, { recursive: true, force: true })
|
||||
await fs.mkdir(cwd)
|
||||
|
||||
const configureOptions = [
|
||||
`--prefix=${rootfs}`,
|
||||
`--with-sysroot=${options.sysroot}`,
|
||||
'--enable-languages=c,c++,lto',
|
||||
'--enable-lto',
|
||||
'--enable-shared',
|
||||
'--with-arch=rv32imafdc',
|
||||
'--with-abi=ilp32d',
|
||||
'--with-tune=rocket',
|
||||
'--with-isa-spec=20191213',
|
||||
'--disable-bootstrap',
|
||||
'--disable-multilib',
|
||||
'--enable-checking=release',
|
||||
'--disable-libssp',
|
||||
'--disable-libquadmath',
|
||||
'--enable-threads=posix',
|
||||
'--with-default-libstdcxx-abi=new',
|
||||
'--disable-nls',
|
||||
`--target=${options.target}`,
|
||||
`--build=${options.build}`,
|
||||
`--host=${options.build}`
|
||||
]
|
||||
const flags = '-O2 -fPIC'
|
||||
const env = {
|
||||
...process.env,
|
||||
CFLAGS: flags,
|
||||
CXXFLAGS: flags,
|
||||
PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`
|
||||
}
|
||||
childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, {
|
||||
stdio: 'inherit',
|
||||
env,
|
||||
cwd
|
||||
})
|
||||
childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd })
|
||||
childProcess.execFileSync('make', ['install'], { stdio: 'inherit', env, cwd })
|
||||
}
|
||||
|
||||
const rootfs = path.join(tmp, 'rootfs')
|
||||
|
||||
const options = await findBuildTarget(gccVersion)
|
||||
options.gxx = options.gcc.replaceAll('c', '+')
|
||||
options.sysroot = path.join(tmp, 'sysroot')
|
||||
options.target = 'riscv32-unknown-linux-gnu'
|
||||
|
||||
for (const targetDirectory of [tmp, rootfs]) {
|
||||
await fs.rm(targetDirectory, { recursive: true, force: true })
|
||||
await fs.mkdir(targetDirectory)
|
||||
}
|
||||
|
||||
const binutilsSource = await buildCrossBinutils(rootfs, options)
|
||||
const gccSource = await buildGCC1(rootfs, options)
|
||||
|
||||
const glibcSource = await copyGlibcHeaders()
|
||||
const kernelImage = await buildKernel(rootfs, options)
|
||||
await buildGlibc(glibcSource, rootfs, options)
|
||||
await buildGCC2(gccSource, rootfs, options)
|
||||
buildInit (rootfs, options)
|
||||
|
||||
console.log(kernelImage)
|
2
tools/files/fstab
Normal file
2
tools/files/fstab
Normal file
@ -0,0 +1,2 @@
|
||||
/dev/vda / ext4 defaults 1 1
|
||||
proc /proc proc defaults 0 0
|
3
tools/files/inittab
Normal file
3
tools/files/inittab
Normal file
@ -0,0 +1,3 @@
|
||||
::sysinit:/etc/init.d/rcS
|
||||
::shutdown:/etc/init.d/rcK
|
||||
::askfirst:-/bin/sh
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user