├── .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 | Saferwall logo 2 | 3 | # ELF File Format Parser 4 | 5 | [![GoDoc](http://godoc.org/github.com/saferwall/elf?status.svg)](https://pkg.go.dev/github.com/saferwall/elf) ![Go Version](https://img.shields.io/badge/go%20version-%3E=1.17-61CFDD.svg) [![Report Card](https://goreportcard.com/badge/github.com/saferwall/elf)](https://goreportcard.com/report/github.com/saferwall/elf) [![codecov](https://codecov.io/gh/saferwall/elf/branch/main/graph/badge.svg?token=ND685DTHZT)](https://codecov.io/gh/saferwall/elf) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/saferwall/elf/ci.yaml?branch=main) 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 --------------------------------------------------------------------------------