├── .editorconfig
├── .gitattributes
├── .github
├── FUNDING.yml
├── assets
│ └── logo.png
└── workflows
│ └── ci.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── .vscode
├── launch.json
└── settings.json
├── CODE_OF_CONDUCT.md
├── LICENSE.md
├── README.md
├── arch.go
├── bin.go
├── cmd
└── elfdump.go
├── doc.go
├── elf32.go
├── elf64.go
├── err.go
├── file.go
├── flags.go
├── gnuver.go
├── go.mod
├── go.sum
├── header.go
├── json.go
├── log
├── README.md
├── filter.go
├── filter_test.go
├── global.go
├── global_test.go
├── helper.go
├── helper_test.go
├── level.go
├── level_test.go
├── log.go
├── log_test.go
├── std.go
├── std_test.go
├── value.go
└── value_test.go
├── parser.go
├── parser_test.go
├── reader.go
├── reloc.go
├── sections.go
├── stringer.go
├── symbol.go
└── test
└── ls
/.editorconfig:
--------------------------------------------------------------------------------
1 | ; https://editorconfig.org/
2 |
3 | root = true
4 |
5 | [*]
6 | insert_final_newline = true
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | indent_style = space
10 | indent_size = 2
11 |
12 | [{Makefile,go.mod,go.sum,*.go,.gitmodules}]
13 | indent_style = tab
14 | indent_size = 4
15 |
16 | [*.md]
17 | indent_size = 4
18 | trim_trailing_whitespace = false
19 |
20 | eclint_indent_style = unset
21 |
22 | [Dockerfile]
23 | indent_size = 4
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Treat all files in the Go repo as binary, with no git magic updating
2 | # line endings. This produces predictable results in different environments.
3 | #
4 | # Windows users contributing to Go will need to use a modern version
5 | # of git and editors capable of LF line endings.
6 | #
7 | # Windows .bat files are known to have multiple bugs when run with LF
8 | # endings, and so they are checked in with CRLF endings, with a test
9 | # in test/winbatch.go to catch problems. (See golang.org/issue/37791.)
10 | #
11 | # We'll prevent accidental CRLF line endings from entering the repo
12 | # via the git-codereview gofmt checks and tests.
13 | #
14 | # See golang.org/issue/9281.
15 |
16 | * -text
17 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: LordNoteworthy
2 |
--------------------------------------------------------------------------------
/.github/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saferwall/elf/33b33db927a3e67b19495c9c3d13879433f65fe1/.github/assets/logo.png
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: build-test
2 |
3 | on: [push]
4 |
5 | jobs:
6 | test:
7 | name: build-test
8 | strategy:
9 | fail-fast: false
10 | matrix:
11 | go-version: [1.17.x, 1.18.x, 1.19.x, 1.20.x, 1.21.x]
12 | os: [ubuntu-latest, macos-latest, windows-latest]
13 | runs-on: ${{ matrix.os }}
14 | steps:
15 | - name: Checkout
16 | uses: actions/checkout@v3
17 |
18 | - name: Install Go
19 | uses: actions/setup-go@v4
20 | with:
21 | go-version: ${{ matrix.go-version }}
22 |
23 | - name: Build
24 | run: |
25 | go env -w GOFLAGS=-mod=mod
26 | go build -v ./...
27 |
28 | - name: Test With Coverage
29 | run: go test -race -coverprofile=coverage -covermode=atomic
30 |
31 | - name: Upload coverage to Codecov
32 | uses: codecov/codecov-action@v2
33 | with:
34 | files: ./coverage
35 | if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.21.x'
36 |
37 | - name: Go vet
38 | run: |
39 | go vet .
40 | if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.21.x'
41 |
42 | - name: Go Static Check
43 | run: |
44 | go install honnef.co/go/tools/cmd/staticcheck@latest
45 | staticcheck .
46 | if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.21.x'
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # If you prefer the allow list template instead of the deny list, see community template:
2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3 | #
4 | # Binaries for programs and plugins
5 | *.exe
6 | *.exe~
7 | *.dll
8 | *.so
9 | *.dylib
10 |
11 | # Test binary, built with `go test -c`
12 | *.test
13 |
14 | # Output of the go coverage tool, specifically when used with LiteIDE
15 | *.out
16 | coverage
17 |
18 | # Go workspace file
19 | go.work
20 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/pre-commit/pre-commit-hooks
3 | rev: v4.2.0
4 | hooks:
5 | - id: trailing-whitespace
6 | - id: end-of-file-fixer
7 |
8 | - repo: https://github.com/dnephin/pre-commit-golang
9 | rev: master
10 | hooks:
11 | - id: go-fmt
12 | - id: go-vet
13 | - id: go-lint
14 | - id: go-imports
15 | - id: go-cyclo
16 | args: [-over=15]
17 | - id: validate-toml
18 | - id: no-go-testing
19 | - id: golangci-lint
20 | - id: go-critic
21 | - id: go-unit-tests
22 | - id: go-build
23 | - id: go-mod-tidy
24 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Launch file",
9 | "type": "go",
10 | "request": "launch",
11 | "mode": "debug",
12 | "program": "${file}",
13 | "args": ["dump", "--file-header", "/bin/ls"]
14 | },
15 | {
16 | "name": "Attach to Process",
17 | "type": "go",
18 | "request": "attach",
19 | "mode": "local",
20 | "processId": 0
21 | },
22 | {
23 | "name": "Launch Package",
24 | "type": "go",
25 | "request": "launch",
26 | "mode": "auto",
27 | "program": "${fileDirname}"
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "elfdump"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at report@saferwall.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # ELF File Format Parser
4 |
5 | [](https://pkg.go.dev/github.com/saferwall/elf)  [](https://goreportcard.com/report/github.com/saferwall/elf) [](https://codecov.io/gh/saferwall/elf) 
6 |
7 | **elf** is a go package for parsing Executable and Linkable Format (ELF). This package is designed for static malware analysis and reverse engineering.
8 |
9 | ## Install
10 |
11 | You can install the ```elf``` package and its dependencies using the ```go get``` command.
12 |
13 | ```sh
14 |
15 | go get github.com/saferwall/elf
16 |
17 | ```
18 |
19 | ## Usage
20 |
21 | ```go
22 |
23 | package main
24 |
25 | import (
26 | "encoding/json"
27 | "fmt"
28 |
29 | "github.com/saferwall/elf"
30 | )
31 |
32 |
33 | func main() {
34 |
35 | p, err := elf.New("/bin/ls")
36 | defer p.CloseFile()
37 | if err != nil {
38 | panic(err)
39 | }
40 | err = p.Parse()
41 | if err != nil {
42 | panic(err)
43 | }
44 | jsonFile, err := p.DumpJSON()
45 | if err != nil {
46 | panic(err)
47 | }
48 | fmt.Println(jsonFile)
49 | }
50 |
51 | ```
52 |
53 | ## References
54 |
55 | - https://refspecs.linuxfoundation.org/elf/elf.pdf
56 | - https://github.com/freebsd/freebsd-src/blob/main/sys/sys/elf_common.h
57 |
--------------------------------------------------------------------------------
/bin.go:
--------------------------------------------------------------------------------
1 | package elf
2 |
3 | // ELFBin64 represents a 64-bit ELF binary.
4 | type ELFBin64 struct {
5 | Header64 ELF64Header
6 | SectionHeaders64 []ELF64SectionHeader
7 | ProgramHeaders64 []ELF64ProgramHeader
8 | Sections64 []*ELF64Section
9 | Symbols64 []ELF64SymbolTableEntry
10 | }
11 |
12 | // ELFBin32 represents a 32-bit ELF binary.
13 | type ELFBin32 struct {
14 | Header32 ELF32Header
15 | SectionHeaders32 []ELF32SectionHeader
16 | ProgramHeaders32 []ELF32ProgramHeader
17 | Sections32 []*ELF32Section
18 | Symbols32 []ELF32SymbolTableEntry
19 | }
20 |
--------------------------------------------------------------------------------
/cmd/elfdump.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 Saferwall. All rights reserved.
2 | // Use of this source code is governed by Apache v2 license
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "flag"
9 | "fmt"
10 | "os"
11 | "strings"
12 | "text/tabwriter"
13 |
14 | "github.com/saferwall/elf"
15 | )
16 |
17 | type config struct {
18 | wantELFFileHeader bool
19 | }
20 |
21 | var Usage = func() {
22 | fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
23 | flag.PrintDefaults()
24 | }
25 |
26 | func dumpFileHeader(fileHdr elf.FileHeader) {
27 | w := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)
28 | fmt.Print("\n\t------[ File Header ]------\n\n")
29 | fmt.Fprintf(w, "Magic:\t %v\n", fileHdr.Ident.Magic)
30 | fmt.Fprintf(w, "Class:\t %v\n", fileHdr.Ident.Class)
31 | fmt.Fprintf(w, "Data:\t %v\n", fileHdr.Ident.Data)
32 | fmt.Fprintf(w, "Version:\t %v\n", fileHdr.Ident.Version)
33 | fmt.Fprintf(w, "OS/ABI:\t %v\n", fileHdr.Ident.ABIVersion)
34 | fmt.Fprintf(w, "Type:\t %v\n", fileHdr.Type)
35 | fmt.Fprintf(w, "Machine:\t %v\n", fileHdr.Machine)
36 | fmt.Fprintf(w, "Version:\t %v\n", fileHdr.Version)
37 | fmt.Fprintf(w, "Entry point address :\t %v\n", fileHdr.Entry)
38 | fmt.Fprintf(w, "Start of program headers:\t %v\n", fileHdr.ProgramHeaderOffset)
39 | fmt.Fprintf(w, "Start of section headers:\t %v\n", fileHdr.SectionHeaderOffset)
40 | fmt.Fprintf(w, "Flags:\t %v\n", fileHdr.Flags)
41 | fmt.Fprintf(w, "Size of this header:\t %v\n", fileHdr.Size)
42 | fmt.Fprintf(w, "Size of program headers:\t %v\n", fileHdr.ProgramHeaderEntrySize)
43 | fmt.Fprintf(w, "Number of program headers:\t %v\n", fileHdr.ProgramHeaderNum)
44 | fmt.Fprintf(w, "Size of section headers:\t %v\n", fileHdr.SectionHeaderEntrySize)
45 | fmt.Fprintf(w, "Number of section headers:\t %v\n", fileHdr.SectionHeaderNum)
46 | fmt.Fprintf(w, "Section header string table index:\t %v\n", fileHdr.SectionHeaderStringIdx)
47 | w.Flush()
48 | }
49 |
50 | func parse(filename string, cfg config) {
51 | p, err := elf.New(filename, nil)
52 | defer p.CloseFile()
53 | if err != nil {
54 | panic(err)
55 | }
56 | err = p.Parse()
57 | if err != nil {
58 | panic(err)
59 | }
60 |
61 | if cfg.wantELFFileHeader {
62 | dumpFileHeader(p.F.FileHeader)
63 | }
64 | }
65 |
66 | func main() {
67 |
68 | flag.Usage = func() {
69 | fmt.Fprintf(os.Stderr, "This is not helpful.\n")
70 | }
71 | dumpCmd := flag.NewFlagSet("dump", flag.ExitOnError)
72 | dumpELFFileHdr := dumpCmd.Bool("file-header", false, "Display the ELF file header")
73 | verCmd := flag.NewFlagSet("version", flag.ExitOnError)
74 |
75 | if len(os.Args) < 2 {
76 | showHelp(nil)
77 | }
78 |
79 | switch os.Args[1] {
80 |
81 | case "dump":
82 | var filename string
83 | for _, arg := range os.Args[2:] {
84 | if !strings.HasPrefix(arg, "-") {
85 | filename = arg
86 | }
87 | }
88 | if len(filename) == 0 {
89 | showHelp(dumpCmd)
90 | }
91 |
92 | if err := dumpCmd.Parse(os.Args[2:]); err != nil {
93 | showHelp(dumpCmd)
94 | }
95 |
96 | cfg := config{
97 | wantELFFileHeader: *dumpELFFileHdr,
98 | }
99 |
100 | parse(filename, cfg)
101 |
102 | case "version":
103 | verCmd.Parse(os.Args[2:])
104 | fmt.Println("You are using version 0.4.0")
105 | default:
106 | showHelp(nil)
107 | }
108 | }
109 |
110 | func showHelp(cmd *flag.FlagSet) {
111 | fmt.Print(
112 | `
113 | ┏┓┏┓┏┓┏┓┳┓┓ ┏┏┓┓ ┓
114 | ┗┓┣┫┣ ┣ ┣┫┃┃┃┣┫┃ ┃
115 | ┗┛┛┗┻ ┗┛┛┗┗┻┛┛┗┗┛┗┛
116 | ┏┓┓ ┏┓ ┏┓┏┓┳┓┏┓┏┓┳┓
117 | ┣ ┃ ┣ ┃┃┣┫┣┫┗┓┣ ┣┫
118 | ┗┛┗┛┻ ┣┛┛┗┛┗┗┛┗┛┛┗
119 |
120 |
121 | An ELF Parser built for speed and malware-analysis in mind.
122 | Brought to you by Saferwall (c) 2018 Apache-2.0
123 | `)
124 | if cmd == nil {
125 | fmt.Println("\nAvailable sub-commands 'dump' or 'version' subcommands !")
126 | } else {
127 | fmt.Println("\nUsage: elfdump elf-file(s)\nOptions are:")
128 | cmd.PrintDefaults()
129 | }
130 |
131 | os.Exit(1)
132 | }
133 |
--------------------------------------------------------------------------------
/doc.go:
--------------------------------------------------------------------------------
1 | package elf
2 |
3 | // ELF Header Structure
4 | // +--------------------+
5 | // + EIDENT (16 bytes) + => ELF Compilation metadata.
6 | // +--------------------+
7 | // + Type (2 bytes) + => Binary type (relocatable object file or executable binary)
8 | // +--------------------+
9 | // + Machine (2 bytes) + => Machine architecture.
10 | // +--------------------+
11 | // + Version (4 bytes) + => ELF File Format version.
12 | // +--------------------+
13 | // + PHOffset (4 bytes) + => File Offset to the beginning of the program header.
14 | // +--------------------+
15 | // + SHOffset (4 bytes) + => File Offset to the beginning of the section header.
16 | // +--------------------+
17 | // + Entry (4 bytes) + => Binary entrypoint (Virtual Address where execution starts).
18 | // +--------------------+
19 | // + Flags (4 bytes) + => Flags specific to the compilation architecture.
20 | // +--------------------+
21 | // + EHSize (2 bytes) + => Size in bytes of the executable header.
22 | // +--------------------+
23 | // + PHEntSize (2 bytes)+ => Program headers size.
24 | // +--------------------+
25 | // + PHNum (2 bytes) + => Program headers number.
26 | // +--------------------+
27 | // + SHEntSize (2 bytes)+ => Section headers size.
28 | // +--------------------+
29 | // + SHNum (2 bytes) + => Section headers numbers.
30 | // +--------------------+
31 | // + SHStrndx (2 bytes) + => Index of the string table ".shstrtab"
32 | // +--------------------+
33 |
34 | // Ident the first 4 bytes of the eIdent array contain the magic bytes of the ELF file format.
35 | // Indexes 4 through 15 contain other metadata.
36 | // Namely indexes 9 through 15 represent EI_PAD field which designate padding.
37 | // Indexes 4 through 9 are symbolically referred to as : EI_CLASS, EI_DATA,EI_VERSION, EI_OSABI and
38 | // EI_ABIVERSION.
39 | // EI_CLASS byte represents the binary class (specifies whether a 32-Bit or 64-Bit binary).
40 | // EI_DATA byte specifies whether integers are encoded as Big-Endian or Little-Endian
41 | // EI_VERSION byte specifies the current elf version, currently the only valid value is EV_CURRENT=1.
42 |
43 | // Standard sections are sections that dominate ELF binaries.
44 | // ----------------------------------------------------------------
45 | // | Name | Type | Flags | Usage |
46 | // |==-------------------------------------------------------------|
47 | // | .bss | SHT_NOBITS | A,W | Unitialized data |
48 | // |---------------------------------------------------------------|
49 | // |.data | SHT_PROGBITS | A,W | Initialized data |
50 | // |---------------------------------------------------------------|
51 | // |.interop| SHT_PROGBITS | [A] | Program interpreter path name |
52 | // |---------------------------------------------------------------|
53 | // |.rodata | SHT_PROGBITS | A | Read only data ()|
54 | // |---------------------------------------------------------------|
55 | // |.text | SHT_PROGBITS | A, X | Executable code |
56 | // |---------------------------------------------------------------|
57 |
--------------------------------------------------------------------------------
/elf32.go:
--------------------------------------------------------------------------------
1 | // Package elf : elf32.go implements the main structures of the 32-bit ELF file format.
2 | // spec : https://refspecs.linuxbase.org/elf/elf.pdf
3 | package elf
4 |
5 | import "io"
6 |
7 | // ELF32Header represents the executable header of the ELF file format for (32-bit architecture).
8 | type ELF32Header struct {
9 | Ident [16]byte // File identification.
10 | Type uint16 // File type.
11 | Machine uint16 // Machine architecture.
12 | Version uint32 // ELF format version.
13 | Entry uint32 // Entry point.
14 | Phoff uint32 // Program header file offset.
15 | Shoff uint32 // Section header file offset.
16 | Flags uint32 // Architecture-specific flags.
17 | Ehsize uint16 // Size of ELF header in bytes.
18 | Phentsize uint16 // Size of program header entry.
19 | Phnum uint16 // Number of program header entries.
20 | Shentsize uint16 // Size of section header entry.
21 | Shnum uint16 // Number of section header entries.
22 | Shstrndx uint16 // Section name strings section.
23 | }
24 |
25 | // ELF32ProgramHeader represents the program header table which is an array
26 | // entries describing each program segment (in executable or shared object files)
27 | // sections are grouped into segments for in-memory loading.
28 | type ELF32ProgramHeader struct {
29 | Type uint32 // Segment type
30 | Off uint32 // Offset in file
31 | Vaddr uint32 // Virtual Address in memory
32 | Paddr uint32 // Reserved
33 | Filesz uint32 // Size of segment in file
34 | Memsz uint32 // Size of segment in memory
35 | Flags uint32 // Segment attributes
36 | Align uint32 // Segment alignment
37 | }
38 |
39 | // ELF32SectionHeader represents the section header of ELF 64-bit binaries.
40 | type ELF32SectionHeader struct {
41 | Name uint32 // Section name index in the Section Header String Table.
42 | Type uint32 // Section type.
43 | Flags uint32 // Section flags.
44 | Addr uint32 // Virtual address in memory.
45 | Off uint32 // Offset in file.
46 | Size uint32 // Section size in bytes.
47 | Link uint32 // Index of a related section.
48 | Info uint32 // Miscellaneous information depends on section type.
49 | AddrAlign uint32 // Address alignment boundary.
50 | EntSize uint32 // Size of each entry in the section.
51 | }
52 |
53 | // ELF32 Compression header.
54 | type ELF32CompressionHeader struct {
55 | Type uint32
56 | Size uint32
57 | AddrAlign uint32
58 | }
59 |
60 | // ELF32Section represents a single ELF section in a 32-bit binary.
61 | type ELF32Section struct {
62 | ELF32SectionHeader
63 | compressionType CompressionType
64 | compressionOffset int64
65 | SectionName string
66 | // Size is the size of this section (compressed) in the file in bytes.
67 | Size uint32
68 | // sectionReader is used to unpack byte data to decode section name
69 | sr *io.SectionReader
70 | }
71 |
72 | // ELF32DynamicTableEntry represents the Dynamic structure.
73 | // The ".dynamic" section contains an array of them.
74 | type ELF32DynamicTableEntry struct {
75 | Tag int32 // Identifies the type of the dynamic table entry.
76 | Val uint32 // Represents integer values
77 | }
78 |
--------------------------------------------------------------------------------
/elf64.go:
--------------------------------------------------------------------------------
1 | // Package elf : elf64.go implements the main structures of the 64-bit ELF file format.
2 | // spec : https://uclibc.org/docs/elf-64-gen.pdf
3 | package elf
4 |
5 | import "io"
6 |
7 | // ELF64Header represents the executable header of the ELF file format for (64-bit architecture).
8 | type ELF64Header struct {
9 | Ident [16]byte // File identification.
10 | Type uint16 // File type.
11 | Machine uint16 // Machine architecture.
12 | Version uint32 // ELF format version.
13 | Entry uint64 // Entry point.
14 | Phoff uint64 // Program header file offset.
15 | Shoff uint64 // Section header file offset.
16 | Flags uint32 // Architecture-specific flags.
17 | Ehsize uint16 // Size of ELF header in bytes.
18 | Phentsize uint16 // Size of program header entry.
19 | Phnum uint16 // Number of program header entries.
20 | Shentsize uint16 // Size of section header entry.
21 | Shnum uint16 // Number of section header entries.
22 | Shstrndx uint16 // Section name strings section.
23 | }
24 |
25 | // ELF64ProgramHeader represents the program header table which is an array
26 | // entries describing each program segment (in executable or shared object files)
27 | // sections are grouped into segments for in-memory loading.
28 | type ELF64ProgramHeader struct {
29 | Type uint32 // Segment type
30 | Flags uint32 // Segment attributes
31 | Off uint64 // Offset in file
32 | Vaddr uint64 // Virtual Address in memory
33 | Paddr uint64 // Reserved
34 | Filesz uint64 // Size of segment in file
35 | Memsz uint64 // Size of segment in memory
36 | Align uint64 // Segment alignment
37 | }
38 |
39 | // ELF64SectionHeader represents the section header of ELF 64-bit binaries.
40 | type ELF64SectionHeader struct {
41 | Name uint32 // Section name index in the Section Header String Table.
42 | Type uint32 // Section type.
43 | Flags uint64 // Section flags.
44 | Addr uint64 // Virtual address in memory.
45 | Off uint64 // Offset in file.
46 | Size uint64 // Section size in bytes.
47 | Link uint32 // Index of a related section.
48 | Info uint32 // Miscellaneous information depends on section type.
49 | AddrAlign uint64 // Address alignment boundary.
50 | EntSize uint64 // Size of each entry in the section.
51 | }
52 |
53 | // ELF64CompressionHeader defines the compression info of the section.
54 | type ELF64CompressionHeader struct {
55 | Type uint32
56 | _ uint32 // Reserved
57 | Size uint64
58 | AddrAlign uint64
59 | }
60 |
61 | // ELF64Section represents a single ELF section in a 32-bit binary.
62 | type ELF64Section struct {
63 | ELF64SectionHeader
64 | compressionType CompressionType
65 | compressionOffset int64
66 | SectionName string
67 | // Size is the size of this section (compressed) in the file in bytes.
68 | Size uint64
69 | // sectionReader is used to unpack byte data to decode section name
70 | sr *io.SectionReader
71 | }
72 |
73 | // ELF64DynamicTableEntry represents the Dynamic structure.
74 | // The ".dynamic" section contains an array of them.
75 | type ELF64DynamicTableEntry struct {
76 | Tag int64 // Identifies the type of the dynamic table entry.
77 | Val uint64 // Represents integer values
78 | }
79 |
--------------------------------------------------------------------------------
/err.go:
--------------------------------------------------------------------------------
1 | // Package elf : err.go implements the main error handling code & error strings.
2 | package elf
3 |
4 | import "errors"
5 |
6 | // ErrNoSymbols is returned by File.Symbols and File.DynamicSymbols
7 | // if there is no such section in the File.
8 | var ErrNoSymbols = errors.New("no symbol section")
9 |
10 | // ErrBadELFClass is returned if the ELF class is unknown.
11 | var ErrBadELFClass = errors.New("bad elf class")
12 |
--------------------------------------------------------------------------------
/file.go:
--------------------------------------------------------------------------------
1 | package elf
2 |
3 | import (
4 | "encoding/binary"
5 | "errors"
6 |
7 | "github.com/saferwall/elf/log"
8 | )
9 |
10 | // FileIdent is a representation of the raw ident array (first 16 bytes of an ELF file)
11 | type FileIdent struct {
12 | // Ident array
13 | Magic Magic `json:"magic"`
14 | Class Class `json:"class"`
15 | Data Data `json:"data"`
16 | Version Version `json:"version"`
17 | OSABI OSABI `json:"os_abi"`
18 | ABIVersion ABIVersion `json:"abi_version"`
19 | ByteOrder binary.ByteOrder `json:"byte_order"`
20 | }
21 |
22 | // FileHeader is an in-memory representation of the raw elf header.
23 | type FileHeader struct {
24 | Ident FileIdent
25 | // ELF Header fields
26 | Type Type `json:"type"` // object file type
27 | Machine Machine `json:"machine"`
28 | Version Version `json:"version"`
29 | Entry uint64 `json:"entrypoint"`
30 | ProgramHeaderOffset uint64 `json:"program_headers_offset"`
31 | SectionHeaderOffset uint64 `json:"section_headers_offset"`
32 | Flags uint32 `json:"processor_flag"`
33 | Size uint16 `json:"header_size"`
34 | ProgramHeaderEntrySize uint16 `json:"ph_entry_size"`
35 | ProgramHeaderNum uint16 `json:"ph_entry_num"`
36 | SectionHeaderEntrySize uint16 `json:"sh_entry_size"`
37 | SectionHeaderNum uint16 `json:"sh_entry_num"`
38 | SectionHeaderStringIdx uint16 `json:"sh_str_idx"`
39 | }
40 |
41 | // A Symbol represents an entry in an ELF symbol table section.
42 | type Symbol struct {
43 | Name string `json:"symbol_name"`
44 | Info byte `json:"symbol_info"`
45 | Other byte `json:"symbol_other"`
46 | Index SectionIndex `json:"symbol_index"`
47 | Value uint64 `json:"symbol_value"`
48 | Size uint64 `json:"symbol_size"`
49 | // Version and Library are present only for the dynamic symbol
50 | // table.
51 | Version string `json:"symbol_version"`
52 | Library string `json:"symbol_library"`
53 | }
54 |
55 | // ELFSymbols represents all symbol data.
56 | type ELFSymbols struct {
57 | NamedSymbols []Symbol `json:",omitempty"`
58 | GNUVersion []GNUVersion `json:",omitempty"`
59 | GNUVersionSym []byte `json:",omitempty"`
60 | }
61 |
62 | // File is an in-memory iterable representation of a raw elf binary.
63 | // this is merely used to ease the use of the package as a library
64 | // and allow feature modification and rebuilding of ELF files.
65 | type File struct {
66 | FileHeader `json:",omitempty"`
67 | ELFBin32 `json:",omitempty"`
68 | ELFBin64 `json:",omitempty"`
69 | ELFSymbols `json:",omitempty"`
70 | opts *Options
71 | logger *log.Helper
72 | }
73 |
74 | func NewBinaryFile() *File {
75 | return &File{}
76 | }
77 |
78 | // Class returns ELFClass of the binary (designates the target architecture of the binary x64 or x86)
79 | func (f *File) Class() Class {
80 | return f.Ident.Class
81 | }
82 |
83 | // ByteOrder returns byte order of the binary.
84 | func (f *File) ByteOrder() binary.ByteOrder {
85 | return f.Ident.ByteOrder
86 | }
87 |
88 | // IsELF64 returns true if the binary was compiled with an x64 architecture target.
89 | func (f *File) IsELF64() bool {
90 | return f.Ident.Class == ELFCLASS64
91 | }
92 |
93 | // SectionNames returns the list of section names
94 | func (f *File) SectionNames() []string {
95 | if len(f.Sections64) != 0 {
96 | sectionNames := make([]string, len(f.Sections64))
97 | for i, s := range f.Sections64 {
98 | sectionNames[i] = s.SectionName
99 | }
100 | return sectionNames
101 | } else if len(f.Sections32) != 0 {
102 | sectionNames := make([]string, len(f.Sections64))
103 | for i, s := range f.Sections32 {
104 | sectionNames[i] = s.SectionName
105 | }
106 | return sectionNames
107 | }
108 |
109 | return []string{""}
110 | }
111 |
112 | // GetSectionByType returns the first section with the given type T (nil otherwise).
113 | func (f *File) GetSectionByType(t SectionType) *ELF64Section {
114 | for _, s := range f.Sections64 {
115 | if s.Type == uint32(t) {
116 | return s
117 | }
118 | }
119 | return nil
120 | }
121 |
122 | // stringTable reads and returns the string table given by the
123 | // specified link value.
124 | func (f *File) stringTable(link uint32) ([]byte, error) {
125 | if link <= 0 || link >= uint32(len(f.Sections64)) {
126 | return nil, errors.New("section has invalid string table link")
127 | }
128 | return f.Sections64[link].Data()
129 | }
130 |
131 | // getString extracts a string from an ELF string table.
132 | func getString(section []byte, start int) (string, bool) {
133 | if start < 0 || start >= len(section) {
134 | return "", false
135 | }
136 | for end := start; end < len(section); end++ {
137 | if section[end] == 0 {
138 | return string(section[start:end]), true
139 | }
140 | }
141 | return "", false
142 | }
143 |
144 | // IsValidELFClass validates the ELF class of the binary.
145 | func IsValidELFClass(c Class) bool {
146 | switch c {
147 | case ELFCLASS32:
148 | return true
149 | case ELFCLASS64:
150 | return true
151 | default:
152 | return false
153 | }
154 | }
155 |
156 | // IsValidByteOrder validates the ELF byte order field.
157 | func IsValidByteOrder(b Data) bool {
158 | switch b {
159 | case ELFDATA2LSB:
160 | return true
161 | case ELFDATA2MSB:
162 | return true
163 | default:
164 | return false
165 | }
166 | }
167 |
168 | // IsValidVersion validates against the current default version flag EV_CURRENT.
169 | func IsValidVersion(b Version) bool {
170 | return b == EV_CURRENT
171 | }
172 |
173 | // goByteOrder encodes a Data field to a native Go byte order field.
174 | func ByteOrder(b Data) binary.ByteOrder {
175 | switch b {
176 | case ELFDATA2LSB:
177 | return binary.LittleEndian
178 | case ELFDATA2MSB:
179 | return binary.BigEndian
180 | default:
181 | return binary.LittleEndian
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/flags.go:
--------------------------------------------------------------------------------
1 | // Package elf : flags.go implements a parser for ELF binaries.
2 | // The current file documents the flags as defined in :
3 | // https://refspecs.linuxbase.org/elf/elf.pdf
4 | package elf
5 |
6 | // Indexes in Ident array.
7 | const (
8 | EI_MAG0 = 0 // Start of magic bytes 0x7f
9 | EI_MAG1 = 1 // 'E'
10 | EI_MAG2 = 2 // 'L'
11 | EI_MAG3 = 3 // 'F'
12 | EI_CLASS = 4 // Class of machine.
13 | EI_DATA = 5 // Data format.
14 | EI_VERSION = 6 // ELF format version.
15 | EI_OSABI = 7 // Operating system / ABI identification
16 | EI_ABIVERSION = 8 // ABI version
17 | EI_PAD = 9 // Start of padding (per SVR4 ABI).
18 | EI_NIDENT = 16 // Size of e_ident array.
19 | )
20 |
21 | // Magic represents the 4 starting bytes representing file defining values.
22 | type Magic [4]byte
23 |
24 | const (
25 | // ELFMAG is the constant prelude to every ELF binary.
26 | ELFMAG = "\177ELF"
27 | )
28 |
29 | func (m Magic) String() string { return string(m[:]) }
30 | func (m Magic) GoString() string { return string(m[:]) }
31 |
32 | // Class takes values of 1 or 2 depending on the architecture 32-bit or 64-bit.
33 | type Class byte
34 |
35 | const (
36 | // ELFCLASSNONE specifies an unknown class.
37 | ELFCLASSNONE Class = 0
38 | // ELFCLASS32 specifies 32-bit binaries.
39 | ELFCLASS32 Class = 1
40 | // ELFCLASS64 specifies 64-bit binaries.
41 | ELFCLASS64 Class = 2
42 | )
43 |
44 | var classStrings = []flagName{
45 | {0, "ELFCLASSNONE"},
46 | {1, "ELFCLASS32"},
47 | {2, "ELFCLASS64"},
48 | }
49 |
50 | func (c Class) String() string { return stringify(uint32(c), classStrings, false) }
51 | func (c Class) GoString() string { return stringify(uint32(c), classStrings, true) }
52 |
53 | // Data takes values of 1 or 2 depending on whether its a little endian or big endian architecture.
54 | type Data byte
55 |
56 | const (
57 | // ELFDATANONE specifes an unknown architecture.
58 | ELFDATANONE Data = 0
59 | // DATA2LSB specifies a little-endian architecture 2's complement values lsb at lowest address.
60 | ELFDATA2LSB Data = 1
61 | // ELFDATA2MSB specifies a big-endian architecture 2's complement value msb at at lowest address.
62 | ELFDATA2MSB Data = 2
63 | )
64 |
65 | var dataStrings = []flagName{
66 | {0, "ELFDATANONE"},
67 | {1, "ELFDATA2LSB"},
68 | {2, "ELFDATA2MSB"},
69 | }
70 |
71 | func (d Data) String() string { return stringify(uint32(d), dataStrings, false) }
72 | func (d Data) GoString() string { return stringify(uint32(d), dataStrings, true) }
73 |
74 | // Version specifies the current ELF version.
75 | type Version byte
76 |
77 | const (
78 | // EV_NONE specifes an unknown version.
79 | EV_NONE Version = 0
80 | // EV_CURRENT specifies the only current elf version equal to 1.
81 | EV_CURRENT Version = 1
82 | )
83 |
84 | var versionStrings = []flagName{
85 | {0, "NONE"},
86 | {1, "CURRENT"},
87 | }
88 |
89 | func (v Version) String() string { return stringify(uint32(v), versionStrings, false) }
90 | func (v Version) GoString() string { return stringify(uint32(v), versionStrings, true) }
91 |
92 | // OSABI specifes the OS ABI version.
93 | type OSABI byte
94 |
95 | const (
96 | ELFOSABI_NONE OSABI = 0 // UNIX System V ABI
97 | ELFOSABI_HPUX OSABI = 1 // HP-UX operating system
98 | ELFOSABI_NETBSD OSABI = 2 // NetBSD
99 | ELFOSABI_LINUX OSABI = 3 // GNU/Linux
100 | ELFOSABI_HURD OSABI = 4 // GNU/Hurd
101 | ELFOSABI_86OPEN OSABI = 5 // 86Open common IA32 ABI
102 | ELFOSABI_SOLARIS OSABI = 6 // Solaris
103 | ELFOSABI_AIX OSABI = 7 // AIX
104 | ELFOSABI_IRIX OSABI = 8 // IRIX
105 | ELFOSABI_FREEBSD OSABI = 9 // FreeBSD
106 | ELFOSABI_TRU64 OSABI = 10 // TRU64 UNIX
107 | ELFOSABI_MODESTO OSABI = 11 // Novell Modesto
108 | ELFOSABI_OPENBSD OSABI = 12 // OpenBSD
109 | ELFOSABI_OPENVMS OSABI = 13 // Open VMS
110 | ELFOSABI_NSK OSABI = 14 // HP Non-Stop Kernel
111 | ELFOSABI_AROS OSABI = 15 // Amiga Research OS
112 | ELFOSABI_FENIXOS OSABI = 16 // The FenixOS highly scalable multi-core OS
113 | ELFOSABI_CLOUDABI OSABI = 17 // Nuxi CloudABI
114 | ELFOSABI_ARM OSABI = 97 // ARM
115 | ELFOSABI_STANDALONE OSABI = 255 // Standalone (embedded) application
116 | )
117 |
118 | var osABIStrings = []flagName{
119 | {0, "ELFOSABI_NONE"},
120 | {1, "ELFOSABI_HPUX"},
121 | {2, "ELFOSABI_NETBSD"},
122 | {3, "ELFOSABI_LINUX"},
123 | {4, "ELFOSABI_HURD"},
124 | {5, "ELFOSABI_86OPEN"},
125 | {6, "ELFOSABI_SOLARIS"},
126 | {7, "ELFOSABI_AIX"},
127 | {8, "ELFOSABI_IRIX"},
128 | {9, "ELFOSABI_FREEBSD"},
129 | {10, "ELFOSABI_TRU64"},
130 | {11, "ELFOSABI_MODESTO"},
131 | {12, "ELFOSABI_OPENBSD"},
132 | {13, "ELFOSABI_OPENVMS"},
133 | {14, "ELFOSABI_NSK"},
134 | {15, "ELFOSABI_AROS"},
135 | {16, "ELFOSABI_FENIXOS"},
136 | {17, "ELFOSABI_CLOUDABI"},
137 | {97, "ELFOSABI_ARM"},
138 | {255, "ELFOSABI_STANDALONE"},
139 | }
140 |
141 | func (o OSABI) String() string { return stringify(uint32(o), osABIStrings, false) }
142 | func (o OSABI) GoString() string { return stringify(uint32(o), osABIStrings, true) }
143 |
144 | // ABIVersion specifies the ELF ABI Version
145 | // it indicates the specific version of the OS ABI that the binary targets.
146 | type ABIVersion byte
147 |
148 | const (
149 | ELFABIVersion_CURRENT = 0
150 | )
151 |
152 | // Type specifies the current binary type (Relocatable object file or Executable binary...)
153 | type Type uint16
154 |
155 | const (
156 | ET_NONE Type = 0 // Unknown type.
157 | ET_REL Type = 1 // Relocatable.
158 | ET_EXEC Type = 2 // Executable.
159 | ET_DYN Type = 3 // Shared object.
160 | ET_CORE Type = 4 // Core file.
161 | ET_LOOS Type = 0xfe00 // First operating system specific.
162 | ET_HIOS Type = 0xfeff // Last operating system-specific.
163 | ET_LOPROC Type = 0xff00 // First processor-specific.
164 | ET_HIPROC Type = 0xffff // Last processor-specific.
165 | )
166 |
167 | var typeStrings = []flagName{
168 | {0, "ET_NONE"},
169 | {1, "ET_REL"},
170 | {2, "ET_EXEC"},
171 | {3, "ET_DYN"},
172 | {4, "ET_CORE"},
173 | {0xfe00, "ET_LOOS"},
174 | {0xfeff, "ET_HIOS"},
175 | {0xff00, "ET_LOPROC"},
176 | {0xffff, "ET_HIPROC"},
177 | }
178 |
179 | func (t Type) String() string { return stringify(uint32(t), typeStrings, false) }
180 | func (t Type) GoString() string { return stringify(uint32(t), typeStrings, true) }
181 |
182 | type Machine uint16
183 |
184 | const (
185 | EM_NONE Machine = 0 // Unknown machine.
186 | EM_M32 Machine = 1 // AT&T WE32100.
187 | EM_SPARC Machine = 2 // Sun SPARC.
188 | EM_386 Machine = 3 // Intel i386.
189 | EM_68K Machine = 4 // Motorola 68000.
190 | EM_88K Machine = 5 // Motorola 88000.
191 | EM_860 Machine = 7 // Intel i860.
192 | EM_MIPS Machine = 8 // MIPS R3000 Big-Endian only.
193 | EM_S370 Machine = 9 // IBM System/370.
194 | EM_MIPS_RS3_LE Machine = 10 // MIPS R3000 Little-Endian.
195 | EM_PARISC Machine = 15 // HP PA-RISC.
196 | EM_VPP500 Machine = 17 // Fujitsu VPP500.
197 | EM_SPARC32PLUS Machine = 18 // SPARC v8plus.
198 | EM_960 Machine = 19 // Intel 80960.
199 | EM_PPC Machine = 20 // PowerPC 32-bit.
200 | EM_PPC64 Machine = 21 // PowerPC 64-bit.
201 | EM_S390 Machine = 22 // IBM System/390.
202 | EM_V800 Machine = 36 // NEC V800.
203 | EM_FR20 Machine = 37 // Fujitsu FR20.
204 | EM_RH32 Machine = 38 // TRW RH-32.
205 | EM_RCE Machine = 39 // Motorola RCE.
206 | EM_ARM Machine = 40 // ARM.
207 | EM_SH Machine = 42 // Hitachi SH.
208 | EM_SPARCV9 Machine = 43 // SPARC v9 64-bit.
209 | EM_TRICORE Machine = 44 // Siemens TriCore embedded processor.
210 | EM_ARC Machine = 45 // Argonaut RISC Core.
211 | EM_H8_300 Machine = 46 // Hitachi H8/300.
212 | EM_H8_300H Machine = 47 // Hitachi H8/300H.
213 | EM_H8S Machine = 48 // Hitachi H8S.
214 | EM_H8_500 Machine = 49 // Hitachi H8/500.
215 | EM_IA_64 Machine = 50 // Intel IA-64 Processor.
216 | EM_MIPS_X Machine = 51 // Stanford MIPS-X.
217 | EM_COLDFIRE Machine = 52 // Motorola ColdFire.
218 | EM_68HC12 Machine = 53 // Motorola M68HC12.
219 | EM_MMA Machine = 54 // Fujitsu MMA.
220 | EM_PCP Machine = 55 // Siemens PCP.
221 | EM_NCPU Machine = 56 // Sony nCPU.
222 | EM_NDR1 Machine = 57 // Denso NDR1 microprocessor.
223 | EM_STARCORE Machine = 58 // Motorola Star*Core processor.
224 | EM_ME16 Machine = 59 // Toyota ME16 processor.
225 | EM_ST100 Machine = 60 // STMicroelectronics ST100 processor.
226 | EM_TINYJ Machine = 61 // Advanced Logic Corp. TinyJ processor.
227 | EM_X86_64 Machine = 62 // Advanced Micro Devices x86-64
228 | EM_PDSP Machine = 63 // Sony DSP Processor
229 | EM_PDP10 Machine = 64 // Digital Equipment Corp. PDP-10
230 | EM_PDP11 Machine = 65 // Digital Equipment Corp. PDP-11
231 | EM_FX66 Machine = 66 // Siemens FX66 microcontroller
232 | EM_ST9PLUS Machine = 67 // STMicroelectronics ST9+ 8/16 bit microcontroller
233 | EM_ST7 Machine = 68 // STMicroelectronics ST7 8-bit microcontroller
234 | EM_68HC16 Machine = 69 // Motorola MC68HC16 Microcontroller
235 | EM_68HC11 Machine = 70 // Motorola MC68HC11 Microcontroller
236 | EM_68HC08 Machine = 71 // Motorola MC68HC08 Microcontroller
237 | EM_68HC05 Machine = 72 // Motorola MC68HC05 Microcontroller
238 | EM_SVX Machine = 73 // Silicon Graphics SVx
239 | EM_ST19 Machine = 74 // STMicroelectronics ST19 8-bit microcontroller
240 | EM_VAX Machine = 75 // Digital VAX
241 | EM_CRIS Machine = 76 // Axis Communications 32-bit embedded processor
242 | EM_JAVELIN Machine = 77 // Infineon Technologies 32-bit embedded processor
243 | EM_FIREPATH Machine = 78 // Element 14 64-bit DSP Processor
244 | EM_ZSP Machine = 79 // LSI Logic 16-bit DSP Processor
245 | EM_MMIX Machine = 80 // Donald Knuth's educational 64-bit processor
246 | EM_HUANY Machine = 81 // Harvard University machine-independent object files
247 | EM_PRISM Machine = 82 // SiTera Prism
248 | EM_AVR Machine = 83 // Atmel AVR 8-bit microcontroller
249 | EM_FR30 Machine = 84 // Fujitsu FR30
250 | EM_D10V Machine = 85 // Mitsubishi D10V
251 | EM_D30V Machine = 86 // Mitsubishi D30V
252 | EM_V850 Machine = 87 // NEC v850
253 | EM_M32R Machine = 88 // Mitsubishi M32R
254 | EM_MN10300 Machine = 89 // Matsushita MN10300
255 | EM_MN10200 Machine = 90 // Matsushita MN10200
256 | EM_PJ Machine = 91 // picoJava
257 | EM_OPENRISC Machine = 92 // OpenRISC 32-bit embedded processor
258 | EM_ARC_COMPACT Machine = 93 // ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5)
259 | EM_XTENSA Machine = 94 // Tensilica Xtensa Architecture
260 | EM_VIDEOCORE Machine = 95 // Alphamosaic VideoCore processor
261 | EM_TMM_GPP Machine = 96 // Thompson Multimedia General Purpose Processor
262 | EM_NS32K Machine = 97 // National Semiconductor 32000 series
263 | EM_TPC Machine = 98 // Tenor Network TPC processor
264 | EM_SNP1K Machine = 99 // Trebia SNP 1000 processor
265 | EM_ST200 Machine = 100 // STMicroelectronics (www.st.com) ST200 microcontroller
266 | EM_IP2K Machine = 101 // Ubicom IP2xxx microcontroller family
267 | EM_MAX Machine = 102 // MAX Processor
268 | EM_CR Machine = 103 // National Semiconductor CompactRISC microprocessor
269 | EM_F2MC16 Machine = 104 // Fujitsu F2MC16
270 | EM_MSP430 Machine = 105 // Texas Instruments embedded microcontroller msp430
271 | EM_BLACKFIN Machine = 106 // Analog Devices Blackfin (DSP) processor
272 | EM_SE_C33 Machine = 107 // S1C33 Family of Seiko Epson processors
273 | EM_SEP Machine = 108 // Sharp embedded microprocessor
274 | EM_ARCA Machine = 109 // Arca RISC Microprocessor
275 | EM_UNICORE Machine = 110 // Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University
276 | EM_EXCESS Machine = 111 // eXcess: 16/32/64-bit configurable embedded CPU
277 | EM_DXP Machine = 112 // Icera Semiconductor Inc. Deep Execution Processor
278 | EM_ALTERA_NIOS2 Machine = 113 // Altera Nios II soft-core processor
279 | EM_CRX Machine = 114 // National Semiconductor CompactRISC CRX microprocessor
280 | EM_XGATE Machine = 115 // Motorola XGATE embedded processor
281 | EM_C166 Machine = 116 // Infineon C16x/XC16x processor
282 | EM_M16C Machine = 117 // Renesas M16C series microprocessors
283 | EM_DSPIC30F Machine = 118 // Microchip Technology dsPIC30F Digital Signal Controller
284 | EM_CE Machine = 119 // Freescale Communication Engine RISC core
285 | EM_M32C Machine = 120 // Renesas M32C series microprocessors
286 | EM_TSK3000 Machine = 131 // Altium TSK3000 core
287 | EM_RS08 Machine = 132 // Freescale RS08 embedded processor
288 | EM_SHARC Machine = 133 // Analog Devices SHARC family of 32-bit DSP processors
289 | EM_ECOG2 Machine = 134 // Cyan Technology eCOG2 microprocessor
290 | EM_SCORE7 Machine = 135 // Sunplus S+core7 RISC processor
291 | EM_DSP24 Machine = 136 // New Japan Radio (NJR) 24-bit DSP Processor
292 | EM_VIDEOCORE3 Machine = 137 // Broadcom VideoCore III processor
293 | EM_LATTICEMICO32 Machine = 138 // RISC processor for Lattice FPGA architecture
294 | EM_SE_C17 Machine = 139 // Seiko Epson C17 family
295 | EM_TI_C6000 Machine = 140 // The Texas Instruments TMS320C6000 DSP family
296 | EM_TI_C2000 Machine = 141 // The Texas Instruments TMS320C2000 DSP family
297 | EM_TI_C5500 Machine = 142 // The Texas Instruments TMS320C55x DSP family
298 | EM_TI_ARP32 Machine = 143 // Texas Instruments Application Specific RISC Processor, 32bit fetch
299 | EM_TI_PRU Machine = 144 // Texas Instruments Programmable Realtime Unit
300 | EM_MMDSP_PLUS Machine = 160 // STMicroelectronics 64bit VLIW Data Signal Processor
301 | EM_CYPRESS_M8C Machine = 161 // Cypress M8C microprocessor
302 | EM_R32C Machine = 162 // Renesas R32C series microprocessors
303 | EM_TRIMEDIA Machine = 163 // NXP Semiconductors TriMedia architecture family
304 | EM_QDSP6 Machine = 164 // QUALCOMM DSP6 Processor
305 | EM_8051 Machine = 165 // Intel 8051 and variants
306 | EM_STXP7X Machine = 166 // STMicroelectronics STxP7x family of configurable and extensible RISC processors
307 | EM_NDS32 Machine = 167 // Andes Technology compact code size embedded RISC processor family
308 | EM_ECOG1 Machine = 168 // Cyan Technology eCOG1X family
309 | EM_ECOG1X Machine = 168 // Cyan Technology eCOG1X family
310 | EM_MAXQ30 Machine = 169 // Dallas Semiconductor MAXQ30 Core Micro-controllers
311 | EM_XIMO16 Machine = 170 // New Japan Radio (NJR) 16-bit DSP Processor
312 | EM_MANIK Machine = 171 // M2000 Reconfigurable RISC Microprocessor
313 | EM_CRAYNV2 Machine = 172 // Cray Inc. NV2 vector architecture
314 | EM_RX Machine = 173 // Renesas RX family
315 | EM_METAG Machine = 174 // Imagination Technologies META processor architecture
316 | EM_MCST_ELBRUS Machine = 175 // MCST Elbrus general purpose hardware architecture
317 | EM_ECOG16 Machine = 176 // Cyan Technology eCOG16 family
318 | EM_CR16 Machine = 177 // National Semiconductor CompactRISC CR16 16-bit microprocessor
319 | EM_ETPU Machine = 178 // Freescale Extended Time Processing Unit
320 | EM_SLE9X Machine = 179 // Infineon Technologies SLE9X core
321 | EM_L10M Machine = 180 // Intel L10M
322 | EM_K10M Machine = 181 // Intel K10M
323 | EM_AARCH64 Machine = 183 // ARM 64-bit Architecture (AArch64)
324 | EM_AVR32 Machine = 185 // Atmel Corporation 32-bit microprocessor family
325 | EM_STM8 Machine = 186 // STMicroeletronics STM8 8-bit microcontroller
326 | EM_TILE64 Machine = 187 // Tilera TILE64 multicore architecture family
327 | EM_TILEPRO Machine = 188 // Tilera TILEPro multicore architecture family
328 | EM_MICROBLAZE Machine = 189 // Xilinx MicroBlaze 32-bit RISC soft processor core
329 | EM_CUDA Machine = 190 // NVIDIA CUDA architecture
330 | EM_TILEGX Machine = 191 // Tilera TILE-Gx multicore architecture family
331 | EM_CLOUDSHIELD Machine = 192 // CloudShield architecture family
332 | EM_COREA_1ST Machine = 193 // KIPO-KAIST Core-A 1st generation processor family
333 | EM_COREA_2ND Machine = 194 // KIPO-KAIST Core-A 2nd generation processor family
334 | EM_ARC_COMPACT2 Machine = 195 // Synopsys ARCompact V2
335 | EM_OPEN8 Machine = 196 // Open8 8-bit RISC soft processor core
336 | EM_RL78 Machine = 197 // Renesas RL78 family
337 | EM_VIDEOCORE5 Machine = 198 // Broadcom VideoCore V processor
338 | EM_78KOR Machine = 199 // Renesas 78KOR family
339 | EM_56800EX Machine = 200 // Freescale 56800EX Digital Signal Controller (DSC)
340 | EM_BA1 Machine = 201 // Beyond BA1 CPU architecture
341 | EM_BA2 Machine = 202 // Beyond BA2 CPU architecture
342 | EM_XCORE Machine = 203 // XMOS xCORE processor family
343 | EM_MCHP_PIC Machine = 204 // Microchip 8-bit PIC(r) family
344 | EM_INTEL205 Machine = 205 // Reserved by Intel
345 | EM_INTEL206 Machine = 206 // Reserved by Intel
346 | EM_INTEL207 Machine = 207 // Reserved by Intel
347 | EM_INTEL208 Machine = 208 // Reserved by Intel
348 | EM_INTEL209 Machine = 209 // Reserved by Intel
349 | EM_KM32 Machine = 210 // KM211 KM32 32-bit processor
350 | EM_KMX32 Machine = 211 // KM211 KMX32 32-bit processor
351 | EM_KMX16 Machine = 212 // KM211 KMX16 16-bit processor
352 | EM_KMX8 Machine = 213 // KM211 KMX8 8-bit processor
353 | EM_KVARC Machine = 214 // KM211 KVARC processor
354 | EM_CDP Machine = 215 // Paneve CDP architecture family
355 | EM_COGE Machine = 216 // Cognitive Smart Memory Processor
356 | EM_COOL Machine = 217 // Bluechip Systems CoolEngine
357 | EM_NORC Machine = 218 // Nanoradio Optimized RISC
358 | EM_CSR_KALIMBA Machine = 219 // CSR Kalimba architecture family
359 | EM_Z80 Machine = 220 // Zilog Z80
360 | EM_VISIUM Machine = 221 // Controls and Data Services VISIUMcore processor
361 | EM_FT32 Machine = 222 // FTDI Chip FT32 high performance 32-bit RISC architecture
362 | EM_MOXIE Machine = 223 // Moxie processor family
363 | EM_AMDGPU Machine = 224 // AMD GPU architecture
364 | EM_RISCV Machine = 243 // RISC-V
365 | EM_LANAI Machine = 244 // Lanai 32-bit processor
366 | EM_BPF Machine = 247 // Linux BPF – in-kernel virtual machine
367 |
368 | // Non-standard or deprecated.
369 | EM_486 Machine = 6 // Intel i486.
370 | EM_MIPS_RS4_BE Machine = 10 // MIPS R4000 Big-Endian
371 | EM_ALPHA_STD Machine = 41 // Digital Alpha (standard value).
372 | EM_ALPHA Machine = 0x9026 // Alpha (written in the absence of an ABI)
373 | )
374 |
375 | var machineStrings = []flagName{
376 | {0, "EM_NONE"},
377 | {1, "EM_M32"},
378 | {2, "EM_SPARC"},
379 | {3, "EM_386"},
380 | {4, "EM_68K"},
381 | {5, "EM_88K"},
382 | {7, "EM_860"},
383 | {8, "EM_MIPS"},
384 | {9, "EM_S370"},
385 | {10, "EM_MIPS_RS3_LE"},
386 | {15, "EM_PARISC"},
387 | {17, "EM_VPP500"},
388 | {18, "EM_SPARC32PLUS"},
389 | {19, "EM_960"},
390 | {20, "EM_PPC"},
391 | {21, "EM_PPC64"},
392 | {22, "EM_S390"},
393 | {36, "EM_V800"},
394 | {37, "EM_FR20"},
395 | {38, "EM_RH32"},
396 | {39, "EM_RCE"},
397 | {40, "EM_ARM"},
398 | {42, "EM_SH"},
399 | {43, "EM_SPARCV9"},
400 | {44, "EM_TRICORE"},
401 | {45, "EM_ARC"},
402 | {46, "EM_H8_300"},
403 | {47, "EM_H8_300H"},
404 | {48, "EM_H8S"},
405 | {49, "EM_H8_500"},
406 | {50, "EM_IA_64"},
407 | {51, "EM_MIPS_X"},
408 | {52, "EM_COLDFIRE"},
409 | {53, "EM_68HC12"},
410 | {54, "EM_MMA"},
411 | {55, "EM_PCP"},
412 | {56, "EM_NCPU"},
413 | {57, "EM_NDR1"},
414 | {58, "EM_STARCORE"},
415 | {59, "EM_ME16"},
416 | {60, "EM_ST100"},
417 | {61, "EM_TINYJ"},
418 | {62, "EM_X86_64"},
419 | {63, "EM_PDSP"},
420 | {64, "EM_PDP10"},
421 | {65, "EM_PDP11"},
422 | {66, "EM_FX66"},
423 | {67, "EM_ST9PLUS"},
424 | {68, "EM_ST7"},
425 | {69, "EM_68HC16"},
426 | {70, "EM_68HC11"},
427 | {71, "EM_68HC08"},
428 | {72, "EM_68HC05"},
429 | {73, "EM_SVX"},
430 | {74, "EM_ST19"},
431 | {75, "EM_VAX"},
432 | {76, "EM_CRIS"},
433 | {77, "EM_JAVELIN"},
434 | {78, "EM_FIREPATH"},
435 | {79, "EM_ZSP"},
436 | {80, "EM_MMIX"},
437 | {81, "EM_HUANY"},
438 | {82, "EM_PRISM"},
439 | {83, "EM_AVR"},
440 | {84, "EM_FR30"},
441 | {85, "EM_D10V"},
442 | {86, "EM_D30V"},
443 | {87, "EM_V850"},
444 | {88, "EM_M32R"},
445 | {89, "EM_MN10300"},
446 | {90, "EM_MN10200"},
447 | {91, "EM_PJ"},
448 | {92, "EM_OPENRISC"},
449 | {93, "EM_ARC_COMPACT"},
450 | {94, "EM_XTENSA"},
451 | {95, "EM_VIDEOCORE"},
452 | {96, "EM_TMM_GPP"},
453 | {97, "EM_NS32K"},
454 | {98, "EM_TPC"},
455 | {99, "EM_SNP1K"},
456 | {100, "EM_ST200"},
457 | {101, "EM_IP2K"},
458 | {102, "EM_MAX"},
459 | {103, "EM_CR"},
460 | {104, "EM_F2MC16"},
461 | {105, "EM_MSP430"},
462 | {106, "EM_BLACKFIN"},
463 | {107, "EM_SE_C33"},
464 | {108, "EM_SEP"},
465 | {109, "EM_ARCA"},
466 | {110, "EM_UNICORE"},
467 | {111, "EM_EXCESS"},
468 | {112, "EM_DXP"},
469 | {113, "EM_ALTERA_NIOS2"},
470 | {114, "EM_CRX"},
471 | {115, "EM_XGATE"},
472 | {116, "EM_C166"},
473 | {117, "EM_M16C"},
474 | {118, "EM_DSPIC30F"},
475 | {119, "EM_CE"},
476 | {120, "EM_M32C"},
477 | {131, "EM_TSK3000"},
478 | {132, "EM_RS08"},
479 | {133, "EM_SHARC"},
480 | {134, "EM_ECOG2"},
481 | {135, "EM_SCORE7"},
482 | {136, "EM_DSP24"},
483 | {137, "EM_VIDEOCORE3"},
484 | {138, "EM_LATTICEMICO32"},
485 | {139, "EM_SE_C17"},
486 | {140, "EM_TI_C6000"},
487 | {141, "EM_TI_C2000"},
488 | {142, "EM_TI_C5500"},
489 | {143, "EM_TI_ARP32"},
490 | {144, "EM_TI_PRU"},
491 | {160, "EM_MMDSP_PLUS"},
492 | {161, "EM_CYPRESS_M8C"},
493 | {162, "EM_R32C"},
494 | {163, "EM_TRIMEDIA"},
495 | {164, "EM_QDSP6"},
496 | {165, "EM_8051"},
497 | {166, "EM_STXP7X"},
498 | {167, "EM_NDS32"},
499 | {168, "EM_ECOG1"},
500 | {169, "EM_MAXQ30"},
501 | {170, "EM_XIMO16"},
502 | {171, "EM_MANIK"},
503 | {172, "EM_CRAYNV2"},
504 | {173, "EM_RX"},
505 | {174, "EM_METAG"},
506 | {175, "EM_MCST_ELBRUS"},
507 | {176, "EM_ECOG16"},
508 | {177, "EM_CR16"},
509 | {178, "EM_ETPU"},
510 | {179, "EM_SLE9X"},
511 | {180, "EM_L10M"},
512 | {181, "EM_K10M"},
513 | {183, "EM_AARCH64"},
514 | {185, "EM_AVR32"},
515 | {186, "EM_STM8"},
516 | {187, "EM_TILE64"},
517 | {188, "EM_TILEPRO"},
518 | {189, "EM_MICROBLAZE"},
519 | {190, "EM_CUDA"},
520 | {191, "EM_TILEGX"},
521 | {192, "EM_CLOUDSHIELD"},
522 | {193, "EM_COREA_1ST"},
523 | {194, "EM_COREA_2ND"},
524 | {195, "EM_ARC_COMPACT2"},
525 | {196, "EM_OPEN8"},
526 | {197, "EM_RL78"},
527 | {198, "EM_VIDEOCORE5"},
528 | {199, "EM_78KOR"},
529 | {200, "EM_56800EX"},
530 | {201, "EM_BA1"},
531 | {202, "EM_BA2"},
532 | {203, "EM_XCORE"},
533 | {204, "EM_MCHP_PIC"},
534 | {205, "EM_INTEL205"},
535 | {206, "EM_INTEL206"},
536 | {207, "EM_INTEL207"},
537 | {208, "EM_INTEL208"},
538 | {209, "EM_INTEL209"},
539 | {210, "EM_KM32"},
540 | {211, "EM_KMX32"},
541 | {212, "EM_KMX16"},
542 | {213, "EM_KMX8"},
543 | {214, "EM_KVARC"},
544 | {215, "EM_CDP"},
545 | {216, "EM_COGE"},
546 | {217, "EM_COOL"},
547 | {218, "EM_NORC"},
548 | {219, "EM_CSR_KALIMBA "},
549 | {220, "EM_Z80 "},
550 | {221, "EM_VISIUM "},
551 | {222, "EM_FT32 "},
552 | {223, "EM_MOXIE"},
553 | {224, "EM_AMDGPU"},
554 | {243, "EM_RISCV"},
555 | {244, "EM_LANAI"},
556 | {247, "EM_BPF"},
557 | /* Non-standard or deprecated. */
558 | {6, "EM_486"},
559 | // {168, "EM_ECOG1X"},
560 | // {10, "EM_MIPS_RS4_BE"},
561 | {41, "EM_ALPHA_STD"},
562 | {0x9026, "EM_ALPHA"},
563 | }
564 |
565 | func (m Machine) String() string { return stringify(uint32(m), machineStrings, false) }
566 | func (m Machine) GoString() string { return stringify(uint32(m), machineStrings, true) }
567 |
568 | // Special section indices.
569 | type SectionIndex int
570 |
571 | const (
572 | SHN_UNDEF SectionIndex = 0 // Undefined, missing, irrelevant.
573 | SHN_LORESERVE SectionIndex = 0xff00 // First of reserved range.
574 | SHN_LOPROC SectionIndex = 0xff00 // First processor-specific.
575 | SHN_HIPROC SectionIndex = 0xff1f // Last processor-specific.
576 | SHN_LOOS SectionIndex = 0xff20 // First operating system-specific.
577 | SHN_HIOS SectionIndex = 0xff3f // Last operating system-specific.
578 | SHN_ABS SectionIndex = 0xfff1 // Absolute values.
579 | SHN_COMMON SectionIndex = 0xfff2 // Common data.
580 | SHN_XINDEX SectionIndex = 0xffff // Escape; index stored elsewhere.
581 | SHN_HIRESERVE SectionIndex = 0xffff // Last of reserved range.
582 | )
583 |
584 | var sectionIndexStrings = []flagName{
585 | {0, "SHN_UNDEF"},
586 | {0xff00, "SHN_LOPROC"},
587 | {0xff20, "SHN_LOOS"},
588 | {0xfff1, "SHN_ABS"},
589 | {0xfff2, "SHN_COMMON"},
590 | {0xffff, "SHN_XINDEX"},
591 | }
592 |
593 | func (si SectionIndex) String() string { return stringify(uint32(si), sectionIndexStrings, false) }
594 | func (si SectionIndex) GoString() string { return stringify(uint32(si), sectionIndexStrings, true) }
595 |
596 | // Section type.
597 | type SectionType uint32
598 |
599 | const (
600 | SHT_NULL SectionType = 0 // inactive
601 | SHT_PROGBITS SectionType = 1 // program defined information
602 | SHT_SYMTAB SectionType = 2 // symbol table section
603 | SHT_STRTAB SectionType = 3 // string table section
604 | SHT_RELA SectionType = 4 // relocation section with addends
605 | SHT_HASH SectionType = 5 // symbol hash table section
606 | SHT_DYNAMIC SectionType = 6 // dynamic section
607 | SHT_NOTE SectionType = 7 // note section
608 | SHT_NOBITS SectionType = 8 // no space section
609 | SHT_REL SectionType = 9 // relocation section - no addends
610 | SHT_SHLIB SectionType = 10 // reserved - purpose unknown
611 | SHT_DYNSYM SectionType = 11 // dynamic symbol table section
612 | SHT_INIT_ARRAY SectionType = 14 // Initialization function pointers.
613 | SHT_FINI_ARRAY SectionType = 15 // Termination function pointers.
614 | SHT_PREINIT_ARRAY SectionType = 16 // Pre-initialization function ptrs.
615 | SHT_GROUP SectionType = 17 // Section group.
616 | SHT_SYMTAB_SHNDX SectionType = 18 // Section indexes (see SHN_XINDEX).
617 | SHT_LOOS SectionType = 0x60000000 // First of OS specific semantics
618 | SHT_GNU_ATTRIBUTES SectionType = 0x6ffffff5 // GNU object attributes
619 | SHT_GNU_HASH SectionType = 0x6ffffff6 // GNU hash table
620 | SHT_GNU_LIBLIST SectionType = 0x6ffffff7 // GNU prelink library list
621 | SHT_GNU_VERDEF SectionType = 0x6ffffffd // GNU version definition section
622 | SHT_GNU_VERNEED SectionType = 0x6ffffffe // GNU version needs section
623 | SHT_GNU_VERSYM SectionType = 0x6fffffff // GNU version symbol table
624 | SHT_HIOS SectionType = 0x6fffffff // Last of OS specific semantics
625 | SHT_LOPROC SectionType = 0x70000000 // reserved range for processor
626 | SHT_HIPROC SectionType = 0x7fffffff // specific section header types
627 | SHT_LOUSER SectionType = 0x80000000 // reserved range for application
628 | SHT_HIUSER SectionType = 0xffffffff // specific indexes
629 | )
630 |
631 | var sectionTypeStrings = []flagName{
632 | {0, "SHT_NULL"},
633 | {1, "SHT_PROGBITS"},
634 | {2, "SHT_SYMTAB"},
635 | {3, "SHT_STRTAB"},
636 | {4, "SHT_RELA"},
637 | {5, "SHT_HASH"},
638 | {6, "SHT_DYNAMIC"},
639 | {7, "SHT_NOTE"},
640 | {8, "SHT_NOBITS"},
641 | {9, "SHT_REL"},
642 | {10, "SHT_SHLIB"},
643 | {11, "SHT_DYNSYM"},
644 | {14, "SHT_INIT_ARRAY"},
645 | {15, "SHT_FINI_ARRAY"},
646 | {16, "SHT_PREINIT_ARRAY"},
647 | {17, "SHT_GROUP"},
648 | {18, "SHT_SYMTAB_SHNDX"},
649 | {0x60000000, "SHT_LOOS"},
650 | {0x6ffffff5, "SHT_GNU_ATTRIBUTES"},
651 | {0x6ffffff6, "SHT_GNU_HASH"},
652 | {0x6ffffff7, "SHT_GNU_LIBLIST"},
653 | {0x6ffffffd, "SHT_GNU_VERDEF"},
654 | {0x6ffffffe, "SHT_GNU_VERNEED"},
655 | {0x6fffffff, "SHT_GNU_VERSYM"},
656 | {0x70000000, "SHT_LOPROC"},
657 | {0x7fffffff, "SHT_HIPROC"},
658 | {0x80000000, "SHT_LOUSER"},
659 | {0xffffffff, "SHT_HIUSER"},
660 | }
661 |
662 | func (st SectionType) String() string { return stringify(uint32(st), sectionTypeStrings, false) }
663 | func (st SectionType) GoString() string { return stringify(uint32(st), sectionTypeStrings, true) }
664 |
665 | // Section flags.
666 | type SectionFlag uint32
667 |
668 | const (
669 | SHF_NONE SectionFlag = 0x0 // Undefined section flag
670 | SHF_WRITE SectionFlag = 0x1 // Section contains writable data.
671 | SHF_ALLOC SectionFlag = 0x2 // Section occupies memory.
672 | SHF_EXECINSTR SectionFlag = 0x4 // Section contains instructions.
673 | SHF_MERGE SectionFlag = 0x10 // Section may be merged.
674 | SHF_STRINGS SectionFlag = 0x20 // Section contains strings.
675 | SHF_INFO_LINK SectionFlag = 0x40 // sh_info holds section index.
676 | SHF_LINK_ORDER SectionFlag = 0x80 // Special ordering requirements.
677 | SHF_OS_NONCONFORMING SectionFlag = 0x100 // OS-specific processing required.
678 | SHF_GROUP SectionFlag = 0x200 // Member of section group.
679 | SHF_TLS SectionFlag = 0x400 // Section contains TLS data.
680 | SHF_COMPRESSED SectionFlag = 0x800 // Section is compressed.
681 | SHF_MASKOS SectionFlag = 0x0ff00000 // OS-specific semantics.
682 | SHF_MASKPROC SectionFlag = 0xf0000000 // Processor-specific semantics.
683 | )
684 |
685 | var sectionFlagStrings = []flagName{
686 | {0x1, "SHF_WRITE"},
687 | {0x2, "SHF_ALLOC"},
688 | {0x4, "SHF_EXECINSTR"},
689 | {0x10, "SHF_MERGE"},
690 | {0x20, "SHF_STRINGS"},
691 | {0x40, "SHF_INFO_LINK"},
692 | {0x80, "SHF_LINK_ORDER"},
693 | {0x100, "SHF_OS_NONCONFORMING"},
694 | {0x200, "SHF_GROUP"},
695 | {0x400, "SHF_TLS"},
696 | {0x800, "SHF_COMPRESSED"},
697 | }
698 |
699 | func (sf SectionFlag) String() string { return matchFlagName(uint32(sf), sectionFlagStrings, false) }
700 | func (sf SectionFlag) GoString() string { return matchFlagName(uint32(sf), sectionFlagStrings, true) }
701 |
702 | // Section compression type.
703 | type CompressionType int
704 |
705 | const (
706 | COMPRESS_ZLIB CompressionType = 1 // ZLIB compression.
707 | COMPRESS_LOOS CompressionType = 0x60000000 // First OS-specific.
708 | COMPRESS_HIOS CompressionType = 0x6fffffff // Last OS-specific.
709 | COMPRESS_LOPROC CompressionType = 0x70000000 // First processor-specific type.
710 | COMPRESS_HIPROC CompressionType = 0x7fffffff // Last processor-specific type.
711 | )
712 |
713 | var compressionStrings = []flagName{
714 | {0, "COMPRESS_ZLIB"},
715 | {0x60000000, "COMPRESS_LOOS"},
716 | {0x6fffffff, "COMPRESS_HIOS"},
717 | {0x70000000, "COMPRESS_LOPROC"},
718 | {0x7fffffff, "COMPRESS_HIPROC"},
719 | }
720 |
721 | func (ct CompressionType) String() string { return stringify(uint32(ct), compressionStrings, false) }
722 | func (ct CompressionType) GoString() string { return stringify(uint32(ct), compressionStrings, true) }
723 |
724 | // Prog.Type
725 | type ProgType int
726 |
727 | const (
728 | PT_NULL ProgType = 0 // Unused entry.
729 | PT_LOAD ProgType = 1 // Loadable segment.
730 | PT_DYNAMIC ProgType = 2 // Dynamic linking information segment.
731 | PT_INTERP ProgType = 3 // Pathname of interpreter.
732 | PT_NOTE ProgType = 4 // Auxiliary information.
733 | PT_SHLIB ProgType = 5 // Reserved (not used).
734 | PT_PHDR ProgType = 6 // Location of program header itself.
735 | PT_TLS ProgType = 7 // Thread local storage segment
736 | PT_LOOS ProgType = 0x60000000 // First OS-specific.
737 | PT_GNU_EH_FRAME ProgType = 0x6474e550 // Frame unwind information
738 | PT_GNU_STACK ProgType = 0x6474e551 // Stack flags
739 | PT_GNU_RELRO ProgType = 0x6474e552 // Read only after relocs
740 | PT_GNU_PROPERTY ProgType = 0x6474e553 // GNU property
741 | PT_GNU_MBIND_LO ProgType = 0x6474e555 // Mbind segments start
742 | PT_GNU_MBIND_HI ProgType = 0x6474f554 // Mbind segments finish
743 | PT_PAX_FLAGS ProgType = 0x65041580 // PAX flags
744 | PT_OPENBSD_RANDOMIZE ProgType = 0x65a3dbe6 // Random data
745 | PT_OPENBSD_WXNEEDED ProgType = 0x65a3dbe7 // W^X violations
746 | PT_OPENBSD_BOOTDATA ProgType = 0x65a41be6 // Boot arguments
747 | PT_SUNW_EH_FRAME ProgType = 0x6474e550 // Frame unwind information
748 | PT_SUNWSTACK ProgType = 0x6ffffffb // Stack segment
749 | PT_HIOS ProgType = 0x6fffffff // Last OS-specific.
750 | PT_LOPROC ProgType = 0x70000000 // First processor-specific type.
751 | PT_ARM_ARCHEXT ProgType = 0x70000000 // Architecture compatibility
752 | PT_ARM_EXIDX ProgType = 0x70000001 // Exception unwind tables
753 | PT_AARCH64_ARCHEXT ProgType = 0x70000000 // Architecture compatibility
754 | PT_AARCH64_UNWIND ProgType = 0x70000001 // Exception unwind tables
755 | PT_MIPS_REGINFO ProgType = 0x70000000 // Register usage
756 | PT_MIPS_RTPROC ProgType = 0x70000001 // Runtime procedures
757 | PT_MIPS_OPTIONS ProgType = 0x70000002 // Options
758 | PT_MIPS_ABIFLAGS ProgType = 0x70000003 // ABI flags
759 | PT_S390_PGSTE ProgType = 0x70000000 // 4k page table size
760 | PT_HIPROC ProgType = 0x7fffffff // Last processor-specific type.
761 | )
762 |
763 | var programTypeStrings = []flagName{
764 | {0, "PT_NULL"},
765 | {1, "PT_LOAD"},
766 | {2, "PT_DYNAMIC"},
767 | {3, "PT_INTERP"},
768 | {4, "PT_NOTE"},
769 | {5, "PT_SHLIB"},
770 | {6, "PT_PHDR"},
771 | {7, "PT_TLS"},
772 | {0x60000000, "PT_LOOS"},
773 | {0x6474e550, "PT_GNU_EH_FRAME"},
774 | {0x6474e551, "PT_GNU_STACK"},
775 | {0x6474e552, "PT_GNU_RELRO"},
776 | {0x6474e553, "PT_GNU_PROPERTY"},
777 | {0x65041580, "PT_PAX_FLAGS"},
778 | {0x65a3dbe6, "PT_OPENBSD_RANDOMIZE"},
779 | {0x65a3dbe7, "PT_OPENBSD_WXNEEDED"},
780 | {0x65a41be6, "PT_OPENBSD_BOOTDATA"},
781 | {0x6ffffffb, "PT_SUNWSTACK"},
782 | {0x6fffffff, "PT_HIOS"},
783 | {0x70000000, "PT_LOPROC"},
784 | // We don't list the processor-dependent ProgTypes,
785 | // as the values overlap.
786 | {0x7fffffff, "PT_HIPROC"},
787 | }
788 |
789 | func (pt ProgType) String() string { return stringify(uint32(pt), programTypeStrings, false) }
790 | func (pt ProgType) GoString() string { return stringify(uint32(pt), programTypeStrings, true) }
791 |
792 | // Prog.Flag
793 | type ProgFlag uint32
794 |
795 | const (
796 | PF_X ProgFlag = 0x1 // Executable.
797 | PF_W ProgFlag = 0x2 // Writable.
798 | PF_R ProgFlag = 0x4 // Readable.
799 | PF_MASKOS ProgFlag = 0x0ff00000 // Operating system-specific.
800 | PF_MASKPROC ProgFlag = 0xf0000000 // Processor-specific.
801 | )
802 |
803 | var programFlagStrings = []flagName{
804 | {0x1, "PF_X"},
805 | {0x2, "PF_W"},
806 | {0x4, "PF_R"},
807 | }
808 |
809 | func (pf ProgFlag) String() string { return matchFlagName(uint32(pf), programFlagStrings, false) }
810 | func (pf ProgFlag) GoString() string { return matchFlagName(uint32(pf), programFlagStrings, true) }
811 |
812 | // DynTag
813 | type DynTag int
814 |
815 | const (
816 | DT_NULL DynTag = 0 /* Terminating entry. */
817 | DT_NEEDED DynTag = 1 /* String table offset of a needed shared library. */
818 | DT_PLTRELSZ DynTag = 2 /* Total size in bytes of PLT relocations. */
819 | DT_PLTGOT DynTag = 3 /* Processor-dependent address. */
820 | DT_HASH DynTag = 4 /* Address of symbol hash table. */
821 | DT_STRTAB DynTag = 5 /* Address of string table. */
822 | DT_SYMTAB DynTag = 6 /* Address of symbol table. */
823 | DT_RELA DynTag = 7 /* Address of ElfNN_Rela relocations. */
824 | DT_RELASZ DynTag = 8 /* Total size of ElfNN_Rela relocations. */
825 | DT_RELAENT DynTag = 9 /* Size of each ElfNN_Rela relocation entry. */
826 | DT_STRSZ DynTag = 10 /* Size of string table. */
827 | DT_SYMENT DynTag = 11 /* Size of each symbol table entry. */
828 | DT_INIT DynTag = 12 /* Address of initialization function. */
829 | DT_FINI DynTag = 13 /* Address of finalization function. */
830 | DT_SONAME DynTag = 14 /* String table offset of shared object name. */
831 | DT_RPATH DynTag = 15 /* String table offset of library path. [sup] */
832 | DT_SYMBOLIC DynTag = 16 /* Indicates "symbolic" linking. [sup] */
833 | DT_REL DynTag = 17 /* Address of ElfNN_Rel relocations. */
834 | DT_RELSZ DynTag = 18 /* Total size of ElfNN_Rel relocations. */
835 | DT_RELENT DynTag = 19 /* Size of each ElfNN_Rel relocation. */
836 | DT_PLTREL DynTag = 20 /* Type of relocation used for PLT. */
837 | DT_DEBUG DynTag = 21 /* Reserved (not used). */
838 | DT_TEXTREL DynTag = 22 /* Indicates there may be relocations in non-writable segments. [sup] */
839 | DT_JMPREL DynTag = 23 /* Address of PLT relocations. */
840 | DT_BIND_NOW DynTag = 24 /* [sup] */
841 | DT_INIT_ARRAY DynTag = 25 /* Address of the array of pointers to initialization functions */
842 | DT_FINI_ARRAY DynTag = 26 /* Address of the array of pointers to termination functions */
843 | DT_INIT_ARRAYSZ DynTag = 27 /* Size in bytes of the array of initialization functions. */
844 | DT_FINI_ARRAYSZ DynTag = 28 /* Size in bytes of the array of termination functions. */
845 | DT_RUNPATH DynTag = 29 /* String table offset of a null-terminated library search path string. */
846 | DT_FLAGS DynTag = 30 /* Object specific flag values. */
847 | DT_ENCODING DynTag = 32 /* Values greater than or equal to DT_ENCODING
848 | and less than DT_LOOS follow the rules for
849 | the interpretation of the d_un union
850 | as follows: even == 'd_ptr', even == 'd_val'
851 | or none */
852 | DT_PREINIT_ARRAY DynTag = 32 /* Address of the array of pointers to pre-initialization functions. */
853 | DT_PREINIT_ARRAYSZ DynTag = 33 /* Size in bytes of the array of pre-initialization functions. */
854 | DT_SYMTAB_SHNDX DynTag = 34 /* Address of SHT_SYMTAB_SHNDX section. */
855 | DT_LOOS DynTag = 0x6000000d /* First OS-specific */
856 | DT_HIOS DynTag = 0x6ffff000 /* Last OS-specific */
857 | DT_VALRNGLO DynTag = 0x6ffffd00
858 | DT_GNU_PRELINKED DynTag = 0x6ffffdf5
859 | DT_GNU_CONFLICTSZ DynTag = 0x6ffffdf6
860 | DT_GNU_LIBLISTSZ DynTag = 0x6ffffdf7
861 | DT_CHECKSUM DynTag = 0x6ffffdf8
862 | DT_PLTPADSZ DynTag = 0x6ffffdf9
863 | DT_MOVEENT DynTag = 0x6ffffdfa
864 | DT_MOVESZ DynTag = 0x6ffffdfb
865 | DT_FEATURE DynTag = 0x6ffffdfc
866 | DT_POSFLAG_1 DynTag = 0x6ffffdfd
867 | DT_SYMINSZ DynTag = 0x6ffffdfe
868 | DT_SYMINENT DynTag = 0x6ffffdff
869 | DT_VALRNGHI DynTag = 0x6ffffdff
870 | DT_ADDRRNGLO DynTag = 0x6ffffe00
871 | DT_GNU_HASH DynTag = 0x6ffffef5
872 | DT_TLSDESC_PLT DynTag = 0x6ffffef6
873 | DT_TLSDESC_GOT DynTag = 0x6ffffef7
874 | DT_GNU_CONFLICT DynTag = 0x6ffffef8
875 | DT_GNU_LIBLIST DynTag = 0x6ffffef9
876 | DT_CONFIG DynTag = 0x6ffffefa
877 | DT_DEPAUDIT DynTag = 0x6ffffefb
878 | DT_AUDIT DynTag = 0x6ffffefc
879 | DT_PLTPAD DynTag = 0x6ffffefd
880 | DT_MOVETAB DynTag = 0x6ffffefe
881 | DT_SYMINFO DynTag = 0x6ffffeff
882 | DT_ADDRRNGHI DynTag = 0x6ffffeff
883 | DT_VERSYM DynTag = 0x6ffffff0
884 | DT_RELACOUNT DynTag = 0x6ffffff9
885 | DT_RELCOUNT DynTag = 0x6ffffffa
886 | DT_FLAGS_1 DynTag = 0x6ffffffb
887 | DT_VERDEF DynTag = 0x6ffffffc
888 | DT_VERDEFNUM DynTag = 0x6ffffffd
889 | DT_VERNEED DynTag = 0x6ffffffe
890 | DT_VERNEEDNUM DynTag = 0x6fffffff
891 | DT_LOPROC DynTag = 0x70000000 /* First processor-specific type. */
892 | DT_MIPS_RLD_VERSION DynTag = 0x70000001
893 | DT_MIPS_TIME_STAMP DynTag = 0x70000002
894 | DT_MIPS_ICHECKSUM DynTag = 0x70000003
895 | DT_MIPS_IVERSION DynTag = 0x70000004
896 | DT_MIPS_FLAGS DynTag = 0x70000005
897 | DT_MIPS_BASE_ADDRESS DynTag = 0x70000006
898 | DT_MIPS_MSYM DynTag = 0x70000007
899 | DT_MIPS_CONFLICT DynTag = 0x70000008
900 | DT_MIPS_LIBLIST DynTag = 0x70000009
901 | DT_MIPS_LOCAL_GOTNO DynTag = 0x7000000a
902 | DT_MIPS_CONFLICTNO DynTag = 0x7000000b
903 | DT_MIPS_LIBLISTNO DynTag = 0x70000010
904 | DT_MIPS_SYMTABNO DynTag = 0x70000011
905 | DT_MIPS_UNREFEXTNO DynTag = 0x70000012
906 | DT_MIPS_GOTSYM DynTag = 0x70000013
907 | DT_MIPS_HIPAGENO DynTag = 0x70000014
908 | DT_MIPS_RLD_MAP DynTag = 0x70000016
909 | DT_MIPS_DELTA_CLASS DynTag = 0x70000017
910 | DT_MIPS_DELTA_CLASS_NO DynTag = 0x70000018
911 | DT_MIPS_DELTA_INSTANCE DynTag = 0x70000019
912 | DT_MIPS_DELTA_INSTANCE_NO DynTag = 0x7000001a
913 | DT_MIPS_DELTA_RELOC DynTag = 0x7000001b
914 | DT_MIPS_DELTA_RELOC_NO DynTag = 0x7000001c
915 | DT_MIPS_DELTA_SYM DynTag = 0x7000001d
916 | DT_MIPS_DELTA_SYM_NO DynTag = 0x7000001e
917 | DT_MIPS_DELTA_CLASSSYM DynTag = 0x70000020
918 | DT_MIPS_DELTA_CLASSSYM_NO DynTag = 0x70000021
919 | DT_MIPS_CXX_FLAGS DynTag = 0x70000022
920 | DT_MIPS_PIXIE_INIT DynTag = 0x70000023
921 | DT_MIPS_SYMBOL_LIB DynTag = 0x70000024
922 | DT_MIPS_LOCALPAGE_GOTIDX DynTag = 0x70000025
923 | DT_MIPS_LOCAL_GOTIDX DynTag = 0x70000026
924 | DT_MIPS_HIDDEN_GOTIDX DynTag = 0x70000027
925 | DT_MIPS_PROTECTED_GOTIDX DynTag = 0x70000028
926 | DT_MIPS_OPTIONS DynTag = 0x70000029
927 | DT_MIPS_INTERFACE DynTag = 0x7000002a
928 | DT_MIPS_DYNSTR_ALIGN DynTag = 0x7000002b
929 | DT_MIPS_INTERFACE_SIZE DynTag = 0x7000002c
930 | DT_MIPS_RLD_TEXT_RESOLVE_ADDR DynTag = 0x7000002d
931 | DT_MIPS_PERF_SUFFIX DynTag = 0x7000002e
932 | DT_MIPS_COMPACT_SIZE DynTag = 0x7000002f
933 | DT_MIPS_GP_VALUE DynTag = 0x70000030
934 | DT_MIPS_AUX_DYNAMIC DynTag = 0x70000031
935 | DT_MIPS_PLTGOT DynTag = 0x70000032
936 | DT_MIPS_RWPLT DynTag = 0x70000034
937 | DT_MIPS_RLD_MAP_REL DynTag = 0x70000035
938 | DT_PPC_GOT DynTag = 0x70000000
939 | DT_PPC_OPT DynTag = 0x70000001
940 | DT_PPC64_GLINK DynTag = 0x70000000
941 | DT_PPC64_OPD DynTag = 0x70000001
942 | DT_PPC64_OPDSZ DynTag = 0x70000002
943 | DT_PPC64_OPT DynTag = 0x70000003
944 | DT_SPARC_REGISTER DynTag = 0x70000001
945 | DT_AUXILIARY DynTag = 0x7ffffffd
946 | DT_USED DynTag = 0x7ffffffe
947 | DT_FILTER DynTag = 0x7fffffff
948 | DT_HIPROC DynTag = 0x7fffffff /* Last processor-specific type. */
949 | )
950 |
951 | var dtStrings = []flagName{
952 | {0, "DT_NULL"},
953 | {1, "DT_NEEDED"},
954 | {2, "DT_PLTRELSZ"},
955 | {3, "DT_PLTGOT"},
956 | {4, "DT_HASH"},
957 | {5, "DT_STRTAB"},
958 | {6, "DT_SYMTAB"},
959 | {7, "DT_RELA"},
960 | {8, "DT_RELASZ"},
961 | {9, "DT_RELAENT"},
962 | {10, "DT_STRSZ"},
963 | {11, "DT_SYMENT"},
964 | {12, "DT_INIT"},
965 | {13, "DT_FINI"},
966 | {14, "DT_SONAME"},
967 | {15, "DT_RPATH"},
968 | {16, "DT_SYMBOLIC"},
969 | {17, "DT_REL"},
970 | {18, "DT_RELSZ"},
971 | {19, "DT_RELENT"},
972 | {20, "DT_PLTREL"},
973 | {21, "DT_DEBUG"},
974 | {22, "DT_TEXTREL"},
975 | {23, "DT_JMPREL"},
976 | {24, "DT_BIND_NOW"},
977 | {25, "DT_INIT_ARRAY"},
978 | {26, "DT_FINI_ARRAY"},
979 | {27, "DT_INIT_ARRAYSZ"},
980 | {28, "DT_FINI_ARRAYSZ"},
981 | {29, "DT_RUNPATH"},
982 | {30, "DT_FLAGS"},
983 | // {32, "DT_ENCODING"},
984 | {33, "DT_PREINIT_ARRAYSZ"},
985 | {34, "DT_SYMTAB_SHNDX"},
986 | {0x6000000d, "DT_LOOS"},
987 | {0x6ffff000, "DT_HIOS"},
988 | {0x6ffffd00, "DT_VALRNGLO"},
989 | {0x6ffffdf5, "DT_GNU_PRELINKED"},
990 | {0x6ffffdf6, "DT_GNU_CONFLICTSZ"},
991 | {0x6ffffdf7, "DT_GNU_LIBLISTSZ"},
992 | {0x6ffffdf8, "DT_CHECKSUM"},
993 | {0x6ffffdf9, "DT_PLTPADSZ"},
994 | {0x6ffffdfa, "DT_MOVEENT"},
995 | {0x6ffffdfb, "DT_MOVESZ"},
996 | {0x6ffffdfc, "DT_FEATURE"},
997 | {0x6ffffdfd, "DT_POSFLAG_1"},
998 | {0x6ffffdfe, "DT_SYMINSZ"},
999 | {0x6ffffdff, "DT_SYMINENT"},
1000 | {0x6ffffe00, "DT_ADDRRNGLO"},
1001 | {0x6ffffef5, "DT_GNU_HASH"},
1002 | {0x6ffffef6, "DT_TLSDESC_PLT"},
1003 | {0x6ffffef7, "DT_TLSDESC_GOT"},
1004 | {0x6ffffef8, "DT_GNU_CONFLICT"},
1005 | {0x6ffffef9, "DT_GNU_LIBLIST"},
1006 | {0x6ffffefa, "DT_CONFIG"},
1007 | {0x6ffffefb, "DT_DEPAUDIT"},
1008 | {0x6ffffefc, "DT_AUDIT"},
1009 | {0x6ffffefd, "DT_PLTPAD"},
1010 | {0x6ffffefe, "DT_MOVETAB"},
1011 | {0x6ffffeff, "DT_SYMINFO"},
1012 | {0x6ffffff0, "DT_VERSYM"},
1013 | {0x6ffffff9, "DT_RELACOUNT"},
1014 | {0x6ffffffa, "DT_RELCOUNT"},
1015 | {0x6ffffffb, "DT_FLAGS_1"},
1016 | {0x6ffffffc, "DT_VERDEF"},
1017 | {0x6ffffffd, "DT_VERDEFNUM"},
1018 | {0x6ffffffe, "DT_VERNEED"},
1019 | {0x6fffffff, "DT_VERNEEDNUM"},
1020 | {0x70000000, "DT_LOPROC"},
1021 | // We don't list the processor-dependent DynTags,
1022 | // as the values overlap.
1023 | // We also stack last duplicate flags.
1024 | {32, "DT_PREINIT_ARRAY"},
1025 | // {0x6ffffeff, "DT_ADDRRNGHI"},
1026 | // {0x6ffffdff, "DT_VALRNGHI"},
1027 | {0x7ffffffd, "DT_AUXILIARY"},
1028 | {0x7ffffffe, "DT_USED"},
1029 | {0x7fffffff, "DT_FILTER"},
1030 | }
1031 |
1032 | func (dt DynTag) String() string { return stringify(uint32(dt), dtStrings, false) }
1033 | func (dt DynTag) GoString() string { return stringify(uint32(dt), dtStrings, true) }
1034 |
1035 | // DT_FLAGS values.
1036 | type DynFlag int
1037 |
1038 | const (
1039 | DF_ORIGIN DynFlag = 0x0001 /* Indicates that the object being loaded may
1040 | make reference to the
1041 | $ORIGIN substitution string */
1042 | DF_SYMBOLIC DynFlag = 0x0002 /* Indicates "symbolic" linking. */
1043 | DF_TEXTREL DynFlag = 0x0004 /* Indicates there may be relocations in non-writable segments. */
1044 | DF_BIND_NOW DynFlag = 0x0008 /* Indicates that the dynamic linker should
1045 | process all relocations for the object
1046 | containing this entry before transferring
1047 | control to the program. */
1048 | DF_STATIC_TLS DynFlag = 0x0010 /* Indicates that the shared object or
1049 | executable contains code using a static
1050 | thread-local storage scheme. */
1051 | )
1052 |
1053 | var dflagStrings = []flagName{
1054 | {0x0001, "DF_ORIGIN"},
1055 | {0x0002, "DF_SYMBOLIC"},
1056 | {0x0004, "DF_TEXTREL"},
1057 | {0x0008, "DF_BIND_NOW"},
1058 | {0x0010, "DF_STATIC_TLS"},
1059 | }
1060 |
1061 | func (df DynFlag) String() string { return matchFlagName(uint32(df), dflagStrings, false) }
1062 | func (df DynFlag) GoString() string { return matchFlagName(uint32(df), dflagStrings, true) }
1063 |
1064 | // NType values; used in core files.
1065 | type NType int
1066 |
1067 | const (
1068 | NT_PRSTATUS NType = 1 /* Process status. */
1069 | NT_FPREGSET NType = 2 /* Floating point registers. */
1070 | NT_PRPSINFO NType = 3 /* Process state info. */
1071 | )
1072 |
1073 | var ntypeStrings = []flagName{
1074 | {1, "NT_PRSTATUS"},
1075 | {2, "NT_FPREGSET"},
1076 | {3, "NT_PRPSINFO"},
1077 | }
1078 |
1079 | func (i NType) String() string { return stringify(uint32(i), ntypeStrings, false) }
1080 | func (i NType) GoString() string { return stringify(uint32(i), ntypeStrings, true) }
1081 |
1082 | /* Symbol Binding - ELFNN_ST_BIND - st_info */
1083 | type SymBind int
1084 |
1085 | const (
1086 | STB_LOCAL SymBind = 0 /* Local symbol */
1087 | STB_GLOBAL SymBind = 1 /* Global symbol */
1088 | STB_WEAK SymBind = 2 /* like global - lower precedence */
1089 | STB_LOOS SymBind = 10 /* Reserved range for operating system */
1090 | STB_HIOS SymBind = 12 /* specific semantics. */
1091 | STB_LOPROC SymBind = 13 /* reserved range for processor */
1092 | STB_HIPROC SymBind = 15 /* specific semantics. */
1093 | )
1094 |
1095 | var stbStrings = []flagName{
1096 | {0, "STB_LOCAL"},
1097 | {1, "STB_GLOBAL"},
1098 | {2, "STB_WEAK"},
1099 | {10, "STB_LOOS"},
1100 | {12, "STB_HIOS"},
1101 | {13, "STB_LOPROC"},
1102 | {15, "STB_HIPROC"},
1103 | }
1104 |
1105 | func (i SymBind) String() string { return stringify(uint32(i), stbStrings, false) }
1106 | func (i SymBind) GoString() string { return stringify(uint32(i), stbStrings, true) }
1107 |
1108 | /* Symbol type - ELFNN_ST_TYPE - st_info */
1109 | type SymType int
1110 |
1111 | const (
1112 | STT_NOTYPE SymType = 0 /* Unspecified type. */
1113 | STT_OBJECT SymType = 1 /* Data object. */
1114 | STT_FUNC SymType = 2 /* Function. */
1115 | STT_SECTION SymType = 3 /* Section. */
1116 | STT_FILE SymType = 4 /* Source file. */
1117 | STT_COMMON SymType = 5 /* Uninitialized common block. */
1118 | STT_TLS SymType = 6 /* TLS object. */
1119 | STT_LOOS SymType = 10 /* Reserved range for operating system */
1120 | STT_HIOS SymType = 12 /* specific semantics. */
1121 | STT_LOPROC SymType = 13 /* reserved range for processor */
1122 | STT_HIPROC SymType = 15 /* specific semantics. */
1123 | )
1124 |
1125 | var sttStrings = []flagName{
1126 | {0, "STT_NOTYPE"},
1127 | {1, "STT_OBJECT"},
1128 | {2, "STT_FUNC"},
1129 | {3, "STT_SECTION"},
1130 | {4, "STT_FILE"},
1131 | {5, "STT_COMMON"},
1132 | {6, "STT_TLS"},
1133 | {10, "STT_LOOS"},
1134 | {12, "STT_HIOS"},
1135 | {13, "STT_LOPROC"},
1136 | {15, "STT_HIPROC"},
1137 | }
1138 |
1139 | func (i SymType) String() string { return stringify(uint32(i), sttStrings, false) }
1140 | func (i SymType) GoString() string { return stringify(uint32(i), sttStrings, true) }
1141 |
1142 | /* Symbol visibility - ELFNN_ST_VISIBILITY - st_other */
1143 | type SymVis int
1144 |
1145 | const (
1146 | STV_DEFAULT SymVis = 0x0 /* Default visibility (see binding). */
1147 | STV_INTERNAL SymVis = 0x1 /* Special meaning in relocatable objects. */
1148 | STV_HIDDEN SymVis = 0x2 /* Not visible. */
1149 | STV_PROTECTED SymVis = 0x3 /* Visible but not preemptible. */
1150 | )
1151 |
1152 | var stvStrings = []flagName{
1153 | {0x0, "STV_DEFAULT"},
1154 | {0x1, "STV_INTERNAL"},
1155 | {0x2, "STV_HIDDEN"},
1156 | {0x3, "STV_PROTECTED"},
1157 | }
1158 |
1159 | func (i SymVis) String() string { return stringify(uint32(i), stvStrings, false) }
1160 | func (i SymVis) GoString() string { return stringify(uint32(i), stvStrings, true) }
1161 |
--------------------------------------------------------------------------------
/gnuver.go:
--------------------------------------------------------------------------------
1 | // Package elf: gnuver.go implements the GNU Version table specification.
2 | package elf
3 |
4 | import "errors"
5 |
6 | // GNUVersion holds the version information
7 | type GNUVersion struct {
8 | File string
9 | Name string
10 | }
11 |
12 | // ImportedSymbol will hold information on external imports.
13 | type ImportedSymbol struct {
14 | Name string
15 | Version string
16 | Library string
17 | }
18 |
19 | // ParseGNUVersionTable parses the GNU version tables.
20 | func (p *Parser) ParseGNUVersionTable(str []byte) error {
21 | if p.F.GNUVersion != nil {
22 | return errors.New("already processed GNU version table")
23 | }
24 |
25 | vn := p.F.GetSectionByType(SHT_GNU_VERNEED)
26 | if vn == nil {
27 | return errors.New("no gnu verneed section in file")
28 | }
29 | d, _ := vn.Data()
30 |
31 | var gnuVer []GNUVersion
32 | i := 0
33 | for {
34 | if i+16 > len(d) {
35 | break
36 | }
37 | vers := p.F.ByteOrder().Uint16(d[i : i+2])
38 | if vers != 1 {
39 | break
40 | }
41 | cnt := p.F.ByteOrder().Uint16(d[i+2 : i+4])
42 | fileoff := p.F.ByteOrder().Uint32(d[i+4 : i+8])
43 | aux := p.F.ByteOrder().Uint32(d[i+8 : i+12])
44 | next := p.F.ByteOrder().Uint32(d[i+12 : i+16])
45 | file, _ := getString(str, int(fileoff))
46 |
47 | var name string
48 |
49 | j := i + int(aux)
50 | for c := 0; c < int(cnt); c++ {
51 | if j+16 > len(d) {
52 | break
53 | }
54 | other := p.F.ByteOrder().Uint16(d[j+6 : j+8])
55 | nameoff := p.F.ByteOrder().Uint32(d[j+8 : j+12])
56 | next := p.F.ByteOrder().Uint32(d[j+12 : j+16])
57 | name, _ = getString(str, int(nameoff))
58 | ndx := int(other)
59 | if ndx >= len(gnuVer) {
60 | a := make([]GNUVersion, 2*(ndx+1))
61 | copy(a, gnuVer)
62 | gnuVer = a
63 | }
64 | gnuVer[ndx] = GNUVersion{file, name}
65 | if next == 0 {
66 | break
67 | }
68 | j += int(next)
69 | }
70 | if next == 0 {
71 | break
72 | }
73 | i += int(next)
74 |
75 | }
76 | // Versym parallels symbol table, indexing into verneed.
77 | vs := p.F.GetSectionByType(SHT_GNU_VERSYM)
78 | if vs == nil {
79 | return errors.New("no gnu versym section in file")
80 | }
81 | d, _ = vs.Data()
82 | p.F.GNUVersion = gnuVer
83 | p.F.GNUVersionSym = d
84 | return nil
85 |
86 | }
87 |
88 | // gnuVersion adds Library and Version information to namedSymbol,
89 | // which came from offset i of the symbol table.
90 | func (p *Parser) gnuVersion(i int) (string, string) {
91 | // Each entry is two bytes.
92 | i = (i + 1) * 2
93 | if i >= len(p.F.GNUVersionSym) {
94 | return "", ""
95 | }
96 | j := int(p.F.ByteOrder().Uint16(p.F.GNUVersionSym[i:]))
97 | if j < 2 || j >= len(p.F.GNUVersion) {
98 | return "", ""
99 | }
100 | n := &p.F.GNUVersion[j]
101 | return n.File, n.Name
102 | }
103 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/saferwall/elf
2 |
3 | go 1.21
4 |
5 | require (
6 | github.com/saferwall/binstream v0.1.1
7 | github.com/stretchr/testify v1.8.4
8 | )
9 |
10 | require (
11 | github.com/davecgh/go-spew v1.1.1 // indirect
12 | github.com/edsrzf/mmap-go v1.1.0 // indirect
13 | github.com/pmezard/go-difflib v1.0.0 // indirect
14 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
15 | gopkg.in/yaml.v3 v3.0.1 // indirect
16 | )
17 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
4 | github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
5 | github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
8 | github.com/saferwall/binstream v0.1.1 h1:ATLUHjjM1w0/75pV+/O7OY1BB5UDLicG1ohewllQsYk=
9 | github.com/saferwall/binstream v0.1.1/go.mod h1:RRSF+ePir1XKbQF4BlnShbs6u1PI0io90/lGvw/Qq1s=
10 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
11 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
12 | golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
13 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
14 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
15 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
17 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
18 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
19 |
--------------------------------------------------------------------------------
/header.go:
--------------------------------------------------------------------------------
1 | package elf
2 |
3 | import "debug/elf"
4 |
5 | // NewELF32Header creates a new ELF 32-bit header.
6 | func NewELF32Header() ELF32Header {
7 | return ELF32Header{}
8 | }
9 |
10 | // GetIdent returns identifier array EI_IDENT.
11 | func (h ELF32Header) GetIdent() [EI_NIDENT]byte {
12 | return h.Ident
13 | }
14 |
15 | // GetType returns file type.
16 | func (h ELF32Header) GetType() uint16 {
17 | return h.Type
18 | }
19 |
20 | // GetMachine returns ELF target machine.
21 | func (h ELF32Header) GetMachine() uint16 {
22 | return h.Machine
23 | }
24 |
25 | // GetEntry returns entrypoint (virtual address) of ELF binary.
26 | func (h ELF32Header) GetEntry() uint32 {
27 | return h.Entry
28 | }
29 |
30 | // ProgramHeadersOffset returns the file offset to the program headers.
31 | func (h ELF32Header) ProgramHeadersOffset() uint32 {
32 | return h.Phoff
33 | }
34 |
35 | // SectionHeadersOffset returns the file offset to the section headers.
36 | func (h ELF32Header) SectionHeadersOffset() uint32 {
37 | return h.Shoff
38 | }
39 |
40 | // SectionHeadersNum returns the number of section headers is in the section headers table.
41 | func (h ELF32Header) SectionHeadersNum() uint16 {
42 | return h.Shnum
43 | }
44 |
45 | // SectionHeadersEntSize returns the size of a section headers entry
46 | func (h ELF32Header) SectionHeadersEntSize() uint16 {
47 | return h.Shentsize
48 | }
49 |
50 | // Size returns the ELF Header size in bytes.
51 | func (h ELF32Header) Size() uint16 {
52 | return h.Ehsize
53 | }
54 |
55 | // NewELF64Header creates a new ELF 64-bit header.
56 | func NewELF64Header() ELF64Header {
57 | return ELF64Header{}
58 | }
59 |
60 | // GetIdent returns identifier array EI_IDENT.
61 | func (h ELF64Header) GetIdent() [elf.EI_NIDENT]byte {
62 | return h.Ident
63 | }
64 |
65 | // GetType returns file type.
66 | func (h ELF64Header) GetType() uint16 {
67 | return h.Type
68 | }
69 |
70 | // GetMachine returns ELF target machine.
71 | func (h ELF64Header) GetMachine() uint16 {
72 | return h.Machine
73 | }
74 |
75 | // GetEntry returns entrypoint (virtual address) of ELF binary.
76 | func (h ELF64Header) GetEntry() uint64 {
77 | return h.Entry
78 | }
79 |
80 | // ProgramHeadersOffset returns the file offset to the program headers.
81 | func (h ELF64Header) ProgramHeadersOffset() uint64 {
82 | return h.Phoff
83 | }
84 |
85 | // SectionHeadersOffset returns the file offset to the section headers.
86 | func (h ELF64Header) SectionHeadersOffset() uint64 {
87 | return h.Shoff
88 | }
89 |
90 | // SectionHeadersNum returns the number of section headers is in the section headers table.
91 | func (h ELF64Header) SectionHeadersNum() uint16 {
92 | return h.Shnum
93 | }
94 |
95 | // SectionHeadersEntSize returns the size of a section headers entry
96 | func (h ELF64Header) SectionHeadersEntSize() uint16 {
97 | return h.Shentsize
98 | }
99 |
100 | // Size returns the ELF Header size in bytes.
101 | func (h ELF64Header) Size() uint16 {
102 | return h.Ehsize
103 | }
104 |
--------------------------------------------------------------------------------
/json.go:
--------------------------------------------------------------------------------
1 | package elf
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "strings"
7 | )
8 |
9 | // DumpRawJSON marshals the raw binary representation into JSON Format.
10 | func (p *Parser) DumpRawJSON() (string, error) {
11 |
12 | var jsonOutput strings.Builder
13 |
14 | switch p.F.Class() {
15 | case ELFCLASS32:
16 | jsonBin, err := json.MarshalIndent(p.F.ELFBin32, "", " ")
17 | if err != nil {
18 | return "", err
19 | }
20 | jsonSymbols, err := json.MarshalIndent(p.F.ELFSymbols, "", " ")
21 | if err != nil {
22 | return "", err
23 | }
24 | _, err = jsonOutput.Write(jsonBin)
25 | if err != nil {
26 | return "", err
27 | }
28 | _, err = jsonOutput.Write(jsonSymbols)
29 | if err != nil {
30 | return "", err
31 | }
32 | return jsonOutput.String(), nil
33 | case ELFCLASS64:
34 | jsonBin, err := json.MarshalIndent(p.F.ELFBin64, "", " ")
35 | if err != nil {
36 | return "", err
37 | }
38 | jsonSymbols, err := json.MarshalIndent(p.F.ELFSymbols, "", " ")
39 | if err != nil {
40 | return "", err
41 | }
42 | _, err = jsonOutput.Write(jsonBin)
43 | if err != nil {
44 | return "", err
45 | }
46 | _, err = jsonOutput.Write(jsonSymbols)
47 | if err != nil {
48 | return "", err
49 | }
50 | return jsonOutput.String(), nil
51 | default:
52 | return "", errors.New("unsupported ELF Class")
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/log/README.md:
--------------------------------------------------------------------------------
1 | # Logger
2 |
3 | This code was taken from the go microservice framework [kratos](https://github.com/go-kratos/kratos).
4 |
5 | ## Usage
6 |
7 | ### Structured logging
8 |
9 | ```go
10 | logger := log.NewStdLogger(os.Stdout)
11 | // fields & valuer
12 | logger = log.With(logger,
13 | "service.name", "hellworld",
14 | "service.version", "v1.0.0",
15 | "ts", log.DefaultTimestamp,
16 | "caller", log.DefaultCaller,
17 | )
18 | logger.Log(log.LevelInfo, "key", "value")
19 |
20 | // helper
21 | helper := log.NewHelper(logger)
22 | helper.Log(log.LevelInfo, "key", "value")
23 | helper.Info("info message")
24 | helper.Infof("info %s", "message")
25 | helper.Infow("key", "value")
26 |
27 | // filter
28 | log := log.NewHelper(log.NewFilter(logger,
29 | log.FilterLevel(log.LevelInfo),
30 | log.FilterKey("foo"),
31 | log.FilterValue("bar"),
32 | log.FilterFunc(customFilter),
33 | ))
34 | log.Debug("debug log")
35 | log.Info("info log")
36 | log.Warn("warn log")
37 | log.Error("warn log")
38 | ```
39 |
40 | ## Third party log library
41 |
42 | If you need to implement a third party logging library like `zap`, have a look this [url](https://github.com/go-kratos/kratos/tree/main/contrib/log).
--------------------------------------------------------------------------------
/log/filter.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | // FilterOption is filter option.
4 | type FilterOption func(*Filter)
5 |
6 | const fuzzyStr = "***"
7 |
8 | // FilterLevel with filter level.
9 | func FilterLevel(level Level) FilterOption {
10 | return func(opts *Filter) {
11 | opts.level = level
12 | }
13 | }
14 |
15 | // FilterKey with filter key.
16 | func FilterKey(key ...string) FilterOption {
17 | return func(o *Filter) {
18 | for _, v := range key {
19 | o.key[v] = struct{}{}
20 | }
21 | }
22 | }
23 |
24 | // FilterValue with filter value.
25 | func FilterValue(value ...string) FilterOption {
26 | return func(o *Filter) {
27 | for _, v := range value {
28 | o.value[v] = struct{}{}
29 | }
30 | }
31 | }
32 |
33 | // FilterFunc with filter func.
34 | func FilterFunc(f func(level Level, keyvals ...interface{}) bool) FilterOption {
35 | return func(o *Filter) {
36 | o.filter = f
37 | }
38 | }
39 |
40 | // Filter is a logger filter.
41 | type Filter struct {
42 | logger Logger
43 | level Level
44 | key map[interface{}]struct{}
45 | value map[interface{}]struct{}
46 | filter func(level Level, keyvals ...interface{}) bool
47 | }
48 |
49 | // NewFilter new a logger filter.
50 | func NewFilter(logger Logger, opts ...FilterOption) *Filter {
51 | options := Filter{
52 | logger: logger,
53 | key: make(map[interface{}]struct{}),
54 | value: make(map[interface{}]struct{}),
55 | }
56 | for _, o := range opts {
57 | o(&options)
58 | }
59 | return &options
60 | }
61 |
62 | // Log Print log by level and keyvals.
63 | func (f *Filter) Log(level Level, keyvals ...interface{}) error {
64 | if level < f.level {
65 | return nil
66 | }
67 | // fkv is used to provide a slice to contains both logger.prefix and keyvals for filter
68 | var fkv []interface{}
69 | if l, ok := f.logger.(*logger); ok {
70 | if len(l.prefix) > 0 {
71 | fkv = make([]interface{}, 0, len(l.prefix)+len(keyvals))
72 | fkv = append(fkv, l.prefix...)
73 | fkv = append(fkv, keyvals...)
74 | }
75 | } else {
76 | fkv = keyvals
77 | }
78 | if f.filter != nil && f.filter(level, fkv...) {
79 | return nil
80 | }
81 | if len(f.key) > 0 || len(f.value) > 0 {
82 | for i := 0; i < len(keyvals); i += 2 {
83 | v := i + 1
84 | if v >= len(keyvals) {
85 | continue
86 | }
87 | if _, ok := f.key[keyvals[i]]; ok {
88 | keyvals[v] = fuzzyStr
89 | }
90 | if _, ok := f.value[keyvals[v]]; ok {
91 | keyvals[v] = fuzzyStr
92 | }
93 | }
94 | }
95 | return f.logger.Log(level, keyvals...)
96 | }
97 |
--------------------------------------------------------------------------------
/log/filter_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "bytes"
5 | "io"
6 | "testing"
7 | )
8 |
9 | func TestFilterAll(t *testing.T) {
10 | logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
11 | log := NewHelper(NewFilter(logger,
12 | FilterLevel(LevelDebug),
13 | FilterKey("username"),
14 | FilterValue("hello"),
15 | FilterFunc(testFilterFunc),
16 | ))
17 | log.Log(LevelDebug, "msg", "test debug")
18 | log.Info("hello")
19 | log.Infow("password", "123456")
20 | log.Infow("username", "kratos")
21 | log.Warn("warn log")
22 | }
23 |
24 | func TestFilterLevel(t *testing.T) {
25 | logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
26 | log := NewHelper(NewFilter(NewFilter(logger, FilterLevel(LevelWarn))))
27 | log.Log(LevelDebug, "msg1", "te1st debug")
28 | log.Debug("test debug")
29 | log.Debugf("test %s", "debug")
30 | log.Debugw("log", "test debug")
31 | log.Warn("warn log")
32 | }
33 |
34 | func TestFilterCaller(t *testing.T) {
35 | logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
36 | log := NewFilter(logger)
37 | _ = log.Log(LevelDebug, "msg1", "te1st debug")
38 | logHelper := NewHelper(NewFilter(logger))
39 | logHelper.Log(LevelDebug, "msg1", "te1st debug")
40 | }
41 |
42 | func TestFilterKey(t *testing.T) {
43 | logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
44 | log := NewHelper(NewFilter(logger, FilterKey("password")))
45 | log.Debugw("password", "123456")
46 | }
47 |
48 | func TestFilterValue(t *testing.T) {
49 | logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
50 | log := NewHelper(NewFilter(logger, FilterValue("debug")))
51 | log.Debugf("test %s", "debug")
52 | }
53 |
54 | func TestFilterFunc(t *testing.T) {
55 | logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
56 | log := NewHelper(NewFilter(logger, FilterFunc(testFilterFunc)))
57 | log.Debug("debug level")
58 | log.Infow("password", "123456")
59 | }
60 |
61 | func BenchmarkFilterKey(b *testing.B) {
62 | log := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterKey("password")))
63 | for i := 0; i < b.N; i++ {
64 | log.Infow("password", "123456")
65 | }
66 | }
67 |
68 | func BenchmarkFilterValue(b *testing.B) {
69 | log := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterValue("password")))
70 | for i := 0; i < b.N; i++ {
71 | log.Infow("password")
72 | }
73 | }
74 |
75 | func BenchmarkFilterFunc(b *testing.B) {
76 | log := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterFunc(testFilterFunc)))
77 | for i := 0; i < b.N; i++ {
78 | log.Info("password", "123456")
79 | }
80 | }
81 |
82 | func testFilterFunc(level Level, keyvals ...interface{}) bool {
83 | if level == LevelWarn {
84 | return true
85 | }
86 | for i := 0; i < len(keyvals); i++ {
87 | if keyvals[i] == "password" {
88 | keyvals[i+1] = fuzzyStr
89 | }
90 | }
91 | return false
92 | }
93 |
94 | func TestFilterFuncWitchLoggerPrefix(t *testing.T) {
95 | buf := new(bytes.Buffer)
96 | tests := []struct {
97 | logger Logger
98 | want string
99 | }{
100 | {
101 | logger: NewFilter(With(NewStdLogger(buf), "caller", "caller", "prefix", "whaterver"), FilterFunc(testFilterFuncWithLoggerPrefix)),
102 | want: "",
103 | },
104 | {
105 | logger: NewFilter(With(NewStdLogger(buf), "caller", "caller"), FilterFunc(testFilterFuncWithLoggerPrefix)),
106 | want: "INFO caller=caller msg=msg\n",
107 | },
108 | }
109 |
110 | for _, tt := range tests {
111 | err := tt.logger.Log(LevelInfo, "msg", "msg")
112 | if err != nil {
113 | t.Fatal("err should be nil")
114 | }
115 | got := buf.String()
116 | if got != tt.want {
117 | t.Fatalf("filter should catch prefix, want %s, got %s.", tt.want, got)
118 | }
119 | buf.Reset()
120 | }
121 | }
122 |
123 | func testFilterFuncWithLoggerPrefix(level Level, keyvals ...interface{}) bool {
124 | if level == LevelWarn {
125 | return true
126 | }
127 | for i := 0; i < len(keyvals); i += 2 {
128 | if keyvals[i] == "prefix" {
129 | return true
130 | }
131 | }
132 | return false
133 | }
134 |
--------------------------------------------------------------------------------
/log/global.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | // globalLogger is designed as a global logger in current process.
8 | var global = &loggerAppliance{}
9 |
10 | // loggerAppliance is the proxy of `Logger` to
11 | // make logger change will affect all sub-logger.
12 | type loggerAppliance struct {
13 | lock sync.Mutex
14 | Logger
15 | helper *Helper
16 | }
17 |
18 | func init() {
19 | global.SetLogger(DefaultLogger)
20 | }
21 |
22 | func (a *loggerAppliance) SetLogger(in Logger) {
23 | a.lock.Lock()
24 | defer a.lock.Unlock()
25 | a.Logger = in
26 | a.helper = NewHelper(a.Logger)
27 | }
28 |
29 | func (a *loggerAppliance) GetLogger() Logger {
30 | return a.Logger
31 | }
32 |
33 | // SetLogger should be called before any other log call.
34 | // And it is NOT THREAD SAFE.
35 | func SetLogger(logger Logger) {
36 | global.SetLogger(logger)
37 | }
38 |
39 | // GetLogger returns global logger appliance as logger in current process.
40 | func GetLogger() Logger {
41 | return global
42 | }
43 |
44 | // Log Print log by level and keyvals.
45 | func Log(level Level, keyvals ...interface{}) {
46 | global.helper.Log(level, keyvals...)
47 | }
48 |
49 | // Debug logs a message at debug level.
50 | func Debug(a ...interface{}) {
51 | global.helper.Debug(a...)
52 | }
53 |
54 | // Debugf logs a message at debug level.
55 | func Debugf(format string, a ...interface{}) {
56 | global.helper.Debugf(format, a...)
57 | }
58 |
59 | // Debugw logs a message at debug level.
60 | func Debugw(keyvals ...interface{}) {
61 | global.helper.Debugw(keyvals...)
62 | }
63 |
64 | // Info logs a message at info level.
65 | func Info(a ...interface{}) {
66 | global.helper.Info(a...)
67 | }
68 |
69 | // Infof logs a message at info level.
70 | func Infof(format string, a ...interface{}) {
71 | global.helper.Infof(format, a...)
72 | }
73 |
74 | // Infow logs a message at info level.
75 | func Infow(keyvals ...interface{}) {
76 | global.helper.Infow(keyvals...)
77 | }
78 |
79 | // Warn logs a message at warn level.
80 | func Warn(a ...interface{}) {
81 | global.helper.Warn(a...)
82 | }
83 |
84 | // Warnf logs a message at warnf level.
85 | func Warnf(format string, a ...interface{}) {
86 | global.helper.Warnf(format, a...)
87 | }
88 |
89 | // Warnw logs a message at warnf level.
90 | func Warnw(keyvals ...interface{}) {
91 | global.helper.Warnw(keyvals...)
92 | }
93 |
94 | // Error logs a message at error level.
95 | func Error(a ...interface{}) {
96 | global.helper.Error(a...)
97 | }
98 |
99 | // Errorf logs a message at error level.
100 | func Errorf(format string, a ...interface{}) {
101 | global.helper.Errorf(format, a...)
102 | }
103 |
104 | // Errorw logs a message at error level.
105 | func Errorw(keyvals ...interface{}) {
106 | global.helper.Errorw(keyvals...)
107 | }
108 |
109 | // Fatal logs a message at fatal level.
110 | func Fatal(a ...interface{}) {
111 | global.helper.Fatal(a...)
112 | }
113 |
114 | // Fatalf logs a message at fatal level.
115 | func Fatalf(format string, a ...interface{}) {
116 | global.helper.Fatalf(format, a...)
117 | }
118 |
119 | // Fatalw logs a message at fatal level.
120 | func Fatalw(keyvals ...interface{}) {
121 | global.helper.Fatalw(keyvals...)
122 | }
123 |
--------------------------------------------------------------------------------
/log/global_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "os"
7 | "strings"
8 | "testing"
9 | )
10 |
11 | func TestGlobalLog(t *testing.T) {
12 | buffer := &bytes.Buffer{}
13 | SetLogger(NewStdLogger(buffer))
14 |
15 | testCases := []struct {
16 | level Level
17 | content []interface{}
18 | }{
19 | {
20 | LevelDebug,
21 | []interface{}{"test debug"},
22 | },
23 | {
24 | LevelInfo,
25 | []interface{}{"test info"},
26 | },
27 | {
28 | LevelInfo,
29 | []interface{}{"test %s", "info"},
30 | },
31 | {
32 | LevelWarn,
33 | []interface{}{"test warn"},
34 | },
35 | {
36 | LevelError,
37 | []interface{}{"test error"},
38 | },
39 | {
40 | LevelError,
41 | []interface{}{"test %s", "error"},
42 | },
43 | }
44 |
45 | expected := []string{}
46 | for _, tc := range testCases {
47 | msg := fmt.Sprintf(tc.content[0].(string), tc.content[1:]...)
48 | switch tc.level {
49 | case LevelDebug:
50 | Debugf(tc.content[0].(string), tc.content[1:]...)
51 | expected = append(expected, fmt.Sprintf("%s msg=%s", "DEBUG", msg))
52 | case LevelInfo:
53 | Infof(tc.content[0].(string), tc.content[1:]...)
54 | expected = append(expected, fmt.Sprintf("%s msg=%s", "INFO", msg))
55 | case LevelWarn:
56 | Warnf(tc.content[0].(string), tc.content[1:]...)
57 | expected = append(expected, fmt.Sprintf("%s msg=%s", "WARN", msg))
58 | case LevelError:
59 | Errorf(tc.content[0].(string), tc.content[1:]...)
60 | expected = append(expected, fmt.Sprintf("%s msg=%s", "ERROR", msg))
61 | }
62 | }
63 | expected = append(expected, "")
64 |
65 | t.Logf("Content: %s", buffer.String())
66 | if buffer.String() != strings.Join(expected, "\n") {
67 | t.Errorf("Expected: %s, got: %s", strings.Join(expected, "\n"), buffer.String())
68 | }
69 | }
70 |
71 | func TestGlobalLogUpdate(t *testing.T) {
72 | l := &loggerAppliance{}
73 | l.SetLogger(NewStdLogger(os.Stdout))
74 | LOG := NewHelper(l)
75 | LOG.Info("Log to stdout")
76 |
77 | buffer := &bytes.Buffer{}
78 | l.SetLogger(NewStdLogger(buffer))
79 | LOG.Info("Log to buffer")
80 |
81 | expected := "INFO msg=Log to buffer\n"
82 | if buffer.String() != expected {
83 | t.Errorf("Expected: %s, got: %s", expected, buffer.String())
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/log/helper.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "os"
7 | )
8 |
9 | // DefaultMessageKey default message key.
10 | var DefaultMessageKey = "msg"
11 |
12 | // Option is Helper option.
13 | type Option func(*Helper)
14 |
15 | // Helper is a logger helper.
16 | type Helper struct {
17 | logger Logger
18 | msgKey string
19 | }
20 |
21 | // WithMessageKey with message key.
22 | func WithMessageKey(k string) Option {
23 | return func(opts *Helper) {
24 | opts.msgKey = k
25 | }
26 | }
27 |
28 | // NewHelper new a logger helper.
29 | func NewHelper(logger Logger, opts ...Option) *Helper {
30 | options := &Helper{
31 | msgKey: DefaultMessageKey, // default message key
32 | logger: logger,
33 | }
34 | for _, o := range opts {
35 | o(options)
36 | }
37 | return options
38 | }
39 |
40 | // WithContext returns a shallow copy of h with its context changed
41 | // to ctx. The provided ctx must be non-nil.
42 | func (h *Helper) WithContext(ctx context.Context) *Helper {
43 | return &Helper{
44 | msgKey: h.msgKey,
45 | logger: WithContext(ctx, h.logger),
46 | }
47 | }
48 |
49 | // Log Print log by level and keyvals.
50 | func (h *Helper) Log(level Level, keyvals ...interface{}) {
51 | _ = h.logger.Log(level, keyvals...)
52 | }
53 |
54 | // Debug logs a message at debug level.
55 | func (h *Helper) Debug(a ...interface{}) {
56 | _ = h.logger.Log(LevelDebug, h.msgKey, fmt.Sprint(a...))
57 | }
58 |
59 | // Debugf logs a message at debug level.
60 | func (h *Helper) Debugf(format string, a ...interface{}) {
61 | _ = h.logger.Log(LevelDebug, h.msgKey, fmt.Sprintf(format, a...))
62 | }
63 |
64 | // Debugw logs a message at debug level.
65 | func (h *Helper) Debugw(keyvals ...interface{}) {
66 | _ = h.logger.Log(LevelDebug, keyvals...)
67 | }
68 |
69 | // Info logs a message at info level.
70 | func (h *Helper) Info(a ...interface{}) {
71 | _ = h.logger.Log(LevelInfo, h.msgKey, fmt.Sprint(a...))
72 | }
73 |
74 | // Infof logs a message at info level.
75 | func (h *Helper) Infof(format string, a ...interface{}) {
76 | _ = h.logger.Log(LevelInfo, h.msgKey, fmt.Sprintf(format, a...))
77 | }
78 |
79 | // Infow logs a message at info level.
80 | func (h *Helper) Infow(keyvals ...interface{}) {
81 | _ = h.logger.Log(LevelInfo, keyvals...)
82 | }
83 |
84 | // Warn logs a message at warn level.
85 | func (h *Helper) Warn(a ...interface{}) {
86 | _ = h.logger.Log(LevelWarn, h.msgKey, fmt.Sprint(a...))
87 | }
88 |
89 | // Warnf logs a message at warnf level.
90 | func (h *Helper) Warnf(format string, a ...interface{}) {
91 | _ = h.logger.Log(LevelWarn, h.msgKey, fmt.Sprintf(format, a...))
92 | }
93 |
94 | // Warnw logs a message at warnf level.
95 | func (h *Helper) Warnw(keyvals ...interface{}) {
96 | _ = h.logger.Log(LevelWarn, keyvals...)
97 | }
98 |
99 | // Error logs a message at error level.
100 | func (h *Helper) Error(a ...interface{}) {
101 | _ = h.logger.Log(LevelError, h.msgKey, fmt.Sprint(a...))
102 | }
103 |
104 | // Errorf logs a message at error level.
105 | func (h *Helper) Errorf(format string, a ...interface{}) {
106 | _ = h.logger.Log(LevelError, h.msgKey, fmt.Sprintf(format, a...))
107 | }
108 |
109 | // Errorw logs a message at error level.
110 | func (h *Helper) Errorw(keyvals ...interface{}) {
111 | _ = h.logger.Log(LevelError, keyvals...)
112 | }
113 |
114 | // Fatal logs a message at fatal level.
115 | func (h *Helper) Fatal(a ...interface{}) {
116 | _ = h.logger.Log(LevelFatal, h.msgKey, fmt.Sprint(a...))
117 | os.Exit(1)
118 | }
119 |
120 | // Fatalf logs a message at fatal level.
121 | func (h *Helper) Fatalf(format string, a ...interface{}) {
122 | _ = h.logger.Log(LevelFatal, h.msgKey, fmt.Sprintf(format, a...))
123 | os.Exit(1)
124 | }
125 |
126 | // Fatalw logs a message at fatal level.
127 | func (h *Helper) Fatalw(keyvals ...interface{}) {
128 | _ = h.logger.Log(LevelFatal, keyvals...)
129 | os.Exit(1)
130 | }
131 |
--------------------------------------------------------------------------------
/log/helper_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "context"
5 | "io"
6 | "os"
7 | "testing"
8 | )
9 |
10 | func TestHelper(t *testing.T) {
11 | logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
12 | log := NewHelper(logger)
13 |
14 | log.Log(LevelDebug, "msg", "test debug")
15 | log.Debug("test debug")
16 | log.Debugf("test %s", "debug")
17 | log.Debugw("log", "test debug")
18 |
19 | log.Warn("test warn")
20 | log.Warnf("test %s", "warn")
21 | log.Warnw("log", "test warn")
22 | }
23 |
24 | func TestHelperWithMsgKey(t *testing.T) {
25 | logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
26 | log := NewHelper(logger, WithMessageKey("message"))
27 | log.Debugf("test %s", "debug")
28 | log.Debugw("log", "test debug")
29 | }
30 |
31 | func TestHelperLevel(t *testing.T) {
32 | log := NewHelper(DefaultLogger)
33 | log.Debug("test debug")
34 | log.Info("test info")
35 | log.Infof("test %s", "info")
36 | log.Warn("test warn")
37 | log.Error("test error")
38 | log.Errorf("test %s", "error")
39 | log.Errorw("log", "test error")
40 | }
41 |
42 | func BenchmarkHelperPrint(b *testing.B) {
43 | log := NewHelper(NewStdLogger(io.Discard))
44 | for i := 0; i < b.N; i++ {
45 | log.Debug("test")
46 | }
47 | }
48 |
49 | func BenchmarkHelperPrintf(b *testing.B) {
50 | log := NewHelper(NewStdLogger(io.Discard))
51 | for i := 0; i < b.N; i++ {
52 | log.Debugf("%s", "test")
53 | }
54 | }
55 |
56 | func BenchmarkHelperPrintw(b *testing.B) {
57 | log := NewHelper(NewStdLogger(io.Discard))
58 | for i := 0; i < b.N; i++ {
59 | log.Debugw("key", "value")
60 | }
61 | }
62 |
63 | type traceKey struct{}
64 |
65 | func TestContext(t *testing.T) {
66 | logger := With(NewStdLogger(os.Stdout),
67 | "trace", Trace(),
68 | )
69 | log := NewHelper(logger)
70 | ctx := context.WithValue(context.Background(), traceKey{}, "2233")
71 | log.WithContext(ctx).Info("got trace!")
72 | }
73 |
74 | func Trace() Valuer {
75 | return func(ctx context.Context) interface{} {
76 | s := ctx.Value(traceKey{}).(string)
77 | return s
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/log/level.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import "strings"
4 |
5 | // Level is a logger level.
6 | type Level int8
7 |
8 | // LevelKey is logger level key.
9 | const LevelKey = "level"
10 |
11 | const (
12 | // LevelDebug is logger debug level.
13 | LevelDebug Level = iota - 1
14 | // LevelInfo is logger info level.
15 | LevelInfo
16 | // LevelWarn is logger warn level.
17 | LevelWarn
18 | // LevelError is logger error level.
19 | LevelError
20 | // LevelFatal is logger fatal level
21 | LevelFatal
22 | )
23 |
24 | func (l Level) String() string {
25 | switch l {
26 | case LevelDebug:
27 | return "DEBUG"
28 | case LevelInfo:
29 | return "INFO"
30 | case LevelWarn:
31 | return "WARN"
32 | case LevelError:
33 | return "ERROR"
34 | case LevelFatal:
35 | return "FATAL"
36 | default:
37 | return ""
38 | }
39 | }
40 |
41 | // ParseLevel parses a level string into a logger Level value.
42 | func ParseLevel(s string) Level {
43 | switch strings.ToUpper(s) {
44 | case "DEBUG":
45 | return LevelDebug
46 | case "INFO":
47 | return LevelInfo
48 | case "WARN":
49 | return LevelWarn
50 | case "ERROR":
51 | return LevelError
52 | case "FATAL":
53 | return LevelFatal
54 | }
55 | return LevelInfo
56 | }
57 |
--------------------------------------------------------------------------------
/log/level_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import "testing"
4 |
5 | func TestLevel_String(t *testing.T) {
6 | tests := []struct {
7 | name string
8 | l Level
9 | want string
10 | }{
11 | {
12 | name: "DEBUG",
13 | l: LevelDebug,
14 | want: "DEBUG",
15 | },
16 | {
17 | name: "INFO",
18 | l: LevelInfo,
19 | want: "INFO",
20 | },
21 | {
22 | name: "WARN",
23 | l: LevelWarn,
24 | want: "WARN",
25 | },
26 | {
27 | name: "ERROR",
28 | l: LevelError,
29 | want: "ERROR",
30 | },
31 | {
32 | name: "FATAL",
33 | l: LevelFatal,
34 | want: "FATAL",
35 | },
36 | {
37 | name: "other",
38 | l: 10,
39 | want: "",
40 | },
41 | }
42 | for _, tt := range tests {
43 | t.Run(tt.name, func(t *testing.T) {
44 | if got := tt.l.String(); got != tt.want {
45 | t.Errorf("String() = %v, want %v", got, tt.want)
46 | }
47 | })
48 | }
49 | }
50 |
51 | func TestParseLevel(t *testing.T) {
52 | tests := []struct {
53 | name string
54 | s string
55 | want Level
56 | }{
57 | {
58 | name: "DEBUG",
59 | want: LevelDebug,
60 | s: "DEBUG",
61 | },
62 | {
63 | name: "INFO",
64 | want: LevelInfo,
65 | s: "INFO",
66 | },
67 | {
68 | name: "WARN",
69 | want: LevelWarn,
70 | s: "WARN",
71 | },
72 | {
73 | name: "ERROR",
74 | want: LevelError,
75 | s: "ERROR",
76 | },
77 | {
78 | name: "FATAL",
79 | want: LevelFatal,
80 | s: "FATAL",
81 | },
82 | {
83 | name: "other",
84 | want: LevelInfo,
85 | s: "other",
86 | },
87 | }
88 | for _, tt := range tests {
89 | t.Run(tt.name, func(t *testing.T) {
90 | if got := ParseLevel(tt.s); got != tt.want {
91 | t.Errorf("ParseLevel() = %v, want %v", got, tt.want)
92 | }
93 | })
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/log/log.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "context"
5 | "log"
6 | )
7 |
8 | // DefaultLogger is default logger.
9 | var DefaultLogger = NewStdLogger(log.Writer())
10 |
11 | // Logger is a logger interface.
12 | type Logger interface {
13 | Log(level Level, keyvals ...interface{}) error
14 | }
15 |
16 | type logger struct {
17 | logs []Logger
18 | prefix []interface{}
19 | hasValuer bool
20 | ctx context.Context
21 | }
22 |
23 | func (c *logger) Log(level Level, keyvals ...interface{}) error {
24 | kvs := make([]interface{}, 0, len(c.prefix)+len(keyvals))
25 | kvs = append(kvs, c.prefix...)
26 | if c.hasValuer {
27 | bindValues(c.ctx, kvs)
28 | }
29 | kvs = append(kvs, keyvals...)
30 | for _, l := range c.logs {
31 | if err := l.Log(level, kvs...); err != nil {
32 | return err
33 | }
34 | }
35 | return nil
36 | }
37 |
38 | // With with logger fields.
39 | func With(l Logger, kv ...interface{}) Logger {
40 | if c, ok := l.(*logger); ok {
41 | kvs := make([]interface{}, 0, len(c.prefix)+len(kv))
42 | kvs = append(kvs, kv...)
43 | kvs = append(kvs, c.prefix...)
44 | return &logger{
45 | logs: c.logs,
46 | prefix: kvs,
47 | hasValuer: containsValuer(kvs),
48 | ctx: c.ctx,
49 | }
50 | }
51 | return &logger{logs: []Logger{l}, prefix: kv, hasValuer: containsValuer(kv)}
52 | }
53 |
54 | // WithContext returns a shallow copy of l with its context changed
55 | // to ctx. The provided ctx must be non-nil.
56 | func WithContext(ctx context.Context, l Logger) Logger {
57 | if c, ok := l.(*logger); ok {
58 | return &logger{
59 | logs: c.logs,
60 | prefix: c.prefix,
61 | hasValuer: c.hasValuer,
62 | ctx: ctx,
63 | }
64 | }
65 | return &logger{logs: []Logger{l}, ctx: ctx}
66 | }
67 |
68 | // MultiLogger wraps multi logger.
69 | func MultiLogger(logs ...Logger) Logger {
70 | return &logger{logs: logs}
71 | }
72 |
--------------------------------------------------------------------------------
/log/log_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "context"
5 | "os"
6 | "testing"
7 | )
8 |
9 | func TestInfo(t *testing.T) {
10 | logger := DefaultLogger
11 | logger = With(logger, "ts", DefaultTimestamp, "caller", DefaultCaller)
12 | _ = logger.Log(LevelInfo, "key1", "value1")
13 | }
14 |
15 | func TestWrapper(t *testing.T) {
16 | out := NewStdLogger(os.Stdout)
17 | err := NewStdLogger(os.Stderr)
18 |
19 | l := With(MultiLogger(out, err), "ts", DefaultTimestamp, "caller", DefaultCaller)
20 | _ = l.Log(LevelInfo, "msg", "test")
21 | }
22 |
23 | func TestWithContext(t *testing.T) {
24 | WithContext(context.Background(), nil)
25 | }
26 |
--------------------------------------------------------------------------------
/log/std.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io"
7 | "log"
8 | "sync"
9 | )
10 |
11 | var _ Logger = (*stdLogger)(nil)
12 |
13 | type stdLogger struct {
14 | log *log.Logger
15 | pool *sync.Pool
16 | }
17 |
18 | // NewStdLogger new a logger with writer.
19 | func NewStdLogger(w io.Writer) Logger {
20 | return &stdLogger{
21 | log: log.New(w, "", 0),
22 | pool: &sync.Pool{
23 | New: func() interface{} {
24 | return new(bytes.Buffer)
25 | },
26 | },
27 | }
28 | }
29 |
30 | // Log print the kv pairs log.
31 | func (l *stdLogger) Log(level Level, keyvals ...interface{}) error {
32 | if len(keyvals) == 0 {
33 | return nil
34 | }
35 | if (len(keyvals) & 1) == 1 {
36 | keyvals = append(keyvals, "KEYVALS UNPAIRED")
37 | }
38 | buf := l.pool.Get().(*bytes.Buffer)
39 | buf.WriteString(level.String())
40 | for i := 0; i < len(keyvals); i += 2 {
41 | _, _ = fmt.Fprintf(buf, " %s=%v", keyvals[i], keyvals[i+1])
42 | }
43 | _ = l.log.Output(4, buf.String()) //nolint:gomnd
44 | buf.Reset()
45 | l.pool.Put(buf)
46 | return nil
47 | }
48 |
--------------------------------------------------------------------------------
/log/std_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import "testing"
4 |
5 | func TestStdLogger(t *testing.T) {
6 | logger := DefaultLogger
7 | logger = With(logger, "caller", DefaultCaller, "ts", DefaultTimestamp)
8 |
9 | _ = logger.Log(LevelInfo, "msg", "test debug")
10 | _ = logger.Log(LevelInfo, "msg", "test info")
11 | _ = logger.Log(LevelInfo, "msg", "test warn")
12 | _ = logger.Log(LevelInfo, "msg", "test error")
13 | _ = logger.Log(LevelDebug, "singular")
14 |
15 | logger2 := DefaultLogger
16 | _ = logger2.Log(LevelDebug)
17 | }
18 |
--------------------------------------------------------------------------------
/log/value.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "context"
5 | "runtime"
6 | "strconv"
7 | "strings"
8 | "time"
9 | )
10 |
11 | var (
12 | defaultDepth = 3
13 | // DefaultCaller is a Valuer that returns the file and line.
14 | DefaultCaller = Caller(defaultDepth)
15 |
16 | // DefaultTimestamp is a Valuer that returns the current wallclock time.
17 | DefaultTimestamp = Timestamp(time.RFC3339)
18 | )
19 |
20 | // Valuer is returns a log value.
21 | type Valuer func(ctx context.Context) interface{}
22 |
23 | // Value return the function value.
24 | func Value(ctx context.Context, v interface{}) interface{} {
25 | if v, ok := v.(Valuer); ok {
26 | return v(ctx)
27 | }
28 | return v
29 | }
30 |
31 | // Caller returns a Valuer that returns a pkg/file:line description of the caller.
32 | func Caller(depth int) Valuer {
33 | return func(context.Context) interface{} {
34 | d := depth
35 | _, file, line, _ := runtime.Caller(d)
36 | if strings.LastIndex(file, "/log/filter.go") > 0 {
37 | d++
38 | _, file, line, _ = runtime.Caller(d)
39 | }
40 | if strings.LastIndex(file, "/log/helper.go") > 0 {
41 | d++
42 | _, file, line, _ = runtime.Caller(d)
43 | }
44 | idx := strings.LastIndexByte(file, '/')
45 | return file[idx+1:] + ":" + strconv.Itoa(line)
46 | }
47 | }
48 |
49 | // Timestamp returns a timestamp Valuer with a custom time format.
50 | func Timestamp(layout string) Valuer {
51 | return func(context.Context) interface{} {
52 | return time.Now().Format(layout)
53 | }
54 | }
55 |
56 | func bindValues(ctx context.Context, keyvals []interface{}) {
57 | for i := 1; i < len(keyvals); i += 2 {
58 | if v, ok := keyvals[i].(Valuer); ok {
59 | keyvals[i] = v(ctx)
60 | }
61 | }
62 | }
63 |
64 | func containsValuer(keyvals []interface{}) bool {
65 | for i := 1; i < len(keyvals); i += 2 {
66 | if _, ok := keyvals[i].(Valuer); ok {
67 | return true
68 | }
69 | }
70 | return false
71 | }
72 |
--------------------------------------------------------------------------------
/log/value_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "context"
5 | "testing"
6 | )
7 |
8 | func TestValue(t *testing.T) {
9 | logger := DefaultLogger
10 | logger = With(logger, "ts", DefaultTimestamp, "caller", DefaultCaller)
11 | _ = logger.Log(LevelInfo, "msg", "helloworld")
12 |
13 | logger = DefaultLogger
14 | logger = With(logger)
15 | _ = logger.Log(LevelDebug, "msg", "helloworld")
16 |
17 | var v1 interface{}
18 | got := Value(context.Background(), v1)
19 | if got != v1 {
20 | t.Errorf("Value() = %v, want %v", got, v1)
21 | }
22 | var v2 Valuer = func(ctx context.Context) interface{} {
23 | return 3
24 | }
25 | got = Value(context.Background(), v2)
26 | res := got.(int)
27 | if res != 3 {
28 | t.Errorf("Value() = %v, want %v", res, 3)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/parser.go:
--------------------------------------------------------------------------------
1 | package elf
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "errors"
7 | "fmt"
8 | "io"
9 | "os"
10 |
11 | "github.com/saferwall/elf/log"
12 |
13 | "github.com/saferwall/binstream"
14 | )
15 |
16 | // Options that influence the PE parsing behaviour.
17 | type Options struct {
18 |
19 | // A custom logger.
20 | Logger log.Logger
21 | }
22 |
23 | // Parser implements a parsing engine for the ELF file format.
24 | type Parser struct {
25 | fs binstream.Stream
26 | F *File
27 | }
28 |
29 | // New creates a new instance of parser.
30 | func New(filename string, opts *Options) (*Parser, error) {
31 | fs, err := binstream.NewFileStream(filename)
32 | if err != nil {
33 | return nil, err
34 | }
35 | file := File{}
36 | if opts != nil {
37 | file.opts = opts
38 | } else {
39 | file.opts = &Options{}
40 | }
41 |
42 | var logger log.Logger
43 | if file.opts.Logger == nil {
44 | logger = log.NewStdLogger(os.Stdout)
45 | file.logger = log.NewHelper(log.NewFilter(logger,
46 | log.FilterLevel(log.LevelError)))
47 | } else {
48 | file.logger = log.NewHelper(file.opts.Logger)
49 | }
50 |
51 | p := &Parser{
52 | fs: fs,
53 | F: &file,
54 | }
55 | return p, nil
56 | }
57 |
58 | // NewBytes creates a new instance of parser from a byte slice representig the ELF binary.
59 | func NewBytes(data []byte, opts *Options) (*Parser, error) {
60 | fs, err := binstream.NewByteStream(data)
61 | if err != nil {
62 | return nil, err
63 | }
64 |
65 | file := File{}
66 | if opts != nil {
67 | file.opts = opts
68 | } else {
69 | file.opts = &Options{}
70 | }
71 |
72 | var logger log.Logger
73 | if file.opts.Logger == nil {
74 | logger = log.NewStdLogger(os.Stdout)
75 | file.logger = log.NewHelper(log.NewFilter(logger,
76 | log.FilterLevel(log.LevelError)))
77 | } else {
78 | file.logger = log.NewHelper(file.opts.Logger)
79 | }
80 |
81 | p := &Parser{
82 | fs: fs,
83 | F: &file,
84 | }
85 | return p, nil
86 | }
87 |
88 | // Parse will parse the entire ELF file.
89 | func (p *Parser) Parse() error {
90 | err := p.ParseIdent()
91 | if err != nil {
92 | return err
93 | }
94 | elfClass := p.F.Ident.Class
95 | err = p.ParseELFHeader(elfClass)
96 | if err != nil {
97 | return err
98 | }
99 | err = p.ParseELFSectionHeaders(elfClass)
100 | if err != nil {
101 | return err
102 | }
103 | err = p.ParseELFSections(elfClass)
104 | if err != nil {
105 | return err
106 | }
107 | err = p.ParseELFProgramHeaders(elfClass)
108 | if err != nil {
109 | return err
110 | }
111 | err = p.ParseELFSymbols(elfClass, SHT_DYNSYM)
112 | if err != nil {
113 | return err
114 | }
115 | return nil
116 | }
117 |
118 | // ParseIdent will parse the identification bytes at the start of the ELF File.
119 | func (p *Parser) ParseIdent() error {
120 |
121 | ident := make([]byte, EI_NIDENT)
122 | // Read the ELF Header E_Ident array.
123 | // This step helps find out the architecture
124 | // that the binary targets, as well as OS ABI version
125 | // and other compilation artefact.
126 | n, err := p.fs.ReadAt(ident, 0)
127 | if n != EI_NIDENT || err != nil {
128 | return err
129 | }
130 |
131 | if n != 16 || string(ident[:4]) != ELFMAG {
132 | return errors.New("bad magic number " + string(ident[:4]) + " expected : " + ELFMAG)
133 | }
134 |
135 | copy(p.F.Ident.Magic[:], ident[:4])
136 |
137 | if !IsValidELFClass(Class(ident[EI_CLASS])) {
138 | return errors.New("invalid ELF class")
139 | }
140 | if !IsValidByteOrder(Data(ident[EI_DATA])) {
141 | return errors.New("invalid ELF byte order")
142 | }
143 | if !IsValidVersion(Version(ident[EI_VERSION])) {
144 | return errors.New("bad ELF version")
145 | }
146 |
147 | p.F.Ident.Class = Class(ident[EI_CLASS])
148 | p.F.Ident.Data = Data(ident[EI_DATA])
149 | p.F.Ident.ByteOrder = ByteOrder(Data(ident[EI_DATA]))
150 | p.F.Ident.Version = Version(ident[EI_VERSION])
151 | p.F.Ident.OSABI = OSABI(ident[EI_OSABI])
152 | p.F.Ident.ABIVersion = ABIVersion(ident[EI_ABIVERSION])
153 |
154 | return nil
155 | }
156 |
157 | // CloseFile will close underlying mmap file
158 | func (p *Parser) CloseFile() error {
159 | return p.fs.Close()
160 | }
161 |
162 | // ParseELFHeader reads the raw elf header depending on the ELF Class (32 or 64).
163 | func (p *Parser) ParseELFHeader(c Class) error {
164 |
165 | // Because of parsing ambiguitiy we need parentheses here
166 | // ref : https://golang.org/ref/spec#Composite_literals
167 | // The two structs are comparable because all the fields are
168 | // comparable values https://golang.org/ref/spec#Comparison_operators
169 | if (FileIdent{} == p.F.Ident) {
170 | err := p.ParseIdent()
171 | if err != nil {
172 | return err
173 | }
174 | }
175 | switch c {
176 | case ELFCLASS32:
177 | return p.parseELFHeader32()
178 | case ELFCLASS64:
179 | return p.parseELFHeader64()
180 | default:
181 | return errors.New("unknown ELF Class")
182 | }
183 | }
184 |
185 | // parseELFHeader32 parses specifically 32-bit built ELF binaries.
186 | func (p *Parser) parseELFHeader32() error {
187 | hdr := NewELF32Header()
188 | n, err := p.fs.Seek(0, io.SeekStart)
189 | if err != nil {
190 | errString := fmt.Errorf(
191 | "failed to seek start of stream with error : %v , read %d expected %d",
192 | err, n, EI_NIDENT,
193 | )
194 | return errors.New(errString.Error())
195 | }
196 | if err := binary.Read(p.fs, p.F.Ident.ByteOrder, &hdr); err != nil {
197 | return err
198 | }
199 | p.F.Header32 = hdr
200 | return nil
201 | }
202 |
203 | // parseELFHeader64 parses specifically 64-bit built ELF binaries.
204 | func (p *Parser) parseELFHeader64() error {
205 | hdr := NewELF64Header()
206 | n, err := p.fs.Seek(0, io.SeekStart)
207 | if err != nil {
208 | errString := fmt.Errorf(
209 | "failed to seek start of stream with error : %v , read %d expected %d",
210 | err, n, EI_NIDENT,
211 | )
212 | return errors.New(errString.Error())
213 | }
214 | if err := binary.Read(p.fs, p.F.Ident.ByteOrder, &hdr); err != nil {
215 | return err
216 | }
217 | p.F.Header64 = hdr
218 | return nil
219 | }
220 |
221 | // ParseELFSectionHeaders reads the raw elf section header.
222 | func (p *Parser) ParseELFSectionHeaders(c Class) error {
223 |
224 | switch c {
225 | case ELFCLASS32:
226 | return p.parseELFSectionHeader32()
227 | case ELFCLASS64:
228 | return p.parseELFSectionHeader64()
229 | default:
230 | return errors.New("unknown ELF class")
231 | }
232 | }
233 |
234 | // parseELFSectionHeader32 parses specifically the raw elf section header of 32-bit binaries.
235 | func (p *Parser) parseELFSectionHeader32() error {
236 | if p.F.Header32 == NewELF32Header() {
237 | return errors.New("header need to be parsed first")
238 | }
239 | if p.F.Header32.Shnum == 0 || p.F.Header32.Shoff == 0 {
240 | return errors.New("ELF file doesn't contain any section header table")
241 | }
242 | shnum := p.F.Header32.SectionHeadersNum()
243 | shoff := p.F.Header32.SectionHeadersOffset()
244 | shentz := p.F.Header32.Shentsize
245 |
246 | names := make([]uint32, shnum)
247 | sectionHeaders := make([]ELF32SectionHeader, shnum)
248 | for i := 0; uint16(i) < shnum; i++ {
249 | // Section index 0, and indices in the range 0xFF00–0xFFFF are reserved for special purposes.
250 | offset := int64(shoff) + int64(i)*int64(shentz)
251 | _, err := p.fs.Seek(offset, io.SeekStart)
252 | if err != nil {
253 | return err
254 | }
255 | // section header file offset
256 | var sh ELF32SectionHeader
257 | if err := binary.Read(p.fs, p.F.Ident.ByteOrder, &sh); err != nil {
258 | return err
259 | }
260 | names[i] = sh.Name
261 | sectionHeaders[i] = sh
262 | p.F.SectionHeaders32 = sectionHeaders
263 | }
264 | return nil
265 | }
266 |
267 | // parseELFSectionHeader64 parses specifically the raw elf section header of 64-bit binaries.
268 | func (p *Parser) parseELFSectionHeader64() error {
269 | if p.F.Header64 == NewELF64Header() {
270 | return errors.New("header need to be parsed first")
271 | }
272 | if p.F.Header64.Shnum == 0 || p.F.Header64.Shoff == 0 {
273 | return errors.New("ELF file doesn't contain any section header table")
274 | }
275 | shnum := p.F.Header64.SectionHeadersNum()
276 | shoff := p.F.Header64.SectionHeadersOffset()
277 | shentz := p.F.Header64.Shentsize
278 |
279 | names := make([]uint32, shnum)
280 | sectionHeaders := make([]ELF64SectionHeader, shnum)
281 | for i := 0; uint16(i) < shnum; i++ {
282 | // Section index 0, and indices in the range 0xFF00–0xFFFF are reserved for special purposes.
283 | offset := int64(shoff) + int64(i)*int64(shentz)
284 | _, err := p.fs.Seek(offset, io.SeekStart)
285 | if err != nil {
286 | return err
287 | }
288 | // section header file offset
289 | var sh ELF64SectionHeader
290 | if err := binary.Read(p.fs, p.F.Ident.ByteOrder, &sh); err != nil {
291 | return err
292 | }
293 | names[i] = sh.Name
294 | sectionHeaders[i] = sh
295 | p.F.SectionHeaders64 = sectionHeaders
296 | }
297 | return nil
298 | }
299 |
300 | // ParseELFSections reads the raw elf sections.
301 | func (p *Parser) ParseELFSections(c Class) error {
302 |
303 | switch c {
304 | case ELFCLASS32:
305 | return p.parseELFSections32()
306 | case ELFCLASS64:
307 | return p.parseELFSections64()
308 | default:
309 | return errors.New("unknown ELF class")
310 | }
311 | }
312 |
313 | // parseELFSections64 parses all sections in a 64-bit ELF binary.
314 | func (p *Parser) parseELFSections64() error {
315 | if len(p.F.SectionHeaders64) == 0 {
316 | err := p.parseELFSectionHeader64()
317 | if err != nil {
318 | return err
319 | }
320 | }
321 | shnum := p.F.Header64.Shnum
322 | sections := make([]*ELF64Section, shnum)
323 |
324 | for i := 0; i < int(shnum); i++ {
325 | s := &ELF64Section{}
326 | size := p.F.SectionHeaders64[i].Size
327 | s.ELF64SectionHeader = p.F.SectionHeaders64[i]
328 | s.sr = io.NewSectionReader(p.fs, int64(s.Off), int64(size))
329 |
330 | if s.Flags&uint64(SHF_COMPRESSED) == 0 {
331 | s.Size = p.F.SectionHeaders64[i].Size
332 | } else {
333 | ch := new(ELF64CompressionHeader)
334 | err := binary.Read(s.sr, p.F.Ident.ByteOrder, ch)
335 | if err != nil {
336 | return errors.New("error reading compressed header " + err.Error())
337 | }
338 | s.compressionType = CompressionType(ch.Type)
339 | s.Size = ch.Size
340 | s.AddrAlign = ch.AddrAlign
341 | s.compressionOffset = int64(binary.Size(ch))
342 | }
343 | sections[i] = s
344 | }
345 | if len(sections) == 0 {
346 | return errors.New("binary has no sections")
347 | }
348 | shstrtab, err := sections[p.F.Header64.Shstrndx].Data()
349 | if err != nil {
350 | return errors.New("error reading the section header strings table " + err.Error())
351 | }
352 |
353 | for i, s := range sections {
354 | var ok bool
355 | s.SectionName, ok = getString(shstrtab, int(p.F.SectionHeaders64[i].Name))
356 | if !ok {
357 | return errors.New("failed to parse string table")
358 | }
359 | }
360 | p.F.Sections64 = sections
361 | return nil
362 | }
363 |
364 | // parseELFSections32 parses all sections in a 32-bit ELF binary.
365 | func (p *Parser) parseELFSections32() error {
366 | if len(p.F.SectionHeaders32) == 0 {
367 | err := p.parseELFSectionHeader32()
368 | if err != nil {
369 | return err
370 | }
371 | }
372 | shnum := p.F.Header32.Shnum
373 | sections := make([]*ELF32Section, shnum)
374 |
375 | for i := 0; i < int(shnum); i++ {
376 | s := &ELF32Section{}
377 | size := p.F.SectionHeaders32[i].Size
378 | s.ELF32SectionHeader = p.F.SectionHeaders32[i]
379 | s.sr = io.NewSectionReader(p.fs, int64(s.Off), int64(size))
380 |
381 | if s.Flags&uint32(SHF_COMPRESSED) == 0 {
382 | s.Size = p.F.SectionHeaders32[i].Size
383 | } else {
384 | ch := new(ELF32CompressionHeader)
385 | err := binary.Read(s.sr, p.F.Ident.ByteOrder, ch)
386 | if err != nil {
387 | return errors.New("error reading compressed header " + err.Error())
388 | }
389 | s.compressionType = CompressionType(ch.Type)
390 | s.Size = ch.Size
391 | s.AddrAlign = ch.AddrAlign
392 | s.compressionOffset = int64(binary.Size(ch))
393 | }
394 | sections[i] = s
395 | }
396 | if len(sections) == 0 {
397 | return errors.New("binary has no sections")
398 | }
399 | shstrtab, err := sections[p.F.Header32.Shstrndx].Data()
400 | if err != nil {
401 | return errors.New("error reading the section header strings table " + err.Error())
402 | }
403 |
404 | for i, s := range sections {
405 | var ok bool
406 | s.SectionName, ok = getString(shstrtab, int(p.F.SectionHeaders32[i].Name))
407 | if !ok {
408 | return errors.New("failed to parse string table")
409 | }
410 | }
411 | p.F.Sections32 = sections
412 | return nil
413 | }
414 |
415 | // ParseELFProgramHeaders reads the raw elf program header.
416 | func (p *Parser) ParseELFProgramHeaders(c Class) error {
417 |
418 | switch c {
419 | case ELFCLASS32:
420 | return p.parseELFProgramHeaders32()
421 | case ELFCLASS64:
422 | return p.parseELFProgramHeaders64()
423 | default:
424 | return errors.New("unknown ELF class")
425 | }
426 | }
427 |
428 | // parseELFProgramHeaders64 parses all program header table entries in a 64-bit ELF binary.
429 | func (p *Parser) parseELFProgramHeaders64() error {
430 | phOff := p.F.Header64.Phoff
431 | phNum := p.F.Header64.Phnum
432 | phEntSize := p.F.Header64.Phentsize
433 | programHeaders := make([]ELF64ProgramHeader, phNum)
434 |
435 | for i := 0; i < int(phNum); i++ {
436 | off := int64(phOff) + int64(i)*int64(phEntSize)
437 | p.fs.Seek(off, io.SeekStart)
438 | var ph ELF64ProgramHeader
439 | err := binary.Read(p.fs, p.F.Ident.ByteOrder, &ph)
440 | if err != nil {
441 | return err
442 | }
443 | programHeaders[i] = ph
444 | }
445 | p.F.ProgramHeaders64 = programHeaders
446 | return nil
447 | }
448 |
449 | // parseELFProgramHeaders32 parses all program header table entries in a 32-bit ELF binary.
450 | func (p *Parser) parseELFProgramHeaders32() error {
451 | phOff := p.F.Header32.Phoff
452 | phNum := p.F.Header32.Phnum
453 | phEntSize := p.F.Header32.Phentsize
454 | programHeaders := make([]ELF32ProgramHeader, phNum)
455 |
456 | for i := 0; i < int(phNum); i++ {
457 | off := int64(phOff) + int64(i)*int64(phEntSize)
458 | p.fs.Seek(off, io.SeekStart)
459 | var ph ELF32ProgramHeader
460 | err := binary.Read(p.fs, p.F.Ident.ByteOrder, &ph)
461 | if err != nil {
462 | return err
463 | }
464 | programHeaders[i] = ph
465 | }
466 | p.F.ProgramHeaders32 = programHeaders
467 | return nil
468 | }
469 |
470 | // ParseELFSymbols returns a slice of Symbols from parsing the symbol table
471 | // with the given type, along with the associated string table
472 | // (the null symbol at index 0 is omitted).
473 | func (p *Parser) ParseELFSymbols(c Class, typ SectionType) error {
474 | switch c {
475 | case ELFCLASS64:
476 | _, _, err := p.getSymbols64(typ)
477 | return err
478 | case ELFCLASS32:
479 | _, _, err := p.getSymbols32(typ)
480 | return err
481 | }
482 | return errors.New("unknown ELF class")
483 | }
484 |
485 | func (p *Parser) getSymbols32(typ SectionType) ([]Symbol, []byte, error) {
486 | symtabSection := p.F.GetSectionByType(typ)
487 | if symtabSection == nil {
488 | return nil, nil, ErrNoSymbols
489 | }
490 | data, err := symtabSection.Data()
491 | if err != nil {
492 | return nil, nil, errors.New("cannot load symbol section")
493 | }
494 | symtab := bytes.NewReader(data)
495 | if symtab.Len()%Sym32Size != 0 {
496 | return nil, nil, errors.New("length of symbol section is not a multiple of SymSize")
497 | }
498 | strdata, err := p.F.stringTable(symtabSection.Link)
499 | if err != nil {
500 | return nil, nil, errors.New("cannot load string table section")
501 | }
502 | // The first entry is all zeros.
503 | var skip [Sym32Size]byte
504 | symtab.Read(skip[:])
505 | symbols := make([]ELF32SymbolTableEntry, symtab.Len()/Sym32Size)
506 | namedSymbols := make([]Symbol, symtab.Len()/Sym32Size)
507 | i := 0
508 | var sym ELF32SymbolTableEntry
509 | for symtab.Len() > 0 {
510 | binary.Read(symtab, p.F.ByteOrder(), &sym)
511 | symbols[i] = sym
512 | str, _ := getString(strdata, int(sym.Name))
513 | namedSymbols[i] = Symbol{
514 | Name: str,
515 | Info: sym.Info,
516 | Other: sym.Other,
517 | Index: SectionIndex(sym.Shndx),
518 | Value: uint64(sym.Value),
519 | Size: uint64(sym.Size),
520 | Version: "",
521 | Library: "",
522 | }
523 | i++
524 | }
525 | p.F.Symbols32 = symbols
526 | p.F.NamedSymbols = namedSymbols
527 | return namedSymbols, strdata, nil
528 | }
529 | func (p *Parser) getSymbols64(typ SectionType) ([]Symbol, []byte, error) {
530 | symtabSection := p.F.GetSectionByType(typ)
531 | if symtabSection == nil {
532 | return nil, nil, ErrNoSymbols
533 | }
534 | data, err := symtabSection.Data()
535 | if err != nil {
536 | return nil, nil, errors.New("cannot load symbol section")
537 | }
538 | symtab := bytes.NewReader(data)
539 | if symtab.Len()%Sym64Size != 0 {
540 | return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size")
541 | }
542 | strdata, err := p.F.stringTable(symtabSection.Link)
543 | if err != nil {
544 | return nil, nil, errors.New("cannot load string table section")
545 | }
546 | // The first entry is all zeros.
547 | var skip [Sym64Size]byte
548 | symtab.Read(skip[:])
549 | symbols := make([]ELF64SymbolTableEntry, symtab.Len()/Sym64Size)
550 | namedSymbols := make([]Symbol, symtab.Len()/Sym64Size)
551 | i := 0
552 | var sym ELF64SymbolTableEntry
553 | for symtab.Len() > 0 {
554 | binary.Read(symtab, p.F.ByteOrder(), &sym)
555 | str, _ := getString(strdata, int(sym.Name))
556 | symbols[i] = ELF64SymbolTableEntry{
557 | Name: sym.Name,
558 | Info: sym.Info,
559 | Other: sym.Other,
560 | Shndx: sym.Shndx,
561 | Value: sym.Value,
562 | Size: sym.Size,
563 | }
564 | namedSymbols[i] = Symbol{
565 | Name: str,
566 | Info: sym.Info,
567 | Other: sym.Other,
568 | Index: SectionIndex(sym.Shndx),
569 | Value: sym.Value,
570 | Size: sym.Size,
571 | Version: "",
572 | Library: "",
573 | }
574 | i++
575 | }
576 | err = p.ParseGNUVersionTable(strdata)
577 | if err == nil {
578 | for i := range namedSymbols {
579 | namedSymbols[i].Library, namedSymbols[i].Version = p.gnuVersion(i)
580 | }
581 | }
582 | p.F.Symbols64 = symbols
583 | p.F.NamedSymbols = namedSymbols
584 | return namedSymbols, strdata, nil
585 | }
586 |
--------------------------------------------------------------------------------
/parser_test.go:
--------------------------------------------------------------------------------
1 | package elf
2 |
3 | import (
4 | "encoding/binary"
5 | "path"
6 | "testing"
7 |
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | // Run Tests against readelf output on /bin/ls.
12 | func TestParser(t *testing.T) {
13 | t.Run("TestELFStructs", func(t *testing.T) {
14 | testCases := []struct {
15 | ELFHeader32 ELF32Header
16 | expectedELFHeader32 ELF32Header
17 | ELFHeader64 ELF64Header
18 | expectedELFHeader64 ELF64Header
19 | }{
20 | {
21 | ELFHeader32: NewELF32Header(),
22 | expectedELFHeader32: ELF32Header{Ident: [16]byte{}, Type: 0, Machine: 0, Version: 0, Entry: 0, Phoff: 0, Shoff: 0, Flags: 0, Ehsize: 0, Phentsize: 0, Phnum: 0, Shentsize: 0, Shnum: 0, Shstrndx: 0},
23 | ELFHeader64: NewELF64Header(),
24 | expectedELFHeader64: ELF64Header{
25 | Ident: [16]byte{},
26 | Type: 0,
27 | Machine: 0,
28 | Version: 0,
29 | Entry: 0,
30 | Phoff: 0,
31 | Shoff: 0,
32 | Flags: 0,
33 | Ehsize: 0,
34 | Phentsize: 0,
35 | Phnum: 0,
36 | Shentsize: 0,
37 | Shnum: 0,
38 | Shstrndx: 0,
39 | },
40 | },
41 | }
42 | for _, tt := range testCases {
43 | if !assert.EqualValues(t, tt.expectedELFHeader32, tt.ELFHeader32) {
44 | t.Fatal("failed to assert empty ELF structs")
45 | }
46 | if !assert.EqualValues(t, tt.expectedELFHeader64, tt.ELFHeader64) {
47 | t.Fatal("failed to assert empty ELF structs")
48 | }
49 | }
50 |
51 | })
52 | t.Run("TestParseHeader", func(t *testing.T) {
53 | testCases := []struct {
54 | path string
55 | expectedIdent FileIdent
56 | expectedHeader ELF64Header
57 | }{
58 | {
59 | path: path.Join("test/", "ls"),
60 | expectedIdent: FileIdent{
61 | Magic: [4]byte{0x7f, 'E', 'L', 'F'},
62 | Class: ELFCLASS64,
63 | Data: ELFDATA2LSB,
64 | Version: EV_CURRENT,
65 | OSABI: ELFOSABI_NONE,
66 | ABIVersion: ELFABIVersion_CURRENT,
67 | ByteOrder: binary.LittleEndian,
68 | },
69 | expectedHeader: ELF64Header{
70 | Ident: [16]byte{0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
71 | Type: uint16(ET_DYN),
72 | Machine: uint16(EM_X86_64),
73 | Version: uint32(EV_CURRENT),
74 | Entry: 0x67d0,
75 | Phoff: 64,
76 | Shoff: 140224,
77 | Flags: 0x0,
78 | Ehsize: 64,
79 | Phentsize: 56,
80 | Phnum: 13,
81 | Shentsize: 64,
82 | Shnum: 30,
83 | Shstrndx: 29,
84 | },
85 | },
86 | }
87 |
88 | for _, tt := range testCases {
89 | p, err := New(tt.path, nil)
90 | if err != nil {
91 | t.Fatal("failed to create new parser with error :", err)
92 | }
93 | err = p.ParseIdent()
94 | if err != nil {
95 | t.Fatal("failed to parse ident with error :", err)
96 | }
97 | assert.EqualValues(t, tt.expectedIdent, p.F.Ident, "expected ident equal")
98 | err = p.ParseELFHeader(ELFCLASS64)
99 | if err != nil {
100 | t.Fatal("failed to parse ELF header with error :", err)
101 | }
102 | assert.EqualValues(t, tt.expectedHeader, p.F.Header64, "expected header equal")
103 | }
104 | })
105 | t.Run("TestParseSectionHeader", func(t *testing.T) {
106 | testCases := []struct {
107 | path string
108 | expectedIdent FileIdent
109 | expectedHeader ELF64Header
110 | expectedSectionHeader64 []ELF64SectionHeader
111 | }{
112 | {
113 | path: path.Join("test/", "ls"),
114 | expectedIdent: FileIdent{
115 | Magic: [4]byte{0x7f, 'E', 'L', 'F'},
116 | Class: ELFCLASS64,
117 | Data: ELFDATA2LSB,
118 | Version: EV_CURRENT,
119 | OSABI: ELFOSABI_NONE,
120 | ABIVersion: ELFABIVersion_CURRENT,
121 | ByteOrder: binary.LittleEndian,
122 | },
123 | expectedHeader: ELF64Header{
124 | Ident: [16]byte{0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
125 | Type: uint16(ET_DYN),
126 | Machine: uint16(EM_X86_64),
127 | Version: uint32(EV_CURRENT),
128 | Entry: 0x67d0,
129 | Phoff: 64,
130 | Shoff: 140224,
131 | Flags: 0x0,
132 | Ehsize: 64,
133 | Phentsize: 56,
134 | Phnum: 13,
135 | Shentsize: 64,
136 | Shnum: 30,
137 | Shstrndx: 29,
138 | },
139 | expectedSectionHeader64: []ELF64SectionHeader{
140 | {
141 | Name: 0,
142 | Type: 0,
143 | Flags: 0,
144 | Addr: 0,
145 | Off: 0,
146 | Size: 0,
147 | Link: 0,
148 | Info: 0,
149 | AddrAlign: 0,
150 | EntSize: 0,
151 | }, {
152 | Name: 11,
153 | Type: uint32(SHT_PROGBITS),
154 | Flags: uint64(SHF_ALLOC),
155 | Addr: 0x318,
156 | Off: 0x318,
157 | Size: 0x1c,
158 | Link: 0,
159 | Info: 0,
160 | AddrAlign: 1,
161 | EntSize: 0,
162 | }, {
163 | Name: 19,
164 | Type: uint32(SHT_NOTE),
165 | Flags: uint64(SHF_ALLOC),
166 | Addr: 0x338,
167 | Off: 0x338,
168 | Size: 0x20,
169 | Link: 0,
170 | Info: 0,
171 | AddrAlign: 8,
172 | EntSize: 0,
173 | }, {
174 | Name: 38,
175 | Type: uint32(SHT_NOTE),
176 | Flags: uint64(SHF_ALLOC),
177 | Addr: 0x358,
178 | Off: 0x358,
179 | Size: 0x24,
180 | Link: 0,
181 | Info: 0,
182 | AddrAlign: 4,
183 | EntSize: 0,
184 | }, {
185 | Name: 57,
186 | Type: uint32(SHT_NOTE),
187 | Flags: uint64(SHF_ALLOC),
188 | Addr: 0x37c,
189 | Off: 0x37c,
190 | Size: 0x20,
191 | Link: 0x0,
192 | Info: 0,
193 | AddrAlign: 4,
194 | EntSize: 0x0,
195 | }, {
196 | Name: 71,
197 | Type: uint32(SHT_GNU_HASH),
198 | Flags: uint64(SHF_ALLOC),
199 | Addr: 0x3a0,
200 | Off: 0x3a0,
201 | Size: 0xe4,
202 | Link: 0x6,
203 | Info: 0,
204 | AddrAlign: 8,
205 | EntSize: 0x0,
206 | }, {
207 | Name: 81,
208 | Type: uint32(SHT_DYNSYM),
209 | Flags: uint64(SHF_ALLOC),
210 | Addr: 0x488,
211 | Off: 0x488,
212 | Size: 0xd08,
213 | Link: 7,
214 | Info: 1,
215 | AddrAlign: 8,
216 | EntSize: 0x18,
217 | }, {
218 | Name: 89,
219 | Type: uint32(SHT_STRTAB),
220 | Flags: uint64(SHF_ALLOC),
221 | Addr: 0x1190,
222 | Off: 0x1190,
223 | Size: 0x64c,
224 | Link: 0x0,
225 | Info: 0,
226 | AddrAlign: 1,
227 | EntSize: 0x0,
228 | }, {
229 | Name: 97,
230 | Type: uint32(SHT_GNU_VERSYM),
231 | Flags: uint64(SHF_ALLOC),
232 | Addr: 0x17dc,
233 | Off: 0x17dc,
234 | Size: 0x116,
235 | Link: 0x6,
236 | Info: 0,
237 | AddrAlign: 2,
238 | EntSize: 0x2,
239 | }, {
240 | Name: 110,
241 | Type: uint32(SHT_GNU_VERNEED),
242 | Flags: uint64(SHF_ALLOC),
243 | Addr: 0x18f8,
244 | Off: 0x18f8,
245 | Size: 0x70,
246 | Link: 0x7,
247 | Info: 1,
248 | AddrAlign: 8,
249 | EntSize: 0x0,
250 | }, {
251 | Name: 125,
252 | Type: uint32(SHT_RELA),
253 | Flags: uint64(SHF_ALLOC),
254 | Addr: 0x1968,
255 | Off: 0x1968,
256 | Size: 0x1350,
257 | Link: 0x6,
258 | Info: 0,
259 | AddrAlign: 8,
260 | EntSize: 0x18,
261 | }, {
262 | Name: 135,
263 | Type: uint32(SHT_RELA),
264 | Flags: uint64(SHF_ALLOC + SHF_INFO_LINK),
265 | Addr: 0x2cb8,
266 | Off: 0x2cb8,
267 | Size: 0x9f0,
268 | Link: 0x6,
269 | Info: 25,
270 | AddrAlign: 8,
271 | EntSize: 0x18,
272 | }, {
273 | Name: 145,
274 | Type: uint32(SHT_PROGBITS),
275 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
276 | Addr: 0x4000,
277 | Off: 0x4000,
278 | Size: 0x1b,
279 | Link: 0x0,
280 | Info: 0,
281 | AddrAlign: 4,
282 | EntSize: 0x0,
283 | }, {
284 | Name: 140,
285 | Type: uint32(SHT_PROGBITS),
286 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
287 | Addr: 0x4020,
288 | Off: 0x4020,
289 | Size: 0x6b0,
290 | Link: 0,
291 | Info: 0,
292 | AddrAlign: 16,
293 | EntSize: 0x10,
294 | }, {
295 | Name: 151,
296 | Type: uint32(SHT_PROGBITS),
297 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
298 | Addr: 0x46d0,
299 | Off: 0x46d0,
300 | Size: 0x30,
301 | Link: 0,
302 | Info: 0,
303 | AddrAlign: 16,
304 | EntSize: 0x10,
305 | }, {
306 | Name: 160,
307 | Type: uint32(SHT_PROGBITS),
308 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
309 | Addr: 0x4700,
310 | Off: 0x4700,
311 | Size: 0x6a0,
312 | Link: 0x0,
313 | Info: 0,
314 | AddrAlign: 16,
315 | EntSize: 0x10,
316 | }, {
317 | Name: 169,
318 | Type: uint32(SHT_PROGBITS),
319 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
320 | Addr: 0x4da0,
321 | Off: 0x4da0,
322 | Size: 0x127d2,
323 | Link: 0x0,
324 | Info: 0,
325 | AddrAlign: 16,
326 | EntSize: 0x0,
327 | }, {
328 | Name: 175,
329 | Type: uint32(SHT_PROGBITS),
330 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
331 | Addr: 0x17574,
332 | Off: 0x17574,
333 | Size: 0xd,
334 | Link: 0,
335 | Info: 0,
336 | AddrAlign: 4,
337 | EntSize: 0x0,
338 | }, {
339 | Name: 181,
340 | Type: uint32(SHT_PROGBITS),
341 | Flags: uint64(SHF_ALLOC),
342 | Addr: 0x18000,
343 | Off: 0x18000,
344 | Size: 0x5249,
345 | Link: 0,
346 | Info: 0,
347 | AddrAlign: 32,
348 | EntSize: 0x0,
349 | }, {
350 | Name: 189,
351 | Type: uint32(SHT_PROGBITS),
352 | Flags: uint64(SHF_ALLOC),
353 | Addr: 0x1d24c,
354 | Off: 0x1d24c,
355 | Size: 0x92c,
356 | Link: 0x0,
357 | Info: 0,
358 | AddrAlign: 4,
359 | EntSize: 0x0,
360 | }, {
361 | Name: 203,
362 | Type: uint32(SHT_PROGBITS),
363 | Flags: uint64(SHF_ALLOC),
364 | Addr: 0x1db78,
365 | Off: 0x1db78,
366 | Size: 0x2fd8,
367 | Link: 0x0,
368 | Info: 0,
369 | AddrAlign: 8,
370 | EntSize: 0x0,
371 | }, {
372 | Name: 213,
373 | Type: uint32(SHT_INIT_ARRAY),
374 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
375 | Addr: 0x22010,
376 | Off: 0x21010,
377 | Size: 0x8,
378 | Link: 0x0,
379 | Info: 0,
380 | AddrAlign: 8,
381 | EntSize: 0x8,
382 | }, {
383 | Name: 225,
384 | Type: uint32(SHT_FINI_ARRAY),
385 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
386 | Addr: 0x22018,
387 | Off: 0x21018,
388 | Size: 0x8,
389 | Link: 0x0,
390 | Info: 0,
391 | AddrAlign: 8,
392 | EntSize: 0x8,
393 | }, {
394 | Name: 237,
395 | Type: uint32(SHT_PROGBITS),
396 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
397 | Addr: 0x22020,
398 | Off: 0x21020,
399 | Size: 0xa38,
400 | Link: 0x0,
401 | Info: 0,
402 | AddrAlign: 32,
403 | EntSize: 0x0,
404 | }, {
405 | Name: 250,
406 | Type: uint32(SHT_DYNAMIC),
407 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
408 | Addr: 0x22a58,
409 | Off: 0x21a58,
410 | Size: 0x200,
411 | Link: 0x7,
412 | Info: 0,
413 | AddrAlign: 8,
414 | EntSize: 0x10,
415 | }, {
416 | Name: 155,
417 | Type: uint32(SHT_PROGBITS),
418 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
419 | Addr: 0x22c58,
420 | Off: 0x21c58,
421 | Size: 0x3a0,
422 | Link: 0,
423 | Info: 0,
424 | AddrAlign: 8,
425 | EntSize: 0x8,
426 | }, {
427 | Name: 259,
428 | Type: uint32(SHT_PROGBITS),
429 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
430 | Addr: 0x23000,
431 | Off: 0x22000,
432 | Size: 0x268,
433 | Link: 0,
434 | Info: 0,
435 | AddrAlign: 32,
436 | EntSize: 0x0,
437 | }, {
438 | Name: 265,
439 | Type: uint32(SHT_NOBITS),
440 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
441 | Addr: 0x23280,
442 | Off: 0x22268,
443 | Size: 0x12d8,
444 | Link: 0,
445 | Info: 0,
446 | AddrAlign: 32,
447 | EntSize: 0x0,
448 | }, {
449 | Name: 270,
450 | Type: uint32(SHT_PROGBITS),
451 | Flags: uint64(SHF_NONE),
452 | Addr: 0x0,
453 | Off: 0x22268,
454 | Size: 0x34,
455 | Link: 0,
456 | Info: 0,
457 | AddrAlign: 4,
458 | EntSize: 0x0,
459 | }, {
460 | Name: 1,
461 | Type: uint32(SHT_STRTAB),
462 | Flags: uint64(SHF_NONE),
463 | Addr: 0x0,
464 | Off: 0x2229c,
465 | Size: 0x11d,
466 | Link: 0,
467 | Info: 0,
468 | AddrAlign: 1,
469 | EntSize: 0x0,
470 | },
471 | },
472 | },
473 | }
474 |
475 | for _, tt := range testCases {
476 | p, err := New(tt.path, nil)
477 | if err != nil {
478 | t.Fatal("failed to create new parser with error :", err)
479 | }
480 | err = p.ParseIdent()
481 | if err != nil {
482 | t.Fatal("failed to parse ident with error :", err)
483 | }
484 | assert.EqualValues(t, tt.expectedIdent, p.F.Ident, "expected ident equal")
485 | err = p.ParseELFHeader(ELFCLASS64)
486 | if err != nil {
487 | t.Fatal("failed to parse ELF header with error :", err)
488 | }
489 | assert.EqualValues(t, tt.expectedHeader, p.F.Header64, "expected header equal")
490 | err = p.ParseELFSectionHeaders(ELFCLASS64)
491 | if err != nil {
492 | t.Fatal("failed to parse ELF section headers with error :", err)
493 | }
494 | assert.EqualValues(t, tt.expectedSectionHeader64, p.F.SectionHeaders64, "expected section headers equal")
495 | }
496 | })
497 | t.Run("TestParseSections", func(t *testing.T) {
498 | testCases := []struct {
499 | path string
500 | expectedIdent FileIdent
501 | expectedHeader ELF64Header
502 | expectedSectionHeader64 []ELF64SectionHeader
503 | expectedSectionNames []string
504 | }{
505 | {
506 | path: path.Join("test/", "ls"),
507 | expectedIdent: FileIdent{
508 | Magic: [4]byte{0x7f, 'E', 'L', 'F'},
509 | Class: ELFCLASS64,
510 | Data: ELFDATA2LSB,
511 | Version: EV_CURRENT,
512 | OSABI: ELFOSABI_NONE,
513 | ABIVersion: ELFABIVersion_CURRENT,
514 | ByteOrder: binary.LittleEndian,
515 | },
516 | expectedHeader: ELF64Header{
517 | Ident: [16]byte{0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
518 | Type: uint16(ET_DYN),
519 | Machine: uint16(EM_X86_64),
520 | Version: uint32(EV_CURRENT),
521 | Entry: 0x67d0,
522 | Phoff: 64,
523 | Shoff: 140224,
524 | Flags: 0x0,
525 | Ehsize: 64,
526 | Phentsize: 56,
527 | Phnum: 13,
528 | Shentsize: 64,
529 | Shnum: 30,
530 | Shstrndx: 29,
531 | },
532 | expectedSectionHeader64: []ELF64SectionHeader{
533 | {
534 | Name: 0,
535 | Type: 0,
536 | Flags: 0,
537 | Addr: 0,
538 | Off: 0,
539 | Size: 0,
540 | Link: 0,
541 | Info: 0,
542 | AddrAlign: 0,
543 | EntSize: 0,
544 | }, {
545 | Name: 11,
546 | Type: uint32(SHT_PROGBITS),
547 | Flags: uint64(SHF_ALLOC),
548 | Addr: 0x318,
549 | Off: 0x318,
550 | Size: 0x1c,
551 | Link: 0,
552 | Info: 0,
553 | AddrAlign: 1,
554 | EntSize: 0,
555 | }, {
556 | Name: 19,
557 | Type: uint32(SHT_NOTE),
558 | Flags: uint64(SHF_ALLOC),
559 | Addr: 0x338,
560 | Off: 0x338,
561 | Size: 0x20,
562 | Link: 0,
563 | Info: 0,
564 | AddrAlign: 8,
565 | EntSize: 0,
566 | }, {
567 | Name: 38,
568 | Type: uint32(SHT_NOTE),
569 | Flags: uint64(SHF_ALLOC),
570 | Addr: 0x358,
571 | Off: 0x358,
572 | Size: 0x24,
573 | Link: 0,
574 | Info: 0,
575 | AddrAlign: 4,
576 | EntSize: 0,
577 | }, {
578 | Name: 57,
579 | Type: uint32(SHT_NOTE),
580 | Flags: uint64(SHF_ALLOC),
581 | Addr: 0x37c,
582 | Off: 0x37c,
583 | Size: 0x20,
584 | Link: 0x0,
585 | Info: 0,
586 | AddrAlign: 4,
587 | EntSize: 0x0,
588 | }, {
589 | Name: 71,
590 | Type: uint32(SHT_GNU_HASH),
591 | Flags: uint64(SHF_ALLOC),
592 | Addr: 0x3a0,
593 | Off: 0x3a0,
594 | Size: 0xe4,
595 | Link: 0x6,
596 | Info: 0,
597 | AddrAlign: 8,
598 | EntSize: 0x0,
599 | }, {
600 | Name: 81,
601 | Type: uint32(SHT_DYNSYM),
602 | Flags: uint64(SHF_ALLOC),
603 | Addr: 0x488,
604 | Off: 0x488,
605 | Size: 0xd08,
606 | Link: 7,
607 | Info: 1,
608 | AddrAlign: 8,
609 | EntSize: 0x18,
610 | }, {
611 | Name: 89,
612 | Type: uint32(SHT_STRTAB),
613 | Flags: uint64(SHF_ALLOC),
614 | Addr: 0x1190,
615 | Off: 0x1190,
616 | Size: 0x64c,
617 | Link: 0x0,
618 | Info: 0,
619 | AddrAlign: 1,
620 | EntSize: 0x0,
621 | }, {
622 | Name: 97,
623 | Type: uint32(SHT_GNU_VERSYM),
624 | Flags: uint64(SHF_ALLOC),
625 | Addr: 0x17dc,
626 | Off: 0x17dc,
627 | Size: 0x116,
628 | Link: 0x6,
629 | Info: 0,
630 | AddrAlign: 2,
631 | EntSize: 0x2,
632 | }, {
633 | Name: 110,
634 | Type: uint32(SHT_GNU_VERNEED),
635 | Flags: uint64(SHF_ALLOC),
636 | Addr: 0x18f8,
637 | Off: 0x18f8,
638 | Size: 0x70,
639 | Link: 0x7,
640 | Info: 1,
641 | AddrAlign: 8,
642 | EntSize: 0x0,
643 | }, {
644 | Name: 125,
645 | Type: uint32(SHT_RELA),
646 | Flags: uint64(SHF_ALLOC),
647 | Addr: 0x1968,
648 | Off: 0x1968,
649 | Size: 0x1350,
650 | Link: 0x6,
651 | Info: 0,
652 | AddrAlign: 8,
653 | EntSize: 0x18,
654 | }, {
655 | Name: 135,
656 | Type: uint32(SHT_RELA),
657 | Flags: uint64(SHF_ALLOC + SHF_INFO_LINK),
658 | Addr: 0x2cb8,
659 | Off: 0x2cb8,
660 | Size: 0x9f0,
661 | Link: 0x6,
662 | Info: 25,
663 | AddrAlign: 8,
664 | EntSize: 0x18,
665 | }, {
666 | Name: 145,
667 | Type: uint32(SHT_PROGBITS),
668 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
669 | Addr: 0x4000,
670 | Off: 0x4000,
671 | Size: 0x1b,
672 | Link: 0x0,
673 | Info: 0,
674 | AddrAlign: 4,
675 | EntSize: 0x0,
676 | }, {
677 | Name: 140,
678 | Type: uint32(SHT_PROGBITS),
679 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
680 | Addr: 0x4020,
681 | Off: 0x4020,
682 | Size: 0x6b0,
683 | Link: 0,
684 | Info: 0,
685 | AddrAlign: 16,
686 | EntSize: 0x10,
687 | }, {
688 | Name: 151,
689 | Type: uint32(SHT_PROGBITS),
690 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
691 | Addr: 0x46d0,
692 | Off: 0x46d0,
693 | Size: 0x30,
694 | Link: 0,
695 | Info: 0,
696 | AddrAlign: 16,
697 | EntSize: 0x10,
698 | }, {
699 | Name: 160,
700 | Type: uint32(SHT_PROGBITS),
701 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
702 | Addr: 0x4700,
703 | Off: 0x4700,
704 | Size: 0x6a0,
705 | Link: 0x0,
706 | Info: 0,
707 | AddrAlign: 16,
708 | EntSize: 0x10,
709 | }, {
710 | Name: 169,
711 | Type: uint32(SHT_PROGBITS),
712 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
713 | Addr: 0x4da0,
714 | Off: 0x4da0,
715 | Size: 0x127d2,
716 | Link: 0x0,
717 | Info: 0,
718 | AddrAlign: 16,
719 | EntSize: 0x0,
720 | }, {
721 | Name: 175,
722 | Type: uint32(SHT_PROGBITS),
723 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
724 | Addr: 0x17574,
725 | Off: 0x17574,
726 | Size: 0xd,
727 | Link: 0,
728 | Info: 0,
729 | AddrAlign: 4,
730 | EntSize: 0x0,
731 | }, {
732 | Name: 181,
733 | Type: uint32(SHT_PROGBITS),
734 | Flags: uint64(SHF_ALLOC),
735 | Addr: 0x18000,
736 | Off: 0x18000,
737 | Size: 0x5249,
738 | Link: 0,
739 | Info: 0,
740 | AddrAlign: 32,
741 | EntSize: 0x0,
742 | }, {
743 | Name: 189,
744 | Type: uint32(SHT_PROGBITS),
745 | Flags: uint64(SHF_ALLOC),
746 | Addr: 0x1d24c,
747 | Off: 0x1d24c,
748 | Size: 0x92c,
749 | Link: 0x0,
750 | Info: 0,
751 | AddrAlign: 4,
752 | EntSize: 0x0,
753 | }, {
754 | Name: 203,
755 | Type: uint32(SHT_PROGBITS),
756 | Flags: uint64(SHF_ALLOC),
757 | Addr: 0x1db78,
758 | Off: 0x1db78,
759 | Size: 0x2fd8,
760 | Link: 0x0,
761 | Info: 0,
762 | AddrAlign: 8,
763 | EntSize: 0x0,
764 | }, {
765 | Name: 213,
766 | Type: uint32(SHT_INIT_ARRAY),
767 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
768 | Addr: 0x22010,
769 | Off: 0x21010,
770 | Size: 0x8,
771 | Link: 0x0,
772 | Info: 0,
773 | AddrAlign: 8,
774 | EntSize: 0x8,
775 | }, {
776 | Name: 225,
777 | Type: uint32(SHT_FINI_ARRAY),
778 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
779 | Addr: 0x22018,
780 | Off: 0x21018,
781 | Size: 0x8,
782 | Link: 0x0,
783 | Info: 0,
784 | AddrAlign: 8,
785 | EntSize: 0x8,
786 | }, {
787 | Name: 237,
788 | Type: uint32(SHT_PROGBITS),
789 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
790 | Addr: 0x22020,
791 | Off: 0x21020,
792 | Size: 0xa38,
793 | Link: 0x0,
794 | Info: 0,
795 | AddrAlign: 32,
796 | EntSize: 0x0,
797 | }, {
798 | Name: 250,
799 | Type: uint32(SHT_DYNAMIC),
800 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
801 | Addr: 0x22a58,
802 | Off: 0x21a58,
803 | Size: 0x200,
804 | Link: 0x7,
805 | Info: 0,
806 | AddrAlign: 8,
807 | EntSize: 0x10,
808 | }, {
809 | Name: 155,
810 | Type: uint32(SHT_PROGBITS),
811 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
812 | Addr: 0x22c58,
813 | Off: 0x21c58,
814 | Size: 0x3a0,
815 | Link: 0,
816 | Info: 0,
817 | AddrAlign: 8,
818 | EntSize: 0x8,
819 | }, {
820 | Name: 259,
821 | Type: uint32(SHT_PROGBITS),
822 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
823 | Addr: 0x23000,
824 | Off: 0x22000,
825 | Size: 0x268,
826 | Link: 0,
827 | Info: 0,
828 | AddrAlign: 32,
829 | EntSize: 0x0,
830 | }, {
831 | Name: 265,
832 | Type: uint32(SHT_NOBITS),
833 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
834 | Addr: 0x23280,
835 | Off: 0x22268,
836 | Size: 0x12d8,
837 | Link: 0,
838 | Info: 0,
839 | AddrAlign: 32,
840 | EntSize: 0x0,
841 | }, {
842 | Name: 270,
843 | Type: uint32(SHT_PROGBITS),
844 | Flags: uint64(SHF_NONE),
845 | Addr: 0x0,
846 | Off: 0x22268,
847 | Size: 0x34,
848 | Link: 0,
849 | Info: 0,
850 | AddrAlign: 4,
851 | EntSize: 0x0,
852 | }, {
853 | Name: 1,
854 | Type: uint32(SHT_STRTAB),
855 | Flags: uint64(SHF_NONE),
856 | Addr: 0x0,
857 | Off: 0x2229c,
858 | Size: 0x11d,
859 | Link: 0,
860 | Info: 0,
861 | AddrAlign: 1,
862 | EntSize: 0x0,
863 | },
864 | },
865 | expectedSectionNames: []string{
866 | "",
867 | ".interp",
868 | ".note.gnu.property",
869 | ".note.gnu.build-id",
870 | ".note.ABI-tag",
871 | ".gnu.hash",
872 | ".dynsym",
873 | ".dynstr",
874 | ".gnu.version",
875 | ".gnu.version_r",
876 | ".rela.dyn",
877 | ".rela.plt",
878 | ".init",
879 | ".plt",
880 | ".plt.got",
881 | ".plt.sec",
882 | ".text",
883 | ".fini",
884 | ".rodata",
885 | ".eh_frame_hdr",
886 | ".eh_frame",
887 | ".init_array",
888 | ".fini_array",
889 | ".data.rel.ro",
890 | ".dynamic",
891 | ".got",
892 | ".data",
893 | ".bss",
894 | ".gnu_debuglink",
895 | ".shstrtab",
896 | },
897 | },
898 | }
899 |
900 | for _, tt := range testCases {
901 | p, err := New(tt.path, nil)
902 | if err != nil {
903 | t.Fatal("failed to create new parser with error :", err)
904 | }
905 | err = p.ParseIdent()
906 | if err != nil {
907 | t.Fatal("failed to parse ident with error :", err)
908 | }
909 | assert.EqualValues(t, tt.expectedIdent, p.F.Ident, "expected ident equal")
910 | err = p.ParseELFHeader(ELFCLASS64)
911 | if err != nil {
912 | t.Fatal("failed to parse ELF header with error :", err)
913 | }
914 | assert.EqualValues(t, tt.expectedHeader, p.F.Header64, "expected header equal")
915 | err = p.ParseELFSectionHeaders(ELFCLASS64)
916 | if err != nil {
917 | t.Fatal("failed to parse ELF section headers with error :", err)
918 | }
919 | assert.EqualValues(t, tt.expectedSectionHeader64, p.F.SectionHeaders64, "expected section headers equal")
920 | err = p.parseELFSections64()
921 | if err != nil {
922 | t.Fatal("failed to parse ELF section headers with error :", err)
923 | }
924 | assert.EqualValues(t, tt.expectedSectionNames, p.F.SectionNames())
925 | }
926 | })
927 | t.Run("TestParseProgramHeaders", func(t *testing.T) {
928 | testCases := []struct {
929 | path string
930 | expectedIdent FileIdent
931 | expectedHeader ELF64Header
932 | expectedProgramHeaders []ELF64ProgramHeader
933 | expectedProgramHeaderCount int
934 | }{
935 | {
936 | path: path.Join("test/", "ls"),
937 | expectedIdent: FileIdent{
938 | Magic: [4]byte{0x7f, 'E', 'L', 'F'},
939 | Class: ELFCLASS64,
940 | Data: ELFDATA2LSB,
941 | Version: EV_CURRENT,
942 | OSABI: ELFOSABI_NONE,
943 | ABIVersion: ELFABIVersion_CURRENT,
944 | ByteOrder: binary.LittleEndian,
945 | },
946 | expectedHeader: ELF64Header{
947 | Ident: [16]byte{0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
948 | Type: uint16(ET_DYN),
949 | Machine: uint16(EM_X86_64),
950 | Version: uint32(EV_CURRENT),
951 | Entry: 0x67d0,
952 | Phoff: 64,
953 | Shoff: 140224,
954 | Flags: 0x0,
955 | Ehsize: 64,
956 | Phentsize: 56,
957 | Phnum: 13,
958 | Shentsize: 64,
959 | Shnum: 30,
960 | Shstrndx: 29,
961 | },
962 | expectedProgramHeaders: []ELF64ProgramHeader{
963 |
964 | {
965 | Type: uint32(PT_PHDR),
966 | Flags: uint32(PF_R),
967 | Off: 0x000040,
968 | Vaddr: 0x0000000000000040,
969 | Paddr: 0x0000000000000040,
970 | Filesz: 0x0002d8,
971 | Memsz: 0x0002d8,
972 | Align: 0x8,
973 | }, {
974 | Type: uint32(PT_INTERP),
975 | Flags: uint32(PF_R),
976 | Off: 0x000318,
977 | Vaddr: 0x0000000000000318,
978 | Paddr: 0x0000000000000318,
979 | Filesz: 0x00001c,
980 | Memsz: 0x00001c,
981 | Align: 0x1,
982 | }, {
983 | Type: uint32(PT_LOAD),
984 | Flags: uint32(PF_R),
985 | Off: 0x000000,
986 | Vaddr: 0x0000000000000000,
987 | Paddr: 0x0000000000000000,
988 | Filesz: 0x0036a8,
989 | Memsz: 0x0036a8,
990 | Align: 0x1000,
991 | }, {
992 | Type: uint32(PT_LOAD),
993 | Flags: uint32(PF_R + PF_X),
994 | Off: 0x004000,
995 | Vaddr: 0x0000000000004000,
996 | Paddr: 0x0000000000004000,
997 | Filesz: 0x013581,
998 | Memsz: 0x013581,
999 | Align: 0x1000,
1000 | }, {
1001 | Type: uint32(PT_LOAD),
1002 | Flags: uint32(PF_R),
1003 | Off: 0x018000,
1004 | Vaddr: 0x0000000000018000,
1005 | Paddr: 0x0000000000018000,
1006 | Filesz: 0x008b50,
1007 | Memsz: 0x008b50,
1008 | Align: 0x1000,
1009 | }, {
1010 | Type: uint32(PT_LOAD),
1011 | Flags: uint32(PF_R + PF_W),
1012 | Off: 0x021010,
1013 | Vaddr: 0x0000000000022010,
1014 | Paddr: 0x0000000000022010,
1015 | Filesz: 0x001258,
1016 | Memsz: 0x002548,
1017 | Align: 0x1000,
1018 | }, {
1019 | Type: uint32(PT_DYNAMIC),
1020 | Flags: uint32(PF_R + PF_W),
1021 | Off: 0x021a58,
1022 | Vaddr: 0x0000000000022a58,
1023 | Paddr: 0x0000000000022a58,
1024 | Filesz: 0x000200,
1025 | Memsz: 0x000200,
1026 | Align: 0x8,
1027 | }, {
1028 | Type: uint32(PT_NOTE),
1029 | Flags: uint32(PF_R),
1030 | Off: 0x000338,
1031 | Vaddr: 0x0000000000000338,
1032 | Paddr: 0x0000000000000338,
1033 | Filesz: 0x000020,
1034 | Memsz: 0x000020,
1035 | Align: 0x8,
1036 | },
1037 | {
1038 | Type: uint32(PT_NOTE),
1039 | Flags: uint32(PF_R),
1040 | Off: 0x000358,
1041 | Vaddr: 0x0000000000000358,
1042 | Paddr: 0x0000000000000358,
1043 | Filesz: 0x000044,
1044 | Memsz: 0x000044,
1045 | Align: 0x4,
1046 | }, {
1047 | Type: uint32(PT_GNU_PROPERTY),
1048 | Flags: uint32(PF_R),
1049 | Off: 0x000338,
1050 | Vaddr: 0x0000000000000338,
1051 | Paddr: 0x0000000000000338,
1052 | Filesz: 0x000020,
1053 | Memsz: 0x000020,
1054 | Align: 0x8,
1055 | }, {
1056 | Type: uint32(PT_GNU_EH_FRAME),
1057 | Flags: uint32(PF_R),
1058 | Off: 0x01d24c,
1059 | Vaddr: 0x000000000001d24c,
1060 | Paddr: 0x000000000001d24c,
1061 | Filesz: 0x00092c,
1062 | Memsz: 0x00092c,
1063 | Align: 0x4,
1064 | }, {
1065 | Type: uint32(PT_GNU_STACK),
1066 | Flags: uint32(PF_R + PF_W),
1067 | Off: 0x000000,
1068 | Vaddr: 0x0000000000000000,
1069 | Paddr: 0x0000000000000000,
1070 | Filesz: 0x000000,
1071 | Memsz: 0x000000,
1072 | Align: 0x10,
1073 | }, {
1074 | Type: uint32(PT_GNU_RELRO),
1075 | Flags: uint32(PF_R),
1076 | Off: 0x021010,
1077 | Vaddr: 0x0000000000022010,
1078 | Paddr: 0x0000000000022010,
1079 | Filesz: 0x000ff0,
1080 | Memsz: 0x000ff0,
1081 | Align: 0x1,
1082 | },
1083 | },
1084 | expectedProgramHeaderCount: 13,
1085 | },
1086 | }
1087 |
1088 | for _, tt := range testCases {
1089 | p, err := New(tt.path, nil)
1090 | if err != nil {
1091 | t.Fatal("failed to create new parser with error :", err)
1092 | }
1093 | err = p.ParseIdent()
1094 | if err != nil {
1095 | t.Fatal("failed to parse ident with error :", err)
1096 | }
1097 | assert.EqualValues(t, tt.expectedIdent, p.F.Ident, "expected ident equal")
1098 | err = p.ParseELFHeader(ELFCLASS64)
1099 | if err != nil {
1100 | t.Fatal("failed to parse ELF header with error :", err)
1101 | }
1102 | assert.EqualValues(t, tt.expectedHeader, p.F.Header64, "expected header equal")
1103 | err = p.parseELFProgramHeaders64()
1104 | if err != nil {
1105 | t.Fatal("failed to parse ELF program headers with error :", err)
1106 | }
1107 | assert.EqualValues(t, tt.expectedProgramHeaders, p.F.ProgramHeaders64)
1108 | assert.EqualValues(t, tt.expectedProgramHeaderCount, len(p.F.ProgramHeaders64))
1109 | }
1110 | })
1111 | t.Run("TestParseSymbols", func(t *testing.T) {
1112 | testCases := []struct {
1113 | path string
1114 | expectedIdent FileIdent
1115 | expectedHeader ELF64Header
1116 | expectedSectionHeader64 []ELF64SectionHeader
1117 | expectedSectionNames []string
1118 | expectedSymbolsLength int
1119 | }{
1120 | {
1121 | path: path.Join("test/", "ls"),
1122 | expectedIdent: FileIdent{
1123 | Magic: [4]byte{0x7f, 'E', 'L', 'F'},
1124 | Class: ELFCLASS64,
1125 | Data: ELFDATA2LSB,
1126 | Version: EV_CURRENT,
1127 | OSABI: ELFOSABI_NONE,
1128 | ABIVersion: ELFABIVersion_CURRENT,
1129 | ByteOrder: binary.LittleEndian,
1130 | },
1131 | expectedHeader: ELF64Header{
1132 | Ident: [16]byte{0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1133 | Type: uint16(ET_DYN),
1134 | Machine: uint16(EM_X86_64),
1135 | Version: uint32(EV_CURRENT),
1136 | Entry: 0x67d0,
1137 | Phoff: 64,
1138 | Shoff: 140224,
1139 | Flags: 0x0,
1140 | Ehsize: 64,
1141 | Phentsize: 56,
1142 | Phnum: 13,
1143 | Shentsize: 64,
1144 | Shnum: 30,
1145 | Shstrndx: 29,
1146 | },
1147 | expectedSectionHeader64: []ELF64SectionHeader{
1148 | {
1149 | Name: 0,
1150 | Type: 0,
1151 | Flags: 0,
1152 | Addr: 0,
1153 | Off: 0,
1154 | Size: 0,
1155 | Link: 0,
1156 | Info: 0,
1157 | AddrAlign: 0,
1158 | EntSize: 0,
1159 | }, {
1160 | Name: 11,
1161 | Type: uint32(SHT_PROGBITS),
1162 | Flags: uint64(SHF_ALLOC),
1163 | Addr: 0x318,
1164 | Off: 0x318,
1165 | Size: 0x1c,
1166 | Link: 0,
1167 | Info: 0,
1168 | AddrAlign: 1,
1169 | EntSize: 0,
1170 | }, {
1171 | Name: 19,
1172 | Type: uint32(SHT_NOTE),
1173 | Flags: uint64(SHF_ALLOC),
1174 | Addr: 0x338,
1175 | Off: 0x338,
1176 | Size: 0x20,
1177 | Link: 0,
1178 | Info: 0,
1179 | AddrAlign: 8,
1180 | EntSize: 0,
1181 | }, {
1182 | Name: 38,
1183 | Type: uint32(SHT_NOTE),
1184 | Flags: uint64(SHF_ALLOC),
1185 | Addr: 0x358,
1186 | Off: 0x358,
1187 | Size: 0x24,
1188 | Link: 0,
1189 | Info: 0,
1190 | AddrAlign: 4,
1191 | EntSize: 0,
1192 | }, {
1193 | Name: 57,
1194 | Type: uint32(SHT_NOTE),
1195 | Flags: uint64(SHF_ALLOC),
1196 | Addr: 0x37c,
1197 | Off: 0x37c,
1198 | Size: 0x20,
1199 | Link: 0x0,
1200 | Info: 0,
1201 | AddrAlign: 4,
1202 | EntSize: 0x0,
1203 | }, {
1204 | Name: 71,
1205 | Type: uint32(SHT_GNU_HASH),
1206 | Flags: uint64(SHF_ALLOC),
1207 | Addr: 0x3a0,
1208 | Off: 0x3a0,
1209 | Size: 0xe4,
1210 | Link: 0x6,
1211 | Info: 0,
1212 | AddrAlign: 8,
1213 | EntSize: 0x0,
1214 | }, {
1215 | Name: 81,
1216 | Type: uint32(SHT_DYNSYM),
1217 | Flags: uint64(SHF_ALLOC),
1218 | Addr: 0x488,
1219 | Off: 0x488,
1220 | Size: 0xd08,
1221 | Link: 7,
1222 | Info: 1,
1223 | AddrAlign: 8,
1224 | EntSize: 0x18,
1225 | }, {
1226 | Name: 89,
1227 | Type: uint32(SHT_STRTAB),
1228 | Flags: uint64(SHF_ALLOC),
1229 | Addr: 0x1190,
1230 | Off: 0x1190,
1231 | Size: 0x64c,
1232 | Link: 0x0,
1233 | Info: 0,
1234 | AddrAlign: 1,
1235 | EntSize: 0x0,
1236 | }, {
1237 | Name: 97,
1238 | Type: uint32(SHT_GNU_VERSYM),
1239 | Flags: uint64(SHF_ALLOC),
1240 | Addr: 0x17dc,
1241 | Off: 0x17dc,
1242 | Size: 0x116,
1243 | Link: 0x6,
1244 | Info: 0,
1245 | AddrAlign: 2,
1246 | EntSize: 0x2,
1247 | }, {
1248 | Name: 110,
1249 | Type: uint32(SHT_GNU_VERNEED),
1250 | Flags: uint64(SHF_ALLOC),
1251 | Addr: 0x18f8,
1252 | Off: 0x18f8,
1253 | Size: 0x70,
1254 | Link: 0x7,
1255 | Info: 1,
1256 | AddrAlign: 8,
1257 | EntSize: 0x0,
1258 | }, {
1259 | Name: 125,
1260 | Type: uint32(SHT_RELA),
1261 | Flags: uint64(SHF_ALLOC),
1262 | Addr: 0x1968,
1263 | Off: 0x1968,
1264 | Size: 0x1350,
1265 | Link: 0x6,
1266 | Info: 0,
1267 | AddrAlign: 8,
1268 | EntSize: 0x18,
1269 | }, {
1270 | Name: 135,
1271 | Type: uint32(SHT_RELA),
1272 | Flags: uint64(SHF_ALLOC + SHF_INFO_LINK),
1273 | Addr: 0x2cb8,
1274 | Off: 0x2cb8,
1275 | Size: 0x9f0,
1276 | Link: 0x6,
1277 | Info: 25,
1278 | AddrAlign: 8,
1279 | EntSize: 0x18,
1280 | }, {
1281 | Name: 145,
1282 | Type: uint32(SHT_PROGBITS),
1283 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
1284 | Addr: 0x4000,
1285 | Off: 0x4000,
1286 | Size: 0x1b,
1287 | Link: 0x0,
1288 | Info: 0,
1289 | AddrAlign: 4,
1290 | EntSize: 0x0,
1291 | }, {
1292 | Name: 140,
1293 | Type: uint32(SHT_PROGBITS),
1294 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
1295 | Addr: 0x4020,
1296 | Off: 0x4020,
1297 | Size: 0x6b0,
1298 | Link: 0,
1299 | Info: 0,
1300 | AddrAlign: 16,
1301 | EntSize: 0x10,
1302 | }, {
1303 | Name: 151,
1304 | Type: uint32(SHT_PROGBITS),
1305 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
1306 | Addr: 0x46d0,
1307 | Off: 0x46d0,
1308 | Size: 0x30,
1309 | Link: 0,
1310 | Info: 0,
1311 | AddrAlign: 16,
1312 | EntSize: 0x10,
1313 | }, {
1314 | Name: 160,
1315 | Type: uint32(SHT_PROGBITS),
1316 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
1317 | Addr: 0x4700,
1318 | Off: 0x4700,
1319 | Size: 0x6a0,
1320 | Link: 0x0,
1321 | Info: 0,
1322 | AddrAlign: 16,
1323 | EntSize: 0x10,
1324 | }, {
1325 | Name: 169,
1326 | Type: uint32(SHT_PROGBITS),
1327 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
1328 | Addr: 0x4da0,
1329 | Off: 0x4da0,
1330 | Size: 0x127d2,
1331 | Link: 0x0,
1332 | Info: 0,
1333 | AddrAlign: 16,
1334 | EntSize: 0x0,
1335 | }, {
1336 | Name: 175,
1337 | Type: uint32(SHT_PROGBITS),
1338 | Flags: uint64(SHF_ALLOC + SHF_EXECINSTR),
1339 | Addr: 0x17574,
1340 | Off: 0x17574,
1341 | Size: 0xd,
1342 | Link: 0,
1343 | Info: 0,
1344 | AddrAlign: 4,
1345 | EntSize: 0x0,
1346 | }, {
1347 | Name: 181,
1348 | Type: uint32(SHT_PROGBITS),
1349 | Flags: uint64(SHF_ALLOC),
1350 | Addr: 0x18000,
1351 | Off: 0x18000,
1352 | Size: 0x5249,
1353 | Link: 0,
1354 | Info: 0,
1355 | AddrAlign: 32,
1356 | EntSize: 0x0,
1357 | }, {
1358 | Name: 189,
1359 | Type: uint32(SHT_PROGBITS),
1360 | Flags: uint64(SHF_ALLOC),
1361 | Addr: 0x1d24c,
1362 | Off: 0x1d24c,
1363 | Size: 0x92c,
1364 | Link: 0x0,
1365 | Info: 0,
1366 | AddrAlign: 4,
1367 | EntSize: 0x0,
1368 | }, {
1369 | Name: 203,
1370 | Type: uint32(SHT_PROGBITS),
1371 | Flags: uint64(SHF_ALLOC),
1372 | Addr: 0x1db78,
1373 | Off: 0x1db78,
1374 | Size: 0x2fd8,
1375 | Link: 0x0,
1376 | Info: 0,
1377 | AddrAlign: 8,
1378 | EntSize: 0x0,
1379 | }, {
1380 | Name: 213,
1381 | Type: uint32(SHT_INIT_ARRAY),
1382 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
1383 | Addr: 0x22010,
1384 | Off: 0x21010,
1385 | Size: 0x8,
1386 | Link: 0x0,
1387 | Info: 0,
1388 | AddrAlign: 8,
1389 | EntSize: 0x8,
1390 | }, {
1391 | Name: 225,
1392 | Type: uint32(SHT_FINI_ARRAY),
1393 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
1394 | Addr: 0x22018,
1395 | Off: 0x21018,
1396 | Size: 0x8,
1397 | Link: 0x0,
1398 | Info: 0,
1399 | AddrAlign: 8,
1400 | EntSize: 0x8,
1401 | }, {
1402 | Name: 237,
1403 | Type: uint32(SHT_PROGBITS),
1404 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
1405 | Addr: 0x22020,
1406 | Off: 0x21020,
1407 | Size: 0xa38,
1408 | Link: 0x0,
1409 | Info: 0,
1410 | AddrAlign: 32,
1411 | EntSize: 0x0,
1412 | }, {
1413 | Name: 250,
1414 | Type: uint32(SHT_DYNAMIC),
1415 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
1416 | Addr: 0x22a58,
1417 | Off: 0x21a58,
1418 | Size: 0x200,
1419 | Link: 0x7,
1420 | Info: 0,
1421 | AddrAlign: 8,
1422 | EntSize: 0x10,
1423 | }, {
1424 | Name: 155,
1425 | Type: uint32(SHT_PROGBITS),
1426 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
1427 | Addr: 0x22c58,
1428 | Off: 0x21c58,
1429 | Size: 0x3a0,
1430 | Link: 0,
1431 | Info: 0,
1432 | AddrAlign: 8,
1433 | EntSize: 0x8,
1434 | }, {
1435 | Name: 259,
1436 | Type: uint32(SHT_PROGBITS),
1437 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
1438 | Addr: 0x23000,
1439 | Off: 0x22000,
1440 | Size: 0x268,
1441 | Link: 0,
1442 | Info: 0,
1443 | AddrAlign: 32,
1444 | EntSize: 0x0,
1445 | }, {
1446 | Name: 265,
1447 | Type: uint32(SHT_NOBITS),
1448 | Flags: uint64(SHF_WRITE + SHF_ALLOC),
1449 | Addr: 0x23280,
1450 | Off: 0x22268,
1451 | Size: 0x12d8,
1452 | Link: 0,
1453 | Info: 0,
1454 | AddrAlign: 32,
1455 | EntSize: 0x0,
1456 | }, {
1457 | Name: 270,
1458 | Type: uint32(SHT_PROGBITS),
1459 | Flags: uint64(SHF_NONE),
1460 | Addr: 0x0,
1461 | Off: 0x22268,
1462 | Size: 0x34,
1463 | Link: 0,
1464 | Info: 0,
1465 | AddrAlign: 4,
1466 | EntSize: 0x0,
1467 | }, {
1468 | Name: 1,
1469 | Type: uint32(SHT_STRTAB),
1470 | Flags: uint64(SHF_NONE),
1471 | Addr: 0x0,
1472 | Off: 0x2229c,
1473 | Size: 0x11d,
1474 | Link: 0,
1475 | Info: 0,
1476 | AddrAlign: 1,
1477 | EntSize: 0x0,
1478 | },
1479 | },
1480 | expectedSectionNames: []string{
1481 | "",
1482 | ".interp",
1483 | ".note.gnu.property",
1484 | ".note.gnu.build-id",
1485 | ".note.ABI-tag",
1486 | ".gnu.hash",
1487 | ".dynsym",
1488 | ".dynstr",
1489 | ".gnu.version",
1490 | ".gnu.version_r",
1491 | ".rela.dyn",
1492 | ".rela.plt",
1493 | ".init",
1494 | ".plt",
1495 | ".plt.got",
1496 | ".plt.sec",
1497 | ".text",
1498 | ".fini",
1499 | ".rodata",
1500 | ".eh_frame_hdr",
1501 | ".eh_frame",
1502 | ".init_array",
1503 | ".fini_array",
1504 | ".data.rel.ro",
1505 | ".dynamic",
1506 | ".got",
1507 | ".data",
1508 | ".bss",
1509 | ".gnu_debuglink",
1510 | ".shstrtab",
1511 | },
1512 | expectedSymbolsLength: 138,
1513 | },
1514 | }
1515 |
1516 | for _, tt := range testCases {
1517 | p, err := New(tt.path, nil)
1518 | if err != nil {
1519 | t.Fatal("failed to create new parser with error :", err)
1520 | }
1521 | err = p.ParseIdent()
1522 | if err != nil {
1523 | t.Fatal("failed to parse ident with error :", err)
1524 | }
1525 | assert.EqualValues(t, tt.expectedIdent, p.F.Ident, "expected ident equal")
1526 | err = p.ParseELFHeader(ELFCLASS64)
1527 | if err != nil {
1528 | t.Fatal("failed to parse ELF header with error :", err)
1529 | }
1530 | assert.EqualValues(t, tt.expectedHeader, p.F.Header64, "expected header equal")
1531 | err = p.ParseELFSectionHeaders(ELFCLASS64)
1532 | if err != nil {
1533 | t.Fatal("failed to parse ELF section headers with error :", err)
1534 | }
1535 | assert.EqualValues(t, tt.expectedSectionHeader64, p.F.SectionHeaders64, "expected section headers equal")
1536 | err = p.parseELFSections64()
1537 | if err != nil {
1538 | t.Fatal("failed to parse ELF section headers with error :", err)
1539 | }
1540 | assert.EqualValues(t, tt.expectedSectionNames, p.F.SectionNames())
1541 | // /bin/ls only has a symbol table for .dynsym section
1542 | // readelf -s /bin/ls will show that the table has 139 entries
1543 | // but the spec enforces a specific length and doesn't include the
1544 | // first notype entry.
1545 | err = p.ParseELFSymbols(ELFCLASS64, SHT_DYNSYM)
1546 | if err != nil {
1547 | t.Fatal("failed to parse ELF section headers with error :", err)
1548 | }
1549 | assert.EqualValues(t, tt.expectedSymbolsLength, len(p.F.NamedSymbols))
1550 | }
1551 | })
1552 | }
1553 |
1554 | func TestFlags(t *testing.T) {
1555 | t.Run("TestStringFlags", func(t *testing.T) {
1556 | for _, v := range classStrings {
1557 | assert.Equal(t, Class(v.flag).String(), v.name)
1558 | }
1559 | for _, v := range dataStrings {
1560 | assert.Equal(t, Data(v.flag).String(), v.name)
1561 | }
1562 | for _, v := range versionStrings {
1563 | assert.Equal(t, Version(v.flag).String(), v.name)
1564 | }
1565 | for _, v := range osABIStrings {
1566 | assert.Equal(t, OSABI(v.flag).String(), v.name)
1567 | }
1568 | for _, v := range typeStrings {
1569 | assert.Equal(t, Type(v.flag).String(), v.name)
1570 | }
1571 | for _, v := range machineStrings {
1572 | assert.Equal(t, Machine(v.flag).String(), v.name)
1573 | }
1574 | for _, v := range sectionIndexStrings {
1575 | assert.Equal(t, SectionIndex(v.flag).String(), v.name)
1576 | }
1577 | for _, v := range sectionFlagStrings {
1578 | assert.Equal(t, SectionFlag(v.flag).String(), v.name)
1579 | }
1580 | for _, v := range sectionTypeStrings {
1581 | assert.Equal(t, SectionType(v.flag).String(), v.name)
1582 | }
1583 | for _, v := range compressionStrings {
1584 | assert.Equal(t, CompressionType(v.flag).String(), v.name)
1585 | }
1586 | for _, v := range programTypeStrings {
1587 | assert.Equal(t, ProgType(v.flag).String(), v.name)
1588 | }
1589 | for _, v := range programFlagStrings {
1590 | assert.Equal(t, ProgFlag(v.flag).String(), v.name)
1591 | }
1592 | for _, v := range dtStrings {
1593 | assert.Equal(t, DynTag(v.flag).String(), v.name)
1594 | }
1595 | for _, v := range dflagStrings {
1596 | assert.Equal(t, DynFlag(v.flag).String(), v.name)
1597 | }
1598 | for _, v := range ntypeStrings {
1599 | assert.Equal(t, NType(v.flag).String(), v.name)
1600 | }
1601 | for _, v := range stbStrings {
1602 | assert.Equal(t, SymBind(v.flag).String(), v.name)
1603 | }
1604 | for _, v := range sttStrings {
1605 | assert.Equal(t, SymType(v.flag).String(), v.name)
1606 | }
1607 | for _, v := range stvStrings {
1608 | assert.Equal(t, SymVis(v.flag).String(), v.name)
1609 | }
1610 | })
1611 | t.Run("TestStringArch", func(t *testing.T) {
1612 | for _, v := range rx86_64Strings {
1613 | assert.Equal(t, R_X86_64(v.flag).String(), v.name)
1614 | assert.Equal(t, R_X86_64(v.flag).GoString(), "elf."+v.name)
1615 |
1616 | }
1617 | for _, v := range raarch64Strings {
1618 | assert.Equal(t, R_AARCH64(v.flag).String(), v.name)
1619 | assert.Equal(t, R_AARCH64(v.flag).GoString(), "elf."+v.name)
1620 |
1621 | }
1622 | for _, v := range ralphaStrings {
1623 | assert.Equal(t, R_ALPHA(v.flag).String(), v.name)
1624 | assert.Equal(t, R_ALPHA(v.flag).GoString(), "elf."+v.name)
1625 |
1626 | }
1627 | for _, v := range rarmStrings {
1628 | assert.Equal(t, R_ARM(v.flag).String(), v.name)
1629 | assert.Equal(t, R_ARM(v.flag).GoString(), "elf."+v.name)
1630 |
1631 | }
1632 | for _, v := range r386Strings {
1633 | assert.Equal(t, R_386(v.flag).String(), v.name)
1634 | assert.Equal(t, R_386(v.flag).GoString(), "elf."+v.name)
1635 |
1636 | }
1637 | for _, v := range rmipsStrings {
1638 | assert.Equal(t, R_MIPS(v.flag).String(), v.name)
1639 | assert.Equal(t, R_MIPS(v.flag).GoString(), "elf."+v.name)
1640 |
1641 | }
1642 | for _, v := range rppcStrings {
1643 | assert.Equal(t, R_PPC(v.flag).String(), v.name)
1644 | assert.Equal(t, R_PPC(v.flag).GoString(), "elf."+v.name)
1645 |
1646 | }
1647 | for _, v := range rppc64Strings {
1648 | assert.Equal(t, R_PPC64(v.flag).String(), v.name)
1649 | assert.Equal(t, R_PPC64(v.flag).GoString(), "elf."+v.name)
1650 | }
1651 | for _, v := range rriscvStrings {
1652 | assert.Equal(t, R_RISCV(v.flag).String(), v.name)
1653 | assert.Equal(t, R_RISCV(v.flag).GoString(), "elf."+v.name)
1654 |
1655 | }
1656 | for _, v := range rsparcStrings {
1657 | assert.Equal(t, R_SPARC(v.flag).String(), v.name)
1658 | assert.Equal(t, R_SPARC(v.flag).GoString(), "elf."+v.name)
1659 |
1660 | }
1661 |
1662 | })
1663 | }
1664 |
--------------------------------------------------------------------------------
/reader.go:
--------------------------------------------------------------------------------
1 | package elf
2 |
3 | // file reader.go was taken from debug/elf as it implements custom utilities to read compressed section data.
4 | import (
5 | "fmt"
6 | "io"
7 | "os"
8 | )
9 |
10 | /*
11 | * ELF reader
12 | */
13 | type FormatError struct {
14 | off int64
15 | msg string
16 | val interface{}
17 | }
18 |
19 | func (e *FormatError) Error() string {
20 | msg := e.msg
21 | if e.val != nil {
22 | msg += fmt.Sprintf(" '%v' ", e.val)
23 | }
24 | msg += fmt.Sprintf("in record at byte %#x", e.off)
25 | return msg
26 | }
27 |
28 | // seekStart, seekCurrent, seekEnd are copies of
29 | // io.SeekStart, io.SeekCurrent, and io.SeekEnd.
30 | // We can't use the ones from package io because
31 | // we want this code to build with Go 1.4 during
32 | // cmd/dist bootstrap.
33 | const (
34 | seekStart int = 0
35 | seekCurrent int = 1
36 | seekEnd int = 2
37 | )
38 |
39 | // errorReader returns error from all operations.
40 | type errorReader struct {
41 | error
42 | }
43 |
44 | func (r errorReader) Read(p []byte) (n int, err error) {
45 | return 0, r.error
46 | }
47 | func (r errorReader) ReadAt(p []byte, off int64) (n int, err error) {
48 | return 0, r.error
49 | }
50 | func (r errorReader) Seek(offset int64, whence int) (int64, error) {
51 | return 0, r.error
52 | }
53 | func (r errorReader) Close() error {
54 | return r.error
55 | }
56 |
57 | // readSeekerFromReader converts an io.Reader into an io.ReadSeeker.
58 | // In general Seek may not be efficient, but it is optimized for
59 | // common cases such as seeking to the end to find the length of the
60 | // data.
61 | type readSeekerFromReader struct {
62 | reset func() (io.Reader, error)
63 | r io.Reader
64 | size int64
65 | offset int64
66 | }
67 |
68 | func (r *readSeekerFromReader) start() {
69 | x, err := r.reset()
70 | if err != nil {
71 | r.r = errorReader{err}
72 | } else {
73 | r.r = x
74 | }
75 | r.offset = 0
76 | }
77 | func (r *readSeekerFromReader) Read(p []byte) (n int, err error) {
78 | if r.r == nil {
79 | r.start()
80 | }
81 | n, err = r.r.Read(p)
82 | r.offset += int64(n)
83 | return n, err
84 | }
85 | func (r *readSeekerFromReader) Seek(offset int64, whence int) (int64, error) {
86 | var newOffset int64
87 | switch whence {
88 | case seekStart:
89 | newOffset = offset
90 | case seekCurrent:
91 | newOffset = r.offset + offset
92 | case seekEnd:
93 | newOffset = r.size + offset
94 | default:
95 | return 0, os.ErrInvalid
96 | }
97 | switch {
98 | case newOffset == r.offset:
99 | return newOffset, nil
100 | case newOffset < 0, newOffset > r.size:
101 | return 0, os.ErrInvalid
102 | case newOffset == 0:
103 | r.r = nil
104 | case newOffset == r.size:
105 | r.r = errorReader{io.EOF}
106 | default:
107 | if newOffset < r.offset {
108 | // Restart at the beginning.
109 | r.start()
110 | }
111 | // Read until we reach offset.
112 | var buf [512]byte
113 | for r.offset < newOffset {
114 | b := buf[:]
115 | if newOffset-r.offset < int64(len(buf)) {
116 | b = buf[:newOffset-r.offset]
117 | }
118 | if _, err := r.Read(b); err != nil {
119 | return 0, err
120 | }
121 | }
122 | }
123 | r.offset = newOffset
124 | return r.offset, nil
125 | }
126 |
--------------------------------------------------------------------------------
/reloc.go:
--------------------------------------------------------------------------------
1 | package elf
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "errors"
7 | )
8 |
9 | // Relocation entries.
10 |
11 | // ELF32 Relocations that don't need an addend field.
12 | type Rel32 struct {
13 | Off uint32 // Location to be relocated.
14 | Info uint32 // Relocation type and symbol index.
15 | }
16 |
17 | // ELF32 Relocations that need an addend field.
18 | type Rela32 struct {
19 | Off uint32 // Location to be relocated.
20 | Info uint32 // Relocation type and symbol index.
21 | Addend int32 // Addend.
22 | }
23 |
24 | func R_SYM32(info uint32) uint32 { return info >> 8 }
25 | func R_TYPE32(info uint32) uint32 { return info & 0xff }
26 | func R_INFO32(sym, typ uint32) uint32 { return sym<<8 | typ }
27 |
28 | // ELF64 relocations that don't need an addend field.
29 | type Rel64 struct {
30 | Off uint64 // Location to be relocated.
31 | Info uint64 // Relocation type and symbol index.
32 | }
33 |
34 | // ELF64 relocations that need an addend field.
35 | type Rela64 struct {
36 | Off uint64 // Location to be relocated.
37 | Info uint64 // Relocation type and symbol index.
38 | Addend int64 // Addend.
39 | }
40 |
41 | func R_SYM64(info uint64) uint32 { return uint32(info >> 32) }
42 | func R_TYPE64(info uint64) uint32 { return uint32(info) }
43 | func R_INFO(sym, typ uint32) uint64 { return uint64(sym)<<32 | uint64(typ) }
44 |
45 | // ApplyRelocations will apply relocations depending on the target binary.
46 | // This step essentially processes symbolic references to their definitions.
47 | func (p *Parser) ApplyRelocations(dst []byte, rels []byte) error {
48 | switch {
49 | case p.F.Class() == ELFCLASS64 && p.F.Machine == EM_X86_64:
50 | return p.applyRelocationsAMD64(dst, rels)
51 | default:
52 | return errors.New("not implemented")
53 | }
54 | }
55 |
56 | // applyRelocationsAMD64 applies relocatons to dst where rels is a relocations section
57 | // for AMD64 (64-bit binaries & x86-64 machine types).
58 | func (p *Parser) applyRelocationsAMD64(dst []byte, rels []byte) error {
59 | // 24 is the size of Rela64.
60 | if len(rels)%24 != 0 {
61 | return errors.New("length of relocation section is not a multiple of 24")
62 | }
63 | symbols, _, err := p.getSymbols64(SHT_SYMTAB)
64 | if err != nil {
65 | return err
66 | }
67 | b := bytes.NewReader(rels)
68 | var rela Rela64
69 | for b.Len() > 0 {
70 | binary.Read(b, p.F.ByteOrder(), &rela)
71 | symNo := rela.Info >> 32
72 | t := R_X86_64(rela.Info & 0xffff)
73 | if symNo == 0 || symNo > uint64(len(symbols)) {
74 | continue
75 | }
76 | sym := &symbols[symNo-1]
77 |
78 | // There are relocations, so this must be a normal
79 | // object file. The code below handles only basic relocations
80 | // of the form S + A (symbol plus addend).
81 | switch t {
82 | case R_X86_64_64:
83 | if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
84 | continue
85 | }
86 | val64 := sym.Value + uint64(rela.Addend)
87 | p.F.ByteOrder().PutUint64(dst[rela.Off:rela.Off+8], val64)
88 | case R_X86_64_32:
89 | if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
90 | continue
91 | }
92 | val32 := uint32(sym.Value) + uint32(rela.Addend)
93 | p.F.ByteOrder().PutUint32(dst[rela.Off:rela.Off+4], val32)
94 | }
95 | }
96 | return nil
97 | }
98 |
--------------------------------------------------------------------------------
/sections.go:
--------------------------------------------------------------------------------
1 | package elf
2 |
3 | import (
4 | "compress/zlib"
5 | "io"
6 | )
7 |
8 | // Data reads and returns the contents of the ELF section.
9 | // Even if the section is stored compressed in the ELF file,
10 | // Data returns uncompressed data.
11 | func (s *ELF32Section) Data() ([]byte, error) {
12 |
13 | var rs io.ReadSeeker
14 | data := make([]byte, s.Size)
15 |
16 | if s.Flags&uint32(SHF_COMPRESSED) == 0 {
17 | rs = io.NewSectionReader(s.sr, 0, 1<<63-1)
18 | } else if s.compressionType == COMPRESS_ZLIB {
19 | rs = &readSeekerFromReader{
20 | reset: func() (io.Reader, error) {
21 | fr := io.NewSectionReader(s.sr, s.compressionOffset, int64(s.Size)-s.compressionOffset)
22 | return zlib.NewReader(fr)
23 | },
24 | size: int64(s.Size),
25 | }
26 | }
27 | n, err := io.ReadFull(rs, data)
28 | return data[0:n], err
29 | }
30 |
31 | // Data reads and returns the contents of the ELF section.
32 | // Even if the section is stored compressed in the ELF file,
33 | // Data returns uncompressed data.
34 | func (s *ELF64Section) Data() ([]byte, error) {
35 |
36 | var rs io.ReadSeeker
37 | data := make([]byte, s.Size)
38 |
39 | if s.Flags&uint64(SHF_COMPRESSED) == 0 {
40 | rs = io.NewSectionReader(s.sr, 0, 1<<63-1)
41 | } else if s.compressionType == COMPRESS_ZLIB {
42 | rs = &readSeekerFromReader{
43 | reset: func() (io.Reader, error) {
44 | fr := io.NewSectionReader(s.sr, s.compressionOffset, int64(s.Size)-s.compressionOffset)
45 | return zlib.NewReader(fr)
46 | },
47 | size: int64(s.Size),
48 | }
49 | }
50 | n, err := io.ReadFull(rs, data)
51 | return data[0:n], err
52 | }
53 |
--------------------------------------------------------------------------------
/stringer.go:
--------------------------------------------------------------------------------
1 | // Package elf : stringer.go implements various string related utilites
2 | // and stringer interface implementation for all custom types.
3 | package elf
4 |
5 | import "strconv"
6 |
7 | // flagName pairs integer flags and a corresponding string.
8 | type flagName struct {
9 | flag uint32
10 | name string
11 | }
12 |
13 | // stringify matches various elf flags against their naming maps.
14 | func stringify(flag uint32, names []flagName, goSyntax bool) string {
15 |
16 | for _, n := range names {
17 | if n.flag == flag {
18 | if goSyntax {
19 | return "elf." + n.name
20 | }
21 | return n.name
22 | }
23 | }
24 |
25 | for j := len(names) - 1; j >= 0; j-- {
26 | n := names[j]
27 | if n.flag < flag {
28 | name := n.name
29 | if goSyntax {
30 | name = "elf." + name
31 | }
32 | return name + "+" + strconv.FormatUint(uint64(flag-n.flag), 10)
33 | }
34 | }
35 |
36 | return strconv.FormatUint(uint64(flag), 10)
37 | }
38 |
39 | // matchFlagName matches a given integer flag against it's corresponding flagname.
40 | func matchFlagName(flag uint32, names []flagName, goSyntax bool) string {
41 | s := ""
42 | for _, n := range names {
43 | if n.flag&flag == n.flag {
44 | if len(s) > 0 {
45 | s += "+"
46 | }
47 | if goSyntax {
48 | s += "elf."
49 | }
50 | s += n.name
51 | flag -= n.flag
52 | }
53 | }
54 | if len(s) == 0 {
55 | return "0x" + strconv.FormatUint(uint64(flag), 16)
56 | }
57 | if flag != 0 {
58 | s += "+0x" + strconv.FormatUint(uint64(flag), 16)
59 | }
60 | return s
61 | }
62 |
--------------------------------------------------------------------------------
/symbol.go:
--------------------------------------------------------------------------------
1 | package elf
2 |
3 | const Sym64Size = 24
4 |
5 | // ELF64SymbolTableEntry represents information needed to locate and relocate
6 | // a program's symbolic definitions, it's an array of SymbolTableEntry
7 | type ELF64SymbolTableEntry struct {
8 | Name uint32 // String table index of name.
9 | Info uint8 // Type and binding information.
10 | Other uint8 // Reserved (not used).
11 | Shndx uint16 // Section index of symbol
12 | Value uint64 // Symbol value.
13 | Size uint64 // Size of associated object.
14 | }
15 |
16 | // ELF32SymbolTableEntry represents information needed to locate and relocate
17 | // a program's symbolic definitions, it's an array of SymbolTableEntry.
18 | type ELF32SymbolTableEntry struct {
19 | Name uint32
20 | Value uint32
21 | Size uint32
22 | Info uint8
23 | Other uint8
24 | Shndx uint16
25 | }
26 |
27 | const Sym32Size = 16
28 |
29 | func ST_BIND(info uint8) SymBind { return SymBind(info >> 4) }
30 | func ST_TYPE(info uint8) SymType { return SymType(info & 0xF) }
31 | func ST_INFO(bind SymBind, typ SymType) uint8 {
32 | return uint8(bind)<<4 | uint8(typ)&0xf
33 | }
34 | func ST_VISIBILITY(other uint8) SymVis { return SymVis(other & 3) }
35 |
--------------------------------------------------------------------------------
/test/ls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saferwall/elf/33b33db927a3e67b19495c9c3d13879433f65fe1/test/ls
--------------------------------------------------------------------------------