mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 14:12:06 +00:00
Merge branch 'akpm' (patches from Andrew)
Merge yet more updates from Andrew Morton: - a pile of minor fs fixes and cleanups - kexec updates - random misc fixes in various places: vmcore, rbtree, eventfd, ipc, seccomp. - a series of python-based kgdb helper scripts * emailed patches from Andrew Morton <akpm@linux-foundation.org>: (58 commits) seccomp: cap SECCOMP_RET_ERRNO data to MAX_ERRNO samples/seccomp: improve label helper ipc,sem: use current->state helpers scripts/gdb: disable pagination while printing from breakpoint handler scripts/gdb: define maintainer scripts/gdb: convert CpuList to generator function scripts/gdb: convert ModuleList to generator function scripts/gdb: use a generator instead of iterator for task list scripts/gdb: ignore byte-compiled python files scripts/gdb: port to python3 / gdb7.7 scripts/gdb: add basic documentation scripts/gdb: add lx-lsmod command scripts/gdb: add class to iterate over CPU masks scripts/gdb: add lx_current convenience function scripts/gdb: add internal helper and convenience function for per-cpu lookup scripts/gdb: add get_gdbserver_type helper scripts/gdb: add internal helper and convenience function to retrieve thread_info scripts/gdb: add is_target_arch helper scripts/gdb: add helper and convenience function to look up tasks scripts/gdb: add task iteration class ...
This commit is contained in:
commit
e2b74f232e
1
.gitignore
vendored
1
.gitignore
vendored
@ -43,6 +43,7 @@ Module.symvers
|
||||
/TAGS
|
||||
/linux
|
||||
/vmlinux
|
||||
/vmlinux-gdb.py
|
||||
/vmlinuz
|
||||
/System.map
|
||||
/Module.markers
|
||||
|
160
Documentation/gdb-kernel-debugging.txt
Normal file
160
Documentation/gdb-kernel-debugging.txt
Normal file
@ -0,0 +1,160 @@
|
||||
Debugging kernel and modules via gdb
|
||||
====================================
|
||||
|
||||
The kernel debugger kgdb, hypervisors like QEMU or JTAG-based hardware
|
||||
interfaces allow to debug the Linux kernel and its modules during runtime
|
||||
using gdb. Gdb comes with a powerful scripting interface for python. The
|
||||
kernel provides a collection of helper scripts that can simplify typical
|
||||
kernel debugging steps. This is a short tutorial about how to enable and use
|
||||
them. It focuses on QEMU/KVM virtual machines as target, but the examples can
|
||||
be transferred to the other gdb stubs as well.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
o gdb 7.2+ (recommended: 7.4+) with python support enabled (typically true
|
||||
for distributions)
|
||||
|
||||
|
||||
Setup
|
||||
-----
|
||||
|
||||
o Create a virtual Linux machine for QEMU/KVM (see www.linux-kvm.org and
|
||||
www.qemu.org for more details). For cross-development,
|
||||
http://landley.net/aboriginal/bin keeps a pool of machine images and
|
||||
toolchains that can be helpful to start from.
|
||||
|
||||
o Build the kernel with CONFIG_GDB_SCRIPTS enabled, but leave
|
||||
CONFIG_DEBUG_INFO_REDUCED off. If your architecture supports
|
||||
CONFIG_FRAME_POINTER, keep it enabled.
|
||||
|
||||
o Install that kernel on the guest.
|
||||
|
||||
Alternatively, QEMU allows to boot the kernel directly using -kernel,
|
||||
-append, -initrd command line switches. This is generally only useful if
|
||||
you do not depend on modules. See QEMU documentation for more details on
|
||||
this mode.
|
||||
|
||||
o Enable the gdb stub of QEMU/KVM, either
|
||||
- at VM startup time by appending "-s" to the QEMU command line
|
||||
or
|
||||
- during runtime by issuing "gdbserver" from the QEMU monitor
|
||||
console
|
||||
|
||||
o cd /path/to/linux-build
|
||||
|
||||
o Start gdb: gdb vmlinux
|
||||
|
||||
Note: Some distros may restrict auto-loading of gdb scripts to known safe
|
||||
directories. In case gdb reports to refuse loading vmlinux-gdb.py, add
|
||||
|
||||
add-auto-load-safe-path /path/to/linux-build
|
||||
|
||||
to ~/.gdbinit. See gdb help for more details.
|
||||
|
||||
o Attach to the booted guest:
|
||||
(gdb) target remote :1234
|
||||
|
||||
|
||||
Examples of using the Linux-provided gdb helpers
|
||||
------------------------------------------------
|
||||
|
||||
o Load module (and main kernel) symbols:
|
||||
(gdb) lx-symbols
|
||||
loading vmlinux
|
||||
scanning for modules in /home/user/linux/build
|
||||
loading @0xffffffffa0020000: /home/user/linux/build/net/netfilter/xt_tcpudp.ko
|
||||
loading @0xffffffffa0016000: /home/user/linux/build/net/netfilter/xt_pkttype.ko
|
||||
loading @0xffffffffa0002000: /home/user/linux/build/net/netfilter/xt_limit.ko
|
||||
loading @0xffffffffa00ca000: /home/user/linux/build/net/packet/af_packet.ko
|
||||
loading @0xffffffffa003c000: /home/user/linux/build/fs/fuse/fuse.ko
|
||||
...
|
||||
loading @0xffffffffa0000000: /home/user/linux/build/drivers/ata/ata_generic.ko
|
||||
|
||||
o Set a breakpoint on some not yet loaded module function, e.g.:
|
||||
(gdb) b btrfs_init_sysfs
|
||||
Function "btrfs_init_sysfs" not defined.
|
||||
Make breakpoint pending on future shared library load? (y or [n]) y
|
||||
Breakpoint 1 (btrfs_init_sysfs) pending.
|
||||
|
||||
o Continue the target
|
||||
(gdb) c
|
||||
|
||||
o Load the module on the target and watch the symbols being loaded as well as
|
||||
the breakpoint hit:
|
||||
loading @0xffffffffa0034000: /home/user/linux/build/lib/libcrc32c.ko
|
||||
loading @0xffffffffa0050000: /home/user/linux/build/lib/lzo/lzo_compress.ko
|
||||
loading @0xffffffffa006e000: /home/user/linux/build/lib/zlib_deflate/zlib_deflate.ko
|
||||
loading @0xffffffffa01b1000: /home/user/linux/build/fs/btrfs/btrfs.ko
|
||||
|
||||
Breakpoint 1, btrfs_init_sysfs () at /home/user/linux/fs/btrfs/sysfs.c:36
|
||||
36 btrfs_kset = kset_create_and_add("btrfs", NULL, fs_kobj);
|
||||
|
||||
o Dump the log buffer of the target kernel:
|
||||
(gdb) lx-dmesg
|
||||
[ 0.000000] Initializing cgroup subsys cpuset
|
||||
[ 0.000000] Initializing cgroup subsys cpu
|
||||
[ 0.000000] Linux version 3.8.0-rc4-dbg+ (...
|
||||
[ 0.000000] Command line: root=/dev/sda2 resume=/dev/sda1 vga=0x314
|
||||
[ 0.000000] e820: BIOS-provided physical RAM map:
|
||||
[ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
|
||||
[ 0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved
|
||||
....
|
||||
|
||||
o Examine fields of the current task struct:
|
||||
(gdb) p $lx_current().pid
|
||||
$1 = 4998
|
||||
(gdb) p $lx_current().comm
|
||||
$2 = "modprobe\000\000\000\000\000\000\000"
|
||||
|
||||
o Make use of the per-cpu function for the current or a specified CPU:
|
||||
(gdb) p $lx_per_cpu("runqueues").nr_running
|
||||
$3 = 1
|
||||
(gdb) p $lx_per_cpu("runqueues", 2).nr_running
|
||||
$4 = 0
|
||||
|
||||
o Dig into hrtimers using the container_of helper:
|
||||
(gdb) set $next = $lx_per_cpu("hrtimer_bases").clock_base[0].active.next
|
||||
(gdb) p *$container_of($next, "struct hrtimer", "node")
|
||||
$5 = {
|
||||
node = {
|
||||
node = {
|
||||
__rb_parent_color = 18446612133355256072,
|
||||
rb_right = 0x0 <irq_stack_union>,
|
||||
rb_left = 0x0 <irq_stack_union>
|
||||
},
|
||||
expires = {
|
||||
tv64 = 1835268000000
|
||||
}
|
||||
},
|
||||
_softexpires = {
|
||||
tv64 = 1835268000000
|
||||
},
|
||||
function = 0xffffffff81078232 <tick_sched_timer>,
|
||||
base = 0xffff88003fd0d6f0,
|
||||
state = 1,
|
||||
start_pid = 0,
|
||||
start_site = 0xffffffff81055c1f <hrtimer_start_range_ns+20>,
|
||||
start_comm = "swapper/2\000\000\000\000\000\000"
|
||||
}
|
||||
|
||||
|
||||
List of commands and functions
|
||||
------------------------------
|
||||
|
||||
The number of commands and convenience functions may evolve over the time,
|
||||
this is just a snapshot of the initial version:
|
||||
|
||||
(gdb) apropos lx
|
||||
function lx_current -- Return current task
|
||||
function lx_module -- Find module by name and return the module variable
|
||||
function lx_per_cpu -- Return per-cpu variable
|
||||
function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable
|
||||
function lx_thread_info -- Calculate Linux thread_info from task variable
|
||||
lx-dmesg -- Print Linux kernel log buffer
|
||||
lx-lsmod -- List currently loaded modules
|
||||
lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules
|
||||
|
||||
Detailed help can be obtained via "help <command-name>" for commands and "help
|
||||
function <function-name>" for convenience functions.
|
@ -4232,6 +4232,11 @@ W: http://www.icp-vortex.com/
|
||||
S: Supported
|
||||
F: drivers/scsi/gdt*
|
||||
|
||||
GDB KERNEL DEBUGGING HELPER SCRIPTS
|
||||
M: Jan Kiszka <jan.kiszka@siemens.com>
|
||||
S: Supported
|
||||
F: scripts/gdb/
|
||||
|
||||
GEMTEK FM RADIO RECEIVER DRIVER
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
5
Makefile
5
Makefile
@ -926,6 +926,9 @@ ifdef CONFIG_SAMPLES
|
||||
endif
|
||||
ifdef CONFIG_BUILD_DOCSRC
|
||||
$(Q)$(MAKE) $(build)=Documentation
|
||||
endif
|
||||
ifdef CONFIG_GDB_SCRIPTS
|
||||
$(Q)ln -fsn `cd $(srctree) && /bin/pwd`/scripts/gdb/vmlinux-gdb.py
|
||||
endif
|
||||
+$(call if_changed,link-vmlinux)
|
||||
|
||||
@ -1181,7 +1184,7 @@ MRPROPER_FILES += .config .config.old .version .old_version $(version_h) \
|
||||
Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \
|
||||
signing_key.priv signing_key.x509 x509.genkey \
|
||||
extra_certificates signing_key.x509.keyid \
|
||||
signing_key.x509.signer
|
||||
signing_key.x509.signer vmlinux-gdb.py
|
||||
|
||||
# clean - Delete most, but leave enough to build external modules
|
||||
#
|
||||
|
@ -96,8 +96,6 @@ int default_machine_kexec_prepare(struct kimage *image)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define IND_FLAGS (IND_DESTINATION | IND_INDIRECTION | IND_DONE | IND_SOURCE)
|
||||
|
||||
static void copy_segments(unsigned long ind)
|
||||
{
|
||||
unsigned long entry;
|
||||
|
@ -30,6 +30,8 @@
|
||||
#define AFFS_AC_SIZE (AFFS_CACHE_SIZE/sizeof(struct affs_ext_key)/2)
|
||||
#define AFFS_AC_MASK (AFFS_AC_SIZE-1)
|
||||
|
||||
#define AFFSNAMEMAX 30U
|
||||
|
||||
struct affs_ext_key {
|
||||
u32 ext; /* idx of the extended block */
|
||||
u32 key; /* block number */
|
||||
|
@ -30,7 +30,7 @@ affs_insert_hash(struct inode *dir, struct buffer_head *bh)
|
||||
ino = bh->b_blocknr;
|
||||
offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]);
|
||||
|
||||
pr_debug("%s(dir=%u, ino=%d)\n", __func__, (u32)dir->i_ino, ino);
|
||||
pr_debug("%s(dir=%lu, ino=%d)\n", __func__, dir->i_ino, ino);
|
||||
|
||||
dir_bh = affs_bread(sb, dir->i_ino);
|
||||
if (!dir_bh)
|
||||
@ -80,8 +80,8 @@ affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)
|
||||
sb = dir->i_sb;
|
||||
rem_ino = rem_bh->b_blocknr;
|
||||
offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]);
|
||||
pr_debug("%s(dir=%d, ino=%d, hashval=%d)\n",
|
||||
__func__, (u32)dir->i_ino, rem_ino, offset);
|
||||
pr_debug("%s(dir=%lu, ino=%d, hashval=%d)\n", __func__, dir->i_ino,
|
||||
rem_ino, offset);
|
||||
|
||||
bh = affs_bread(sb, dir->i_ino);
|
||||
if (!bh)
|
||||
@ -483,11 +483,10 @@ affs_check_name(const unsigned char *name, int len, bool notruncate)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (len > 30) {
|
||||
if (len > AFFSNAMEMAX) {
|
||||
if (notruncate)
|
||||
return -ENAMETOOLONG;
|
||||
else
|
||||
len = 30;
|
||||
len = AFFSNAMEMAX;
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
if (name[i] < ' ' || name[i] == ':'
|
||||
@ -508,7 +507,7 @@ affs_check_name(const unsigned char *name, int len, bool notruncate)
|
||||
int
|
||||
affs_copy_name(unsigned char *bstr, struct dentry *dentry)
|
||||
{
|
||||
int len = min(dentry->d_name.len, 30u);
|
||||
u32 len = min(dentry->d_name.len, AFFSNAMEMAX);
|
||||
|
||||
*bstr++ = len;
|
||||
memcpy(bstr, dentry->d_name.name, len);
|
||||
|
@ -99,7 +99,6 @@ err_bh_read:
|
||||
|
||||
err_range:
|
||||
affs_error(sb, "affs_free_block","Block %u outside partition", block);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -54,8 +54,7 @@ affs_readdir(struct file *file, struct dir_context *ctx)
|
||||
u32 ino;
|
||||
int error = 0;
|
||||
|
||||
pr_debug("%s(ino=%lu,f_pos=%lx)\n",
|
||||
__func__, inode->i_ino, (unsigned long)ctx->pos);
|
||||
pr_debug("%s(ino=%lu,f_pos=%llx)\n", __func__, inode->i_ino, ctx->pos);
|
||||
|
||||
if (ctx->pos < 2) {
|
||||
file->private_data = (void *)0;
|
||||
@ -115,11 +114,11 @@ inside:
|
||||
break;
|
||||
}
|
||||
|
||||
namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)30);
|
||||
namelen = min(AFFS_TAIL(sb, fh_bh)->name[0],
|
||||
(u8)AFFSNAMEMAX);
|
||||
name = AFFS_TAIL(sb, fh_bh)->name + 1;
|
||||
pr_debug("readdir(): dir_emit(\"%.*s\", "
|
||||
"ino=%u), hash=%d, f_pos=%x\n",
|
||||
namelen, name, ino, hash_pos, (u32)ctx->pos);
|
||||
pr_debug("readdir(): dir_emit(\"%.*s\", ino=%u), hash=%d, f_pos=%llx\n",
|
||||
namelen, name, ino, hash_pos, ctx->pos);
|
||||
|
||||
if (!dir_emit(ctx, name, namelen, ino, DT_UNKNOWN))
|
||||
goto done;
|
||||
|
@ -180,8 +180,7 @@ affs_get_extblock_slow(struct inode *inode, u32 ext)
|
||||
ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
|
||||
if (ext < AFFS_I(inode)->i_extcnt)
|
||||
goto read_ext;
|
||||
if (ext > AFFS_I(inode)->i_extcnt)
|
||||
BUG();
|
||||
BUG_ON(ext > AFFS_I(inode)->i_extcnt);
|
||||
bh = affs_alloc_extblock(inode, bh, ext);
|
||||
if (IS_ERR(bh))
|
||||
return bh;
|
||||
@ -198,8 +197,7 @@ affs_get_extblock_slow(struct inode *inode, u32 ext)
|
||||
struct buffer_head *prev_bh;
|
||||
|
||||
/* allocate a new extended block */
|
||||
if (ext > AFFS_I(inode)->i_extcnt)
|
||||
BUG();
|
||||
BUG_ON(ext > AFFS_I(inode)->i_extcnt);
|
||||
|
||||
/* get previous extended block */
|
||||
prev_bh = affs_get_extblock(inode, ext - 1);
|
||||
@ -299,8 +297,8 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul
|
||||
struct buffer_head *ext_bh;
|
||||
u32 ext;
|
||||
|
||||
pr_debug("%s(%u, %lu)\n",
|
||||
__func__, (u32)inode->i_ino, (unsigned long)block);
|
||||
pr_debug("%s(%lu, %llu)\n", __func__, inode->i_ino,
|
||||
(unsigned long long)block);
|
||||
|
||||
BUG_ON(block > (sector_t)0x7fffffffUL);
|
||||
|
||||
@ -330,8 +328,9 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul
|
||||
|
||||
/* store new block */
|
||||
if (bh_result->b_blocknr)
|
||||
affs_warning(sb, "get_block", "block already set (%lx)",
|
||||
(unsigned long)bh_result->b_blocknr);
|
||||
affs_warning(sb, "get_block",
|
||||
"block already set (%llx)",
|
||||
(unsigned long long)bh_result->b_blocknr);
|
||||
AFFS_BLOCK(sb, ext_bh, block) = cpu_to_be32(blocknr);
|
||||
AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(block + 1);
|
||||
affs_adjust_checksum(ext_bh, blocknr - bh_result->b_blocknr + 1);
|
||||
@ -353,8 +352,8 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul
|
||||
return 0;
|
||||
|
||||
err_big:
|
||||
affs_error(inode->i_sb, "get_block", "strange block request %d",
|
||||
(int)block);
|
||||
affs_error(inode->i_sb, "get_block", "strange block request %llu",
|
||||
(unsigned long long)block);
|
||||
return -EIO;
|
||||
err_ext:
|
||||
// unlock cache
|
||||
@ -399,6 +398,13 @@ affs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter,
|
||||
size_t count = iov_iter_count(iter);
|
||||
ssize_t ret;
|
||||
|
||||
if (rw == WRITE) {
|
||||
loff_t size = offset + count;
|
||||
|
||||
if (AFFS_I(inode)->mmu_private < size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, affs_get_block);
|
||||
if (ret < 0 && (rw & WRITE))
|
||||
affs_write_failed(mapping, offset + count);
|
||||
@ -503,7 +509,7 @@ affs_do_readpage_ofs(struct page *page, unsigned to)
|
||||
u32 bidx, boff, bsize;
|
||||
u32 tmp;
|
||||
|
||||
pr_debug("%s(%u, %ld, 0, %d)\n", __func__, (u32)inode->i_ino,
|
||||
pr_debug("%s(%lu, %ld, 0, %d)\n", __func__, inode->i_ino,
|
||||
page->index, to);
|
||||
BUG_ON(to > PAGE_CACHE_SIZE);
|
||||
kmap(page);
|
||||
@ -539,7 +545,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize)
|
||||
u32 size, bsize;
|
||||
u32 tmp;
|
||||
|
||||
pr_debug("%s(%u, %d)\n", __func__, (u32)inode->i_ino, newsize);
|
||||
pr_debug("%s(%lu, %d)\n", __func__, inode->i_ino, newsize);
|
||||
bsize = AFFS_SB(sb)->s_data_blksize;
|
||||
bh = NULL;
|
||||
size = AFFS_I(inode)->mmu_private;
|
||||
@ -608,7 +614,7 @@ affs_readpage_ofs(struct file *file, struct page *page)
|
||||
u32 to;
|
||||
int err;
|
||||
|
||||
pr_debug("%s(%u, %ld)\n", __func__, (u32)inode->i_ino, page->index);
|
||||
pr_debug("%s(%lu, %ld)\n", __func__, inode->i_ino, page->index);
|
||||
to = PAGE_CACHE_SIZE;
|
||||
if (((page->index + 1) << PAGE_CACHE_SHIFT) > inode->i_size) {
|
||||
to = inode->i_size & ~PAGE_CACHE_MASK;
|
||||
@ -631,8 +637,8 @@ static int affs_write_begin_ofs(struct file *file, struct address_space *mapping
|
||||
pgoff_t index;
|
||||
int err = 0;
|
||||
|
||||
pr_debug("%s(%u, %llu, %llu)\n", __func__, (u32)inode->i_ino,
|
||||
(unsigned long long)pos, (unsigned long long)pos + len);
|
||||
pr_debug("%s(%lu, %llu, %llu)\n", __func__, inode->i_ino, pos,
|
||||
pos + len);
|
||||
if (pos > AFFS_I(inode)->mmu_private) {
|
||||
/* XXX: this probably leaves a too-big i_size in case of
|
||||
* failure. Should really be updating i_size at write_end time
|
||||
@ -681,9 +687,8 @@ static int affs_write_end_ofs(struct file *file, struct address_space *mapping,
|
||||
* due to write_begin.
|
||||
*/
|
||||
|
||||
pr_debug("%s(%u, %llu, %llu)\n",
|
||||
__func__, (u32)inode->i_ino, (unsigned long long)pos,
|
||||
(unsigned long long)pos + len);
|
||||
pr_debug("%s(%lu, %llu, %llu)\n", __func__, inode->i_ino, pos,
|
||||
pos + len);
|
||||
bsize = AFFS_SB(sb)->s_data_blksize;
|
||||
data = page_address(page);
|
||||
|
||||
@ -831,8 +836,8 @@ affs_truncate(struct inode *inode)
|
||||
struct buffer_head *ext_bh;
|
||||
int i;
|
||||
|
||||
pr_debug("truncate(inode=%d, oldsize=%u, newsize=%u)\n",
|
||||
(u32)inode->i_ino, (u32)AFFS_I(inode)->mmu_private, (u32)inode->i_size);
|
||||
pr_debug("truncate(inode=%lu, oldsize=%llu, newsize=%llu)\n",
|
||||
inode->i_ino, AFFS_I(inode)->mmu_private, inode->i_size);
|
||||
|
||||
last_blk = 0;
|
||||
ext = 0;
|
||||
@ -863,7 +868,7 @@ affs_truncate(struct inode *inode)
|
||||
if (IS_ERR(ext_bh)) {
|
||||
affs_warning(sb, "truncate",
|
||||
"unexpected read error for ext block %u (%ld)",
|
||||
(unsigned int)ext, PTR_ERR(ext_bh));
|
||||
ext, PTR_ERR(ext_bh));
|
||||
return;
|
||||
}
|
||||
if (AFFS_I(inode)->i_lc) {
|
||||
@ -911,7 +916,7 @@ affs_truncate(struct inode *inode)
|
||||
if (IS_ERR(bh)) {
|
||||
affs_warning(sb, "truncate",
|
||||
"unexpected read error for last block %u (%ld)",
|
||||
(unsigned int)ext, PTR_ERR(bh));
|
||||
ext, PTR_ERR(bh));
|
||||
return;
|
||||
}
|
||||
tmp = be32_to_cpu(AFFS_DATA_HEAD(bh)->next);
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include <linux/gfp.h>
|
||||
#include "affs.h"
|
||||
|
||||
extern const struct inode_operations affs_symlink_inode_operations;
|
||||
|
||||
struct inode *affs_iget(struct super_block *sb, unsigned long ino)
|
||||
{
|
||||
struct affs_sb_info *sbi = AFFS_SB(sb);
|
||||
@ -348,9 +346,8 @@ affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s3
|
||||
u32 block = 0;
|
||||
int retval;
|
||||
|
||||
pr_debug("%s(dir=%u, inode=%u, \"%pd\", type=%d)\n",
|
||||
__func__, (u32)dir->i_ino,
|
||||
(u32)inode->i_ino, dentry, type);
|
||||
pr_debug("%s(dir=%lu, inode=%lu, \"%pd\", type=%d)\n", __func__,
|
||||
dir->i_ino, inode->i_ino, dentry, type);
|
||||
|
||||
retval = -EIO;
|
||||
bh = affs_bread(sb, inode->i_ino);
|
||||
|
@ -64,15 +64,16 @@ __affs_hash_dentry(struct qstr *qstr, toupper_t toupper, bool notruncate)
|
||||
{
|
||||
const u8 *name = qstr->name;
|
||||
unsigned long hash;
|
||||
int i;
|
||||
int retval;
|
||||
u32 len;
|
||||
|
||||
i = affs_check_name(qstr->name, qstr->len, notruncate);
|
||||
if (i)
|
||||
return i;
|
||||
retval = affs_check_name(qstr->name, qstr->len, notruncate);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
hash = init_name_hash();
|
||||
i = min(qstr->len, 30u);
|
||||
for (; i > 0; name++, i--)
|
||||
len = min(qstr->len, AFFSNAMEMAX);
|
||||
for (; len > 0; name++, len--)
|
||||
hash = partial_name_hash(toupper(*name), hash);
|
||||
qstr->hash = end_name_hash(hash);
|
||||
|
||||
@ -114,10 +115,10 @@ static inline int __affs_compare_dentry(unsigned int len,
|
||||
* If the names are longer than the allowed 30 chars,
|
||||
* the excess is ignored, so their length may differ.
|
||||
*/
|
||||
if (len >= 30) {
|
||||
if (name->len < 30)
|
||||
if (len >= AFFSNAMEMAX) {
|
||||
if (name->len < AFFSNAMEMAX)
|
||||
return 1;
|
||||
len = 30;
|
||||
len = AFFSNAMEMAX;
|
||||
} else if (len != name->len)
|
||||
return 1;
|
||||
|
||||
@ -156,10 +157,10 @@ affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
|
||||
const u8 *name = dentry->d_name.name;
|
||||
int len = dentry->d_name.len;
|
||||
|
||||
if (len >= 30) {
|
||||
if (*name2 < 30)
|
||||
if (len >= AFFSNAMEMAX) {
|
||||
if (*name2 < AFFSNAMEMAX)
|
||||
return 0;
|
||||
len = 30;
|
||||
len = AFFSNAMEMAX;
|
||||
} else if (len != *name2)
|
||||
return 0;
|
||||
|
||||
@ -173,9 +174,9 @@ int
|
||||
affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
|
||||
{
|
||||
toupper_t toupper = affs_get_toupper(sb);
|
||||
int hash;
|
||||
u32 hash;
|
||||
|
||||
hash = len = min(len, 30u);
|
||||
hash = len = min(len, AFFSNAMEMAX);
|
||||
for (; len > 0; len--)
|
||||
hash = (hash * 13 + toupper(*name++)) & 0x7ff;
|
||||
|
||||
@ -248,9 +249,8 @@ affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
||||
int
|
||||
affs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
pr_debug("%s(dir=%d, %lu \"%pd\")\n",
|
||||
__func__, (u32)dir->i_ino, dentry->d_inode->i_ino,
|
||||
dentry);
|
||||
pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino,
|
||||
dentry->d_inode->i_ino, dentry);
|
||||
|
||||
return affs_remove_header(dentry);
|
||||
}
|
||||
@ -317,9 +317,8 @@ affs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
int
|
||||
affs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
pr_debug("%s(dir=%u, %lu \"%pd\")\n",
|
||||
__func__, (u32)dir->i_ino, dentry->d_inode->i_ino,
|
||||
dentry);
|
||||
pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino,
|
||||
dentry->d_inode->i_ino, dentry);
|
||||
|
||||
return affs_remove_header(dentry);
|
||||
}
|
||||
@ -404,8 +403,7 @@ affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = old_dentry->d_inode;
|
||||
|
||||
pr_debug("%s(%u, %u, \"%pd\")\n",
|
||||
__func__, (u32)inode->i_ino, (u32)dir->i_ino,
|
||||
pr_debug("%s(%lu, %lu, \"%pd\")\n", __func__, inode->i_ino, dir->i_ino,
|
||||
dentry);
|
||||
|
||||
return affs_add_entry(dir, inode, dentry, ST_LINKFILE);
|
||||
@ -419,9 +417,8 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct buffer_head *bh = NULL;
|
||||
int retval;
|
||||
|
||||
pr_debug("%s(old=%u,\"%pd\" to new=%u,\"%pd\")\n",
|
||||
__func__, (u32)old_dir->i_ino, old_dentry,
|
||||
(u32)new_dir->i_ino, new_dentry);
|
||||
pr_debug("%s(old=%lu,\"%pd\" to new=%lu,\"%pd\")\n", __func__,
|
||||
old_dir->i_ino, old_dentry, new_dir->i_ino, new_dentry);
|
||||
|
||||
retval = affs_check_name(new_dentry->d_name.name,
|
||||
new_dentry->d_name.len,
|
||||
|
@ -432,39 +432,39 @@ got_root:
|
||||
sb->s_flags |= MS_RDONLY;
|
||||
}
|
||||
switch (chksum) {
|
||||
case MUFS_FS:
|
||||
case MUFS_INTLFFS:
|
||||
case MUFS_DCFFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
/* fall thru */
|
||||
case FS_INTLFFS:
|
||||
case FS_DCFFS:
|
||||
sbi->s_flags |= SF_INTL;
|
||||
break;
|
||||
case MUFS_FFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
break;
|
||||
case FS_FFS:
|
||||
break;
|
||||
case MUFS_OFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
/* fall thru */
|
||||
case FS_OFS:
|
||||
sbi->s_flags |= SF_OFS;
|
||||
sb->s_flags |= MS_NOEXEC;
|
||||
break;
|
||||
case MUFS_DCOFS:
|
||||
case MUFS_INTLOFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
case FS_DCOFS:
|
||||
case FS_INTLOFS:
|
||||
sbi->s_flags |= SF_INTL | SF_OFS;
|
||||
sb->s_flags |= MS_NOEXEC;
|
||||
break;
|
||||
default:
|
||||
pr_err("Unknown filesystem on device %s: %08X\n",
|
||||
sb->s_id, chksum);
|
||||
return -EINVAL;
|
||||
case MUFS_FS:
|
||||
case MUFS_INTLFFS:
|
||||
case MUFS_DCFFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
/* fall thru */
|
||||
case FS_INTLFFS:
|
||||
case FS_DCFFS:
|
||||
sbi->s_flags |= SF_INTL;
|
||||
break;
|
||||
case MUFS_FFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
break;
|
||||
case FS_FFS:
|
||||
break;
|
||||
case MUFS_OFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
/* fall thru */
|
||||
case FS_OFS:
|
||||
sbi->s_flags |= SF_OFS;
|
||||
sb->s_flags |= MS_NOEXEC;
|
||||
break;
|
||||
case MUFS_DCOFS:
|
||||
case MUFS_INTLOFS:
|
||||
sbi->s_flags |= SF_MUFS;
|
||||
case FS_DCOFS:
|
||||
case FS_INTLOFS:
|
||||
sbi->s_flags |= SF_INTL | SF_OFS;
|
||||
sb->s_flags |= MS_NOEXEC;
|
||||
break;
|
||||
default:
|
||||
pr_err("Unknown filesystem on device %s: %08X\n",
|
||||
sb->s_id, chksum);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mount_flags & SF_VERBOSE) {
|
||||
@ -584,7 +584,7 @@ affs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
buf->f_bavail = free;
|
||||
buf->f_fsid.val[0] = (u32)id;
|
||||
buf->f_fsid.val[1] = (u32)(id >> 32);
|
||||
buf->f_namelen = 30;
|
||||
buf->f_namelen = AFFSNAMEMAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -602,6 +602,7 @@ static void affs_kill_sb(struct super_block *sb)
|
||||
affs_free_bitmap(sb);
|
||||
affs_brelse(sbi->s_root_bh);
|
||||
kfree(sbi->s_prefix);
|
||||
mutex_destroy(&sbi->s_bmlock);
|
||||
kfree(sbi);
|
||||
}
|
||||
}
|
||||
|
@ -274,9 +274,9 @@ more:
|
||||
static struct inode *
|
||||
befs_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct befs_inode_info *bi;
|
||||
bi = (struct befs_inode_info *)kmem_cache_alloc(befs_inode_cachep,
|
||||
GFP_KERNEL);
|
||||
struct befs_inode_info *bi;
|
||||
|
||||
bi = kmem_cache_alloc(befs_inode_cachep, GFP_KERNEL);
|
||||
if (!bi)
|
||||
return NULL;
|
||||
return &bi->vfs_inode;
|
||||
|
138
fs/coda/dir.c
138
fs/coda/dir.c
@ -28,29 +28,6 @@
|
||||
|
||||
#include "coda_int.h"
|
||||
|
||||
/* dir inode-ops */
|
||||
static int coda_create(struct inode *dir, struct dentry *new, umode_t mode, bool excl);
|
||||
static struct dentry *coda_lookup(struct inode *dir, struct dentry *target, unsigned int flags);
|
||||
static int coda_link(struct dentry *old_dentry, struct inode *dir_inode,
|
||||
struct dentry *entry);
|
||||
static int coda_unlink(struct inode *dir_inode, struct dentry *entry);
|
||||
static int coda_symlink(struct inode *dir_inode, struct dentry *entry,
|
||||
const char *symname);
|
||||
static int coda_mkdir(struct inode *dir_inode, struct dentry *entry, umode_t mode);
|
||||
static int coda_rmdir(struct inode *dir_inode, struct dentry *entry);
|
||||
static int coda_rename(struct inode *old_inode, struct dentry *old_dentry,
|
||||
struct inode *new_inode, struct dentry *new_dentry);
|
||||
|
||||
/* dir file-ops */
|
||||
static int coda_readdir(struct file *file, struct dir_context *ctx);
|
||||
|
||||
/* dentry ops */
|
||||
static int coda_dentry_revalidate(struct dentry *de, unsigned int flags);
|
||||
static int coda_dentry_delete(const struct dentry *);
|
||||
|
||||
/* support routines */
|
||||
static int coda_venus_readdir(struct file *, struct dir_context *);
|
||||
|
||||
/* same as fs/bad_inode.c */
|
||||
static int coda_return_EIO(void)
|
||||
{
|
||||
@ -58,38 +35,6 @@ static int coda_return_EIO(void)
|
||||
}
|
||||
#define CODA_EIO_ERROR ((void *) (coda_return_EIO))
|
||||
|
||||
const struct dentry_operations coda_dentry_operations =
|
||||
{
|
||||
.d_revalidate = coda_dentry_revalidate,
|
||||
.d_delete = coda_dentry_delete,
|
||||
};
|
||||
|
||||
const struct inode_operations coda_dir_inode_operations =
|
||||
{
|
||||
.create = coda_create,
|
||||
.lookup = coda_lookup,
|
||||
.link = coda_link,
|
||||
.unlink = coda_unlink,
|
||||
.symlink = coda_symlink,
|
||||
.mkdir = coda_mkdir,
|
||||
.rmdir = coda_rmdir,
|
||||
.mknod = CODA_EIO_ERROR,
|
||||
.rename = coda_rename,
|
||||
.permission = coda_permission,
|
||||
.getattr = coda_getattr,
|
||||
.setattr = coda_setattr,
|
||||
};
|
||||
|
||||
const struct file_operations coda_dir_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
.iterate = coda_readdir,
|
||||
.open = coda_open,
|
||||
.release = coda_release,
|
||||
.fsync = coda_fsync,
|
||||
};
|
||||
|
||||
|
||||
/* inode operations for directories */
|
||||
/* access routines: lookup, readlink, permission */
|
||||
static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, unsigned int flags)
|
||||
@ -374,33 +319,6 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/* file operations for directories */
|
||||
static int coda_readdir(struct file *coda_file, struct dir_context *ctx)
|
||||
{
|
||||
struct coda_file_info *cfi;
|
||||
struct file *host_file;
|
||||
int ret;
|
||||
|
||||
cfi = CODA_FTOC(coda_file);
|
||||
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
|
||||
host_file = cfi->cfi_container;
|
||||
|
||||
if (host_file->f_op->iterate) {
|
||||
struct inode *host_inode = file_inode(host_file);
|
||||
mutex_lock(&host_inode->i_mutex);
|
||||
ret = -ENOENT;
|
||||
if (!IS_DEADDIR(host_inode)) {
|
||||
ret = host_file->f_op->iterate(host_file, ctx);
|
||||
file_accessed(host_file);
|
||||
}
|
||||
mutex_unlock(&host_inode->i_mutex);
|
||||
return ret;
|
||||
}
|
||||
/* Venus: we must read Venus dirents from a file */
|
||||
return coda_venus_readdir(coda_file, ctx);
|
||||
}
|
||||
|
||||
static inline unsigned int CDT2DT(unsigned char cdt)
|
||||
{
|
||||
unsigned int dt;
|
||||
@ -495,6 +413,33 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* file operations for directories */
|
||||
static int coda_readdir(struct file *coda_file, struct dir_context *ctx)
|
||||
{
|
||||
struct coda_file_info *cfi;
|
||||
struct file *host_file;
|
||||
int ret;
|
||||
|
||||
cfi = CODA_FTOC(coda_file);
|
||||
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
|
||||
host_file = cfi->cfi_container;
|
||||
|
||||
if (host_file->f_op->iterate) {
|
||||
struct inode *host_inode = file_inode(host_file);
|
||||
|
||||
mutex_lock(&host_inode->i_mutex);
|
||||
ret = -ENOENT;
|
||||
if (!IS_DEADDIR(host_inode)) {
|
||||
ret = host_file->f_op->iterate(host_file, ctx);
|
||||
file_accessed(host_file);
|
||||
}
|
||||
mutex_unlock(&host_inode->i_mutex);
|
||||
return ret;
|
||||
}
|
||||
/* Venus: we must read Venus dirents from a file */
|
||||
return coda_venus_readdir(coda_file, ctx);
|
||||
}
|
||||
|
||||
/* called when a cache lookup succeeds */
|
||||
static int coda_dentry_revalidate(struct dentry *de, unsigned int flags)
|
||||
{
|
||||
@ -603,3 +548,32 @@ int coda_revalidate_inode(struct inode *inode)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct dentry_operations coda_dentry_operations = {
|
||||
.d_revalidate = coda_dentry_revalidate,
|
||||
.d_delete = coda_dentry_delete,
|
||||
};
|
||||
|
||||
const struct inode_operations coda_dir_inode_operations = {
|
||||
.create = coda_create,
|
||||
.lookup = coda_lookup,
|
||||
.link = coda_link,
|
||||
.unlink = coda_unlink,
|
||||
.symlink = coda_symlink,
|
||||
.mkdir = coda_mkdir,
|
||||
.rmdir = coda_rmdir,
|
||||
.mknod = CODA_EIO_ERROR,
|
||||
.rename = coda_rename,
|
||||
.permission = coda_permission,
|
||||
.getattr = coda_getattr,
|
||||
.setattr = coda_setattr,
|
||||
};
|
||||
|
||||
const struct file_operations coda_dir_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
.iterate = coda_readdir,
|
||||
.open = coda_open,
|
||||
.release = coda_release,
|
||||
.fsync = coda_fsync,
|
||||
};
|
||||
|
12
fs/eventfd.c
12
fs/eventfd.c
@ -118,18 +118,18 @@ static unsigned int eventfd_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct eventfd_ctx *ctx = file->private_data;
|
||||
unsigned int events = 0;
|
||||
unsigned long flags;
|
||||
u64 count;
|
||||
|
||||
poll_wait(file, &ctx->wqh, wait);
|
||||
smp_rmb();
|
||||
count = ctx->count;
|
||||
|
||||
spin_lock_irqsave(&ctx->wqh.lock, flags);
|
||||
if (ctx->count > 0)
|
||||
if (count > 0)
|
||||
events |= POLLIN;
|
||||
if (ctx->count == ULLONG_MAX)
|
||||
if (count == ULLONG_MAX)
|
||||
events |= POLLERR;
|
||||
if (ULLONG_MAX - 1 > ctx->count)
|
||||
if (ULLONG_MAX - 1 > count)
|
||||
events |= POLLOUT;
|
||||
spin_unlock_irqrestore(&ctx->wqh.lock, flags);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
@ -580,7 +580,7 @@ static void fat_set_state(struct super_block *sb,
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
struct fat_boot_sector *b;
|
||||
struct msdos_sb_info *sbi = sb->s_fs_info;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
|
||||
/* do not change any thing if mounted read only */
|
||||
if ((sb->s_flags & MS_RDONLY) && !force)
|
||||
|
@ -546,8 +546,8 @@ static int __init update_note_header_size_elf64(const Elf64_Ehdr *ehdr_ptr)
|
||||
nhdr_ptr = notes_section;
|
||||
while (nhdr_ptr->n_namesz != 0) {
|
||||
sz = sizeof(Elf64_Nhdr) +
|
||||
((nhdr_ptr->n_namesz + 3) & ~3) +
|
||||
((nhdr_ptr->n_descsz + 3) & ~3);
|
||||
(((u64)nhdr_ptr->n_namesz + 3) & ~3) +
|
||||
(((u64)nhdr_ptr->n_descsz + 3) & ~3);
|
||||
if ((real_sz + sz) > max_sz) {
|
||||
pr_warn("Warning: Exceeded p_memsz, dropping PT_NOTE entry n_namesz=0x%x, n_descsz=0x%x\n",
|
||||
nhdr_ptr->n_namesz, nhdr_ptr->n_descsz);
|
||||
@ -732,8 +732,8 @@ static int __init update_note_header_size_elf32(const Elf32_Ehdr *ehdr_ptr)
|
||||
nhdr_ptr = notes_section;
|
||||
while (nhdr_ptr->n_namesz != 0) {
|
||||
sz = sizeof(Elf32_Nhdr) +
|
||||
((nhdr_ptr->n_namesz + 3) & ~3) +
|
||||
((nhdr_ptr->n_descsz + 3) & ~3);
|
||||
(((u64)nhdr_ptr->n_namesz + 3) & ~3) +
|
||||
(((u64)nhdr_ptr->n_descsz + 3) & ~3);
|
||||
if ((real_sz + sz) > max_sz) {
|
||||
pr_warn("Warning: Exceeded p_memsz, dropping PT_NOTE entry n_namesz=0x%x, n_descsz=0x%x\n",
|
||||
nhdr_ptr->n_namesz, nhdr_ptr->n_descsz);
|
||||
|
@ -2766,7 +2766,7 @@ static int reiserfs_write_begin(struct file *file,
|
||||
int old_ref = 0;
|
||||
|
||||
inode = mapping->host;
|
||||
*fsdata = 0;
|
||||
*fsdata = NULL;
|
||||
if (flags & AOP_FLAG_CONT_EXPAND &&
|
||||
(pos & (inode->i_sb->s_blocksize - 1)) == 0) {
|
||||
pos ++;
|
||||
|
@ -95,22 +95,18 @@
|
||||
|
||||
void lock_ufs(struct super_block *sb)
|
||||
{
|
||||
#if defined(CONFIG_SMP) || defined (CONFIG_PREEMPT)
|
||||
struct ufs_sb_info *sbi = UFS_SB(sb);
|
||||
|
||||
mutex_lock(&sbi->mutex);
|
||||
sbi->mutex_owner = current;
|
||||
#endif
|
||||
}
|
||||
|
||||
void unlock_ufs(struct super_block *sb)
|
||||
{
|
||||
#if defined(CONFIG_SMP) || defined (CONFIG_PREEMPT)
|
||||
struct ufs_sb_info *sbi = UFS_SB(sb);
|
||||
|
||||
sbi->mutex_owner = NULL;
|
||||
mutex_unlock(&sbi->mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct inode *ufs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation)
|
||||
@ -1415,9 +1411,11 @@ static struct kmem_cache * ufs_inode_cachep;
|
||||
static struct inode *ufs_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct ufs_inode_info *ei;
|
||||
ei = (struct ufs_inode_info *)kmem_cache_alloc(ufs_inode_cachep, GFP_NOFS);
|
||||
|
||||
ei = kmem_cache_alloc(ufs_inode_cachep, GFP_NOFS);
|
||||
if (!ei)
|
||||
return NULL;
|
||||
|
||||
ei->vfs_inode.i_version = 1;
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
@ -1,6 +1,19 @@
|
||||
#ifndef LINUX_KEXEC_H
|
||||
#define LINUX_KEXEC_H
|
||||
|
||||
#define IND_DESTINATION_BIT 0
|
||||
#define IND_INDIRECTION_BIT 1
|
||||
#define IND_DONE_BIT 2
|
||||
#define IND_SOURCE_BIT 3
|
||||
|
||||
#define IND_DESTINATION (1 << IND_DESTINATION_BIT)
|
||||
#define IND_INDIRECTION (1 << IND_INDIRECTION_BIT)
|
||||
#define IND_DONE (1 << IND_DONE_BIT)
|
||||
#define IND_SOURCE (1 << IND_SOURCE_BIT)
|
||||
#define IND_FLAGS (IND_DESTINATION | IND_INDIRECTION | IND_DONE | IND_SOURCE)
|
||||
|
||||
#if !defined(__ASSEMBLY__)
|
||||
|
||||
#include <uapi/linux/kexec.h>
|
||||
|
||||
#ifdef CONFIG_KEXEC
|
||||
@ -64,10 +77,6 @@
|
||||
*/
|
||||
|
||||
typedef unsigned long kimage_entry_t;
|
||||
#define IND_DESTINATION 0x1
|
||||
#define IND_INDIRECTION 0x2
|
||||
#define IND_DONE 0x4
|
||||
#define IND_SOURCE 0x8
|
||||
|
||||
struct kexec_segment {
|
||||
/*
|
||||
@ -122,8 +131,6 @@ struct kimage {
|
||||
kimage_entry_t *entry;
|
||||
kimage_entry_t *last_entry;
|
||||
|
||||
unsigned long destination;
|
||||
|
||||
unsigned long start;
|
||||
struct page *control_code_page;
|
||||
struct page *swap_page;
|
||||
@ -313,4 +320,7 @@ struct task_struct;
|
||||
static inline void crash_kexec(struct pt_regs *regs) { }
|
||||
static inline int kexec_should_crash(struct task_struct *p) { return 0; }
|
||||
#endif /* CONFIG_KEXEC */
|
||||
|
||||
#endif /* !defined(__ASSEBMLY__) */
|
||||
|
||||
#endif /* LINUX_KEXEC_H */
|
||||
|
@ -51,7 +51,7 @@ struct rb_root {
|
||||
|
||||
#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)
|
||||
|
||||
/* 'empty' nodes are nodes that are known not to be inserted in an rbree */
|
||||
/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */
|
||||
#define RB_EMPTY_NODE(node) \
|
||||
((node)->__rb_parent_color == (unsigned long)(node))
|
||||
#define RB_CLEAR_NODE(node) \
|
||||
|
@ -55,12 +55,6 @@ struct kexec_segment {
|
||||
size_t memsz;
|
||||
};
|
||||
|
||||
/* Load a new kernel image as described by the kexec_segment array
|
||||
* consisting of passed number of segments at the entry-point address.
|
||||
* The flags allow different useage types.
|
||||
*/
|
||||
extern int kexec_load(void *, size_t, struct kexec_segment *,
|
||||
unsigned long int);
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _UAPILINUX_KEXEC_H */
|
||||
|
@ -1941,7 +1941,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
|
||||
queue.sleeper = current;
|
||||
|
||||
sleep_again:
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
__set_current_state(TASK_INTERRUPTIBLE);
|
||||
sem_unlock(sma, locknum);
|
||||
rcu_read_unlock();
|
||||
|
||||
|
@ -444,7 +444,7 @@ arch_kexec_apply_relocations(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
|
||||
}
|
||||
|
||||
/*
|
||||
* Free up memory used by kernel, initrd, and comand line. This is temporary
|
||||
* Free up memory used by kernel, initrd, and command line. This is temporary
|
||||
* memory allocation which is not needed any more after these buffers have
|
||||
* been loaded into separate segments and have been copied elsewhere.
|
||||
*/
|
||||
@ -856,8 +856,6 @@ static int kimage_set_destination(struct kimage *image,
|
||||
|
||||
destination &= PAGE_MASK;
|
||||
result = kimage_add_entry(image, destination | IND_DESTINATION);
|
||||
if (result == 0)
|
||||
image->destination = destination;
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -869,8 +867,6 @@ static int kimage_add_page(struct kimage *image, unsigned long page)
|
||||
|
||||
page &= PAGE_MASK;
|
||||
result = kimage_add_entry(image, page | IND_SOURCE);
|
||||
if (result == 0)
|
||||
image->destination += PAGE_SIZE;
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -1288,19 +1284,22 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
|
||||
if (nr_segments > 0) {
|
||||
unsigned long i;
|
||||
|
||||
/* Loading another kernel to reboot into */
|
||||
if ((flags & KEXEC_ON_CRASH) == 0)
|
||||
result = kimage_alloc_init(&image, entry, nr_segments,
|
||||
segments, flags);
|
||||
/* Loading another kernel to switch to if this one crashes */
|
||||
else if (flags & KEXEC_ON_CRASH) {
|
||||
/* Free any current crash dump kernel before
|
||||
if (flags & KEXEC_ON_CRASH) {
|
||||
/*
|
||||
* Loading another kernel to switch to if this one
|
||||
* crashes. Free any current crash dump kernel before
|
||||
* we corrupt it.
|
||||
*/
|
||||
|
||||
kimage_free(xchg(&kexec_crash_image, NULL));
|
||||
result = kimage_alloc_init(&image, entry, nr_segments,
|
||||
segments, flags);
|
||||
crash_map_reserved_pages();
|
||||
} else {
|
||||
/* Loading another kernel to reboot into. */
|
||||
|
||||
result = kimage_alloc_init(&image, entry, nr_segments,
|
||||
segments, flags);
|
||||
}
|
||||
if (result)
|
||||
goto out;
|
||||
|
@ -3025,8 +3025,13 @@ static void do_free_init(struct rcu_head *head)
|
||||
kfree(m);
|
||||
}
|
||||
|
||||
/* This is where the real work happens */
|
||||
static int do_init_module(struct module *mod)
|
||||
/*
|
||||
* This is where the real work happens.
|
||||
*
|
||||
* Keep it uninlined to provide a reliable breakpoint target, e.g. for the gdb
|
||||
* helper command 'lx-symbols'.
|
||||
*/
|
||||
static noinline int do_init_module(struct module *mod)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mod_initfree *freeinit;
|
||||
|
@ -1077,7 +1077,6 @@ int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,
|
||||
}
|
||||
|
||||
#if defined CONFIG_COMPAT
|
||||
#include <linux/compat.h>
|
||||
|
||||
int compat_ptrace_request(struct task_struct *child, compat_long_t request,
|
||||
compat_ulong_t addr, compat_ulong_t data)
|
||||
|
@ -629,7 +629,9 @@ static u32 __seccomp_phase1_filter(int this_syscall, struct seccomp_data *sd)
|
||||
|
||||
switch (action) {
|
||||
case SECCOMP_RET_ERRNO:
|
||||
/* Set the low-order 16-bits as a errno. */
|
||||
/* Set low-order bits as an errno, capped at MAX_ERRNO. */
|
||||
if (data > MAX_ERRNO)
|
||||
data = MAX_ERRNO;
|
||||
syscall_set_return_value(current, task_pt_regs(current),
|
||||
-data, 0);
|
||||
goto skip;
|
||||
|
@ -3550,7 +3550,7 @@ SYSCALL_DEFINE2(signal, int, sig, __sighandler_t, handler)
|
||||
SYSCALL_DEFINE0(pause)
|
||||
{
|
||||
while (!signal_pending(current)) {
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
__set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule();
|
||||
}
|
||||
return -ERESTARTNOHAND;
|
||||
@ -3563,7 +3563,7 @@ int sigsuspend(sigset_t *set)
|
||||
current->saved_sigmask = current->blocked;
|
||||
set_current_blocked(set);
|
||||
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
__set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule();
|
||||
set_restore_sigmask();
|
||||
return -ERESTARTNOHAND;
|
||||
|
@ -167,6 +167,17 @@ config DEBUG_INFO_DWARF4
|
||||
But it significantly improves the success of resolving
|
||||
variables in gdb on optimized code.
|
||||
|
||||
config GDB_SCRIPTS
|
||||
bool "Provide GDB scripts for kernel debugging"
|
||||
depends on DEBUG_INFO
|
||||
help
|
||||
This creates the required links to GDB helper scripts in the
|
||||
build directory. If you load vmlinux into gdb, the helper
|
||||
scripts will be automatically imported by gdb as well, and
|
||||
additional functions are available to analyze a Linux kernel
|
||||
instance. See Documentation/gdb-kernel-debugging.txt for further
|
||||
details.
|
||||
|
||||
config ENABLE_WARN_DEPRECATED
|
||||
bool "Enable __deprecated logic"
|
||||
default y
|
||||
|
@ -25,7 +25,9 @@
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct bpf_labels l;
|
||||
struct bpf_labels l = {
|
||||
.count = 0,
|
||||
};
|
||||
static const char msg1[] = "Please type something: ";
|
||||
static const char msg2[] = "You typed: ";
|
||||
char buf[256];
|
||||
|
@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bpf-helper.h"
|
||||
@ -63,6 +64,11 @@ __u32 seccomp_bpf_label(struct bpf_labels *labels, const char *label)
|
||||
{
|
||||
struct __bpf_label *begin = labels->labels, *end;
|
||||
int id;
|
||||
|
||||
if (labels->count == BPF_LABELS_MAX) {
|
||||
fprintf(stderr, "Too many labels\n");
|
||||
exit(1);
|
||||
}
|
||||
if (labels->count == 0) {
|
||||
begin->label = label;
|
||||
begin->location = 0xffffffff;
|
||||
|
@ -36,6 +36,7 @@ subdir-$(CONFIG_MODVERSIONS) += genksyms
|
||||
subdir-y += mod
|
||||
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
|
||||
subdir-$(CONFIG_DTC) += dtc
|
||||
subdir-$(CONFIG_GDB_SCRIPTS) += gdb
|
||||
|
||||
# Let clean descend into subdirs
|
||||
subdir- += basic kconfig package
|
||||
|
1
scripts/gdb/Makefile
Normal file
1
scripts/gdb/Makefile
Normal file
@ -0,0 +1 @@
|
||||
subdir-y := linux
|
2
scripts/gdb/linux/.gitignore
vendored
Normal file
2
scripts/gdb/linux/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.pyc
|
||||
*.pyo
|
11
scripts/gdb/linux/Makefile
Normal file
11
scripts/gdb/linux/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
always := gdb-scripts
|
||||
|
||||
SRCTREE := $(shell cd $(srctree) && /bin/pwd)
|
||||
|
||||
$(obj)/gdb-scripts:
|
||||
ifneq ($(KBUILD_SRC),)
|
||||
$(Q)ln -fsn $(SRCTREE)/$(obj)/*.py $(objtree)/$(obj)
|
||||
endif
|
||||
@:
|
||||
|
||||
clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py)
|
135
scripts/gdb/linux/cpus.py
Normal file
135
scripts/gdb/linux/cpus.py
Normal file
@ -0,0 +1,135 @@
|
||||
#
|
||||
# gdb helper commands and functions for Linux kernel debugging
|
||||
#
|
||||
# per-cpu tools
|
||||
#
|
||||
# Copyright (c) Siemens AG, 2011-2013
|
||||
#
|
||||
# Authors:
|
||||
# Jan Kiszka <jan.kiszka@siemens.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL version 2.
|
||||
#
|
||||
|
||||
import gdb
|
||||
|
||||
from linux import tasks, utils
|
||||
|
||||
|
||||
MAX_CPUS = 4096
|
||||
|
||||
|
||||
def get_current_cpu():
|
||||
if utils.get_gdbserver_type() == utils.GDBSERVER_QEMU:
|
||||
return gdb.selected_thread().num - 1
|
||||
elif utils.get_gdbserver_type() == utils.GDBSERVER_KGDB:
|
||||
tid = gdb.selected_thread().ptid[2]
|
||||
if tid > (0x100000000 - MAX_CPUS - 2):
|
||||
return 0x100000000 - tid - 2
|
||||
else:
|
||||
return tasks.get_thread_info(tasks.get_task_by_pid(tid))['cpu']
|
||||
else:
|
||||
raise gdb.GdbError("Sorry, obtaining the current CPU is not yet "
|
||||
"supported with this gdb server.")
|
||||
|
||||
|
||||
def per_cpu(var_ptr, cpu):
|
||||
if cpu == -1:
|
||||
cpu = get_current_cpu()
|
||||
if utils.is_target_arch("sparc:v9"):
|
||||
offset = gdb.parse_and_eval(
|
||||
"trap_block[{0}].__per_cpu_base".format(str(cpu)))
|
||||
else:
|
||||
try:
|
||||
offset = gdb.parse_and_eval(
|
||||
"__per_cpu_offset[{0}]".format(str(cpu)))
|
||||
except gdb.error:
|
||||
# !CONFIG_SMP case
|
||||
offset = 0
|
||||
pointer = var_ptr.cast(utils.get_long_type()) + offset
|
||||
return pointer.cast(var_ptr.type).dereference()
|
||||
|
||||
|
||||
cpu_mask = {}
|
||||
|
||||
|
||||
def cpu_mask_invalidate(event):
|
||||
global cpu_mask
|
||||
cpu_mask = {}
|
||||
gdb.events.stop.disconnect(cpu_mask_invalidate)
|
||||
if hasattr(gdb.events, 'new_objfile'):
|
||||
gdb.events.new_objfile.disconnect(cpu_mask_invalidate)
|
||||
|
||||
|
||||
def cpu_list(mask_name):
|
||||
global cpu_mask
|
||||
mask = None
|
||||
if mask_name in cpu_mask:
|
||||
mask = cpu_mask[mask_name]
|
||||
if mask is None:
|
||||
mask = gdb.parse_and_eval(mask_name + ".bits")
|
||||
if hasattr(gdb, 'events'):
|
||||
cpu_mask[mask_name] = mask
|
||||
gdb.events.stop.connect(cpu_mask_invalidate)
|
||||
if hasattr(gdb.events, 'new_objfile'):
|
||||
gdb.events.new_objfile.connect(cpu_mask_invalidate)
|
||||
bits_per_entry = mask[0].type.sizeof * 8
|
||||
num_entries = mask.type.sizeof * 8 / bits_per_entry
|
||||
entry = -1
|
||||
bits = 0
|
||||
|
||||
while True:
|
||||
while bits == 0:
|
||||
entry += 1
|
||||
if entry == num_entries:
|
||||
return
|
||||
bits = mask[entry]
|
||||
if bits != 0:
|
||||
bit = 0
|
||||
break
|
||||
|
||||
while bits & 1 == 0:
|
||||
bits >>= 1
|
||||
bit += 1
|
||||
|
||||
cpu = entry * bits_per_entry + bit
|
||||
|
||||
bits >>= 1
|
||||
bit += 1
|
||||
|
||||
yield cpu
|
||||
|
||||
|
||||
class PerCpu(gdb.Function):
|
||||
"""Return per-cpu variable.
|
||||
|
||||
$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the
|
||||
given CPU number. If CPU is omitted, the CPU of the current context is used.
|
||||
Note that VAR has to be quoted as string."""
|
||||
|
||||
def __init__(self):
|
||||
super(PerCpu, self).__init__("lx_per_cpu")
|
||||
|
||||
def invoke(self, var_name, cpu=-1):
|
||||
var_ptr = gdb.parse_and_eval("&" + var_name.string())
|
||||
return per_cpu(var_ptr, cpu)
|
||||
|
||||
|
||||
PerCpu()
|
||||
|
||||
|
||||
class LxCurrentFunc(gdb.Function):
|
||||
"""Return current task.
|
||||
|
||||
$lx_current([CPU]): Return the per-cpu task variable for the given CPU
|
||||
number. If CPU is omitted, the CPU of the current context is used."""
|
||||
|
||||
def __init__(self):
|
||||
super(LxCurrentFunc, self).__init__("lx_current")
|
||||
|
||||
def invoke(self, cpu=-1):
|
||||
var_ptr = gdb.parse_and_eval("¤t_task")
|
||||
return per_cpu(var_ptr, cpu).dereference()
|
||||
|
||||
|
||||
LxCurrentFunc()
|
65
scripts/gdb/linux/dmesg.py
Normal file
65
scripts/gdb/linux/dmesg.py
Normal file
@ -0,0 +1,65 @@
|
||||
#
|
||||
# gdb helper commands and functions for Linux kernel debugging
|
||||
#
|
||||
# kernel log buffer dump
|
||||
#
|
||||
# Copyright (c) Siemens AG, 2011, 2012
|
||||
#
|
||||
# Authors:
|
||||
# Jan Kiszka <jan.kiszka@siemens.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL version 2.
|
||||
#
|
||||
|
||||
import gdb
|
||||
import string
|
||||
|
||||
from linux import utils
|
||||
|
||||
|
||||
class LxDmesg(gdb.Command):
|
||||
"""Print Linux kernel log buffer."""
|
||||
|
||||
def __init__(self):
|
||||
super(LxDmesg, self).__init__("lx-dmesg", gdb.COMMAND_DATA)
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
log_buf_addr = int(str(gdb.parse_and_eval("log_buf")).split()[0], 16)
|
||||
log_first_idx = int(gdb.parse_and_eval("log_first_idx"))
|
||||
log_next_idx = int(gdb.parse_and_eval("log_next_idx"))
|
||||
log_buf_len = int(gdb.parse_and_eval("log_buf_len"))
|
||||
|
||||
inf = gdb.inferiors()[0]
|
||||
start = log_buf_addr + log_first_idx
|
||||
if log_first_idx < log_next_idx:
|
||||
log_buf_2nd_half = -1
|
||||
length = log_next_idx - log_first_idx
|
||||
log_buf = inf.read_memory(start, length)
|
||||
else:
|
||||
log_buf_2nd_half = log_buf_len - log_first_idx
|
||||
log_buf = inf.read_memory(start, log_buf_2nd_half) + \
|
||||
inf.read_memory(log_buf_addr, log_next_idx)
|
||||
|
||||
pos = 0
|
||||
while pos < log_buf.__len__():
|
||||
length = utils.read_u16(log_buf[pos + 8:pos + 10])
|
||||
if length == 0:
|
||||
if log_buf_2nd_half == -1:
|
||||
gdb.write("Corrupted log buffer!\n")
|
||||
break
|
||||
pos = log_buf_2nd_half
|
||||
continue
|
||||
|
||||
text_len = utils.read_u16(log_buf[pos + 10:pos + 12])
|
||||
text = log_buf[pos + 16:pos + 16 + text_len]
|
||||
time_stamp = utils.read_u64(log_buf[pos:pos + 8])
|
||||
|
||||
for line in memoryview(text).tobytes().splitlines():
|
||||
gdb.write("[{time:12.6f}] {line}\n".format(
|
||||
time=time_stamp / 1000000000.0,
|
||||
line=line))
|
||||
|
||||
pos += length
|
||||
|
||||
|
||||
LxDmesg()
|
103
scripts/gdb/linux/modules.py
Normal file
103
scripts/gdb/linux/modules.py
Normal file
@ -0,0 +1,103 @@
|
||||
#
|
||||
# gdb helper commands and functions for Linux kernel debugging
|
||||
#
|
||||
# module tools
|
||||
#
|
||||
# Copyright (c) Siemens AG, 2013
|
||||
#
|
||||
# Authors:
|
||||
# Jan Kiszka <jan.kiszka@siemens.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL version 2.
|
||||
#
|
||||
|
||||
import gdb
|
||||
|
||||
from linux import cpus, utils
|
||||
|
||||
|
||||
module_type = utils.CachedType("struct module")
|
||||
|
||||
|
||||
def module_list():
|
||||
global module_type
|
||||
module_ptr_type = module_type.get_type().pointer()
|
||||
modules = gdb.parse_and_eval("modules")
|
||||
entry = modules['next']
|
||||
end_of_list = modules.address
|
||||
|
||||
while entry != end_of_list:
|
||||
yield utils.container_of(entry, module_ptr_type, "list")
|
||||
entry = entry['next']
|
||||
|
||||
|
||||
def find_module_by_name(name):
|
||||
for module in module_list():
|
||||
if module['name'].string() == name:
|
||||
return module
|
||||
return None
|
||||
|
||||
|
||||
class LxModule(gdb.Function):
|
||||
"""Find module by name and return the module variable.
|
||||
|
||||
$lx_module("MODULE"): Given the name MODULE, iterate over all loaded modules
|
||||
of the target and return that module variable which MODULE matches."""
|
||||
|
||||
def __init__(self):
|
||||
super(LxModule, self).__init__("lx_module")
|
||||
|
||||
def invoke(self, mod_name):
|
||||
mod_name = mod_name.string()
|
||||
module = find_module_by_name(mod_name)
|
||||
if module:
|
||||
return module.dereference()
|
||||
else:
|
||||
raise gdb.GdbError("Unable to find MODULE " + mod_name)
|
||||
|
||||
|
||||
LxModule()
|
||||
|
||||
|
||||
class LxLsmod(gdb.Command):
|
||||
"""List currently loaded modules."""
|
||||
|
||||
_module_use_type = utils.CachedType("struct module_use")
|
||||
|
||||
def __init__(self):
|
||||
super(LxLsmod, self).__init__("lx-lsmod", gdb.COMMAND_DATA)
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
gdb.write(
|
||||
"Address{0} Module Size Used by\n".format(
|
||||
" " if utils.get_long_type().sizeof == 8 else ""))
|
||||
|
||||
for module in module_list():
|
||||
ref = 0
|
||||
module_refptr = module['refptr']
|
||||
for cpu in cpus.cpu_list("cpu_possible_mask"):
|
||||
refptr = cpus.per_cpu(module_refptr, cpu)
|
||||
ref += refptr['incs']
|
||||
ref -= refptr['decs']
|
||||
|
||||
gdb.write("{address} {name:<19} {size:>8} {ref}".format(
|
||||
address=str(module['module_core']).split()[0],
|
||||
name=module['name'].string(),
|
||||
size=str(module['core_size']),
|
||||
ref=str(ref)))
|
||||
|
||||
source_list = module['source_list']
|
||||
t = self._module_use_type.get_type().pointer()
|
||||
entry = source_list['next']
|
||||
first = True
|
||||
while entry != source_list.address:
|
||||
use = utils.container_of(entry, t, "source_list")
|
||||
gdb.write("{separator}{name}".format(
|
||||
separator=" " if first else ",",
|
||||
name=use['source']['name'].string()))
|
||||
first = False
|
||||
entry = entry['next']
|
||||
gdb.write("\n")
|
||||
|
||||
|
||||
LxLsmod()
|
177
scripts/gdb/linux/symbols.py
Normal file
177
scripts/gdb/linux/symbols.py
Normal file
@ -0,0 +1,177 @@
|
||||
#
|
||||
# gdb helper commands and functions for Linux kernel debugging
|
||||
#
|
||||
# load kernel and module symbols
|
||||
#
|
||||
# Copyright (c) Siemens AG, 2011-2013
|
||||
#
|
||||
# Authors:
|
||||
# Jan Kiszka <jan.kiszka@siemens.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL version 2.
|
||||
#
|
||||
|
||||
import gdb
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
|
||||
from linux import modules, utils
|
||||
|
||||
|
||||
if hasattr(gdb, 'Breakpoint'):
|
||||
class LoadModuleBreakpoint(gdb.Breakpoint):
|
||||
def __init__(self, spec, gdb_command):
|
||||
super(LoadModuleBreakpoint, self).__init__(spec, internal=True)
|
||||
self.silent = True
|
||||
self.gdb_command = gdb_command
|
||||
|
||||
def stop(self):
|
||||
module = gdb.parse_and_eval("mod")
|
||||
module_name = module['name'].string()
|
||||
cmd = self.gdb_command
|
||||
|
||||
# enforce update if object file is not found
|
||||
cmd.module_files_updated = False
|
||||
|
||||
# Disable pagination while reporting symbol (re-)loading.
|
||||
# The console input is blocked in this context so that we would
|
||||
# get stuck waiting for the user to acknowledge paged output.
|
||||
show_pagination = gdb.execute("show pagination", to_string=True)
|
||||
pagination = show_pagination.endswith("on.\n")
|
||||
gdb.execute("set pagination off")
|
||||
|
||||
if module_name in cmd.loaded_modules:
|
||||
gdb.write("refreshing all symbols to reload module "
|
||||
"'{0}'\n".format(module_name))
|
||||
cmd.load_all_symbols()
|
||||
else:
|
||||
cmd.load_module_symbols(module)
|
||||
|
||||
# restore pagination state
|
||||
gdb.execute("set pagination %s" % ("on" if pagination else "off"))
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class LxSymbols(gdb.Command):
|
||||
"""(Re-)load symbols of Linux kernel and currently loaded modules.
|
||||
|
||||
The kernel (vmlinux) is taken from the current working directly. Modules (.ko)
|
||||
are scanned recursively, starting in the same directory. Optionally, the module
|
||||
search path can be extended by a space separated list of paths passed to the
|
||||
lx-symbols command."""
|
||||
|
||||
module_paths = []
|
||||
module_files = []
|
||||
module_files_updated = False
|
||||
loaded_modules = []
|
||||
breakpoint = None
|
||||
|
||||
def __init__(self):
|
||||
super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES,
|
||||
gdb.COMPLETE_FILENAME)
|
||||
|
||||
def _update_module_files(self):
|
||||
self.module_files = []
|
||||
for path in self.module_paths:
|
||||
gdb.write("scanning for modules in {0}\n".format(path))
|
||||
for root, dirs, files in os.walk(path):
|
||||
for name in files:
|
||||
if name.endswith(".ko"):
|
||||
self.module_files.append(root + "/" + name)
|
||||
self.module_files_updated = True
|
||||
|
||||
def _get_module_file(self, module_name):
|
||||
module_pattern = ".*/{0}\.ko$".format(
|
||||
module_name.replace("_", r"[_\-]"))
|
||||
for name in self.module_files:
|
||||
if re.match(module_pattern, name) and os.path.exists(name):
|
||||
return name
|
||||
return None
|
||||
|
||||
def _section_arguments(self, module):
|
||||
try:
|
||||
sect_attrs = module['sect_attrs'].dereference()
|
||||
except gdb.error:
|
||||
return ""
|
||||
attrs = sect_attrs['attrs']
|
||||
section_name_to_address = {
|
||||
attrs[n]['name'].string() : attrs[n]['address']
|
||||
for n in range(int(sect_attrs['nsections']))}
|
||||
args = []
|
||||
for section_name in [".data", ".data..read_mostly", ".rodata", ".bss"]:
|
||||
address = section_name_to_address.get(section_name)
|
||||
if address:
|
||||
args.append(" -s {name} {addr}".format(
|
||||
name=section_name, addr=str(address)))
|
||||
return "".join(args)
|
||||
|
||||
def load_module_symbols(self, module):
|
||||
module_name = module['name'].string()
|
||||
module_addr = str(module['module_core']).split()[0]
|
||||
|
||||
module_file = self._get_module_file(module_name)
|
||||
if not module_file and not self.module_files_updated:
|
||||
self._update_module_files()
|
||||
module_file = self._get_module_file(module_name)
|
||||
|
||||
if module_file:
|
||||
gdb.write("loading @{addr}: {filename}\n".format(
|
||||
addr=module_addr, filename=module_file))
|
||||
cmdline = "add-symbol-file {filename} {addr}{sections}".format(
|
||||
filename=module_file,
|
||||
addr=module_addr,
|
||||
sections=self._section_arguments(module))
|
||||
gdb.execute(cmdline, to_string=True)
|
||||
if not module_name in self.loaded_modules:
|
||||
self.loaded_modules.append(module_name)
|
||||
else:
|
||||
gdb.write("no module object found for '{0}'\n".format(module_name))
|
||||
|
||||
def load_all_symbols(self):
|
||||
gdb.write("loading vmlinux\n")
|
||||
|
||||
# Dropping symbols will disable all breakpoints. So save their states
|
||||
# and restore them afterward.
|
||||
saved_states = []
|
||||
if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None:
|
||||
for bp in gdb.breakpoints():
|
||||
saved_states.append({'breakpoint': bp, 'enabled': bp.enabled})
|
||||
|
||||
# drop all current symbols and reload vmlinux
|
||||
gdb.execute("symbol-file", to_string=True)
|
||||
gdb.execute("symbol-file vmlinux")
|
||||
|
||||
self.loaded_modules = []
|
||||
module_list = modules.module_list()
|
||||
if not module_list:
|
||||
gdb.write("no modules found\n")
|
||||
else:
|
||||
[self.load_module_symbols(module) for module in module_list]
|
||||
|
||||
for saved_state in saved_states:
|
||||
saved_state['breakpoint'].enabled = saved_state['enabled']
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
self.module_paths = arg.split()
|
||||
self.module_paths.append(os.getcwd())
|
||||
|
||||
# enforce update
|
||||
self.module_files = []
|
||||
self.module_files_updated = False
|
||||
|
||||
self.load_all_symbols()
|
||||
|
||||
if hasattr(gdb, 'Breakpoint'):
|
||||
if not self.breakpoint is None:
|
||||
self.breakpoint.delete()
|
||||
self.breakpoint = None
|
||||
self.breakpoint = LoadModuleBreakpoint(
|
||||
"kernel/module.c:do_init_module", self)
|
||||
else:
|
||||
gdb.write("Note: symbol update on module loading not supported "
|
||||
"with this gdb version\n")
|
||||
|
||||
|
||||
LxSymbols()
|
100
scripts/gdb/linux/tasks.py
Normal file
100
scripts/gdb/linux/tasks.py
Normal file
@ -0,0 +1,100 @@
|
||||
#
|
||||
# gdb helper commands and functions for Linux kernel debugging
|
||||
#
|
||||
# task & thread tools
|
||||
#
|
||||
# Copyright (c) Siemens AG, 2011-2013
|
||||
#
|
||||
# Authors:
|
||||
# Jan Kiszka <jan.kiszka@siemens.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL version 2.
|
||||
#
|
||||
|
||||
import gdb
|
||||
|
||||
from linux import utils
|
||||
|
||||
|
||||
task_type = utils.CachedType("struct task_struct")
|
||||
|
||||
def task_lists():
|
||||
global task_type
|
||||
task_ptr_type = task_type.get_type().pointer()
|
||||
init_task = gdb.parse_and_eval("init_task").address
|
||||
t = g = init_task
|
||||
|
||||
while True:
|
||||
while True:
|
||||
yield t
|
||||
|
||||
t = utils.container_of(t['thread_group']['next'],
|
||||
task_ptr_type, "thread_group")
|
||||
if t == g:
|
||||
break
|
||||
|
||||
t = g = utils.container_of(g['tasks']['next'],
|
||||
task_ptr_type, "tasks")
|
||||
if t == init_task:
|
||||
return
|
||||
|
||||
def get_task_by_pid(pid):
|
||||
for task in task_lists():
|
||||
if int(task['pid']) == pid:
|
||||
return task
|
||||
return None
|
||||
|
||||
|
||||
class LxTaskByPidFunc(gdb.Function):
|
||||
"""Find Linux task by PID and return the task_struct variable.
|
||||
|
||||
$lx_task_by_pid(PID): Given PID, iterate over all tasks of the target and
|
||||
return that task_struct variable which PID matches."""
|
||||
|
||||
def __init__(self):
|
||||
super(LxTaskByPidFunc, self).__init__("lx_task_by_pid")
|
||||
|
||||
def invoke(self, pid):
|
||||
task = get_task_by_pid(pid)
|
||||
if task:
|
||||
return task.dereference()
|
||||
else:
|
||||
raise gdb.GdbError("No task of PID " + str(pid))
|
||||
|
||||
|
||||
LxTaskByPidFunc()
|
||||
|
||||
|
||||
thread_info_type = utils.CachedType("struct thread_info")
|
||||
|
||||
ia64_task_size = None
|
||||
|
||||
|
||||
def get_thread_info(task):
|
||||
global thread_info_type
|
||||
thread_info_ptr_type = thread_info_type.get_type().pointer()
|
||||
if utils.is_target_arch("ia64"):
|
||||
global ia64_task_size
|
||||
if ia64_task_size is None:
|
||||
ia64_task_size = gdb.parse_and_eval("sizeof(struct task_struct)")
|
||||
thread_info_addr = task.address + ia64_task_size
|
||||
thread_info = thread_info_addr.cast(thread_info_ptr_type)
|
||||
else:
|
||||
thread_info = task['stack'].cast(thread_info_ptr_type)
|
||||
return thread_info.dereference()
|
||||
|
||||
|
||||
class LxThreadInfoFunc (gdb.Function):
|
||||
"""Calculate Linux thread_info from task variable.
|
||||
|
||||
$lx_thread_info(TASK): Given TASK, return the corresponding thread_info
|
||||
variable."""
|
||||
|
||||
def __init__(self):
|
||||
super(LxThreadInfoFunc, self).__init__("lx_thread_info")
|
||||
|
||||
def invoke(self, task):
|
||||
return get_thread_info(task)
|
||||
|
||||
|
||||
LxThreadInfoFunc()
|
156
scripts/gdb/linux/utils.py
Normal file
156
scripts/gdb/linux/utils.py
Normal file
@ -0,0 +1,156 @@
|
||||
#
|
||||
# gdb helper commands and functions for Linux kernel debugging
|
||||
#
|
||||
# common utilities
|
||||
#
|
||||
# Copyright (c) Siemens AG, 2011-2013
|
||||
#
|
||||
# Authors:
|
||||
# Jan Kiszka <jan.kiszka@siemens.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL version 2.
|
||||
#
|
||||
|
||||
import gdb
|
||||
|
||||
|
||||
class CachedType:
|
||||
def __init__(self, name):
|
||||
self._type = None
|
||||
self._name = name
|
||||
|
||||
def _new_objfile_handler(self, event):
|
||||
self._type = None
|
||||
gdb.events.new_objfile.disconnect(self._new_objfile_handler)
|
||||
|
||||
def get_type(self):
|
||||
if self._type is None:
|
||||
self._type = gdb.lookup_type(self._name)
|
||||
if self._type is None:
|
||||
raise gdb.GdbError(
|
||||
"cannot resolve type '{0}'".format(self._name))
|
||||
if hasattr(gdb, 'events') and hasattr(gdb.events, 'new_objfile'):
|
||||
gdb.events.new_objfile.connect(self._new_objfile_handler)
|
||||
return self._type
|
||||
|
||||
|
||||
long_type = CachedType("long")
|
||||
|
||||
|
||||
def get_long_type():
|
||||
global long_type
|
||||
return long_type.get_type()
|
||||
|
||||
|
||||
def offset_of(typeobj, field):
|
||||
element = gdb.Value(0).cast(typeobj)
|
||||
return int(str(element[field].address).split()[0], 16)
|
||||
|
||||
|
||||
def container_of(ptr, typeobj, member):
|
||||
return (ptr.cast(get_long_type()) -
|
||||
offset_of(typeobj, member)).cast(typeobj)
|
||||
|
||||
|
||||
class ContainerOf(gdb.Function):
|
||||
"""Return pointer to containing data structure.
|
||||
|
||||
$container_of(PTR, "TYPE", "ELEMENT"): Given PTR, return a pointer to the
|
||||
data structure of the type TYPE in which PTR is the address of ELEMENT.
|
||||
Note that TYPE and ELEMENT have to be quoted as strings."""
|
||||
|
||||
def __init__(self):
|
||||
super(ContainerOf, self).__init__("container_of")
|
||||
|
||||
def invoke(self, ptr, typename, elementname):
|
||||
return container_of(ptr, gdb.lookup_type(typename.string()).pointer(),
|
||||
elementname.string())
|
||||
|
||||
ContainerOf()
|
||||
|
||||
|
||||
BIG_ENDIAN = 0
|
||||
LITTLE_ENDIAN = 1
|
||||
target_endianness = None
|
||||
|
||||
|
||||
def get_target_endianness():
|
||||
global target_endianness
|
||||
if target_endianness is None:
|
||||
endian = gdb.execute("show endian", to_string=True)
|
||||
if "little endian" in endian:
|
||||
target_endianness = LITTLE_ENDIAN
|
||||
elif "big endian" in endian:
|
||||
target_endianness = BIG_ENDIAN
|
||||
else:
|
||||
raise gdb.GdgError("unknown endianness '{0}'".format(str(endian)))
|
||||
return target_endianness
|
||||
|
||||
|
||||
def read_u16(buffer):
|
||||
if get_target_endianness() == LITTLE_ENDIAN:
|
||||
return ord(buffer[0]) + (ord(buffer[1]) << 8)
|
||||
else:
|
||||
return ord(buffer[1]) + (ord(buffer[0]) << 8)
|
||||
|
||||
|
||||
def read_u32(buffer):
|
||||
if get_target_endianness() == LITTLE_ENDIAN:
|
||||
return read_u16(buffer[0:2]) + (read_u16(buffer[2:4]) << 16)
|
||||
else:
|
||||
return read_u16(buffer[2:4]) + (read_u16(buffer[0:2]) << 16)
|
||||
|
||||
|
||||
def read_u64(buffer):
|
||||
if get_target_endianness() == LITTLE_ENDIAN:
|
||||
return read_u32(buffer[0:4]) + (read_u32(buffer[4:8]) << 32)
|
||||
else:
|
||||
return read_u32(buffer[4:8]) + (read_u32(buffer[0:4]) << 32)
|
||||
|
||||
|
||||
target_arch = None
|
||||
|
||||
|
||||
def is_target_arch(arch):
|
||||
if hasattr(gdb.Frame, 'architecture'):
|
||||
return arch in gdb.newest_frame().architecture().name()
|
||||
else:
|
||||
global target_arch
|
||||
if target_arch is None:
|
||||
target_arch = gdb.execute("show architecture", to_string=True)
|
||||
return arch in target_arch
|
||||
|
||||
|
||||
GDBSERVER_QEMU = 0
|
||||
GDBSERVER_KGDB = 1
|
||||
gdbserver_type = None
|
||||
|
||||
|
||||
def get_gdbserver_type():
|
||||
def exit_handler(event):
|
||||
global gdbserver_type
|
||||
gdbserver_type = None
|
||||
gdb.events.exited.disconnect(exit_handler)
|
||||
|
||||
def probe_qemu():
|
||||
try:
|
||||
return gdb.execute("monitor info version", to_string=True) != ""
|
||||
except:
|
||||
return False
|
||||
|
||||
def probe_kgdb():
|
||||
try:
|
||||
thread_info = gdb.execute("info thread 2", to_string=True)
|
||||
return "shadowCPU0" in thread_info
|
||||
except:
|
||||
return False
|
||||
|
||||
global gdbserver_type
|
||||
if gdbserver_type is None:
|
||||
if probe_qemu():
|
||||
gdbserver_type = GDBSERVER_QEMU
|
||||
elif probe_kgdb():
|
||||
gdbserver_type = GDBSERVER_KGDB
|
||||
if not gdbserver_type is None and hasattr(gdb, 'events'):
|
||||
gdb.events.exited.connect(exit_handler)
|
||||
return gdbserver_type
|
30
scripts/gdb/vmlinux-gdb.py
Normal file
30
scripts/gdb/vmlinux-gdb.py
Normal file
@ -0,0 +1,30 @@
|
||||
#
|
||||
# gdb helper commands and functions for Linux kernel debugging
|
||||
#
|
||||
# loader module
|
||||
#
|
||||
# Copyright (c) Siemens AG, 2012, 2013
|
||||
#
|
||||
# Authors:
|
||||
# Jan Kiszka <jan.kiszka@siemens.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL version 2.
|
||||
#
|
||||
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__) + "/scripts/gdb")
|
||||
|
||||
try:
|
||||
gdb.parse_and_eval("0")
|
||||
gdb.execute("", to_string=True)
|
||||
except:
|
||||
gdb.write("NOTE: gdb 7.2 or later required for Linux helper scripts to "
|
||||
"work.\n")
|
||||
else:
|
||||
import linux.utils
|
||||
import linux.symbols
|
||||
import linux.modules
|
||||
import linux.dmesg
|
||||
import linux.tasks
|
||||
import linux.cpus
|
Loading…
Reference in New Issue
Block a user